//- // ========================================================================== // Copyright (C) 1995 - 2005 Alias Systems Corp. and/or its licensors. All // rights reserved. // // The coded instructions, statements, computer programs, and/or related // material (collectively the "Data") in these files are provided by Alias // Systems Corp. ("Alias") and/or its licensors for the exclusive use of the // Customer (as defined in the Alias Software License Agreement that // accompanies this Alias software). Such Customer has the right to use, // modify, and incorporate the Data into other products and to distribute such // products for use by end-users. // // THE DATA IS PROVIDED "AS IS". ALIAS HEREBY DISCLAIMS ALL WARRANTIES // RELATING TO THE DATA, INCLUDING, WITHOUT LIMITATION, ANY AND ALL EXPRESS OR // IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND/OR FITNESS FOR A // PARTICULAR PURPOSE. IN NO EVENT SHALL ALIAS BE LIABLE FOR ANY DAMAGES // WHATSOEVER, WHETHER DIRECT, INDIRECT, SPECIAL, OR PUNITIVE, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, OR IN EQUITY, // ARISING OUT OF ACCESS TO, USE OF, OR RELIANCE UPON THE DATA. // ========================================================================== //+ /***************************************************** 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. Alias 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