Kyle Hayward's

Portfolio

Impostor Rendering Code Samples

For a detailed description of the project, please see the impostor rendering project page. Two samples are provided: reflector and morphedMesh

reflector.cpp , reflector.h

This class represents a reflective object. It is responsible for setting up and managing diffuse and reflective Impostor objects that the reflector reflects and refracts. This includes updating impostors, linking with reflective impostors, and creating render targets for the impostors. It is also responsible for setting up refraction parameters for the refraction shader, and provides functions for binding Cg parameters.

During rendering, an appropriate shader material is applied depending on the state of the GUI. Shaders include simple billboard impostor reflection, depth impostor reflection, and non-pinhole impostor reflection.

#include <GL/glew.h>
#include <GL/glut.h>
#include "glext.h"

#include "scene.h"
#include "CG/cgInit.h"
#include "reflector.h"

using namespace std;

/*--------------------------------------------------------------------------
 * Constructor: create materials, etc.
 *--------------------------------------------------------------------------*/
Reflector::Reflector()
{
    reflectorMesh = NULL;

    //TODO: automate this in loading script
    GCRMaterial = new Material("GCR", false);
    SPOCRMaterial = new Material("SPOCR", false);
    SPOCRefractMaterial = new Material("SPOCRefract", false);
    PPCRMaterial = new Material("imposterReflections", false);
    PPCRefractMaterial = new Material("imposterRefractions", false);
    BBRMaterial = new Material("environmentMappedReflections", false);
    PhongMaterial = new Material("phongShading", false);
    RefractionConstrMaterial = new Material("RefractionConstruction", false);

    lightInterfaceType = ReflectorType;

    normalsRT = NULL;
    refractedRaysRT = NULL;

    clearColor = Vex3(100 / 255.0f, 149 / 255.0f, 237 / 255.0f);

    reflectorMeshIsMorpher = false;

    renderSPOCRays = false;
    RaysN = 0;
    VertsN = 0;
    impostorIndex = 0;
}

/*--------------------------------------------------------------------------
 * Release memory 
 *--------------------------------------------------------------------------*/
Reflector::~Reflector()
{
    if(reflectorMesh)
        delete reflectorMesh;

    reflectiveImposters.clear();
    diffuseImposters.clear();

    if(normalsRT)
        delete normalsRT;

    if(refractedRaysRT)
        delete refractedRaysRT;

    //materials unloaded by material manager
}

/*--------------------------------------------------------------------------
 * Set the reference mesh that will be a reflector
 *--------------------------------------------------------------------------*/
void Reflector::SetReferenceMesh(TriangleMesh *mesh)
{
    reflectorMesh = mesh;

    refractionParams.objectScale = reflectorMesh->scaleFactor;

    if(!strcmp(typeid(*reflectorMesh).name(), "class MorphedMesh"))
        reflectorMeshIsMorpher = true;
}

/*--------------------------------------------------------------------------
 * Add a diffuse or reflective impostor to this reflectors list of impostors
 *--------------------------------------------------------------------------*/
void Reflector::AddImposter(ImposterType imposterType, TriangleMesh *mesh)
{
    Imposter *imposter = new Imposter();
    imposter->SetReferenceMesh(mesh);
    imposter->imposterType = imposterType;

    if(mesh->isReflector)
    {
        reflectiveImposters.push_back(imposter);
    }
    else
    {
        diffuseImposters.push_back(imposter);
    }
}

/*--------------------------------------------------------------------------
 * Update the diffuse/reflective impostors for this reflector
 *--------------------------------------------------------------------------*/
