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

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