blindDataShader/blindDataShader.cpp
 
 
 
blindDataShader/blindDataShader.cpp
//-
// ==========================================================================
// Copyright 1995,2006,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.
// ==========================================================================
//+

#ifdef WIN32
#pragma warning( disable : 4786 )
#endif

#include <maya/MIOStream.h>
#include <maya/MDagPath.h>
#include <maya/MDoubleArray.h>
#include <maya/MFloatVector.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnLightDataAttribute.h>
#include <maya/MFnMesh.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MGlobal.h>
#include <maya/MPlug.h>
#include <maya/MPlugArray.h>
#include <maya/MString.h>

#if defined(OSMac_MachO_)
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#else
#include <GL/gl.h>
#include <GL/glu.h>
#endif

#include "blindDataShader.h"

void mergeSort( int startIndex, int sortingOffset,
                            MIntArray& sortingArray, int* tempSortingArray,
                                MDoubleArray& otherArray, double* tempOtherArray)
//
// Description:
// This is a very dirty recursive implementation of the merge sort
// that is very application-specific
//
{
        if (sortingOffset == 2)
        {
                if (sortingArray[startIndex] > sortingArray[startIndex+1])
                {
                        int temp = sortingArray[startIndex+1];
                        sortingArray[startIndex+1] = sortingArray[startIndex];
                        sortingArray[startIndex] = temp;
                        double ftemp = otherArray[startIndex+1];
                        otherArray[startIndex+1] = otherArray[startIndex];
                        otherArray[startIndex] = ftemp;
                }
        }
        else if (sortingOffset > 2)
        {
                // First, sort each half
                //
                int leftCount = sortingOffset / 2;
                int rightCount = sortingOffset - leftCount;
                int leftIndex = startIndex;
                int rightIndex = startIndex + leftCount;
                mergeSort(leftIndex, leftCount, sortingArray, tempSortingArray,
                        otherArray, tempOtherArray);
                mergeSort(rightIndex, rightCount, sortingArray, tempSortingArray,
                        otherArray, tempOtherArray);

                // Second, merge the halves into the temporary arrays
                //
                int index = startIndex;
                while (leftIndex < (startIndex + leftCount)
                        && rightIndex < (startIndex + sortingOffset) )
                {
                        if (sortingArray[leftIndex] < sortingArray[rightIndex])
                        {
                                tempSortingArray[index] = sortingArray[leftIndex];
                                tempOtherArray[index++] = otherArray[leftIndex++];
                        }
                        else
                        {
                                tempSortingArray[index] = sortingArray[rightIndex];
                                tempOtherArray[index++] = otherArray[rightIndex++];
                        }
                }

                // One of the sides is done, so add the other side
                //
                while (leftIndex < (startIndex + leftCount) )
                {
                        tempSortingArray[index] = sortingArray[leftIndex];
                        tempOtherArray[index++] = otherArray[leftIndex++];
                }
                while (rightIndex < (startIndex + sortingOffset) )
                {
                        tempSortingArray[index] = sortingArray[rightIndex];
                        tempOtherArray[index++] = otherArray[rightIndex++];
                }

                // Finally, copy the temporary sorting array over to the other side
                //
                for (index = startIndex; index < (startIndex + sortingOffset); ++index)
                {
                        sortingArray[index] = tempSortingArray[index];
                        otherArray[index] = tempOtherArray[index];
                }
        }
}

int binarySearch(int searchValue, MIntArray& searchArray)
//
// Description:
// Application-specific implementation of the binary search algorithm.
// It returns the index in the "searchArray" where the "searchValue"
// is found or -1 if it is not found.
//
{
        int max = searchArray.length();
        int min = 0;
        while (max - min > 1)
        {
                int currentIndex = ((max - min) / 2) + min;
                int currentValue = searchArray[currentIndex];
                if (currentValue == searchValue) return currentIndex;
                else if (currentValue < searchValue) min = currentIndex;
                else max = currentIndex;
        }
        if (searchArray[min] == searchValue) return min;
        else return -1;
}

/****************************************************************************
 * Blind Data Shader class
 ***************************************************************************/

// Static class member variables
//
// Unique Node TypeId
MTypeId blindDataShader::id( 0x00086000 );

void* blindDataShader::creator()
//
// Description: Static member function that returns a new
// dynamically-allocated instance of the class.
//
{
    return new blindDataShader();
}

MStatus blindDataShader::initialize()
//
// Description: Static member function that initializes the static
// member variables of the class. In this case, it does nothing, since
// "id" is already set to the default value.
//
{
        return MS::kSuccess;
}

MStatus blindDataShader::compute(
const MPlug&      plug,
      MDataBlock& block ) 
{ 
    bool k = false;
    k |= (plug==outColor);
    k |= (plug==outColorR);
    k |= (plug==outColorG);
    k |= (plug==outColorB);
    if( !k ) return MS::kUnknownParameter;

        // Always return black for now.
    MFloatVector resultColor(0.0,0.0,0.0);

    // set ouput color attribute
    MDataHandle outColorHandle = block.outputValue( outColor );
    MFloatVector& outColor = outColorHandle.asFloatVector();
    outColor = resultColor;
    outColorHandle.setClean();

    return MS::kSuccess;
}

MStatus blindDataShader::bind(const MDrawRequest& request, M3dView& view)
{
        view.beginGL();

        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

        view.endGL();

        return MS::kSuccess;
}


