- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files
- Directories
- Examples

# 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();
        cgGLSetDebugMode( CG_FALSE );

        cgGLSetManageTextureParameters(m_CGContext, CG_TRUE);
        cgSetParameterSettingMode(m_CGContext, CG_DEFERRED_PARAMETER_SETTING);

        /* 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 =
            cgCreateProgramFromFile(
              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 =
            cgCreateProgramFromFile(
              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);
    }