diff options
| author | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
|---|---|---|
| committer | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
| commit | bd0027e737c6512397f841c22786274ed74b927f (patch) | |
| tree | f7ffbdb8f3741bb7f24635616cc189cba5cb865c /mayaPlug/shaveObjExporter.cpp | |
| download | shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.tar.xz shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.zip | |
Adding Shave-and-a-Haircut 9.6
Diffstat (limited to 'mayaPlug/shaveObjExporter.cpp')
| -rw-r--r-- | mayaPlug/shaveObjExporter.cpp | 2054 |
1 files changed, 2054 insertions, 0 deletions
diff --git a/mayaPlug/shaveObjExporter.cpp b/mayaPlug/shaveObjExporter.cpp new file mode 100644 index 0000000..c982550 --- /dev/null +++ b/mayaPlug/shaveObjExporter.cpp @@ -0,0 +1,2054 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +#include "shaveIO.h" + +#include "shaveHairShape.h" +#include "shaveSDK.h" +#include "shaveObjExporter.h" +#include "shaveUtil.h" +#include <maya/MDagModifier.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFnMesh.h> +#include <maya/MFnNurbsSurface.h> +#include <maya/MFnSingleIndexedComponent.h> +#include <maya/MIntArray.h> +#include <maya/MPlugArray.h> +#include <maya/MRenderUtil.h> +#include <maya/MUint64Array.h> + +#ifdef OSMac_ +#include "shaveMacCarbon.h" +#endif + + +#define MEMFILE_CHUNKSIZE 1024 + +static const int numSamplesPerCurve = 30; + + +// We've got a few globals we need to make sure are initialized. +shaveObjTranslator::shaveObjTranslator() +: space(MSpace::kWorld) +{ +} + + +void* shaveObjTranslator::creator() +{ + return new shaveObjTranslator; +} + + +//! export all of the objects we need to build hair for this shaveHairShape. +/** +exportThis is called by shaveHairShape and shaveRender to export geometry to +.obj files and the WFType structure. +*/ +MStatus shaveObjTranslator::exportThis( + MDataBlock& block, ShaveExportData& exportData +) +{ + MFnDependencyNode nodeFn(exportData.shaveHairShape); + shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); + MObjectArray tesselations; + +#if GET_GEOM_FROM_DATABLOCK + nodePtr->getGrowthGeom(block, hairObjects, hairComponents); + nodePtr->getCollisionGeom(block, skullObjects, skullComponents); +#else + nodePtr->getGrowthList(hairSelections); + nodePtr->getCollisionList(skullSelections); +#endif + + int uTess = exportData.uSubdivs; + int vTess = exportData.vSubdivs; + + int subdDepth = exportData.subdDepth; + int subdSamples = exportData.subdSamples; + + newtopo = exportData.newtopo; + + objListInit(); + wfInit(uTess, vTess, subdDepth, subdSamples, exportData, tesselations); + doWFType(exportData.theObj, exportData.uvs, tesselations); + + // + // If we detected new topology, then generate an OBJ file which the + // caller can pass to Shave in a SHAVExplant() call, to let it know the + // new geometry. + // + // However, if the caller did not pass us a filename for the OBJ file, + // then they don't want us to generate an OBJ file. + // + if (newtopo && (exportData.objFileName != "")) + { + bool** growthFaces = NULL; + bool** collisionFaces = NULL; + + mtlLookupInit(growthFaces, collisionFaces); + + doExport( + exportData.objFileName, tesselations, growthFaces, collisionFaces + ); + + mtlLookupCleanup(growthFaces, collisionFaces); + } + + exportData.newtopo = newtopo; + + tesselations.clear(); + fullHairObjects.clear(); + partialHairObjects.clear(); + fullSkullObjects.clear(); + partialSkullObjects.clear(); +#if GET_GEOM_FROM_DATABLOCK + partialHairComponents.clear(); + partialSkullComponents.clear(); +#else + hairSelections.clear(); + skullSelections.clear(); +#endif + + return MS::kSuccess; +} + + +MStatus shaveObjTranslator::objListInit() +{ + MStatus st; + + // + // Some growth or collision surfaces may encompass the entire object + // while others may only involve a subset of the object's faces. + // + // In the case of a subset of faces, we have to do extra processing to + // keep track of which faces are involved, and which are not. The + // first step of that is to separate the full and partial objects into + // separate lists. + // + st = separateOutPartialMeshes( +#if GET_GEOM_FROM_DATABLOCK + hairObjects, + hairComponents, + fullHairObjects, + partialHairObjects, + partialHairComponents +#else + hairSelections, fullHairObjects, partialHairObjects +#endif + ); + + if (st) + { + st = separateOutPartialMeshes( +#if GET_GEOM_FROM_DATABLOCK + skullObjects, + skullComponents. + fullSkullObjects, + partialSkullObjects, + partialSkullComponents +#else + skullSelections, fullSkullObjects, partialSkullObjects +#endif + ); + } + + return st; +} + + +#if GET_GEOM_FROM_DATABLOCK +MStatus shaveObjTranslator::separateOutPartialMeshes( + MObjectArray& objects, + MObjectArray& components, + MObjectArray& fullObjects, + MObjectArray& partialMeshes, + MObjectArray& partialMeshComponents +) +{ + fullObjects.clear(); + partialMeshes.clear(); + partialMeshComponents.clear(); + + unsigned i; + + for (i = 0; i < objects.length(); i++) + { + if (!components[i].isNull() && objects[i].hasFn(MFn::kMesh)) + { + partialMeshes.append(objects[i]); + partialMeshComponents.append(components[i]); + } + else + fullObjects.append(objects[i]); + } + + return MS::kSuccess; +} +#else +MStatus shaveObjTranslator::separateOutPartialMeshes( + MSelectionList& objectList, + MDagPathArray& fullObjects, + MSelectionList& partialMeshes +) +{ + MObject component; + MFnDagNode nodeFn; + MDagPath nodePath; + MDagPath shapePath; + + // + // Separate out the objects into those which are full objects, and + // those which are just a subset of a mesh. + // + fullObjects.clear(); + partialMeshes.clear(); + + MItSelectionList iter(objectList); + + for (iter.reset(); !iter.isDone(); iter.next()) + { + iter.getDagPath(nodePath, component); + + // + // 'nodePath' may be pointing to a transform, so get a path to + // the actual shape node beneath it. + // + shapePath = nodePath; + shapePath.extendToShape(); + + if (shapePath.hasFn(MFn::kMesh)) + { + // + // If there's no component specified, then the entire mesh is + // being used. + // + if (component.isNull()) + fullObjects.append(shapePath); + else + partialMeshes.add(nodePath, component); + } + else if (shapePath.hasFn(MFn::kNurbsCurve) + || shapePath.hasFn(MFn::kNurbsSurface) + || shapePath.hasFn(MFn::kSubdiv)) + { + // + // We don't allow partial NURBS curves or surfaces, or + // partial subdivision surfaces, so they always go on the + // 'Full' list. + // + fullObjects.append(shapePath); + } + } + + return MS::kSuccess; +} +#endif + + +MStatus shaveObjTranslator::mtlLookupInit( + bool**& growthMtlTbl, bool**& collisionMtlTbl +) +{ + MStatus st; + + st = createMtlLookup(partialHairObjects, growthMtlTbl); + + if (st) st = createMtlLookup(partialSkullObjects, collisionMtlTbl); + + return st; +} + + +void shaveObjTranslator::mtlLookupCleanup( + bool**& growthMtlTbl, bool**& collisionMtlTbl +) +{ + deleteMtlLookup(partialHairObjects, growthMtlTbl); + deleteMtlLookup(partialSkullObjects, collisionMtlTbl); +} + + +// +// For a mesh, it is possible that only some of its faces are being used +// to grow or collide with hair. So we need to build lookup tables to tell +// use which faces those are. +// +MStatus shaveObjTranslator::createMtlLookup( + MSelectionList& objectList, bool**& lookupTbl +) +{ + // + // Set up a lookup table for partial hair meshes. + // + unsigned int objectCount = objectList.length(); + + if (objectCount > 0) + { + MObject component; + unsigned int i; + int numFaces; + MDagPath shapePath; + + lookupTbl = new bool*[objectCount]; + + for (i = 0; i < objectCount; i++) + { + objectList.getDagPath(i, shapePath, component); + + MItMeshPolygon fullPolyIter(shapePath); + + // + // Allocate an array of flags for all the faces. + // + numFaces = fullPolyIter.count(); + lookupTbl[i] = new bool[numFaces]; + + if (lookupTbl[i] == NULL) return MS::kFailure; + + // + // Initialize all the table entries to false, which means that + // those faces will use the 'default' material. + // + for (int k = 0; k < numFaces; k++) + lookupTbl[i][k] = false; + + // + // For those faces listed in the component, set their table + // entries to true, which means that they will use the 'hair' + // material. + // + MItMeshPolygon partialPolyIter(shapePath, component); + + for (partialPolyIter.reset(); + !partialPolyIter.isDone(); + partialPolyIter.next()) + { + int faceID = partialPolyIter.index(); + + // + // It is possible that because of changes in the topology + // of the growth surface since the hair was assigned, our + // component list will contain faces which no longer exist. + // Oddly enough, the poly iterator will still return those + // faces to us, so we have to be careful to discard any + // which no longer exist. + // + if (faceID < numFaces) + lookupTbl[i][faceID] = true; + } + } + } + + return MS::kSuccess; +} + + +void shaveObjTranslator::deleteMtlLookup( + MSelectionList& objectList, bool**& lookupTbl +) +{ + if (lookupTbl != NULL) + { + unsigned int i; + + for (i = 0; i < objectList.length(); i++) + delete [] lookupTbl[i]; + + delete [] lookupTbl; + } +} + + +// +// Export an array of objects which are completely active. (i.e. hair grows +// from the entire object, or the entire object can collide with hair, etc). +// +void shaveObjTranslator::exportFullObjects( + FILE* exportFile, +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& objects, +#else + MDagPathArray& objects, +#endif + MObjectArray tesselations, + const char* surfaceMaterial, + const char* curveMaterial, + int& objectIndex, + int& totalNumVerts, + int& totalNumFaces +) +{ + int i; + int j; + int objectCount = objects.length(); + int vertCount; +#if GET_GEOM_FROM_DATABLOCK + MObject object; +#else + MDagPath object; +#endif + MPoint p; + + for (i = 0; i < objectCount; i++) + { + unsigned int objStartVertIndex = totalNumVerts; + + object = objects[i]; + + if (object.hasFn(MFn::kMesh)) + { + // + // Output the vertex positions. + // + MItMeshPolygon polyIter(object); + MItMeshVertex vtxIter(object); + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next()) + { + p = vtxIter.position(space); + + fprintf( + exportFile, + "v %f %f %f\n", + (float)p.x, + (float)p.y, + (float)p.z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl %s\n", surfaceMaterial); + + // + // Output the face information. + // + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + fprintf(exportFile, "f "); + + vertCount = polyIter.polygonVertexCount(); + + for(j = 0; j < vertCount; j++) + { + fprintf( + exportFile, + "%d ", + objStartVertIndex + polyIter.vertexIndex(j) + 1 + ); + } + + fprintf(exportFile, "\n"); + + totalNumFaces++; + } + + totalNumVerts += vtxIter.count(); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kNurbsSurface)) +#else + else if (object.hasFn(MFn::kNurbsSurface) + || object.hasFn(MFn::kSubdiv)) +#endif + { + // + // Output the vertex positions of the temporary mesh which + // contains this surface's tesselation. + // + MItMeshVertex vtxIter(tesselations[objectIndex]); + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next() ) + { + p = vtxIter.position(space); + + fprintf( + exportFile, + "v %f %f %f\n", + (float)p.x, + (float)p.y, + (float)p.z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl %s\n", surfaceMaterial); + + // + // Output the face information. + // + MItMeshPolygon faceIter(tesselations[objectIndex]); + + for (faceIter.reset(); !faceIter.isDone(); faceIter.next()) + { + fprintf(exportFile, "f "); + + vertCount = faceIter.polygonVertexCount(); + + for(j = 0; j < vertCount; j++) + { + fprintf( + exportFile, + "%d ", + objStartVertIndex + faceIter.vertexIndex(j) + 1 + ); + } + + fprintf(exportFile, "\n"); + totalNumFaces++; + } + + totalNumVerts += vtxIter.count(); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kSubdiv)) + { + // + // Output a list of all the vertices for the triangulated level + // 1 faces of the subd. + // + MPointArray objVerts; + MIntArray objVertIndices; + MUint64Array quadIDs; + unsigned int v; + + shaveUtil::getSubdQuads( + objectPath, + quadIDs, + objVerts, + objVertIndices + ); + + for (v = 0; v < objVerts.length(); v++) + { + fprintf( + exportFile, + "v %f %f %f\n", + (float)objVerts[v].x, + (float)objVerts[v].y, + (float)objVerts[v].z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl %s\n", surfaceMaterial); + + // + // Output the face information. + // + unsigned int numQuads = quadIDs.length(); + unsigned int q; + + v = 0; + + for (q = 0; q < numQuads; q++) + { + // + // Each quad is split into two triangles: one using verts + // 0, 1 and 2, the other using 0, 2 and 3. + // + // Vertex numbering in the OBJ file begins at 1 but our + // internal indices are all 0-based, so we have to add one + // to the indices before writing them to the file. + // + fprintf( + exportFile, + "f %d %d %d\n", + objStartVertIndex + objVertIndices[v] + 1, + objStartVertIndex + objVertIndices[v+1] + 1, + objStartVertIndex + objVertIndices[v+2] + 1 + ); + + fprintf( + exportFile, + "f %d %d %d\n", + objStartVertIndex + objVertIndices[v] + 1, + objStartVertIndex + objVertIndices[v+2] + 1, + objStartVertIndex + objVertIndices[v+3] + 1 + ); + + v += 4; + } + + totalNumVerts += objVerts.length(); + totalNumFaces += numQuads * 2; + } +#endif + else if (object.hasFn(MFn::kNurbsCurve) && (curveMaterial != NULL)) + { + MFloatPointArray samples; + + getCurveSamples(object, samples); + + unsigned int s; + + for (s = 0; s < samples.length(); s++) + { + fprintf( + exportFile, + "v %f %f %f\n", + samples[s].x, + samples[s].y, + samples[s].z + ); + } + + // + // Output the material type. + // + fprintf(exportFile, "usemtl spline\n"); + + // + // Output the entire curve as a single face. + // + fprintf(exportFile, "f "); + + for (s = samples.length(); s > 0; s--) + { + // + // You may notice that in storeObjectInWFTYPE() we have a + // similar loop which subtracts 1 from the vertex index + // while here we don't. The reason is that the vertex + // indices in an OBJ file are 1-based, while the arrays in + // the WFTYPE are 0-based. + // + fprintf(exportFile, "%d ", objStartVertIndex + s); + } + + fprintf(exportFile, "\n"); + + totalNumFaces++; + totalNumVerts += samples.length(); + } + + fprintf(exportFile, "# end of object.\n"); + objectIndex++; + } +} + + +// +// Export a list of meshes which have some active and some inactive faces. +// +void shaveObjTranslator::exportPartialMeshes( + FILE* exportFile, +#if GET_GEOM_FROM_DATABLOCK + MObjectArray& meshList, +#else + MSelectionList& meshList, +#endif + bool** activeFaceMap, + const char* activeFaceMaterial, + int& objectIndex, + int& vertexOffset, + int& faceOffset +) +{ + int objectCount = meshList.length(); + int i; +#if !GET_GEOM_FROM_DATABLOCK + MDagPath shapePath; +#endif + MPoint p; + + for (i = 0; i < objectCount; i++) + { +#if !GET_GEOM_FROM_DATABLOCK + meshList.getDagPath(i, shapePath); +#endif + + // + // Write out the vertices. + // +#if GET_GEOM_FROM_DATABLOCK + MItMeshVertex vtxIter(meshList[i]); +#else + MItMeshVertex vtxIter(shapePath); +#endif + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next()) + { + p = vtxIter.position(space); + + fprintf( + exportFile, "v %f %f %f\n", (float)p.x, (float)p.y, (float)p.z + ); + } + + // + // Write out the face information. + // + bool firstTime = true; + bool faceIsActive = false; + bool prevFaceIsActive = true; + + MItMeshPolygon polyIter(shapePath); + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + faceIsActive = activeFaceMap[i][polyIter.index()]; + + if (firstTime || (faceIsActive != prevFaceIsActive)) + { + fprintf( + exportFile, + "usemtl %s\n", + faceIsActive ? activeFaceMaterial : "default" + ); + + prevFaceIsActive = faceIsActive; + firstTime = false; + } + + fprintf(exportFile, "f "); + + int vertCount = polyIter.polygonVertexCount(); + + for (int j = 0; j < vertCount; j++) + { + fprintf( + exportFile, "%d ", polyIter.vertexIndex(j)+1+vertexOffset + ); + } + + fprintf(exportFile, "\n"); + faceOffset++; + } + + fprintf(exportFile, "# end of mesh.\n"); + + vertexOffset += vtxIter.count(); + objectIndex++; + } +} + + +// +// Export all surfaces and splines associated with the current shaveHairShape +// into the specified object file. +// +MStatus shaveObjTranslator::doExport( + MString fileName, + MObjectArray tesselations, + bool** growthFaces, + bool** collisionFaces +) +{ + FILE* fp = NULL; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + fileName = shaveMacCarbon::makeMacFilename(fileName); +#endif + + if (fileName == "") + { + cerr << "Shave: internal error: no OBJ file supplied to doExport()." + << endl; + return MS::kFailure; + } + + // + // Open the obj file for writing. Bail as gracefully as possible if + // this fails. + // + fp = fopen(fileName.asChar(), "w"); + + if (fp == NULL) + { + cerr << "Shave: doExport() could not write to '" +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + << fileName.asChar() +#else + << fileName +#endif + << "'." << endl; + + return MS::kFailure; + } + + int objectIndex = 0; + int faceOffset = 0; + int vertOffset = 0; + + // + // Export the hair objects. + // + exportFullObjects( + fp, + fullHairObjects, + tesselations, + "hair", + "spline", + objectIndex, + vertOffset, + faceOffset + ); + + exportPartialMeshes( + fp, + partialHairObjects, + growthFaces, + "hair", + objectIndex, + vertOffset, + faceOffset + ); + + // + // Export the skull objects. + // + exportFullObjects( + fp, + fullSkullObjects, + tesselations, + "skull", + NULL, + objectIndex, + vertOffset, + faceOffset + ); + + exportPartialMeshes( + fp, + partialSkullObjects, + collisionFaces, + "skull", + objectIndex, + vertOffset, + faceOffset + ); + + fflush(fp); + fclose(fp); + + return MS::kSuccess; +} + + +void shaveObjTranslator::getObjectCounts( +#if GET_GEOM_FROM_DATABLOCK + MObject& object, +#else + MDagPath& object, +#endif + GeomType geomType, + int& totalNumVerts, + int& totalNumFaces, + int& totalNumFaceVerts, + int uTess, + int vTess, + int sDept, + int sSamp, + ShaveExportData* exportData, + MObjectArray& tesselations, + bool includeDisplacements +) +{ + MObject tesselation = MObject::kNullObj; + + if (exportData) + { + exportData->meshes.append(object); + exportData->startFaces.append(totalNumFaces); + exportData->startVerts.append(totalNumVerts); + } + + if (object.hasFn(MFn::kMesh)) + { + MStatus st; + MItMeshPolygon* faceIter = 0; + MItMeshVertex* vtxIter = 0; + + // I used to have this inside the 'else' clause where it is used, + // except this was another of those cases where MObject ref + // counting screwed up, leaving the iterators invalid outside the + // else clause. So we put it here instead. + MObject meshDataObj; + + if (includeDisplacements && shaveUtil::isSurfaceDisplaced(object)) + { + tesselation = shaveUtil::getMesh(object, uTess, vTess, sDept, sSamp, true); + faceIter = new MItMeshPolygon(tesselation); + vtxIter = new MItMeshVertex(tesselation); + } + else + { +#if GET_GEOM_FROM_PLUGS + // See comments at top of shaveObjExporter.h + MFnMesh meshFn(object, &st); + MPlug worldMeshArray = meshFn.findPlug("worldMesh", true, &st); + MPlug worldMeshPlug = worldMeshArray.elementByLogicalIndex(object.instanceNumber(), &st); + + st = worldMeshPlug.getValue(meshDataObj); + + faceIter = new MItMeshPolygon(meshDataObj, &st); + vtxIter = new MItMeshVertex(meshDataObj, &st); +#else + faceIter = new MItMeshPolygon(object); + vtxIter = new MItMeshVertex(object); +#endif + } + + totalNumVerts += vtxIter->count(); + totalNumFaces += faceIter->count(); + + for (faceIter->reset(); !faceIter->isDone(); faceIter->next() ) + { + totalNumFaceVerts += faceIter->polygonVertexCount(); + } + + delete faceIter; + delete vtxIter; + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kNurbsSurface)) +#else + else if (object.hasFn(MFn::kNurbsSurface) || object.hasFn(MFn::kSubdiv)) +#endif + { + // + // Tesselate the surface into a temporary mesh. + // + tesselation = shaveUtil::getMesh( + object, uTess, vTess, sDept, sSamp, includeDisplacements + ); + + // + // Get the counts from the temporary mesh. + // + MItMeshPolygon faceIter(tesselation); + MItMeshVertex vtxIter(tesselation); + + totalNumVerts += vtxIter.count(); + totalNumFaces += faceIter.count(); + + for (faceIter.reset(); !faceIter.isDone(); faceIter.next() ) + { + totalNumFaceVerts += faceIter.polygonVertexCount(); + } + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kSubdiv)) + { + MUint64Array quadIDs; + MPointArray verts; + MIntArray vertIndices; + + shaveUtil::getSubdQuads(object, quadIDs, verts, vertIndices); + + unsigned int numTriangles = quadIDs.length() * 2; + + totalNumVerts += verts.length(); + totalNumFaceVerts += numTriangles * 3; + totalNumFaces += numTriangles; + } +#endif + // We can use curves as growth geometry, but we cannot collide with + // curves and they do not occlude. + // + else if (object.hasFn(MFn::kNurbsCurve) && (geomType == kGrowthGeom)) + { + totalNumVerts += numSamplesPerCurve; + totalNumFaceVerts += numSamplesPerCurve; + + // + // We treat a curve as a single face. + // + totalNumFaces += 1; + } + + tesselations.append(tesselation); +} + + +//! initialize variables needed when we fill the WFType struct. +/** +Here we assign values to totalverts, totalfverts, and totalfaces so that we know +how much memory to set aside when we go to fill the WFType struct. for this node. +Originally it was planned that all of this would only happen once on the initial +call to the exporter, but it turns out that because of the architecture, we end up +calling this every time we come through the exporter. It might make sense to re-scope +totalverts, totalfverts, and totalfaces, it makes no sense for them to be global any +more. +*/ + +MStatus shaveObjTranslator::wfInit( + int uTess, + int vTess, + int sDept, + int sSamp, + ShaveExportData& exportData, + MObjectArray& tesselations +) +{ + totalverts = 0; + totalfaces = 0; + totalfverts = 0; + MDagPath node; + MFnDagNode nodeFn; + MObject component; + MFnMesh meshFn; + + exportData.meshes.clear(); + exportData.startFaces.clear(); + exportData.startVerts.clear(); + tesselations.clear(); + + // + // In order to handle the material lookup, we need to get a count of + // which objects we will be exporting, and how many faces those objects + // have. Unfortunately, this means doing an iteration pass through the + // selections to get these counts. Once we know what we are dealing + // with, we can build the lookups on the next iteration through the + // selections. This first pass could be removed by doing a little + // extra bookkeeping during the selection process- it's not that big a + // deal though, since these lookups (and so this pre-process pass) only + // need to happen during the disk export, and so will not effect the + // time-critical WFTYPE creation. + // + MDagPath shapePath; + int objectCount; + int i; + + objectCount = fullHairObjects.length(); + + for (i = 0; i < objectCount; i++) + { + getObjectCounts( + fullHairObjects[i], + kGrowthGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + objectCount = partialHairObjects.length(); + + for (i = 0; i < objectCount; i++) + { + partialHairObjects.getDagPath(i, shapePath); + + getObjectCounts( + shapePath, + kGrowthGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + objectCount = fullSkullObjects.length(); + + for (i = 0; i < objectCount; i++) + { + getObjectCounts( + fullSkullObjects[i], + kCollisionGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + objectCount = partialSkullObjects.length(); + + for (i = 0; i < objectCount; i++) + { + partialSkullObjects.getDagPath(i, shapePath); + + getObjectCounts( + shapePath, + kCollisionGeom, + totalverts, + totalfaces, + totalfverts, + uTess, + vTess, + sDept, + sSamp, + &exportData, + tesselations + ); + } + + // Add an extra element to each of these so that we can easily figure + // out the number of verts/faces in the last object. + exportData.startFaces.append(totalfaces); + exportData.startVerts.append(totalverts); + + return MS::kSuccess; +} + + +void shaveObjTranslator::storeMeshInWFTYPE( + WFTYPE* wf, + WFTYPE* uvs, + MItMeshVertex& vertexIter, + MItMeshPolygon& faceIter, + int& vertexIndex, + int& faceIndex, + int& faceVertexIndex +) +{ + float2 vertUV; + int startOfMesh = vertexIndex; + + // + // Fill in the vertex table. + // + for (vertexIter.reset(); !vertexIter.isDone(); vertexIter.next()) + { + MPoint p = vertexIter.position(space); + + wf->v[vertexIndex].x = (float)p.x; + wf->v[vertexIndex].y = (float)p.y; + wf->v[vertexIndex].z = (float)p.z; + + // + // Workaround for Maya bug #178517 + // + // MItMeshVertex::getUV() will cause Maya to crash if the + // vertex has no UVs, so we check the UV count first. + // + int numUVs; + vertexIter.numUVs(numUVs); + + if (numUVs > 0) + { + vertexIter.getUV(vertUV); +// wf->uv[vertexIndex].x = (float)vertUV[0]; +// wf->uv[vertexIndex].y = (float)vertUV[1]; +// wf->uv[vertexIndex].z = 0.0f; + } + + vertexIndex++; + } + + // + // Fill in the face list. For each poly, iterate through its vertex + // references, and write them to the WFTYPE. voff serves as an index + // incremented each time we finish an iteration, provides the offset so + // we ref. the right verts. + // + if (newtopo) + { + for (faceIter.reset(); !faceIter.isDone(); faceIter.next()) + { + bool hasUVs = faceIter.hasUVs(); + int faceVertexCount = faceIter.polygonVertexCount(); + wf->face_start[faceIndex] = faceVertexIndex; + uvs->face_start[faceIndex] = faceVertexIndex; + + for (int vtx=0; vtx < faceVertexCount; vtx++) + { + wf->facelist[faceVertexIndex] = faceIter.vertexIndex(vtx) + + startOfMesh; + uvs->facelist[faceVertexIndex] = faceVertexIndex; + + if (hasUVs) + { + faceIter.getUV(vtx, vertUV); + uvs->v[faceVertexIndex].x = vertUV[0]; + uvs->v[faceVertexIndex].y = vertUV[1]; + wf->uv[faceVertexIndex].x = vertUV[0]; + wf->uv[faceVertexIndex].y = vertUV[1]; + + + } + + faceVertexIndex++; + } + + wf->face_end[faceIndex] = faceVertexIndex; + uvs->face_end[faceIndex] = faceVertexIndex; + faceIndex++; + } + } +} + + +void shaveObjTranslator::storeObjectInWFTYPE( + WFTYPE* wf, + WFTYPE* uvs, +#if GET_GEOM_FROM_DATABLOCK + MObject& object, +#else + MDagPath& object, +#endif + MObject tesselation, + int& totalNumVerts, + int& totalNumFaces, + int& totalNumFaceVerts +) +{ + if (object.hasFn(MFn::kMesh)) + { +#if GET_GEOM_FROM_PLUGS + // See comments at top of shaveObjExporter.h + MFnMesh meshFn(object); + MPlug worldMeshArray = meshFn.findPlug("worldMesh"); + MPlug worldMeshPlug = worldMeshArray.elementByLogicalIndex(object.instanceNumber()); + MObject meshDataObj; + + worldMeshPlug.getValue(meshDataObj); + + MItMeshPolygon faceIter(meshDataObj); + MItMeshVertex vtxIter(meshDataObj); +#else + MItMeshPolygon faceIter(object); + MItMeshVertex vtxIter(object); +#endif + + storeMeshInWFTYPE( + wf, + uvs, + vtxIter, + faceIter, + totalNumVerts, + totalNumFaces, + totalNumFaceVerts + ); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kNurbsSurface)) +#else + else if (object.hasFn(MFn::kNurbsSurface) || object.hasFn(MFn::kSubdiv)) +#endif + { + MItMeshVertex vtxIter(tesselation); + MItMeshPolygon faceIter(tesselation); + + ////// debug //////// + //MGlobal::displayInfo(MString("storeObjectInWFTYPE: tesselation ") + vtxIter.count() + " " + faceIter.count()); + ///////////////////// + + storeMeshInWFTYPE( + wf, + uvs, + vtxIter, + faceIter, + totalNumVerts, + totalNumFaces, + totalNumFaceVerts + ); + } +#ifdef EVALUATE_POSITION_FIXED + else if (object.hasFn(MFn::kSubdiv)) + { + MPointArray objVerts; + MIntArray objVertIndices; + MUint64Array quadIDs; + MDoubleArray vertUs; + MDoubleArray vertVs; + + shaveUtil::getSubdQuads( + object, quadIDs, objVerts, objVertIndices, &vertUs, &vertVs + ); + + // + // Save the vertex positions. + // + unsigned int i; + unsigned int objStartVertIndex = totalNumVerts; + + for (i = 0; i < objVerts.length(); i++) + { + wf->v[totalNumVerts].x = objVerts[i].x; + wf->v[totalNumVerts].y = objVerts[i].y; + wf->v[totalNumVerts].z = objVerts[i].z; + + if (newtopo) + { + wf->uv[totalNumVerts].x = vertUs[i]; + wf->uv[totalNumVerts].y = vertVs[i]; + } + + totalNumVerts++; + } + + if (newtopo) + { + // + // Save the per-face lists of vertex indices. Note that each + // quad becomes two triangular faces. + // + unsigned int numQuads = quadIDs.length(); + unsigned int objVertIndex = 0; + + for (i = 0; i < numQuads; i++) + { + // + // FIRST TRIANGLE + // + // Record the index of the triangle's first vertex. + // + wf->face_start[totalNumFaces] = totalNumFaceVerts; + uvs->face_start[totalNumFaces] = totalNumFaceVerts; + + // + // Record the indices of the triangle's three face-vertices. + // + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+1]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+2]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + // + // Record the index of the triangle's last vertex + // (actually, the index of the first vertex beyond the + // triangle's last vertex, since that's what Shave expects). + // + wf->face_end[totalNumFaces] = totalNumFaceVerts; + uvs->face_end[totalNumFaces] = totalNumFaceVerts; + + totalNumFaces++; + + // + // SECOND TRIANGLE + // + wf->face_start[totalNumFaces] = totalNumFaceVerts; + uvs->face_start[totalNumFaces] = totalNumFaceVerts; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+2]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->facelist[totalNumFaceVerts] = + objStartVertIndex + objVertIndices[objVertIndex+3]; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts] = wf->uv[wf->facelist[totalNumFaceVerts]]; + + totalNumFaceVerts++; + + wf->face_end[totalNumFaces] = totalNumFaceVerts; + uvs->face_end[totalNumFaces] = totalNumFaceVerts; + + totalNumFaces++; + + // + // Advance by 4 object verts to get to the next quad. + // + objVertIndex += 4; + } + } + } +#endif + else if (object.hasFn(MFn::kNurbsCurve)) + { + MFloatPointArray samples; + + getCurveSamples(object, samples); + + unsigned int x; + unsigned int objStartVertIndex = totalNumVerts; + + for (x = 0; x < samples.length(); x++) + { + wf->v[totalNumVerts].x = samples[x].x; + wf->v[totalNumVerts].y = samples[x].y; + wf->v[totalNumVerts].z = samples[x].z; + + if(newtopo) + { + wf->uv[totalNumVerts].x = 0.0f; + wf->uv[totalNumVerts].y = 0.0f; + } + + totalNumVerts++; + } + + wf->face_start[totalNumFaces] = totalNumFaceVerts; + uvs->face_start[totalNumFaces] = totalNumFaceVerts; + + for (x = samples.length(); x > 0; x--) + { + wf->facelist[totalNumFaceVerts] = objStartVertIndex + x - 1; + + uvs->facelist[totalNumFaceVerts] = totalNumFaceVerts; + uvs->v[totalNumFaceVerts].x = 0.0f; + uvs->v[totalNumFaceVerts].y = 0.0f; + + totalNumFaceVerts++; + } + + wf->face_end[totalNumFaces] = totalNumFaceVerts; + uvs->face_end[totalNumFaces] = totalNumFaceVerts; + + totalNumFaces++; + } +} + + +// Here we write the WFType. Will be careful in here, and offload any +// preprocessing to other places so that this segment can scream as quickly +// as possible. + +MStatus shaveObjTranslator::doWFType( + WFTYPE* wf, WFTYPE* uvs, MObjectArray tesselations +) +{ + // printf("totalverts is %d, totalfaces is %d.\n",totalverts, totalfaces); + // set aside a little memory within the WFTYPE so we can fill in the lists. + if((wf->totalverts != totalverts) || (wf->totalfaces != totalfaces)) + { + newtopo = true; + + free_geomWF(wf); + free_geomWF(uvs); + + wf->totalverts = totalverts; + wf->totalfaces = totalfaces; + wf->totalfverts = totalfverts; + + // We need the UVs to be per-face-vert, which means no sharing + // of UVs, so the total number of 'verts' must be the same as the + // number of face-verts. + uvs->totalverts = uvs->totalfverts = totalfverts; + uvs->totalfaces = totalfaces; + + alloc_geomWF(wf); + alloc_geomWF(uvs); + + if ((wf->v == NULL) + || (wf->face_start == NULL) + || (wf->face_end == NULL) + || (uvs->v == NULL) + || (uvs->face_start == NULL) + || (uvs->face_end == NULL)) + { + return MS::kFailure; + } + } + + int objectIndex = 0; + int faceIndex = 0; + int faceVertexIndex = 0; + int vertexIndex = 0; + int objectCount; + int i; +#if !GET_GEOM_FROM_DATABLOCK + MDagPath object; +#endif + + // + // Store the hair objects into myObj. + // + objectCount = fullHairObjects.length(); + + for(i = 0; i < objectCount; i++) + { + storeObjectInWFTYPE( + wf, + uvs, + fullHairObjects[i], + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + objectCount = partialHairObjects.length(); + + for(i = 0; i < objectCount; i++) + { +#if !GET_GEOM_FROM_DATABLOCK + partialHairObjects.getDagPath(i, object); +#endif + + storeObjectInWFTYPE( + wf, + uvs, +#if GET_GEOM_FROM_DATABLOCK + partialHairObjects[i], +#else + object, +#endif + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + // + // Store the skull objects into myObj. + // + objectCount = fullSkullObjects.length(); + + for(i = 0; i < objectCount; i++) + { + storeObjectInWFTYPE( + wf, + uvs, + fullSkullObjects[i], + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + objectCount = partialSkullObjects.length(); + + for(i = 0; i < objectCount; i++) + { +#if !GET_GEOM_FROM_DATABLOCK + partialSkullObjects.getDagPath(i, object); +#endif + + storeObjectInWFTYPE( + wf, + uvs, +#if GET_GEOM_FROM_DATABLOCK + partialSkullObjects[i], +#else + object, +#endif + tesselations[objectIndex++], + vertexIndex, + faceIndex, + faceVertexIndex + ); + } + + return MS::kSuccess; +} + + +void shaveObjTranslator::exportInstanceObj( + MObject mesh, MObject shader, MString objFileName +) +{ + FILE* fp = NULL; + MFnMesh meshFn(mesh); + int vertCount; + int j; + MPoint p; + +#if defined(OSMac_) && !defined(OSMac_MachO_) + objFileName = shaveMacCarbon::makeMacFilename(objFileName); +#endif + + if (objFileName == "") + { + cerr << "Shave: internal error: no OBJ file supplied to" + << " exportInstanceObj()." << endl; + return; + } + + // + // Open the obj file for writing. Bail as gracefully as possible if + // this fails. + // + fp = fopen (objFileName.asChar(), "w"); + + if(fp == NULL) + { + cerr << "Shave: exportInstanceObj() could not write to '" +#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) + << objFileName.asChar() +#else + << objFileName +#endif + << "'." << endl; + return; + } + + fprintf(fp, "# obj file created by shaveObjExporter.\n\n"); + + MItMeshPolygon polyIter(mesh); + MFloatPointArray pointArray; + meshFn.getPoints(pointArray); + // iterate through the vertex list, writing each vertex to the file. + + MFloatArray uArray; + MFloatArray vArray; + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + MFloatMatrix camMatrix; + meshFn.getUVs(uArray, vArray); + + MString texturePlugName(getShaderTexture(shader)); + int vertn; + + for(vertn = 0; vertn < (int)pointArray.length(); vertn++) + { + fprintf(fp,"v %f %f %f\n", pointArray[vertn].x, pointArray[vertn].y, pointArray[vertn].z); + } + + // + // If the instance's shader doesn't have a texture driving its colour, + // then we'll let shave determine the colour for itself using the root, + // tip, etc, parameters. + // + if (texturePlugName == "") + { + fprintf(fp, "usemtl default\n\n"); + } + else + { + MRenderUtil::sampleShadingNetwork( + texturePlugName, //plug to sample + (int)pointArray.length(), //number of samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL, //u tangents + NULL, //v tangents + NULL, //filter size + vertColorArray, //out color + vertTranspArray //out transparency + ); + + for(int vertn = 0; vertn < (int)vertColorArray.length(); vertn++) + { + fprintf(fp,"#vc %f %f %f\n", vertColorArray[vertn].x*255, vertColorArray[vertn].y*255, vertColorArray[vertn].z*255); + } + + fprintf(fp, "usemtl colorlock\n\n"); + } + + // + // Output the list of verts in each face. + // + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + fprintf(fp, "f "); + vertCount = polyIter.polygonVertexCount(); + for(j = 0; j < vertCount; j++) + { + fprintf(fp, "%d ",polyIter.vertexIndex(j)+1); + } + fprintf(fp, "\n"); + } + + // + // Output the list of normals for each face's verts. + // + MVector norm; + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next() ) + { + fprintf(fp, "n"); + vertCount = polyIter.polygonVertexCount(); + for(j = 0; j < vertCount; j++) + { + polyIter.getNormal(j, norm); + fprintf(fp, " %f %f %f", norm.x, norm.y, norm.z); + } + fprintf(fp, "\n"); + } + + fprintf(fp, "# end of obj file.\n\n"); + + fflush(fp); + fclose(fp); + + return; +} + + +MStatus shaveObjTranslator::exportOcclusion( + MDagPathArray& objects, WFTYPE * memObj +) +{ + MDagPath transformPath; + MDagPath shapePath; + unsigned int i; + unsigned int numObjects = objects.length(); + MObjectArray tesselations; + + // + // Before we can allocate the vertex arrays in the WFTYPE, we need to + // know how many vertices we have. So let's count them. + // + free_geomWF(memObj); + + totalverts = 0; + totalfaces = 0; + totalfverts = 0; + + for (i = 0; i < numObjects; i++) + { + shapePath = objects[i]; + shapePath.extendToShape(); + + // + // Passing in -1 for the tesselation values will cause the object's + // own tesselation values to be used. + // + getObjectCounts( + shapePath, + kOcclusionGeom, + totalverts, + totalfaces, + totalfverts, + -1, + -1, + 1, + 1, + NULL, + tesselations, + true + ); + } + + // + // Allocate whatever space we need to store all the vertices. + // + memObj->totalverts = totalverts; + memObj->totalfaces = totalfaces; + memObj->totalfverts = totalfverts; + + alloc_geomWF(memObj); + + if ((memObj->v == NULL) + || (memObj->face_start == NULL) + || (memObj->face_end == NULL)) + { + tesselations.clear(); + + return MS::kFailure; + } + + // + // Now we can actually store the vertices into the WFTYPE. + // + int globalFaceIndex = 0; + int globalFaceVtxIndex = 0; + int globalVtxIndex = 0; + int objStartVertex = 0; + + for (i = 0; i < numObjects; i++) + { + objStartVertex = globalVtxIndex; + + shapePath = objects[i]; + shapePath.extendToShape(); + transformPath = shapePath; + transformPath.pop(); + + if(shapePath.hasFn(MFn::kMesh)) + { + MItMeshPolygon* polyIter = 0; + MItMeshVertex* vtxIter = 0; + + if (tesselations[i].isNull()) + { + polyIter = new MItMeshPolygon(shapePath); + vtxIter = new MItMeshVertex(shapePath); + } + else + { + polyIter = new MItMeshPolygon(tesselations[i]); + vtxIter = new MItMeshVertex(tesselations[i]); + } + + // + // Store the vertices into the WFTYPE object which was passed + // to us. + // + for (vtxIter->reset(); !vtxIter->isDone(); vtxIter->next()) + { + MPoint p = vtxIter->position (space); + + memObj->v[globalVtxIndex].x = (float) p.x; + memObj->v[globalVtxIndex].y = (float) p.y; + memObj->v[globalVtxIndex].z = (float) p.z; + globalVtxIndex++; + } + + // + // Store the face-vertices into the WFTYPE object. + // + for (polyIter->reset(); !polyIter->isDone(); polyIter->next()) + { + int polyVertexCount = polyIter->polygonVertexCount(); + + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + for (int vtx=0; vtx < polyVertexCount; vtx++) + { + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + polyIter->vertexIndex(vtx); + } + + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + } + + delete polyIter; + delete vtxIter; + } +#ifdef EVALUATE_POSITION_FIXED + else if (shapePath.hasFn(MFn::kNurbsSurface)) +#else + else if (shapePath.hasFn(MFn::kNurbsSurface) + || shapePath.hasFn(MFn::kSubdiv)) +#endif + { + // + // Store the vertices into the WFTYPE object which was passed + // to us. + // + MItMeshVertex vtxIter(tesselations[i]); + + for (vtxIter.reset(); !vtxIter.isDone(); vtxIter.next()) + { + MPoint p = vtxIter.position(space); + memObj->v[globalVtxIndex].x = (float)p.x; + memObj->v[globalVtxIndex].y = (float)p.y; + memObj->v[globalVtxIndex].z = (float)p.z; + globalVtxIndex++; + } + + // + // Store the face-vertices into the WFTYPE object. + // + MItMeshPolygon polyIter(tesselations[i]); + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next()) + { + int polyVertexCount = polyIter.polygonVertexCount(); + + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + for (int vtx=0; vtx < polyVertexCount; vtx++) + { + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + polyIter.vertexIndex(vtx); + } + + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + } + } +#ifdef EVALUATE_POSITION_FIXED + else if (shapePath.hasFn(MFn::kSubdiv)) + { + MPointArray objVerts; + MIntArray objVertIndices; + MUint64Array quadIDs; + + shaveUtil::getSubdQuads( + shapePath, quadIDs, objVerts, objVertIndices + ); + + // + // Save the vertex positions. + // + unsigned int j; + + for (j = 0; j < objVerts.length(); j++) + { + memObj->v[globalVtxIndex].x = objVerts[j].x; + memObj->v[globalVtxIndex].y = objVerts[j].y; + memObj->v[globalVtxIndex].z = objVerts[j].z; + + globalVtxIndex++; + } + + // + // Save the per-face lists of vertex indices. Note that each + // quad becomes two triangles. + // + unsigned int numQuads = quadIDs.length(); + unsigned int objVertIndex = 0; + + for (j = 0; j < numQuads; j++) + { + // + // Record the index of the first triangle's first vertex. + // + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + // + // Record the indices of the first triangle's 3 vertices. + // + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+1]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+2]; + + // + // Record the index of the first vertex beyond the first + // triangle's last vertex. + // + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + + // + // Record the index of the second triangle's first vertex. + // + memObj->face_start[globalFaceIndex] = globalFaceVtxIndex; + + // + // Record the indices of the second triangle's 3 vertices. + // + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+2]; + memObj->facelist[globalFaceVtxIndex++] = + objStartVertex + objVertIndices[objVertIndex+3]; + + // + // Record the index of the first vertex beyond the second + // triangle's last vertex. + // + memObj->face_end[globalFaceIndex++] = globalFaceVtxIndex; + + // + // Advance to the next quad. + // + objVertIndex += 4; + } + } +#endif + } + + tesselations.clear(); + + return MS::kSuccess; +} + + +MObject shaveObjTranslator::getConnectedShader(MFnMesh meshFn) +{ + MObjectArray shaderList; + MIntArray indecies; + meshFn.getConnectedShaders(0, shaderList, indecies); + MObject ret(shaderList[0]); + return ret; +} + + +void shaveObjTranslator::getCurveSamples( +#if GET_GEOM_FROM_DATABLOCK + MObject curve, MFloatPointArray& samples +#else + MDagPath curve, MFloatPointArray& samples +#endif +) +{ + MFnNurbsCurve curveFn(curve); + double maxParam; + double minParam; + double param; + int s; + + curveFn.getKnotDomain(minParam, maxParam); + + double paramIncrement = (maxParam - minParam) + / (double)(numSamplesPerCurve - 1); + + samples.clear(); + + for (s = 0, param = minParam; + s < numSamplesPerCurve; + s++, param += paramIncrement) + { + if (s == numSamplesPerCurve - 1) param = maxParam; + + MPoint p ;; + curveFn.getPointAtParam(param, p, MSpace::kWorld ); + + samples.append((float)p.x, (float)p.y, (float)p.z); + } +} + + +MString shaveObjTranslator::getShaderTexture(MObject shadingGroup) +{ + MString retval = ""; + + if (!shadingGroup.isNull()) + { + MStatus status; + MFnDependencyNode mtlDependNode(shadingGroup, &status); + MPlug tmpPlug; + MPlugArray texPlugArray; + tmpPlug = mtlDependNode.findPlug("surfaceShader"); + tmpPlug.connectedTo(texPlugArray, true, false); + + MObject shader = texPlugArray[0].node(); + texPlugArray.clear(); + mtlDependNode.setObject(shader); + + tmpPlug = mtlDependNode.findPlug("color"); + + if (tmpPlug.isConnected()) + { + tmpPlug.connectedTo(texPlugArray, true, false); + retval = texPlugArray[0].name(); + } + } + + return retval; +} + |