|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include "hsWindows.h"
|
|
|
|
#include <max.h>
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#include "Shaders.h"
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// Useful variables...
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
static PhongShader phongShader;
|
|
|
|
static BlinnShader blinnShader;
|
|
|
|
static MetalShader metalShader;
|
|
|
|
static hsMaxShader plasmaShader;
|
|
|
|
|
|
|
|
static Shader *shaders[3] = {
|
|
|
|
&phongShader,
|
|
|
|
&metalShader,
|
|
|
|
&blinnShader,
|
|
|
|
};
|
|
|
|
|
|
|
|
AColor black(0.0f,0.0f,0.0f,0.0f);
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// Useful functions...
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
Shader *GetShader(int s) { return shaders[s]; };
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
// Quadratic
|
|
|
|
static inline float Soften(float r) {
|
|
|
|
return r*(2.0f-r);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
// Cubic
|
|
|
|
static inline float Soften(float r) {
|
|
|
|
return r*r*(3.0f-2.0f*r);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
float CompK(float f0) {
|
|
|
|
return float(2.0*sqrt(f0)/sqrt(1.0-f0));
|
|
|
|
}
|
|
|
|
|
|
|
|
float fres_metal(float c, float k) {
|
|
|
|
float b,rpl, rpp,c2;
|
|
|
|
b = k*k + 1.0f;
|
|
|
|
c2 = c*c;
|
|
|
|
rpl = (b*c2-2*c+1)/(b*c2+2*c+1);
|
|
|
|
rpp = (b-2*c+c2)/(b+2*c+c2);
|
|
|
|
return (.5f*(rpl+rpp));
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// Phong shader... don't know if this is ever going to be used
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void PhongShader::Illum(ShadeContext &sc, SIllumParams &ip) {
|
|
|
|
LightDesc *l;
|
|
|
|
Color lightCol;
|
|
|
|
BOOL is_shiny;
|
|
|
|
Point3 R;
|
|
|
|
if (is_shiny=(ip.sh_str>0.0f))
|
|
|
|
R = sc.ReflectVector();
|
|
|
|
|
|
|
|
for (int i=0; i<sc.nLights; i++) {
|
|
|
|
l = sc.Light(i);
|
|
|
|
register float NL, diffCoef;
|
|
|
|
Point3 L;
|
|
|
|
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) {
|
|
|
|
// diffuse
|
|
|
|
if (NL<=0.0f)
|
|
|
|
continue;
|
|
|
|
if (l->affectDiffuse)
|
|
|
|
ip.diffIllum += diffCoef*lightCol;
|
|
|
|
if (is_shiny&&l->affectSpecular) {
|
|
|
|
// specular (Phong)
|
|
|
|
float c = DotProd(L,R);
|
|
|
|
if (c>0.0f) {
|
|
|
|
if (ip.softThresh!=0.0&&diffCoef<ip.softThresh) {
|
|
|
|
float r = diffCoef/ip.softThresh;
|
|
|
|
c *= Soften(r);
|
|
|
|
}
|
|
|
|
c = (float)pow((double)c, (double)ip.ph_exp); // could use table lookup for speed
|
|
|
|
ip.specIllum += c*ip.sh_str*lightCol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip.specIllum *= ip.spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// hsMax shader... write this?
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void hsMaxShader::Illum(ShadeContext &sc, SIllumParams &ip) {
|
|
|
|
LightDesc *l;
|
|
|
|
Color lightCol;
|
|
|
|
BOOL is_shiny;
|
|
|
|
Point3 R;
|
|
|
|
if (is_shiny=(ip.sh_str>0.0f))
|
|
|
|
R = sc.ReflectVector();
|
|
|
|
|
|
|
|
for (int i=0; i<sc.nLights; i++) {
|
|
|
|
l = sc.Light(i);
|
|
|
|
register float NL, diffCoef;
|
|
|
|
Point3 L;
|
|
|
|
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) {
|
|
|
|
// diffuse
|
|
|
|
if (NL<=0.0f)
|
|
|
|
continue;
|
|
|
|
if (l->affectDiffuse)
|
|
|
|
ip.diffIllum += diffCoef*lightCol;
|
|
|
|
if (is_shiny&&l->affectSpecular) {
|
|
|
|
// specular
|
|
|
|
float c = DotProd(L,R);
|
|
|
|
if (c>0.0f) {
|
|
|
|
c = (float)pow((double)c, (double)ip.ph_exp); // could use table lookup for speed
|
|
|
|
ip.specIllum += c*ip.sh_str*lightCol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip.specIllum *= ip.spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// Blinn shader... don't know if this is ever going to be used
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void BlinnShader::Illum(ShadeContext &sc, SIllumParams &ip) {
|
|
|
|
LightDesc *l;
|
|
|
|
Color lightCol;
|
|
|
|
|
|
|
|
// Blinn style phong
|
|
|
|
BOOL is_shiny=(ip.sh_str>0.0f)?1:0;
|
|
|
|
double ph_exp = double(ip.ph_exp)*4.0; // This is to make the hilite compatible with normal phong
|
|
|
|
for (int i=0; i<sc.nLights; i++) {
|
|
|
|
l = sc.Light(i);
|
|
|
|
register float NL, diffCoef;
|
|
|
|
Point3 L;
|
|
|
|
if (l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef)) {
|
|
|
|
// diffuse
|
|
|
|
if (NL<=0.0f)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (l->affectDiffuse)
|
|
|
|
ip.diffIllum += diffCoef*lightCol;
|
|
|
|
|
|
|
|
// specular (Phong)
|
|
|
|
if (is_shiny&&l->affectSpecular) {
|
|
|
|
Point3 H = FNormalize(L-ip.V);
|
|
|
|
float c = DotProd(ip.N,H);
|
|
|
|
if (c>0.0f) {
|
|
|
|
if (ip.softThresh!=0.0&&diffCoef<ip.softThresh) {
|
|
|
|
c *= Soften(diffCoef/ip.softThresh);
|
|
|
|
}
|
|
|
|
c = (float)pow((double)c, ph_exp); // could use table lookup for speed
|
|
|
|
ip.specIllum += c*ip.sh_str*lightCol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip.specIllum *= ip.spec;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
// Metal shader... don't know if this is ever going to be used
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
void MetalShader::Illum(ShadeContext &sc, SIllumParams &ip) {
|
|
|
|
LightDesc *l;
|
|
|
|
Color lightCol;
|
|
|
|
BOOL gotKav = FALSE;
|
|
|
|
float kav, fav0, m2inv,NV;
|
|
|
|
|
|
|
|
//IPoint2 sp = sc.ScreenCoord();
|
|
|
|
|
|
|
|
BOOL is_shiny;
|
|
|
|
if (ip.sh_str>0.0f) {
|
|
|
|
NV = -DotProd(ip.N,ip.V); // N dot V: view vector is TOWARDS us.
|
|
|
|
is_shiny = 1;
|
|
|
|
float r = 1.0f-ip.shine;
|
|
|
|
if (r==0.0f) r = .00001f;
|
|
|
|
m2inv = 1.0f/(r*r);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
is_shiny = 0;
|
|
|
|
|
|
|
|
|
|
|
|
for (int i=0; i<sc.nLights; i++) {
|
|
|
|
l = sc.Light(i);
|
|
|
|
register float NL, diffCoef;
|
|
|
|
Point3 L;
|
|
|
|
|
|
|
|
if (!l->Illuminate(sc,ip.N,lightCol,L,NL,diffCoef))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// diffuse
|
|
|
|
if (NL>0.0f&&l->affectDiffuse) // TBD is the NL test necessary?
|
|
|
|
ip.diffIllum += diffCoef*lightCol;
|
|
|
|
|
|
|
|
if (is_shiny&&l->affectSpecular) { // SPECULAR
|
|
|
|
Color fcol;
|
|
|
|
float LH,NH,VH;
|
|
|
|
float sec2; // Was double?? TBD
|
|
|
|
Point3 H;
|
|
|
|
|
|
|
|
if (NV<0.0f) continue;
|
|
|
|
|
|
|
|
H = FNormalize(L-ip.V);
|
|
|
|
|
|
|
|
LH = DotProd(L,H); // cos(phi)
|
|
|
|
NH = DotProd(ip.N,H); // cos(alpha)
|
|
|
|
if (NH==0.0f) continue;
|
|
|
|
VH = -DotProd(ip.V,H);
|
|
|
|
|
|
|
|
// compute geometrical attenuation factor
|
|
|
|
float G = (NV<NL)? (2.0f*NV*NH/VH): (2.0f*NL*NH/VH);
|
|
|
|
if (G>0.0f) {
|
|
|
|
// Compute (approximate) indices of refraction
|
|
|
|
// this can be factored out for non-texture-mapped mtls
|
|
|
|
if (!gotKav) {
|
|
|
|
fav0 = Intens(ip.diff);
|
|
|
|
if (fav0>=1.0f) fav0 = .9999f;
|
|
|
|
kav = CompK(fav0);
|
|
|
|
gotKav = TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
float fav = fres_metal(LH,kav);
|
|
|
|
float t = (fav-fav0)/(1.0f-fav0);
|
|
|
|
fcol = (1.0f-t)*ip.diff + Color(t,t,t);
|
|
|
|
|
|
|
|
// Beckman distribution (from Cook-Torrance paper)
|
|
|
|
sec2 = 1.0f/(NH*NH); // 1/sqr(cos)
|
|
|
|
float D = (.5f/PI)*sec2*sec2*m2inv*(float)exp((1.0f-sec2)*m2inv);
|
|
|
|
if (G>1.0f) G = 1.0f;
|
|
|
|
float Rs = ip.sh_str*D*G/(NV+.05f);
|
|
|
|
ip.specIllum += fcol*Rs*lightCol;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ip.diffIllum *= 1.0f - ip.sh_str;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void MetalShader::SetShininess(float shininess, float shineStr) {
|
|
|
|
float r = 1.0f-shininess;
|
|
|
|
if (r==0.0f) r = .00001f;
|
|
|
|
fm2inv = 1.0f/(r*r);
|
|
|
|
fshin_str = shineStr;
|
|
|
|
}
|
|
|
|
|
|
|
|
float MetalShader::EvalHilite(float x) {
|
|
|
|
float c = (float)cos(x*PI);
|
|
|
|
float sec2 = 1.0f/(c*c); /* 1/sqr(cos) */
|
|
|
|
return fshin_str*(.5f/PI)*sec2*sec2*fm2inv*(float)exp((1.0f-sec2)*fm2inv);
|
|
|
|
}
|
|
|
|
|