apiMeshShape/apiMeshCreator.cpp
 
 
 
apiMeshShape/apiMeshCreator.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.
// ==========================================================================
//+

//
// apiMeshCreator.cpp
//

#include <math.h>
#include <maya/MIOStream.h>

#include <apiMeshCreator.h>           
#include <apiMeshData.h>
#include <api_macros.h>

#include <maya/MFnMesh.h>
#include <maya/MFnPluginData.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnNumericAttribute.h>

#ifndef M_PI
#define M_PI            3.14159265358979323846
#endif

#ifndef M_PI_2
#define M_PI_2          1.57079632679489661923
#endif

//
// Shape implementation
//

MObject apiMeshCreator::size;
MObject apiMeshCreator::shapeType;
MObject apiMeshCreator::inputMesh;
MObject apiMeshCreator::outputSurface;

MTypeId apiMeshCreator::id( 0x80089 );

apiMeshCreator::apiMeshCreator() {}
apiMeshCreator::~apiMeshCreator() {}

//
// Overrides
//

/* override */
MStatus apiMeshCreator::compute( const MPlug& plug, MDataBlock& datablock )
//
// Description
//
//    When input attributes are dirty this method will be called to
//    recompute the output attributes.
//
{ 
    MStatus stat;
    if ( plug == outputSurface ) {
                // Create some user defined geometry data and access the
                // geometry so we can set it
                //
                MFnPluginData fnDataCreator;
                MTypeId tmpid( apiMeshData::id );
                
                fnDataCreator.create( tmpid, &stat );
                MCHECKERROR( stat, "compute : error creating apiMeshData")

                apiMeshData * newData = (apiMeshData*)fnDataCreator.data( &stat );
                MCHECKERROR( stat, "compute : error gettin at proxy apiMeshData object")

                apiMeshGeom * geomPtr = newData->fGeometry;

                // If there is an input mesh then copy it's values
                // and construct some apiMeshGeom for it.
                //
                bool hasHistory = computeInputMesh( plug, datablock,
                                                                                        geomPtr->vertices,
                                                                                        geomPtr->face_counts,
                                                                                        geomPtr->face_connects,
                                                                                        geomPtr->normals, 
                                                                                        geomPtr->uvcoords
                        );
                                                                                        
                // There is no input mesh so check the shapeType attribute
                // and create either a cube or a sphere.
                //
                if ( !hasHistory ) {
                        MDataHandle sizeHandle = datablock.inputValue( size );
                        double shape_size = sizeHandle.asDouble();
                        MDataHandle typeHandle = datablock.inputValue( shapeType );
                        short shape_type = typeHandle.asShort();

                        switch( shape_type )
                        {
                                case 0 : // build a cube
                                        buildCube( shape_size,
                                                           geomPtr->vertices,
                                                           geomPtr->face_counts,
                                                           geomPtr->face_connects,
                                                           geomPtr->normals, 
                                                           geomPtr->uvcoords
                                                );
                                        break;
                        
                                case 1 : // buld a sphere
                                        buildSphere( shape_size,
                                                                 32,
                                                                 geomPtr->vertices,
                                                                 geomPtr->face_counts,
                                                                 geomPtr->face_connects,
                                                                 geomPtr->normals, 
                                                                 geomPtr->uvcoords
                                                );
                                        break;
                        } // end switch
                }

                geomPtr->faceCount = geomPtr->face_counts.length();

                // Assign the new data to the outputSurface handle
                //
                MDataHandle outHandle = datablock.outputValue( outputSurface );
                outHandle.set( newData );
                datablock.setClean( plug );
                return MS::kSuccess;
    }
    else {
        return MS::kUnknownParameter;
    }
}

//
// Helper routines
//