void Reflector::UpdateImposters(RenderMode renderMode)
{
    for(unsigned int i = 0; i < diffuseImposters.size(); i++)
    {
        if(diffuseImposters[i]->hasMoved)
        {
            diffuseImposters[i]->UpdateImposter(reflectorMesh->GetCenter(), renderMode);

            if(diffuseImposters[i]->refMeshIsMorpher)
                diffuseImposters[i]->hasMoved = true;
            else
                diffuseImposters[i]->hasMoved = false;
        }
    }

    Material *oldMaterial;
    PhongMaterial->active = true;
    for(unsigned int i = 0; i < reflectiveImposters.size(); i++)
    {
        if(reflectiveImposters[i]->hasMoved)
        {
            //disable reflective material
            oldMaterial = reflectiveImposters[i]->referenceMesh->material;
            reflectiveImposters[i]->referenceMesh->material = PhongMaterial;

            reflectiveImposters[i]->UpdateImposter(reflectorMesh->GetCenter(), renderMode);
            reflectiveImposters[i]->hasMoved = false;

            reflectiveImposters[i]->referenceMesh->material = oldMaterial;
        }
    }
}

/*--------------------------------------------------------------------------
 * Link this reflector with the reflective impostor at specified index
 *--------------------------------------------------------------------------*/
void Reflector::LinkReflectiveImposterWithReflector(int index, Reflector* reflector)
{
    reflectiveImposters[index]->reflectorMeshLink = reflector;
}

/*--------------------------------------------------------------------------
 * Create the render targets for this reflector. These are used for refraction
 *--------------------------------------------------------------------------*/
void Reflector::CreateRenderTargets()
{
    normalsRT = new RenderTarget();
    normalsRT->currentConfig = RenderTarget::verifiedFBOconfig;
    normalsRT->currentFBO = RenderTarget::verifiedFBOdata;
    normalsRT->currentConfig.colorBits = NormalsRTBitsN;
    normalsRT->currentConfig.colorFormat = NormalsRTFormat;
    normalsRT->currentConfig.width = RenderTargetResolution;
    normalsRT->currentConfig.height = RenderTargetResolution;

    //no multisampling
    normalsRT->currentConfig.coverageSamples = 0;
    normalsRT->currentConfig.depthSamples = 0;

    refractedRaysRT = new RenderTarget();
    refractedRaysRT->currentConfig = RenderTarget::verifiedFBOconfig;
    refractedRaysRT->currentFBO = RenderTarget::verifiedFBOdata;
    refractedRaysRT->currentConfig.colorBits = RefractedRayRTBitsN;
    refractedRaysRT->currentConfig.colorFormat = RefractedRayRTFormat;
    refractedRaysRT->currentConfig.width = RenderTargetResolution;
    refractedRaysRT->currentConfig.height = RenderTargetResolution;

    //no multisampling
    refractedRaysRT->currentConfig.coverageSamples = 0;
    refractedRaysRT->currentConfig.depthSamples = 0;

    //check to see if the configurations are valid
    if(!RenderTarget::CreateFBO(normalsRT->currentConfig, normalsRT->currentFBO))
    {
        cerr << "ERROR: error creating render target!" << endl;
    }

    if(!RenderTarget::CreateFBO(refractedRaysRT->currentConfig, refractedRaysRT->currentFBO))
    {
        cerr << "ERROR: error creating render target!" << endl;
    }

    //create the render targets for the impostors
    //these render targets are sampled by the reflector in a reflection shader
    for(unsigned int i = 0; i < diffuseImposters.size(); i++)
    {
        diffuseImposters[i]->CreateRenderTarget();
    }

    for(unsigned int i = 0; i < reflectiveImposters.size(); i++)
    {
        reflectiveImposters[i]->CreateRenderTarget();
    }
}

/*--------------------------------------------------------------------------
 * Draw all the impostors. For testing only.
 * Draws their billboarded quad or their visible mesh geometry(if PPC/SPOC)
 *--------------------------------------------------------------------------*/
void Reflector::RenderImposters(RenderMode renderMode)
{
    for(unsigned int i = 0; i < diffuseImposters.size(); i++)
    {
        if(diffuseImposters[i]->imposterMesh)
            diffuseImposters[i]->imposterMesh->RenderHW(renderMode);
        else
            diffuseImposters[i]->RenderHW(renderMode);
        break;
    }

    for(unsigned int i = 0; i < reflectiveImposters.size(); i++)
    {
        reflectiveImposters[i]->RenderHW(renderMode);
    }
}

