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;
}