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

# blindDataMesh.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.
    // ==========================================================================
    //+

    #include <maya/MFnMesh.h>
    #include <maya/MFloatPoint.h>
    #include <maya/MFloatPointArray.h>
    #include <maya/MIntArray.h>
    #include <maya/MFnTypedAttribute.h>
    #include <maya/MFnNumericAttribute.h>
    #include <maya/MPxNode.h>
    #include <maya/MObject.h>
    #include <maya/MPlug.h>
    #include <maya/MDataBlock.h>
    #include <maya/MFnMeshData.h>
    #include <maya/MIOStream.h>
    #include <maya/MItMeshVertex.h>

    #include "blindDataMesh.h"

    MStatus returnStatus;

    #define McheckErr(stat,msg)         \
        if ( MS::kSuccess != stat ) {   \
            cerr << msg;                \
            return MS::kFailure;        \
        }

    MObject blindDataMesh::seed;
    MObject blindDataMesh::outputMesh;
    MTypeId blindDataMesh::id( 0x60EA );

    void* blindDataMesh::creator()
    {
        return new blindDataMesh;
    }

    MStatus blindDataMesh::initialize()
    {
        MFnTypedAttribute typedAttr;
        MStatus returnStatus;

        blindDataMesh::outputMesh = typedAttr.create( "outputMesh", "out",
            MFnData::kMesh, &returnStatus );
        McheckErr(returnStatus, "ERROR creating blindDataMesh output attribute\n");
        typedAttr.setStorable(false);

        returnStatus = addAttribute(blindDataMesh::outputMesh);
        McheckErr(returnStatus, "ERROR adding outputMesh attribute\n");

        MFnNumericAttribute numAttr;
        blindDataMesh::seed = numAttr.create( "randomSeed", "seed",
            MFnNumericData::kLong, 0, &returnStatus );
        McheckErr(returnStatus, "ERROR creating blindDataMesh input attribute\n");

        returnStatus = addAttribute(blindDataMesh::seed);
        McheckErr(returnStatus, "ERROR adding input attribute\n");

        returnStatus = attributeAffects(blindDataMesh::seed,
                                        blindDataMesh::outputMesh);
        McheckErr(returnStatus, "ERROR in attributeAffects\n");

        return MS::kSuccess;
    }

    MObject blindDataMesh::createMesh(long seed, MObject& outData, MStatus& stat)
    {
        MFloatPointArray vertices;
        MIntArray faceDegrees;
        MIntArray faceVertices;
        int i, j;

        srand(seed);

        float planeSize = 20.0f;
        float planeOffset = planeSize / 2.0f;
        float planeDim = 0.5f;

        int numDivisions = (int) (planeSize / planeDim);
        // int numVertices = (numDivisions + 1) * (numDivisions + 1);
        // int numEdge = (2 * numDivisions) * (numDivisions + 1);
        int numFaces = numDivisions * numDivisions;

        // Set up an array containing the vertex positions for the plane. The
        // vertices are placed equi-distant on the X-Z plane to form a square
        // grid that has a side length of "planeSize".
        //
        // The Y-coordinate of each vertex is the average of the neighbors already
        // calculated, if there are any, with a small random offset added. Because
        // of the way the vertices are calculated, the whole plane will look like
        // it is streaked in a diagonal direction with mountains and depressions.
        //
        for (i = 0; i < (numDivisions + 1); ++i)
        {
            for (j = 0; j < (numDivisions + 1); ++j)
            {
                float height;

                if (i == 0 && j == 0)
                {
                    height = ((rand() % 101) / 100.0f - 0.5f);
                }
                else if (i == 0)
                {
                    float previousHeight = vertices[j - 1][1];
                    height = previousHeight + ((rand() % 101) / 100.0f - 0.5f);
                }
                else if (j == 0)
                {
                    float previousHeight = vertices[(i-1)*(numDivisions + 1)][1];
                    height = previousHeight + ((rand() % 101) / 100.0f - 0.5f);
                }
                else
                {
                    float previousHeight
                        = vertices[(i-1)*(numDivisions + 1) + j][1];
                    float previousHeight2
                        = vertices[i*(numDivisions + 1) + j - 1][1];
                    height = (previousHeight + previousHeight2)
                        / 2.0f + ((rand() % 101) / 100.0f - 0.5f);
                }

                MFloatPoint vtx( i * planeDim - planeOffset, height,
                    j * planeDim - planeOffset );
                vertices.append(vtx);
            }
        }

        // Set up an array containing the number of vertices
        // for each of the plane's faces
        //
        for (i = 0; i < numFaces; ++i)
        {
            faceDegrees.append(4);
        }

        // Set up an array to assign the vertices for each face
        //
        for (i = 0; i < numDivisions; ++i)
        {
            for (j = 0; j < numDivisions; ++j)
            {
                faceVertices.append(i*(numDivisions+1) + j);
                faceVertices.append(i*(numDivisions+1) + j + 1);
                faceVertices.append((i+1)*(numDivisions+1) + j + 1);
                faceVertices.append((i+1)*(numDivisions+1) + j);
            }
        }

        MFnMesh meshFn;
        MObject newMesh = meshFn.create(vertices.length(), numFaces, vertices,
            faceDegrees, faceVertices, outData, &stat);

        return newMesh;
    }

    MStatus blindDataMesh::compute(const MPlug& plug, MDataBlock& data)

    {
        MStatus returnStatus;

        if (plug == outputMesh) {

            // Get the given random number generator seed. We need to use a
            // seed, because a pseudo-random number generator will always give
            // the same random numbers for a constant seed. This means that the
            // mesh will not change when it is recalculated.
            //
            MDataHandle seedHandle = data.inputValue(seed, &returnStatus);
            McheckErr(returnStatus,"ERROR getting random number generator seed\n");

            long seed = seedHandle.asLong();

            // Get the handle to the output mesh. The creation of the output mesh
            // is done in two steps. First, the mesh is created. That involves
            // calculating the position of the vertices and their connectivity.
            //
            // Second, blind data is associated to the vertices on the mesh.
            // For this example, three double blind data values is associated
            // to each vertex: "red", "green" and "blue".
            //
            MFnMeshData dataCreator;
            MDataHandle outputHandle = data.outputValue(outputMesh, &returnStatus);
            McheckErr(returnStatus, "ERROR getting polygon data handle\n");
            MObject newOutputData = dataCreator.create(&returnStatus);
            McheckErr(returnStatus, "ERROR creating outputData");
            createMesh(seed, newOutputData, returnStatus);
            McheckErr(returnStatus, "ERROR creating new plane");
            returnStatus = setMeshBlindData(newOutputData);
            McheckErr(returnStatus, "ERROR setting the blind Data on the plane");

            outputHandle.set(newOutputData);
            data.setClean( plug );
        } else
            return MS::kUnknownParameter;

        return MS::kSuccess;
    }

    MStatus blindDataMesh::setMeshBlindData(MObject& mesh)
    {
        MStatus stat = MS::kSuccess;
        MFnMesh meshFn(mesh);

        // First, make sure that the blind data attribute exists,
        // Otherwise, create it.
        //
        int blindDataID = 60;
        if (!meshFn.isBlindDataTypeUsed(blindDataID, &stat))
        {
            MStringArray longNames, shortNames, formatNames;

            longNames.append("red_color");
            shortNames.append("red");
            formatNames.append("double");

            longNames.append("green");
            shortNames.append("grn");
            formatNames.append("double");

            longNames.append("blue_color");
            shortNames.append("blue");
            formatNames.append("double");

            stat = meshFn.createBlindDataType(
                blindDataID, longNames, shortNames, formatNames );
            if (!stat) return stat;
        }
        else if (!stat) return stat;

        // Iterate through the mesh vertices and assign to each some
        // color value that is related to the height of the vertex
        // so that it goes from dark blue at the lowest point to
        // white at the highest..
        //
        // Find the lowest and the highest points.
        //
        MItMeshVertex itVertex(mesh);
        double lowest = 1e10, highest = -1e10;
        for ( ; !itVertex.isDone(); itVertex.next() )
        {
            MPoint vertexPosition = itVertex.position();
            double height = vertexPosition[1];
            if (height < lowest) lowest = height;
            if (height > highest) highest = height;
        }

        double range = highest - lowest;
        for ( itVertex.reset(mesh); !itVertex.isDone(); itVertex.next() )
        {
            MPoint vertexPosition = itVertex.position();
            double height = vertexPosition[1] - lowest;
            double red, green, blue;

            // Calculate the interpolated color for each vertex
            // using its relative height
            //
            red = 2.0 * ( height / range ) - 1.0;
            if (height > range/2.0) {
                if (red < 0.7) green = 0.7;
                else green = red;
            }
            else green = 0.7 * (1.0 - (((range/2.0) - height) / (range/2.0))
                * (((range/2.0) - height) / (range/2.0)));
            if (height > range/2.0) blue = red;
            else blue = 1.0 - (green*green);
            if (red < 0.0) red = 0.0;

            // Set the color values in the blind data
            //
            stat = meshFn.setDoubleBlindData(itVertex.index(),
                MFn::kMeshVertComponent, blindDataID, "red", red);
            if (!stat) return stat;
            stat = meshFn.setDoubleBlindData(itVertex.index(),
                MFn::kMeshVertComponent, blindDataID, "green", green);
            if (!stat) return stat;
            stat = meshFn.setDoubleBlindData(itVertex.index(),
                MFn::kMeshVertComponent, blindDataID, "blue", blue);
            if (!stat) return stat;
        }

        return stat;
    }