/*--------------------------------------------------------------------------
 * Update the refraciton maps of this reflector.
 *--------------------------------------------------------------------------*/
void Reflector::UpdateRefractionMaps(RenderMode renderMode)
{
    //set the current reflector to be used when setting parameters in cgInit
    scene->currReflector = this;

    Material *oldMaterial = reflectorMesh->material;

    //1st pass: render the depth and normals of the back faces
    reflectorMesh->material = PhongMaterial;
    reflectorMesh->material->active = true;

    glCullFace( GL_FRONT );

    glPushAttrib(GL_VIEWPORT_BIT); 
    glViewport(0, 0, normalsRT->currentConfig.width, normalsRT->currentConfig.height);

    normalsRT->BeginRender(clearColor, 0);

    reflectorMesh->RenderHW(renderMode);

    normalsRT->EndRender();

    glPopAttrib();

    glCullFace( GL_BACK );

    //2nd pass: compute the final refracted ray
    reflectorMesh->material = RefractionConstrMaterial;
    reflectorMesh->material->active = true;

    //set the vertex distances used for thickness
    glClientActiveTexture(GL_TEXTURE0);
    glTexCoordPointer(1, GL_FLOAT, 0, reflectorMesh->vertexDistances);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

    glPushAttrib(GL_VIEWPORT_BIT); 
    glViewport(0, 0, refractedRaysRT->currentConfig.width, refractedRaysRT->currentConfig.height);

    refractedRaysRT->BeginRender(clearColor, 0);

    reflectorMesh->RenderHW(renderMode);

    refractedRaysRT->EndRender();

    glClientActiveTexture(GL_TEXTURE0);
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);

    glPopAttrib();

    reflectorMesh->material = oldMaterial;

    scene->currReflector = 0;
}
/*--------------------------------------------------------------------------
 * Draw the geometry assigned to this reflector
 *--------------------------------------------------------------------------*/
void Reflector::RenderHW(RenderMode renderMode)
{
    //set the current reflector to be used when setting parameters in cgInit
    scene->currReflector = this;

    Material *oldMaterial = reflectorMesh->material;

    if(lightInterfaceType == ReflectorType)
    {
        //get the current type of impostor from the GUI
        int imposterType = scene->GetImposterTypeFromGUI(0);
        switch(imposterType)
        {
        case imposterTypeBB:
            reflectorMesh->material = BBRMaterial;
            break;
        case imposterTypePPC:
            reflectorMesh->material = PPCRMaterial;
            break;
        case imposterTypeSPOC:
            reflectorMesh->material = SPOCRMaterial;
            break;
        case imposterTypeSPOC2:
            reflectorMesh->material = SPOCRMaterial;
            break;
        case imposterTypeGC:
            reflectorMesh->material = GCRMaterial;
            break;
        default:
            cout << "Error: No applicable material can be applied!" << endl;
            break;
        }
    }
    else
    {
        //1st and 2nd passes performed during update
        //3rd pass: finally render the refractor with the PHC or SPOC impostors
        int imposterType = scene->GetImposterTypeFromGUI(0);
        switch(imposterType)
        {
        case imposterTypeBB:
            reflectorMesh->material = BBRMaterial;
            break;
        case imposterTypePPC:
            reflectorMesh->material = PPCRefractMaterial;
            break;
        case imposterTypeSPOC:
            reflectorMesh->material = SPOCRefractMaterial;
            break;
        case imposterTypeSPOC2:
            reflectorMesh->material = SPOCRefractMaterial;
            break;
        default:
            cout << "Error: No applicable material can be applied!" << endl;
            break;
        }
    }

    reflectorMesh->material->active = true;

    reflectorMesh->RenderHW(renderMode);

    reflectorMesh->material = oldMaterial;

    scene->currReflector = 0;
}

/*--------------------------------------------------------------------------
 * Binds the vertices of type VertexType to the cgParam
 *--------------------------------------------------------------------------*/
