/*==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 .
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 "hsTypes.h"
#include "max.h"
//#include "mtlhdr.h"
#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; iIlluminate(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&&diffCoef0.0f))
R = sc.ReflectVector();
for (int i=0; iIlluminate(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; iIlluminate(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&&diffCoef0.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; iIlluminate(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 = (NV0.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);
}