#include "ToonMaterial.h"
#include <QtCore/QDir>
MB_PLUGIN( "ToonMaterial", "Toon material", "Autodesk", "http://www.mudbox3d.com", 0 );
IMPLEMENT_CLASS( ToonMaterial, Material, "Toon Material" );
static float diffuseRamp(float x);
static float specularRamp(float x);
static float edgeRamp(float x);
static void loadRamp(GLuint texobj, int size, float (*func)(float x));
ToonMaterial::ToonMaterial( void ) :
m_aKd(this, "Kd"),
m_aKs(this, "Ks"),
m_aShininess(this, "Shiny")
{
SetName( "Toon Material" );
m_CGContext = cgCreateContext();
cgGLSetDebugMode( CG_FALSE );
cgGLSetManageTextureParameters(m_CGContext, CG_TRUE);
cgSetParameterSettingMode(m_CGContext, CG_DEFERRED_PARAMETER_SETTING);
m_VertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
cgGLSetOptimalOptions(m_VertexProfile);
QDir pluginDir( Kernel()->PluginDirectory("ToonMaterial") );
QFileInfo vertexPath( pluginDir, QString("toon_vertex.cg") );
QFileInfo fragmentPath( pluginDir, QString("toon_fragment.cg") );
QByteArray qbaVertexPath = QFile::encodeName(vertexPath.filePath());
m_VertexProgram =
cgCreateProgramFromFile(
m_CGContext,
CG_SOURCE,
qbaVertexPath.constData(),
m_VertexProfile,
"main",
NULL);
cgGLLoadProgram(m_VertexProgram);
m_ModelViewProjParam = cgGetNamedParameter(m_VertexProgram, "modelViewProj");
m_LightPositionParam = cgGetNamedParameter(m_VertexProgram, "lightPosition");
m_EyePositionParam = cgGetNamedParameter(m_VertexProgram, "eyePosition");
m_ShininessParam = cgGetNamedParameter(m_VertexProgram, "shininess");
m_FragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
cgGLSetOptimalOptions(m_FragmentProfile);
QByteArray qbaFragmentPath = QFile::encodeName(fragmentPath.filePath());
m_FragmentProgram =
cgCreateProgramFromFile(
m_CGContext,
CG_SOURCE,
qbaFragmentPath.constData(),
m_FragmentProfile,
"main",
NULL);
cgGLLoadProgram(m_FragmentProgram);
m_KdParam = cgGetNamedParameter(m_FragmentProgram, "Kd");
m_KsParam = cgGetNamedParameter(m_FragmentProgram, "Ks");
m_DiffuseRampParam = cgGetNamedParameter(m_FragmentProgram, "diffuseRamp");
m_SpecularRampParam = cgGetNamedParameter(m_FragmentProgram, "specularRamp");
m_EdgeRampParam = cgGetNamedParameter(m_FragmentProgram, "edgeRamp");
glGenTextures(1, &m_iDiffuseRamp);
loadRamp(m_iDiffuseRamp, 255, diffuseRamp);
cgGLSetTextureParameter(m_DiffuseRampParam, m_iDiffuseRamp);
glGenTextures(1, &m_iSpecularRamp);
loadRamp(m_iSpecularRamp, 255, specularRamp);
cgGLSetTextureParameter(m_SpecularRampParam, m_iSpecularRamp);
glGenTextures(1, &m_iEdgeRamp);
loadRamp(m_iEdgeRamp, 255, edgeRamp);
cgGLSetTextureParameter(m_EdgeRampParam, m_iEdgeRamp);
m_aKd = Color(0.8f, 0.6f, 0.2f, 1.0f);
m_aKs = Color(0.3f, 0.3f, 4.0f, 0.0f);
m_aShininess.SetMax(25);
m_aShininess.SetMin(1);
m_aShininess = 5.2f;
};
ToonMaterial::~ToonMaterial( void )
{
cgDestroyContext(m_CGContext);
glDeleteTextures(1, &m_iDiffuseRamp);
glDeleteTextures(1, &m_iSpecularRamp);
glDeleteTextures(1, &m_iEdgeRamp);
};
bool ToonMaterial::Activate( const Mesh *pMesh, const AxisAlignedBoundingBox &cUVArea, const Color &cColor )
{
if (!pMesh)
return false;
cgGLEnableProfile(m_VertexProfile);
cgGLEnableProfile(m_FragmentProfile);
cgGLBindProgram(m_VertexProgram);
cgGLBindProgram(m_FragmentProgram);
cgSetParameter4fv(m_KdParam, m_aKd.Value());
cgSetParameter4fv(m_KsParam, m_aKs.Value());
cgSetParameter1f(m_ShininessParam, m_aShininess.Value());
if (Kernel()->Scene()->LightCount())
{
Light *pLight = Kernel()->Scene()->Light(0);
Vector v(0,0,1);
if (pLight->Type() == Light::LIGHT_POINT)
{
v = pLight->Transformation()->Position();
}
else if (pLight->Type() == Light::LIGHT_DIRECTIONAL)
{
if (pLight->IsLockedToCamera())
v = pLight->LockedToCameraMatrix().Transform(v);
else
v = pLight->Transformation()->WorldToLocalMatrix().Invert().Transform(v);
v.Normalize();
v*=100;
}
cgSetParameter3fv(m_LightPositionParam, v);
}
cgSetParameter3fv(m_EyePositionParam, Kernel()->Scene()->ActiveCamera()->Position());
Matrix mModelViewProj = pMesh->Geometry()->Transformation()->LocalToWorldMatrix()*Kernel()->Scene()->ActiveCamera()->Matrix(true);
cgSetParameterValuefc(m_ModelViewProjParam, 16, mModelViewProj);
cgUpdateProgramParameters(m_VertexProgram);
return true;
};
void ToonMaterial::Deactivate( void )
{
cgGLDisableProfile(m_VertexProfile);
cgGLDisableProfile(m_FragmentProfile);
};
void ToonMaterial::OnNodeEvent( const Attribute &cAttribute, NodeEventType cType )
{
bool bRedraw = false;
if(cType == etValueChanged)
{
if (cAttribute == m_aKs ||
cAttribute == m_aKd ||
cAttribute == m_aShininess)
bRedraw = true;
}
Material::OnNodeEvent( cAttribute, cType );
if (bRedraw)
Kernel()->Redraw();
};
float diffuseRamp(float x)
{
if (x > 0.5) {
return x*x*(3-2*x);
} else {
return 0.5f;
}
}
float specularRamp(float x)
{
if (x > 0.2f) {
return x;
} else {
return 0.0f;
}
}
float edgeRamp(float x)
{
if (x < 0.2f) {
return 1.0f;
} else {
return 0.85f;
}
}
void loadRamp(GLuint texobj, int size, float (*func)(float x))
{
int bytesForRamp = size*sizeof(float);
float *ramp = (float *) malloc(bytesForRamp);
float *slot = ramp;
float dx = 1.0f / (float) size;
float x;
int i;
for (i=0, x=0.0, slot=ramp; i<size; i++, x += dx, slot++) {
float v = func(x);
*slot = v;
}
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
glBindTexture(GL_TEXTURE_1D, texobj);
glTexImage1D(GL_TEXTURE_1D, 0, GL_INTENSITY16, size, 0, GL_LUMINANCE, GL_FLOAT, ramp);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}