void Reflector::CGBindImposterVerts(CGparameter *cgParam, VertexType type, int index, bool diffuse)
{
    switch(type)
    {
        case Quad:
        {
            static float verts[4 * 3];
            int k = 0;

            if(diffuse)
            {
                for(int i = 0; i < 4; i++)
                {
                    verts[k++] = diffuseImposters[index]->imposterVerts[i].x;
                    verts[k++] = diffuseImposters[index]->imposterVerts[i].y;
                    verts[k++] = diffuseImposters[index]->imposterVerts[i].z;
                }
            }
            else
            {
                for(int i = 0; i < 4; i++)
                {
                    verts[k++] = reflectiveImposters[index]->imposterVerts[i].x;
                    verts[k++] = reflectiveImposters[index]->imposterVerts[i].y;
                    verts[k++] = reflectiveImposters[index]->imposterVerts[i].z;
                }
            }            

            cgGLSetParameterArray3f(*cgParam, 0, 0, verts);
            break;
        }
        case BoundingVolume:
        {
            static float verts[8 * 3];
            int k = 0;

            if(diffuse)
            {
                for(int i = 0; i < 8; i++)
                {
                    verts[k++] = diffuseImposters[index]->boundingVolume[i].x;
                    verts[k++] = diffuseImposters[index]->boundingVolume[i].y;
                    verts[k++] = diffuseImposters[index]->boundingVolume[i].z;
                }
            }
            else
            {
                for(int i = 0; i < 8; i++)
                {
                    verts[k++] = reflectiveImposters[index]->boundingVolume[i].x;
                    verts[k++] = reflectiveImposters[index]->boundingVolume[i].y;
                    verts[k++] = reflectiveImposters[index]->boundingVolume[i].z;
                }
            }            

            cgGLSetParameterArray3f(*cgParam, 0, 0, verts);
            break;
        }
    }

}

/*--------------------------------------------------------------------------
 * Binds a float of type FloatType to the cgParam
 *--------------------------------------------------------------------------*/
void Reflector::CGBindImposterFloat(CGparameter *cgParam, FloatType type, int index, bool diffuse)
{
    float value = 0;

    switch(type)
    {
        case CamProp_f:
        {
            if(diffuse)
                value = diffuseImposters[index]->view->phc->Getf();
            else
                value = reflectiveImposters[index]->view->phc->Getf();
            break;
        }
        case NearZ:
        {
            if(diffuse)
                value = diffuseImposters[index]->nearZ;
            else
                value = reflectiveImposters[index]->nearZ;
            break;
        }
        case FarZ:
        {
            if(diffuse)
                value = diffuseImposters[index]->farZ;
            else
                value = reflectiveImposters[index]->farZ;
            break;
        }
        case IsReflective:
        {
            if(!diffuse)
            {
                value = 1.0f;
            }
            break;
        }
        default:
            break;
    }

    cgGLSetParameter1f(*cgParam, value);
}

/*--------------------------------------------------------------------------
 * Binds a vector of type VectorType to the cgParam
 *--------------------------------------------------------------------------*/
void Reflector::CGBindImposterVector(CGparameter *cgParam, VectorType type, int index, bool diffuse)
{
    Vex3 vector;

    switch(type)
    {
        case Normal:
        {
            if(diffuse)
                vector = diffuseImposters[index]->GetNormal();
            else
                vector = reflectiveImposters[index]->GetNormal();
            break;
        }
        case Tangent:
        {
            if(diffuse)
                vector = diffuseImposters[index]->GetTangent();
            else
                vector = reflectiveImposters[index]->GetTangent();
            break;
        }
        case CamProp_C:
        {
            if(diffuse)
                vector = diffuseImposters[index]->view->phc->GetC();
            else
                vector = reflectiveImposters[index]->view->phc->GetC();
            break;
        }
        case CamProp_a:
        {
            if(diffuse)
                vector = diffuseImposters[index]->view->phc->_a;
            else
                vector = reflectiveImposters[index]->view->phc->_a;
            break;
        }
        case CamProp_b:
        {
            if(diffuse)
                vector = diffuseImposters[index]->view->phc->_b;
            else
                vector = reflectiveImposters[index]->view->phc->_b;
            break;
        }
        case CamProp_c:
        {
            if(diffuse)
                vector = diffuseImposters[index]->view->phc->_c;
            else
                vector = reflectiveImposters[index]->view->phc->_c;
            break;
        }
        default:
            break;
    }

    cgGLSetParameter3f(*cgParam, vector.x, vector.y, vector.z);
}

