ToonMaterial/ToonMaterial.cpp

ToonMaterial/ToonMaterial.cpp
//**************************************************************************/
// Copyright (c) 2008 Autodesk, Inc.
// All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk license
// agreement provided at the time of installation or download, or which
// otherwise accompanies this software in either electronic or hard copy form.
//
//**************************************************************************/
// DESCRIPTION:
// CREATED: October 2008
//**************************************************************************/
#include "ToonMaterial.h"
#include <QtCore/QDir>
// NOTE : This plugin does not have any exception handling. This was done
// for brevity reasons.
// Plugin registration macros
MB_PLUGIN( "ToonMaterial", "Toon material", "Autodesk", "http://www.mudbox3d.com", 0 );
IMPLEMENT_CLASS( ToonMaterial, Material, "Toon Material" );
// forward declarations
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();
/* Compile and load the vertex program. */
m_VertexProfile = cgGLGetLatestProfile(CG_GL_VERTEX);
cgGLSetOptimalOptions(m_VertexProfile);
// Get the path to where the plug-in was loaded from
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 =
m_CGContext, /* Cg runtime context */
CG_SOURCE, /* Program in human-readable form */
qbaVertexPath.constData(), /* Name of file containing program */
m_VertexProfile, /* Profile: OpenGL ARB vertex program */
"main", /* Entry function name */
NULL); /* No extra commyPiler options */
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);
/* Compile and load the fragment program. */
QByteArray qbaFragmentPath = QFile::encodeName(fragmentPath.filePath());
m_FragmentProgram =
m_CGContext, /* Cg runtime context */
CG_SOURCE, /* Program in human-readable form */
qbaFragmentPath.constData(), /* Name of file containing program */
m_FragmentProfile, /* Profile: OpenGL ARB vertex program */
"main", /* Entry function name */
NULL); /* No extra compiler options */
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); // m_iDiffuseRamp = 1;
loadRamp(m_iDiffuseRamp, 255, diffuseRamp);
cgGLSetTextureParameter(m_DiffuseRampParam, m_iDiffuseRamp);
glGenTextures(1, &m_iSpecularRamp); // m_iSpecularRamp = 2;
loadRamp(m_iSpecularRamp, 255, specularRamp);
cgGLSetTextureParameter(m_SpecularRampParam, m_iSpecularRamp);
glGenTextures(1, &m_iEdgeRamp); // m_iEdgeRamp = 3;
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);
// Set min & max of shininess slider.
m_aShininess.SetMax(25);
m_aShininess.SetMin(1);
m_aShininess = 5.2f;
};
ToonMaterial::~ToonMaterial( void )
{
// This destroys all programs as well
cgDestroyContext(m_CGContext);
// Clean up
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;
// Enable the profiles
cgGLEnableProfile(m_VertexProfile);
cgGLEnableProfile(m_FragmentProfile);
// Bind the programs
cgGLBindProgram(m_VertexProgram);
cgGLBindProgram(m_FragmentProgram);
// Ordinarily you'd want to only set these values if they change. For
// this sample we'll just set them here.
cgSetParameter4fv(m_KdParam, m_aKd.Value());
cgSetParameter4fv(m_KsParam, m_aKs.Value());
cgSetParameter1f(m_ShininessParam, m_aShininess.Value());
// Set the light on the shader.
// Mudbox supports both point & directional lights. If
// the first light is directional we'll push it out from
// the origin. The shader assumes point light
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();
// Scoot this away from the origin to give it the appearance of a
// point light.
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();
};
/* Callback function for loadRamp */
float diffuseRamp(float x)
{
if (x > 0.5) {
return x*x*(3-2*x);
} else {
return 0.5f;
}
}
/* Callback function for loadRamp */
float specularRamp(float x)
{
if (x > 0.2f) {
return x;
} else {
return 0.0f;
}
}
/* Callback function for loadRamp */
float edgeRamp(float x)
{
if (x < 0.2f) {
return 1.0f;
} else {
return 0.85f;
}
}
/* Create a 1D texture ramp by evaluating func over the range [0,1]. */
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 /* Added by OpenGL 1.2 */
#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);
}