//- // ========================================================================== // Copyright (C) 1995 - 2006 Autodesk, Inc. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files contain unpublished // information proprietary to Autodesk, Inc. ("Autodesk") and/or its // licensors, which is protected by U.S. and Canadian federal copyright // law and by international treaties. // // The Data is provided for use exclusively by You. You have the right // to use, modify, and incorporate this Data into other products for // purposes authorized by the Autodesk software license agreement, // without fee. // // The copyright notices in the Software and this entire statement, // including the above license grant, this restriction and the // following disclaimer, must be included in all copies of the // Software, in whole or in part, and all derivative works of // the Software, unless such copies or derivative works are solely // in the form of machine-executable object code generated by a // source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. // AUTODESK DOES NOT MAKE AND HEREBY DISCLAIMS ANY EXPRESS OR IMPLIED // WARRANTIES INCLUDING, BUT NOT LIMITED TO, THE WARRANTIES OF // NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR // PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE, OR // TRADE PRACTICE. IN NO EVENT WILL AUTODESK AND/OR ITS LICENSORS // BE LIABLE FOR ANY LOST REVENUES, DATA, OR PROFITS, OR SPECIAL, // DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES, EVEN IF AUTODESK // AND/OR ITS LICENSORS HAS BEEN ADVISED OF THE POSSIBILITY // OR PROBABILITY OF SUCH DAMAGES. // // ========================================================================== //+ /***************************************************** IMPORTANT NOTE: In order to compile and link ribExport.cpp you must have Pixar's ri.h header file as well as all necessary libraries needed for linking. Autodesk cannot distribute these files. Please contact Pixar about how to obtain these files. *****************************************************/ #include #include #include #if defined (_WIN32) #include #else #include #endif #if defined (OSMac_) extern "C" char * strdup (const char *rhs); #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //////////////////////// // Macros and Defines // //////////////////////// // Equivalence test for floats. Equality tests are dangerous for floating // point values // #define FLOAT_EPSILON 0.0001 inline bool equiv( float val1, float val2 ) { return ( fabsf( val1 - val2 ) < FLOAT_EPSILON ); } // Specifies how the start/end frame is set // #define USE_GLOBALS 0 #define USE_TIMESLIDER 1 #ifndef MM_TO_INCH # define MM_TO_INCH 0.03937 #endif /////////// // Enums // /////////// enum ObjectType { MRT_Unknown = 0, MRT_Nurbs = 1, MRT_Mesh = 2, MRT_Light = 3, MRT_Weirdo = 4 }; enum LightType { MRLT_Unknown = 0, MRLT_Ambient = 1, MRLT_Distant = 2, MRLT_Point = 3, MRLT_Spot = 4 }; enum AnimType { MRX_Const = 0, MRX_Animated = 1, MRX_Incompatible = 2 }; ///////////////////////////// // Global helper functions // ///////////////////////////// bool isObjectVisible( const MDagPath & path ) // // Description: // Check if the given object is visible // { MFnDagNode fnDN( path ); // Check the visibility attribute of the node // MPlug vPlug = fnDN.findPlug( "visibility" ); // Also check to see if the node is an intermediate object in // a computation. For example, it could be in the middle of a // chain of deformations. Intermediate objects are not visible. // MPlug iPlug = fnDN.findPlug( "intermediateObject" ); bool visible, intermediate; vPlug.getValue( visible ); iPlug.getValue( intermediate ); return visible && !intermediate; } bool areObjectAndParentsVisible( const MDagPath & path ) // // Description: // Check if this object and all of its parents are visible. In Maya, // visibility is determined by heirarchy. So, if one of a node's // parents is invisible, then so is the node. // { bool result = true; MDagPath searchPath( path ); while ( true ) { if ( !isObjectVisible( searchPath ) ){ result = false; break; } if ( searchPath.length() == 1 ) break; searchPath.pop(); } return result; } ////////////////////// // Geometry Classes // ////////////////////// // Base class for storing maya object data in a RIB compatible form // class RibData { public: virtual ~RibData(); virtual void write() = 0; virtual bool compare( const RibData & other ) const = 0; virtual ObjectType type() const = 0; }; RibData::~RibData() {} // Storage for Nurbs Surface data // class RibSurfaceData : public RibData { public: // Methods RibSurfaceData( MObject surface ); virtual ~RibSurfaceData(); virtual void write(); virtual bool compare( const RibData & other ) const; virtual ObjectType type() const; bool hasTrimCurves() const; void writeTrimCurves() const; private: // Data bool hasTrims; RtInt nu, nv; RtInt uorder, vorder; RtFloat * uknot; RtFloat * vknot; RtFloat umin, umax, vmin, vmax; RtFloat * CVs; // Trim information // RtInt nloops; RtInt * ncurves, * order, * n; RtFloat * knot, * minKnot, * maxKnot, * u, * v, * w; }; RibSurfaceData::RibSurfaceData( MObject surface ) // // Description: // create a RIB compatible representation of a Maya nurbs surface // : uknot( NULL ), vknot( NULL ), CVs( NULL ), ncurves( NULL ), order( NULL ), n( NULL ), knot( NULL ), minKnot( NULL ), maxKnot( NULL ), u( NULL ), v( NULL ), w( NULL ), hasTrims( false ) { MStatus status = MS::kSuccess; MFnNurbsSurface nurbs( surface, &status ); if (status != MS::kSuccess) { // seems like some ill-defined patches are sometimes // present in the database (cf: test scene) // MString error("Empty nurbs object, skipped"); throw( error ); } // Extract the order and number of CVs in the surface keeping // in mind that UV order is switched between Renderman and Maya // uorder = nurbs.degreeV() + 1; // uv order is switched vorder = nurbs.degreeU() + 1; // uv order is switched nv = nurbs.numCVsInU(); // uv order is switched nu = nurbs.numCVsInV(); // uv order is switched if (nu < 2 || nv < 2) { // seems like some ill-defined patches are sometimes // present in the database (cf: test scene) // MString error("Ill-defined nurbs object: "); error += nurbs.name(); throw( error ); } // Read the knot information // MDoubleArray uKnots, vKnots; nurbs.getKnotsInU(vKnots); // uv order is switched nurbs.getKnotsInV(uKnots); // uv order is switched double uMin_d, uMax_d, vMin_d, vMax_d; nurbs.getKnotDomain(uMin_d, uMax_d, vMin_d, vMax_d); umin = (RtFloat)vMin_d; // uv order is switched umax = (RtFloat)vMax_d; // uv order is switched vmin = (RtFloat)uMin_d; // uv order is switched vmax = (RtFloat)uMax_d; // uv order is switched // Allocate CV and knot storage // CVs = (RtFloat*)malloc( sizeof( RtFloat ) * ( nu * nv * 4 ) ); uknot = (RtFloat*)malloc( sizeof( RtFloat ) * ( uKnots.length() + 2 ) ); vknot = (RtFloat*)malloc( sizeof( RtFloat ) * ( vKnots.length() + 2 ) ); unsigned k; for ( k = 0; k < uKnots.length(); k++ ) uknot[k+1] = (RtFloat)uKnots[k]; // Maya doesn't store the first and last knots, so we double them up // manually // uknot[0] = uknot[1]; uknot[k+1] = uknot[k]; for ( k = 0; k < vKnots.length(); k++ ) vknot[k+1] = (RtFloat)vKnots[k]; // Maya doesn't store the first and last knots, so we double them up // manually // vknot[0] = vknot[1]; vknot[k+1] = vknot[k]; // Read CV information // MItSurfaceCV cvs( surface, false ); RtFloat* cvPtr = CVs; while(!cvs.isDone()) { while(!cvs.isRowDone()) { MPoint pt = cvs.position(MSpace::kObject); // Maya points are predivided, whereas RIB ones are not. *cvPtr++ = (RtFloat)(pt.x * pt.w); *cvPtr++ = (RtFloat)(pt.y * pt.w); *cvPtr++ = (RtFloat)(pt.z * pt.w); *cvPtr++ = (RtFloat)pt.w; cvs.next(); } cvs.nextRow(); } // Store trim information // if (nurbs.isTrimmedSurface()) { hasTrims = true; unsigned numRegions, numBoundaries, numEdges, numCurves; unsigned r, b, e, c; // Get the number of loops // numRegions = nurbs.numRegions(); nloops = 0; for ( r = 0; r < numRegions; r++ ) { numBoundaries = nurbs.numBoundaries( r ); nloops += numBoundaries; } MIntArray numCurvesPerLoop, orderArray, numCVsArray; MFloatArray knotArray, minArray, maxArray; MPointArray cvArray; // Get the number of trim curves in each loop and gather curve // information // for ( r = 0; r < (unsigned)nloops; r++ ) { numBoundaries = nurbs.numBoundaries( r ); for ( b = 0; b < numBoundaries; b++ ) { numCurves = 0; numEdges = nurbs.numEdges( r, b ); for ( e = 0; e < numEdges; e++ ) { MObjectArray curves = nurbs.edge( r, b, e, true ); numCurves += curves.length(); // Gather extra stats for each curve // for ( c = 0; c < curves.length(); c++ ) { unsigned i; // Read the # of CVs in and the order of each curve // MFnNurbsCurve curveFn(curves[c]); orderArray.append( curveFn.degree() + 1 ); numCVsArray.append( curveFn.numCVs() ); // Read the CVs for each curve // MPoint pnt; unsigned last = curveFn.numCVs(); for ( i = 0; i < last; ++i ) { curveFn.getCV( i, pnt ); // Maya points are predivided, whereas RIB // ones are not. pnt[0] *= pnt[3]; pnt[1] *= pnt[3]; pnt[2] *= pnt[3]; cvArray.append( pnt ); } // Read the knot array for each curve // MDoubleArray knotsTmpArray; curveFn.getKnots( knotsTmpArray ); last = knotsTmpArray.length(); knotArray.append( (float)knotsTmpArray[0] ); for ( i = 0; i < last; ++i ) { knotArray.append( (float)knotsTmpArray[i] ); } knotArray.append( (float)knotsTmpArray[last-1] ); // Read the knot domain for each curve // double start, end; curveFn.getKnotDomain( start, end ); minArray.append( (float) start ); maxArray.append( (float) end ); } } numCurvesPerLoop.append( numCurves ); } } // Store the trim information in RIB format // ncurves = (RtInt*)malloc( sizeof( RtInt ) * numCurvesPerLoop.length() ); numCurvesPerLoop.get( (int*)ncurves ); order = (RtInt*)malloc( sizeof( RtInt ) * orderArray.length() ); orderArray.get( (int*)order ); n = (RtInt*)malloc( sizeof( RtInt ) * numCVsArray.length() ); numCVsArray.get( (int*)n ); knot = (RtFloat*)malloc( sizeof( RtFloat ) * knotArray.length() ); knotArray.get( knot ); minKnot = (RtFloat*)malloc( sizeof( RtFloat ) * minArray.length() ); minArray.get( minKnot ); maxKnot = (RtFloat*)malloc( sizeof( RtFloat ) * maxArray.length() ); maxArray.get( maxKnot ); unsigned last = cvArray.length(); u = (RtFloat*)malloc( sizeof( RtFloat ) * last ); v = (RtFloat*)malloc( sizeof( RtFloat ) * last ); w = (RtFloat*)malloc( sizeof( RtFloat ) * last ); for ( unsigned i = 0; i < last; ++i ) { u[i] = (RtFloat)( cvArray[i].y * cvArray[i].w ); // u v[i] = (RtFloat)( cvArray[i].x * cvArray[i].w ); // v w[i] = (RtFloat) cvArray[i].w; // w } } } RibSurfaceData::~RibSurfaceData() // // Description: // class destructor // { // Free all arrays // if ( NULL != uknot ) free( uknot ); if ( NULL != vknot ) free( vknot ); if ( NULL != CVs ) free( CVs ); if ( NULL != ncurves ) free( ncurves ); if ( NULL != order ) free( order ); if ( NULL != n ) free( n ); if ( NULL != knot ) free( knot ); if ( NULL != minKnot ) free( minKnot ); if ( NULL != maxKnot ) free( maxKnot ); if ( NULL != u ) free( u ); if ( NULL != v ) free( v ); if ( NULL != w ) free( w ); } void RibSurfaceData::write() // // Description: // Write the RIB for this surface // { RiNuPatch( nu, uorder, uknot, umin, umax, nv, vorder, vknot, vmin, vmax, RI_PW, (RtPointer)CVs, RI_NULL ); } bool RibSurfaceData::compare( const RibData & otherObj ) const // // Description: // Compare this surface to the other for the purpose of determining // if it is animated. // { if ( otherObj.type() != MRT_Nurbs ) return false; const RibSurfaceData & other = (RibSurfaceData&)otherObj; if ( ( nu != other.nu ) || ( nv != other.nv ) || ( uorder != other.uorder ) || ( vorder != other.vorder ) || !equiv( umin, other.umin ) || !equiv( umax, other.umax ) || !equiv( vmin, other.vmin ) || !equiv( vmax, other.vmax ) ) { return false; } // Check Knots // unsigned i; unsigned last = nu + uorder; for ( i = 0; i < last; ++i ) { if ( !equiv( uknot[i], other.uknot[i] ) ) return false; } last = nv + vorder; for ( i = 0; i < last; ++i ) { if ( !equiv( vknot[i], other.vknot[i] ) ) return false; } // Check CVs // last = nu * nv * 4; for ( i = 0; i < last; ++i ) { if ( !equiv( CVs[i], other.CVs[i] ) ) return false; } // FUTURE: Check trims as well return true; } ObjectType RibSurfaceData::type() const // // Description: // return the geometry type // { return MRT_Nurbs; } bool RibSurfaceData::hasTrimCurves() const { return hasTrims; } void RibSurfaceData::writeTrimCurves() const { if ( hasTrims ) { RiTrimCurve( nloops, ncurves, order, knot, minKnot, maxKnot, n, u, v, w ); } } // Storage for Mesh data // class RibMeshData : public RibData { public: // Methods RibMeshData( MObject mesh ); virtual ~RibMeshData(); virtual void write(); virtual bool compare( const RibData & other ) const; virtual ObjectType type() const; private: // Data RtInt npolys; RtInt * nverts; RtInt * verts; RtPoint * vertexParam; RtPoint * normalParam; unsigned totalNumOfVertices; }; RibMeshData::RibMeshData( MObject mesh ) // // Description: // create a RIB compatible representation of a Maya polygon mesh // : npolys( 0 ), nverts( NULL ), verts( NULL ), vertexParam( NULL ), normalParam( NULL ), totalNumOfVertices( 0 ) { MFnMesh fnMesh( mesh ); // To handle the cases where there are multiple normals per // vertices (hard-edges) we store a vertex for each normal. // totalNumOfVertices = fnMesh.numNormals(); npolys = fnMesh.numPolygons(); // Allocate memory for arrays // vertexParam = (RtPoint*)malloc( sizeof( RtPoint ) * totalNumOfVertices ); normalParam = (RtPoint*)malloc( sizeof( RtPoint ) * totalNumOfVertices ); nverts = (RtInt*) malloc( sizeof( RtInt ) * npolys ); // Use maya array for per polygon vertex lists because we don't // know the exact size in advance // MIntArray perPolyVertices; // Get per face information // MPoint position; unsigned count, index = 0; for ( MItMeshPolygon polyIt( mesh ); !polyIt.isDone(); polyIt.next() ) { count = polyIt.polygonVertexCount(); nverts[index] = count; // Note that we need to reverse the normals for RIB so we // get the vertex id's in reverse order do { count--; unsigned normalIndex = polyIt.normalIndex( count ); perPolyVertices.append( normalIndex ); position = polyIt.point( count ); vertexParam[normalIndex][0] = (RtFloat) position.x; vertexParam[normalIndex][1] = (RtFloat) position.y; vertexParam[normalIndex][2] = (RtFloat) position.z; } while (count != 0); ++index; } verts = (RtInt*)malloc( sizeof( RtInt ) * perPolyVertices.length() ); perPolyVertices.get( (int*)verts ); MFloatVectorArray normals; fnMesh.getNormals( normals ); for ( index = 0; index