/*--------------------------------------------------------------------------
 * Binds the near and far points to the cg parameters
 *--------------------------------------------------------------------------*/
void Reflector::CGBindImposterNearFarP(CGparameter *cgParam1, CGparameter *cgParam2, int index, bool diffuse)
{
    Vex3 nearp, farp;

    if(diffuse)
    {
        nearp = diffuseImposters[index]->nearPoint;
        farp = diffuseImposters[index]->farPoint;
    }
    else
    {
        nearp = reflectiveImposters[index]->nearPoint;
        farp = reflectiveImposters[index]->farPoint;
    }

    cgGLSetParameter3f(*cgParam1, nearp.x, nearp.y, nearp.z);
    cgGLSetParameter3f(*cgParam2, farp.x, farp.y, farp.z);
}
/*--------------------------------------------------------------------------
 * Binds the texture of the render target to the cgParam
 *--------------------------------------------------------------------------*/
void Reflector::CGBindImposterTex(CGparameter *cgParam, RenderTargetType type, int index, bool diffuse)
{
    switch(type)
    {
        case Tex:
        {
            if(diffuse)
                cgGLSetTextureParameter(*cgParam, diffuseImposters[index]->renderTarget->GetTextureID());
            else
                cgGLSetTextureParameter(*cgParam, reflectiveImposters[index]->renderTarget->GetTextureID());
            break;
        }
        case DepthTex:
        {
            if(diffuse)
                cgGLSetTextureParameter(*cgParam, diffuseImposters[index]->renderTarget->GetDepthTexID());
            else
                cgGLSetTextureParameter(*cgParam, reflectiveImposters[index]->renderTarget->GetDepthTexID());
            break;
        }
    }
}

/*--------------------------------------------------------------------------
 * Binds a matrix of type MatrixType to the cgParam
 *--------------------------------------------------------------------------*/
void Reflector::CGBindImposterMatrix(CGparameter* cgParam, MatrixType type, int index, bool diffuse)
{
    double* matrix;

    switch(type)
    {
        case MVP:
        {
            if(diffuse)
                matrix = diffuseImposters[index]->mvp;
            else
                matrix = reflectiveImposters[index]->mvp;
            break;
        }
        case InvMVP:
        {
            if(diffuse)
                matrix = diffuseImposters[index]->invMVP;
            else
                matrix = reflectiveImposters[index]->invMVP;
            break;
        }
    }

    cgGLSetMatrixParameterdc(*cgParam, matrix);
}

/*--------------------------------------------------------------------------
 * Updates the impostors if a request is sent from the GUI
 *--------------------------------------------------------------------------*/
bool Reflector::RequestUpdateIfNeeded(ImposterType *newTypes) {

    bool ret = false;
    for (unsigned int i = 0; i < diffuseImposters.size(); i++) 
    {
        if (diffuseImposters[i]->imposterType != newTypes[0]) 
        {
            diffuseImposters[i]->imposterType = newTypes[0];
            diffuseImposters[i]->hasMoved = true;
            ret = true;
        }
    }

    for (unsigned int i = 0; i < reflectiveImposters.size(); i++) 
    {
        if (reflectiveImposters[i]->imposterType != newTypes[0]) 
        {
            reflectiveImposters[i]->imposterType = newTypes[0];
            reflectiveImposters[i]->hasMoved = true;
            ret = true;
        }
    }

    return ret;

}