void apiMeshCreator::buildCube( 
        double cube_size, 
        MPointArray& pa,
        MIntArray& faceCounts, 
        MIntArray& faceConnects,
        MVectorArray& normals, 
        apiMeshGeomUV& uvs
)
//
// Description
//
//    Constructs a cube
//
{
        const int num_faces                     = 6;
        const int num_face_connects     = 24;
        const double normal_value   = 0.5775;
        const int uv_count                      = 14; 
        
        pa.clear(); faceCounts.clear(); faceConnects.clear();
        uvs.reset(); 

        pa.append( MPoint( -cube_size, -cube_size, -cube_size ) );
        pa.append( MPoint(  cube_size, -cube_size, -cube_size ) );
        pa.append( MPoint(  cube_size, -cube_size,  cube_size ) );
        pa.append( MPoint( -cube_size, -cube_size,  cube_size ) );
        pa.append( MPoint( -cube_size,  cube_size, -cube_size ) );
        pa.append( MPoint( -cube_size,  cube_size,  cube_size ) );
        pa.append( MPoint(  cube_size,  cube_size,  cube_size ) );
        pa.append( MPoint(  cube_size,  cube_size, -cube_size ) );

        normals.append( MVector( -normal_value, -normal_value, -normal_value ) );
        normals.append( MVector(  normal_value, -normal_value, -normal_value ) );
        normals.append( MVector(  normal_value, -normal_value,  normal_value ) );
        normals.append( MVector( -normal_value, -normal_value,  normal_value ) );
        normals.append( MVector( -normal_value,  normal_value, -normal_value ) );
        normals.append( MVector( -normal_value,  normal_value,  normal_value ) );
        normals.append( MVector(  normal_value,  normal_value,  normal_value ) );
        normals.append( MVector(  normal_value,  normal_value, -normal_value ) );

        // Define the UVs for the cube. 
        //
        float uv_pts[uv_count * 2] = { 0.375, 0.0, 
                                                                   0.625, 0.0,
                                                                   0.625, 0.25, 
                                                                   0.375, 0.25,
                                                                   0.625, 0.5,
                                                                   0.375, 0.5,
                                                                   0.625, 0.75,
                                                                   0.375, 0.75,
                                                                   0.625, 1.0,
                                                                   0.375, 1.0,
                                                                   0.875, 0.0,
                                                                   0.875, 0.25,
                                                                   0.125, 0.0,
                                                                   0.125, 0.25 
        }; 
        
        // UV Face Vertex Id. 
        //
        int uv_fvid [ num_face_connects ] = {  0, 1, 2, 3, 
                                                                                   3, 2, 4, 5, 
                                                                                   5, 4, 6, 7, 
                                                                                   7, 6, 8, 9, 
                                                                                   1, 10, 11, 2, 
                                                                                   12, 0, 3, 13 };

        int i;
        for ( i = 0; i < uv_count; i ++ ) {
                uvs.append_uv( uv_pts[i*2], uv_pts[i*2 + 1] ); 
        }
        
        for ( i = 0; i < num_face_connects; i ++ ) { 
                uvs.faceVertexIndex.append( uv_fvid[i] ); 
        }

        // Set up an array containing the number of vertices
        // for each of the 6 cube faces (4 verticies per face)
        //
        int face_counts[num_faces] = { 4, 4, 4, 4, 4, 4 };

        for ( i=0; i<num_faces; i++ )
        {
                faceCounts.append( face_counts[i] );
        }

        // Set up and array to assign vertices from pa to each face 
        //
        int face_connects[ num_face_connects ] = {      0, 1, 2, 3,
                                                                                                4, 5, 6, 7,
                                                                                                3, 2, 6, 5,
                                                                                                0, 3, 5, 4,
                                                                                                0, 4, 7, 1,
                                                                                                1, 7, 6, 2      };
        for ( i=0; i<num_face_connects; i++ )
        {
                faceConnects.append( face_connects[i] );
        }
}

void apiMeshCreator::buildSphere( 
        double                          rad, 
        int                                     div, 
        MPointArray &           vertices,
        MIntArray &                     counts, 
        MIntArray &                     connects,
        MVectorArray &          normals, 
        apiMeshGeomUV &         uvs
)
//
// Description
//
//    Create circles of vertices starting with 
//    the top pole ending with the botton pole
//
{
        double u = -M_PI_2;
        double v = -M_PI;
        double u_delta = M_PI / ((double)div); 
        double v_delta = 2 * M_PI / ((double)div); 

        MPoint topPole( 0.0, rad, 0.0 );
        MPoint botPole( 0.0, -rad, 0.0 );

        // Build the vertex and normal table
        //
        vertices.append( botPole );
        normals.append( botPole-MPoint::origin );
        int i;
        for ( i=0; i<(div-1); i++ )
        {
                u += u_delta;
                v = -M_PI;

                for ( int j=0; j<div; j++ )
                {
                        double x = rad * cos(u) * cos(v);
                        double y = rad * sin(u);
                        double z = rad * cos(u) * sin(v) ;
                        MPoint pnt( x, y, z );
                        vertices.append( pnt );
                        normals.append( pnt-MPoint::origin );
                        v += v_delta;
                }
        }
        vertices.append( topPole );
        normals.append( topPole-MPoint::origin );

        // Create the connectivity lists
        //
        int vid = 1;
        int numV = 0;
        for ( i=0; i<div; i++ )
        {
                for ( int j=0; j<div; j++ )
                {
                        if ( i==0 ) {
                                counts.append( 3 );
                                connects.append( 0 );
                                connects.append( j+vid );
                                connects.append( (j==(div-1)) ? vid : j+vid+1 );
                        }
                        else if ( i==(div-1) ) {
                                counts.append( 3 );
                                connects.append( j+vid+1-div );
                                connects.append( vid+1 );
                                connects.append( j==(div-1) ? vid+1-div : j+vid+2-div );
                        }
                        else {
                                counts.append( 4 );
                                connects.append( j + vid+1-div );
                                connects.append( j + vid+1 );
                                connects.append( j == (div-1) ? vid+1 : j+vid+2 );
                                connects.append( j == (div-1) ? vid+1-div : j+vid+2-div );
                        }
                        numV++;
                }
                vid = numV;
        }

        // TODO: Define UVs for sphere ...
        //
}