MStatus blindDataShader::unbind(const MDrawRequest& request,
                           M3dView& view)
{
        view.beginGL(); 

        glPopClientAttrib();
        glPopAttrib();

        view.endGL();

        return MS::kSuccess;
}

MStatus blindDataShader::geometry( const MDrawRequest& request,
                                                                M3dView& view,
                                                            int prim,
                                                                unsigned int writable,
                                                                int indexCount,
                                                                const unsigned int * indexArray,
                                                                int vertexCount,
                                                            const int * vertexIDs,
                                                                const float * vertexArray,
                                                                int normalCount,
                                                                const float ** normalArrays,
                                                                int colorCount,
                                                                const float ** colorArrays,
                                                                int texCoordCount,
                                                                const float ** texCoordArrays)
//
// Description:
// Once everything is plugged, this function is called by the Maya rendering
// engine to render the given objects. It will be called for each frame, so 
// the efficiency of this algorithm is important.
//
{
        const int blindDataUniqueID = 60;
        MStatus stat;
        int i;

        // Create our own empty colour array and
        // populate it with the default colour value for the attribute.
        //
        double* colours = new double[vertexCount * 3];
        float defaultColour[3] = { 0.0f, 0.0f, 0.0f };
        MPlug plug( thisMObject(), outColor );
        MObject colorObject;
        plug.getValue(colorObject);
        MFnNumericData data(colorObject);
        data.getData(defaultColour[0], defaultColour[1], defaultColour[2]);

        // Use the blind data to set independant colour values
        //
        MFnMesh mesh(request.multiPath().node(), &stat);
        if (stat && mesh.hasBlindData(MFn::kMeshVertComponent, &stat)
                && mesh.hasBlindData(vertexIDs[0], MFn::kMeshVertComponent,
                blindDataUniqueID, &stat))
        {
                MIntArray redComponentIDs, greenComponentIDs, blueComponentIDs;
                MDoubleArray redColour, greenColour, blueColour;

                stat = mesh.getDoubleBlindData( MFn::kMeshVertComponent,
                        blindDataUniqueID, "red", redComponentIDs, redColour);
                if (stat) stat = mesh.getDoubleBlindData( MFn::kMeshVertComponent,
                        blindDataUniqueID, "green", greenComponentIDs, greenColour);
                if (stat) stat = mesh.getDoubleBlindData( MFn::kMeshVertComponent,
                        blindDataUniqueID, "blue", blueComponentIDs, blueColour);

                if (stat && redComponentIDs.length() == redColour.length()
                        && greenComponentIDs.length() == greenColour.length()
                        && blueComponentIDs.length() == blueColour.length())
                {
                        // Now that I have all the blind data,
                        // use the vertex IDs and the component IDs arrays
                        // to map the blind data values inside the colour array.
                        //

                        // Sort the components IDs and the colour values in increasing
                        // order to speed up the association
                        //
                        int maxArrayLength =
                                (redComponentIDs.length() > greenComponentIDs.length()) ?
                                ((redComponentIDs.length() > blueComponentIDs.length()) ?
                                        redComponentIDs.length() : blueComponentIDs.length()) :
                                ((greenComponentIDs.length() > blueComponentIDs.length()) ?
                                        greenComponentIDs.length() : blueComponentIDs.length());
                        int* tempIntArray = new int[maxArrayLength];
                        double* tempFloatArray = new double[maxArrayLength];
                        mergeSort(0, redComponentIDs.length(), redComponentIDs,
                                tempIntArray, redColour, tempFloatArray);
                        mergeSort(0, greenComponentIDs.length(), greenComponentIDs,
                                tempIntArray, greenColour, tempFloatArray);
                        mergeSort(0, blueComponentIDs.length(), blueComponentIDs,
                                tempIntArray, blueColour, tempFloatArray);

                        // Associate the vertex IDs with the colour values
                        //
                        for (i = 0; i < vertexCount; ++i)
                        {
                                int index = binarySearch(vertexIDs[i], redComponentIDs);
                                if (index != -1) colours[3*i] = redColour[index];
                                else colours[3*i] = defaultColour[0];

                                index = binarySearch(vertexIDs[i], greenComponentIDs);
                                if (index != -1) colours[3*i+1] = greenColour[index];
                                else colours[3*i+1] = defaultColour[1];

                                index = binarySearch(vertexIDs[i], blueComponentIDs);
                                if (index != -1) colours[3*i+2] = blueColour[index];
                                else colours[3*i+2] = defaultColour[2];
                        }
                }
        }

        // Render the triangles using the new vertex colour information
        // taken from the blind data values of the mesh
        //
        view.beginGL();

        glPushAttrib( GL_ALL_ATTRIB_BITS );
        glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT);

        glDisable(GL_LIGHTING);

        glVertexPointer(3, GL_FLOAT, 0, vertexArray);
        glEnableClientState(GL_VERTEX_ARRAY);

        // Is the next line necessary?
        glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );

        glColorPointer(3, GL_DOUBLE, 0, colours);
        glEnableClientState(GL_COLOR_ARRAY);

        glDrawElements(prim, indexCount, GL_UNSIGNED_INT, indexArray);

        glDisable(GL_COLOR_MATERIAL);

        glPopClientAttrib();
        glPopAttrib();

        view.endGL();

        return MS::kSuccess;
}