/*--------------------------------------------------------------------------
 * Image-space Refraction Params
 *--------------------------------------------------------------------------*/
//param = float[4]
void RefractionParams::GetLocal1(float *param)
{
    // local1 = { 2*f*n, f-n, f+n, <unused> }
    // f = distance to the OpenGL far plane
    // n = distance to the OpwnGL near plane 
    float znear = scene->frameBuffer->phc->hither;
    float zfar = scene->frameBuffer->phc->yon;

    param[0] = 2 * zfar * znear;
    param[1] = zfar - znear;
    param[2] = zfar + znear;
    param[3] = 0.0f;
}

//param = float[4]
void RefractionParams::GetLocal2(float *param)
{
    // local2 = { n_i/n_t, (n_i/n_t)^2, <unused>, <unused> }
    // n_i = index of refraction of external material (air)
    // n_t = index of refraction of internal material

    param[0] = index_1 / index_2;
    param[1] = param[0] * param[0];
    param[2] = 0.0f;
    param[3] = 0.0f;
}

//param = float[4]
void RefractionParams::GetLocal3(float *param)
{
    // local3 = { n_t/n_i, (n_t/n_i)^2, objectSize, sqrt( 1 - (n_i/n_t)^2 ) }

    param[0] = index_1 / index_2;
    param[1] = param[0] * param[0];
    param[2] = objectScale;
    param[3] = sqrtf( 1 - param[1] );
}

morphedMesh.cpp , morphedMesh.h

This is a simple class that morphs from one mesh to another. It decides whether to use hardware vertex morphing vs. software vertex morphing. If no destination mesh is set, then a destination mesh is computed from the bounding sphere of the source mesh.

#include <GL/glew.h>
#include <GL/glut.h>
#include "glext.h"
#include "morphedMesh.h"
#include "scene.h"

/*--------------------------------------------------------------------------
 * Constructor
 *--------------------------------------------------------------------------*/
MorphedMesh::MorphedMesh() : TriangleMesh()
{
    dstMeshVerts = 0;
    dstMeshNormals = 0;

    srcVerts = 0;
    srcNormals = 0;

    hardwareVertexMorph = true;
    useExistingData = false;

    dstVertexBuffer = 0;
    dstNormalBuffer = 0;
}

/*--------------------------------------------------------------------------
 * Destructor: delete vertex and normal buffers
 *--------------------------------------------------------------------------*/
MorphedMesh::~MorphedMesh()
{
    if(!useExistingData)
    {
        if(dstMeshVerts)
            delete[] dstMeshVerts;

        if(dstMeshNormals)
            delete[] dstMeshNormals;
    }

    if(!hardwareVertexMorph)
    {
        if(srcVerts)
            delete[] srcVerts;

        if(srcNormals)
            delete[] srcNormals;
    }

    //vertices and normals deleted by parent
}

/*--------------------------------------------------------------------------
 * LoadDstMesh: Sets the destination mesh on this morphed mesh.
 * @dst - destination mesh, must contain the same amount of vertices as this
 *          mesh (which will act as the source)
 * @hwVertexMorphing - whether or not to use hardware vertex morphing
 *
 * If the dst mesh is NULL, compute the destination vertices on the bounding
 * sphere of the source mesh (this).
 * If this mesh is using a VBO, copy the data into buffer objects
 *--------------------------------------------------------------------------*/