MStatus apiMeshCreator::computeInputMesh( 
        const MPlug&            plug,
        MDataBlock&                     datablock,
        MPointArray&            vertices,
        MIntArray&                      counts,
        MIntArray&                      connects,
        MVectorArray&           normals, 
        apiMeshGeomUV&          uvs
)
//
// Description
//
//     This function takes an input surface of type kMeshData and converts
//     the geometry into this nodes attributes.
//     Returns kFailure if nothing is connected.
//
{
        MStatus stat;

    // Get the input subdiv
    //        
    MDataHandle inputData = datablock.inputValue( inputMesh, &stat );
    MCHECKERROR( stat, "compute get inputMesh")
    MObject surf = inputData.asMesh();

        // Check if anything is connected
        //
        MObject thisObj = thisMObject();
        MPlug surfPlug( thisObj, inputMesh );
        if ( !surfPlug.isConnected() ) {
        stat = datablock.setClean( plug );
            MCHECKERROR( stat, "compute setClean" )
                return MS::kFailure;
        }

        // Extract the mesh data
        //
        MFnMesh surfFn (surf, &stat);
        MCHECKERROR( stat, "compute - MFnMesh error" );
        stat = surfFn.getPoints( vertices, MSpace::kObject );
        MCHECKERROR( stat, "compute getPoints"); 

        // Check to see if we have UVs to copy. 
        //
        bool hasUVs = surfFn.numUVs() > 0;      
        surfFn.getUVs( uvs.ucoord, uvs.vcoord ); 

        for ( int i=0; i<surfFn.numPolygons(); i++ )
        {
                MIntArray polyVerts;
                surfFn.getPolygonVertices( i, polyVerts );
                int pvc = polyVerts.length();
                counts.append( pvc );
                int uvId; 
                for ( int v=0; v<pvc; v++ ) {
                        if ( hasUVs ) {
                                surfFn.getPolygonUVid( i, v, uvId );
                                uvs.faceVertexIndex.append( uvId );
                        }
                        connects.append( polyVerts[v] );
                }
        }

        for ( int n=0; n<(int)vertices.length(); n++ )
        {
                MVector normal;
                surfFn.getVertexNormal( n, normal );
                normals.append( normal );
        }

        return MS::kSuccess;
}

void* apiMeshCreator::creator()
//
// Description
//    Called internally to create a new instance of the users MPx node.
//
{
        return new apiMeshCreator();
}

MStatus apiMeshCreator::initialize()
//
// Description
//
//    Attribute (static) initialization. See api_macros.h.
//
{ 
        MStatus                         stat;
    MFnTypedAttribute   typedAttr;
        MFnEnumAttribute        enumAttr;

        // ----------------------- INPUTS -------------------------

        MAKE_NUMERIC_ATTR(      size, "size", "sz",
                                                MFnNumericData::kDouble, 1,
                                                false, false, true );

        shapeType = enumAttr.create( "shapeType", "st", 0, &stat );
        MCHECKERROR( stat, "create shapeType attribute" );
        stat = enumAttr.addField( "cube", 0 );
        MCHECKERROR( stat, "add enum type cube" );
        stat = enumAttr.addField( "sphere", 1 );
        MCHECKERROR( stat, "add enum type sphere" );
    CHECK_MSTATUS( enumAttr.setHidden( false ) );
    CHECK_MSTATUS( enumAttr.setKeyable( true ) );
        ADD_ATTRIBUTE( shapeType );
        
        MAKE_TYPED_ATTR( inputMesh, "inputMesh", "im", MFnData::kMesh, NULL );

        // ----------------------- OUTPUTS -------------------------
    outputSurface = typedAttr.create( "outputSurface", "os",
                                                                          apiMeshData::id,
                                                                          MObject::kNullObj, &stat );
    MCHECKERROR( stat, "create outputSurface attribute" )
        typedAttr.setWritable( false );
    ADD_ATTRIBUTE( outputSurface );

        // ---------- Specify what inputs affect the outputs ----------
        //
    ATTRIBUTE_AFFECTS( inputMesh, outputSurface );
    ATTRIBUTE_AFFECTS( size, outputSurface );
    ATTRIBUTE_AFFECTS( shapeType, outputSurface );

        return MS::kSuccess;
}