void MorphedMesh::LoadDstMesh(TriangleMesh *dst, bool hwVertexMorphing)
{
    hardwareVertexMorph = hwVertexMorphing;

    //if dst, then just use its vertices and normals
    //else, manually compute the destination vertices on the bounding sphere
    if(dst)
    {
        this->dstMeshVerts = dst->vertices;
        this->dstMeshNormals = dst->normals;

        useExistingData = true;
    }
    else
    {
        ComputeBoundingSphere();

        this->dstMeshVerts = new Vex3[this->verticesN];
        this->dstMeshNormals = new Vex3[this->verticesN];

        float r = 0.0f;
        Vex3 direction;
        float newRadius = radius;
        for(int i = 0; i < verticesN; i++)
        {
            direction = vertices[i] - center;
            r = direction.Length();
            dstMeshVerts[i] = vertices[i] + direction.UnitVector() * (newRadius - r);
        }

        //compute normals from the dst verts
        OutputNormals(this->dstMeshNormals, this->dstMeshVerts);
    }

    //if no hw vertex morph, copy vertices and normals into src buffers
    if(!hardwareVertexMorph)
    {
        srcVerts = new Vex3[this->verticesN];
        srcNormals = new Vex3[this->verticesN];

        memcpy(srcVerts, vertices, sizeof(float) * 3 * verticesN);
        memcpy(srcNormals, normals, sizeof(float) * 3 * verticesN);
    }

    //copy data into VBOs
    if(useVBO)
    {
        //------create the vertex buffer--------//
        glGenBuffersARB(1, &dstVertexBuffer);

        glBindBufferARB(GL_ARRAY_BUFFER_ARB, dstVertexBuffer);

        glBufferDataARB(GL_ARRAY_BUFFER_ARB,
                        sizeof(float) * 3 * verticesN,
                        (float*)this->dstMeshVerts,
                        GL_STATIC_DRAW_ARB);

        //------create the normal buffer--------//
        glGenBuffersARB(1, &dstNormalBuffer);

        glBindBufferARB(GL_ARRAY_BUFFER_ARB, dstNormalBuffer);

        glBufferDataARB(GL_ARRAY_BUFFER_ARB,
                        sizeof(float) * 3 * verticesN,
                        (float*)this->dstMeshNormals,
                        GL_STATIC_DRAW_ARB);

        //unbind the buffer
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    }
}

/*--------------------------------------------------------------------------
 * RenderHW: Renders the morphing mesh. Interpolates in software or hardware
 * @renderMode - type of rendering mode to use (e.g. wireframe, solid)
 *--------------------------------------------------------------------------*/
void MorphedMesh::RenderHW(RenderMode renderMode)
{
    //if no mesh provided, default to the bounding sphere
    if(dstMeshVerts == NULL)
    {
        LoadDstMesh(NULL, true);
    }

    if(hardwareVertexMorph)
    {
        if(useVBO)
        {
            //enable destination vertices
            glClientActiveTexture(GL_TEXTURE0);

            glBindBufferARB(GL_ARRAY_BUFFER_ARB, dstVertexBuffer);
            glTexCoordPointer(3, GL_FLOAT, 0, NULL);

            glEnableClientState(GL_TEXTURE_COORD_ARRAY);

            //enable destination normals
            glClientActiveTexture(GL_TEXTURE1);

            glBindBufferARB(GL_ARRAY_BUFFER_ARB, dstNormalBuffer);
            glTexCoordPointer(3, GL_FLOAT, 0, NULL);

            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        }
        else
        {
            //enable destination vertices
            glClientActiveTexture(GL_TEXTURE0);
            glTexCoordPointer(3, GL_FLOAT, 0, (float*) dstMeshVerts);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);

            //enable destination normals
            glClientActiveTexture(GL_TEXTURE1);
            glTexCoordPointer(3, GL_FLOAT, 0, (float*) dstMeshNormals);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        }
    }
    else //no hw vertex blending, manually find the interoplated vertices
    {
        float blend = 0.0f;
        if(scene->cameraPath->PathLength() > 0)
            blend = (float)scene->cameraPath->PathIndex() / (float)scene->cameraPath->PathLength();
        for(int i = 0; i < verticesN; i++)
        {
            vertices[i] = srcVerts[i] + (dstMeshVerts[i] - srcVerts[i]) * blend;
            normals[i] = srcNormals[i] + (dstMeshNormals[i] - srcNormals[i]) * blend;
        }
    }

    TriangleMesh::RenderHW(renderMode);

    //disable texcoord arrays
    if(hardwareVertexMorph)
    {
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);

        glClientActiveTexture(GL_TEXTURE0);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    }
}