diff options
Diffstat (limited to 'mayaPlug/shaveTextureStore.cpp')
| -rw-r--r-- | mayaPlug/shaveTextureStore.cpp | 2812 |
1 files changed, 2812 insertions, 0 deletions
diff --git a/mayaPlug/shaveTextureStore.cpp b/mayaPlug/shaveTextureStore.cpp new file mode 100644 index 0000000..148903d --- /dev/null +++ b/mayaPlug/shaveTextureStore.cpp @@ -0,0 +1,2812 @@ +// Shave and a Haircut +// (c) 2019 Epic Games +// US Patent 6720962 + +//Qt headers must be included before any others !!! +#include <QtCore/QEvent> +#include <QtCore/QElapsedTimer> +#include <QtGui/QMouseEvent> +#include <QtGui/QTabletEvent> + +#if QT_VERSION < 0x050000 +# include <QtGui/QApplication> +# include <QtGui/QWidget> +#else +# include <QtWidgets/QApplication> +# include <QtWidgets/QWidget> +#endif + +#include "shaveIO.h" + +#include <map> +#include <vector> + +#include <maya/MArgDatabase.h> +#include <maya/MArgList.h> +#include <maya/MDataBlock.h> +#include <maya/MFloatMatrix.h> +#include <maya/MFloatPoint.h> +#include <maya/MFloatPointArray.h> +#include <maya/MFnSet.h> +#include <maya/MFnSubd.h> +#include <maya/MFnSubdNames.h> +#include <maya/MGlobal.h> +#include <maya/MIntArray.h> +#include <maya/MPlugArray.h> +#include <maya/MRenderUtil.h> +#include <maya/MSyntax.h> +#include <maya/MUint64Array.h> +#include <maya/MItMeshVertex.h> + +#include "shaveHairShape.h" +#include "ShavePerVertTexInfo.h" +#include "shaveRender.h" +#include "shaveSDK.h" +#include "shaveTextureStore.h" +#include "shaveUtil.h" +#include "shaveHairUI.h" + + + + +namespace +{ + typedef struct + { + int pntid; + float u; + float v; + } VertUVInfo; + + struct compMString + { + bool operator()(const MString s1, const MString s2) const + { + return (strcmp(s1.asChar(), s2.asChar()) < 0); + } + }; + + typedef std::vector<VertUVInfo> FaceUVInfo; + typedef std::vector<FaceUVInfo> SurfaceUVInfo; + typedef std::vector<SurfaceUVInfo*> UVSetUVInfo; + typedef std::map<MString, UVSetUVInfo*, compMString> UVInfoCache; +}; + + +static SurfaceUVInfo* cacheUVs( + shaveHairShape* sNode, + unsigned int surfIdx, + const MString& uvSet, + UVInfoCache& uvInfoCache +); + +static unsigned int* createFaceIDMap(const MIntArray& startIndices); + +static void makeTempColourConnections( + const MObject& shavenode, MDGModifier& dgMod +); + + +#define McheckErr(stat,m) \ + if ( MS::kSuccess != stat ) { \ + cerr << "ERROR: " << m << endl; \ + } + +static bool buildingLookups = false; +static int nodeCount = 0; +static int* texIDMap = NULL; +static unsigned texIDMapSize = 0; +static NODETEXINFO* texInfoLookup = NULL; +static unsigned texInfoLookupSize = 0; +static std::map<int, ShavePerVertTexInfo*> vertTexInfoMap; + +bool IsBuildingLookups() +{ + return buildingLookups; +} + + +// Some of the Shave parameters apply to growth vertices (i.e. for use by +// guides) while all the others apply to hairs. +static unsigned int vertParams[] = { 8, 21, 40 }; +static const unsigned int numVertParams = sizeof(vertParams) / sizeof(unsigned int); + +MObjectArray standinMeshes; +MDagPathArray standinMeshOrigSurfaces; + + +static inline void getHairUV( + const SurfaceUVInfo& surfUVInfo, + const unsigned int polyIdx, + const int baryPointIDs[3], + const float baryWeights[3], + float& u, + float& v +) +{ + u = v = 0.0f; + + if (polyIdx < surfUVInfo.size()) + { + const FaceUVInfo& faceUVInfo = surfUVInfo[polyIdx]; + size_t numVerts = faceUVInfo.size(); + + for (unsigned int i = 0; i < 3; ++i) + { + // faceUVInfo is a sparse array of VertUVInfo, with one for + // each vertex of this poly. So we have to run through them to + // find the one which has the same point ID as the bary coord. + for (size_t j = 0; j < numVerts; ++j) + { + if (faceUVInfo[j].pntid == baryPointIDs[i]) + { + u += faceUVInfo[j].u * baryWeights[i]; + v += faceUVInfo[j].v * baryWeights[i]; + break; + } + } + } + } +} + + +// This evaluates the hair-root textures. These are needed during +// rendering and export, but not during Live Mode. +void initTexInfoLookup2( + const MObjectArray& shaveHairShapes, + MString meshUVSet, + bool verbose, + bool displayHairsOnly, + MObject onlyThis +) +{ + //if (verbose) cerr << endl << "Beginning texture pre-process." << endl; +#ifdef DO_PROFILE + if(!Profile::GetDiagFile()) + Profile::ProfileStart(NULL); + Profile::ProfileDump("initTexInfoLookup2", NULL); +#endif + // + // There is a bit of recursion here which we need to be careful with. + // To build the texture lookups, we'll need to get the positions of + // each hair from Shave using either SHAVEmake_a_curve() or + // SHAVEmake_a_curveROOT(). + // + // Both of those functions will call the SHAVEapply_texture() callback + // to try and fill in any textured attributes for the hair. + // + // SHAVEapply_texture() will in turn call getTexInfoLookup() to + // retrieve the shaveHairShape's texture information -- which is exactly + // what we're in the process of building. + // + // To break the cycle, we set a flag which tells getTexInfoLookup() and + // getVertInfoLookup() to return null pointers until we're done. + // + buildingLookups = true; + + MStatus status; + shaveHairShape* onlyThisShape = NULL; + if (onlyThis != MObject::kNullObj) + { + MFnDependencyNode shaveDependNode(onlyThis); + onlyThisShape = (shaveHairShape*) shaveDependNode.userNode (&status); + } + + nodeCount = (int)shaveHairShapes.length(); + + unsigned int numShaveIDs = (unsigned)(shaveHairShape::getMaxShaveID() + 1); + + // We can only reuse the existing texInfoLookup and texIDMap arrays if + // we're initializing a single hair node (i.e. 'onlyThisShape' is not + // NULL) and the sizes of the arrays haven't changed. Otherwise we have to + // blow the old arrays away and make new ones. + // + bool needNewArrays = ( (onlyThisShape == NULL) + || (texIDMapSize != numShaveIDs) + || (nodeCount != texInfoLookupSize) + || (texInfoLookup == NULL) ); + + if (needNewArrays) { + freeTextureLookup(); + } + + // if we have nodes (we always should if we get here) then initialize + // the lookup structures. + if (nodeCount > 0) + { + // + // Allocate the top (node) level of the texture tables. + // + if (needNewArrays) + { + texInfoLookupSize = nodeCount; + texInfoLookup = (NODETEXINFO*)malloc(texInfoLookupSize*sizeof(NODETEXINFO)); + memset(texInfoLookup, 0, texInfoLookupSize*sizeof(NODETEXINFO)); + + // We need a lookup table to translate Shave node IDs into indices + // into our texture tables. + // + texIDMapSize = numShaveIDs; + texIDMap = new int[texIDMapSize]; + + for (int i = 0; i < (int)texIDMapSize; i++) + texIDMap[i] = -1; + } + + if (texInfoLookup == NULL) + { + cerr << "ERROR- COULD NOT ALLOCATE MEMORY FOR TEXTURE LOOKUP" + << " STRUCTURES, PROCEEDING W/O TEXTURES." << endl; + } + else + { + for (int node = 0; node < nodeCount; node++) + { + // get the shaveHairShape; + MFnDependencyNode shaveDependNode(shaveHairShapes[node]); + shaveHairShape* sNode = (shaveHairShape*) shaveDependNode.userNode (&status); + + //printf("02 - hasPendingEvents: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + if (needNewArrays || (onlyThisShape == sNode)) + { + NODETEXINFO* nodeInfo = &texInfoLookup[node]; + + if (onlyThisShape != NULL) + freeTextureLookup(nodeInfo); + else + { + nodeInfo->displacement = NULL; + nodeInfo->textureLookup = NULL; + nodeInfo->u = NULL; + nodeInfo->v = NULL; + } + + nodeInfo->node = sNode; + + // + // Get Shave's internal SHAVENODE structure. Note that + // getting it this way will force the guides to update if + // the geometry has changed, e.g. because we're on a new + // frame. + // + SHAVENODE* hairNode = sNode->getHairNode(); + int hairGroup = sNode->getHairGroup(); + + // + // Make sure that our parameters are all up to date with + // any changes the user may have made. + // + // %%% Shouldn't the 'getHairNode' call above already have + // taken care of that? + // + //sNode->updateParams(); + + // + // Add the node's Shave ID to our map. + // + texIDMap[sNode->getShaveID()] = node; + + // + // Let Shave know which node our calls will be referring + // to. + // + sNode->makeCurrent(); + + int numPasses = hairNode->shavep.passes[hairGroup]; + nodeInfo->maxPasses = numPasses; + + // our lookup will return a float, indexed by + // [pass][parm][hairid], here we allocate the [pass] + // level, now that we know what that number is. + nodeInfo->textureLookup = (float***) + // malloc(1*sizeof(float**)); + malloc(numPasses*sizeof(float**)); + // nodeInfo->u = new float*[1]; + // nodeInfo->v = new float*[1]; + nodeInfo->u = new float*[numPasses]; + nodeInfo->v = new float*[numPasses]; + + // get the haircount for this node + if (displayHairsOnly) + nodeInfo->count = sNode->getNumDisplayHairs(false); + else + nodeInfo->count = hairNode->shavep.haircount[hairGroup]*numPasses; + + // TODO: Why do we set these to 1 here? + numPasses=1; + nodeInfo->maxPasses=1; + + + // init the textured boolean to false; + for(int l = 0; l < SHAVE_NUM_PARAMS; l++) + { + nodeInfo->textured[l] = false; + nodeInfo->maxIndex[l] = -1; + } + + // we've got a bit of work to do to find the uv we want. + // We'll need to get the selection list for this node, then + // we'll need to be able to determing which mesh the + // faceIndex belongs to, and then we'll need to correlate + // the pointids to a local face vertid, in order to get the + // UVs. Yuck. + + + // get the texture node plug. We'll use this array plug in + // the next loop to figure out where we need to do our + // lookups from, and which parms are carrying textures. + MDGModifier dgMod; + MIntArray connectedIndexes; + + MPlug texPlug(shaveHairShapes[node], shaveHairShape::shaveTextureAttr); + MPlugArray texPlugArray; + MString currentNodeName = sNode->name(); + unsigned int numGrowthSurfaces = 0; + + //Only need to do this if the growth type is not Spline. + bool splineNode = (hairGroup == 4); + + if (!splineNode) + { + short uTess; + short vTess; + MPlug plug(shaveHairShapes[node], shaveHairShape::surfTessU); + plug.getValue(uTess); + plug.setAttribute(shaveHairShape::surfTessV); + plug.getValue(vTess); + + short sDept; + short sSamp; + MPlug plug2(shaveHairShapes[node], shaveHairShape::subdTessDept); + plug2.getValue(sDept); + plug2.setAttribute(shaveHairShape::subdTessSamp); + plug2.getValue(sSamp); + + + tesselateGrowthSurfaces( + sNode->exportData.meshes, uTess, vTess, sDept, sSamp + ); + + numGrowthSurfaces = sNode->exportData.meshes.length(); + } + //printf("03 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // + // Most texture connections are made directly to elements + // of the shaveHairShape's 'shaveTex' array attribute. + // + // However, a colour attribute is a compound, which can + // be connected either as a single compound plug, or as + // individual component plugs. Since the 'shaveTex' array + // doesn't allow for compounds, the shaveHairShape's colour + // attributes have their own separate connections. + // + // To make life easier on ourselves, we'd like everything + // to be in the 'shaveTex' array, so let's break the + // compounds up into their constituent parts and make + // temporary connections to the appropriate 'shaveTex' + // array elements. + // + makeTempColourConnections(shaveHairShapes[node], dgMod); + status = dgMod.doIt(); + + //printf("04 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // now we need to make sure that the plug is actually + // connected to something. + // Thanks, Maya. + MPlug checkPlug; + + unsigned int pi = 0; + for (pi = 0; pi < 60; pi++) // was 50 + { + // Filter out those parameters which apply to growth + // vertices, not hairs. + unsigned int j; + for (j = 0; j < numVertParams; ++j) + if (pi == vertParams[j]) break; + + if (j == numVertParams) + { + checkPlug = texPlug.elementByLogicalIndex(pi); + + if(checkPlug.isConnected()) + connectedIndexes.append(pi); + } + } + + bool haveTextures = (connectedIndexes.length() > 0); + + //printf("05 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // + // This structure defines those few items of + // information from each hair's CURVEINFO which we need + // to keep track of. + // + struct HairBaryInfo + { + int UTpid; + int pntid[3]; + float wgt[3]; + + } **hairInfo = NULL; + + // + // If we have textures, then we'll be needing the hairInfo + // array. + // + if (haveTextures) + hairInfo = new struct HairBaryInfo*[numPasses]; + + UVInfoCache uvInfoCache; + + nodeInfo->displacement = NULL; + + // Cache the vertex UVs so that we can later generate + // hair-root UVs for use in SHAVEapply_texture(). + unsigned int i; + SurfaceUVInfo** uvInfo = NULL; + + if (!splineNode) + { + uvInfo = new SurfaceUVInfo*[numGrowthSurfaces]; + + for (i = 0; i < numGrowthSurfaces; i++) + { + uvInfo[i] = NULL; + + MDagPath surf = sNode->exportData.meshes[i]; + surf.extendToShape(); + + #ifdef EVALUATE_POSITION_FIXED + if (!surf.hasFn(MFn::kMesh)) + #else + if (!surf.hasFn(MFn::kMesh) + && !surf.hasFn(MFn::kSubdiv)) + #endif + { + // For non-mesh surfaces Shave uses a mesh to + // approximate the surface. To get the hairs + // rooted at the correct point we calculate + // the 'displacement' (i.e. the difference + // between the approximate mesh and the real + // surface) for every hair root and cache it. + if (nodeInfo->displacement == NULL) + nodeInfo->displacement = new VERT*[numPasses]; + + // Non-meshes only have a single, default UV + // parameterization. + uvInfo[i] = cacheUVs(sNode, i, "", uvInfoCache); + } + else + { + uvInfo[i] = cacheUVs(sNode, i, meshUVSet, uvInfoCache); + } + } + } + + //printf("06 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // Cache the displacements and barycentric info for each hair. + CURVEINFO curveInfo; + unsigned int* faceToMesh = NULL; + int hairnum; + unsigned int meshIndex; + MDagPath meshNode; + int pass; + float u; + float v; + WFTYPE wt; + + init_geomWF(&wt); + + for (pass = 0; pass < numPasses; pass++) + { + if (nodeInfo->displacement) + nodeInfo->displacement[pass] = new VERT[nodeInfo->count]; + + nodeInfo->u[pass] = new float[nodeInfo->count]; + nodeInfo->v[pass] = new float[nodeInfo->count]; + + if (haveTextures) + { + hairInfo[pass] = + new struct HairBaryInfo[nodeInfo->count]; + } + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + SHAVEmake_a_curveROOT( + pass, + hairGroup, + hairnum, + &wt, + &curveInfo + ); + + if (haveTextures) + { + // Save the barycentric info from curveInfo, + // which we'll need later on. + hairInfo[pass][hairnum].UTpid = curveInfo.UTpid; + + for (int i = 0; i < 3; i++) + { + hairInfo[pass][hairnum].pntid[i] = + curveInfo.pntid[i]; + hairInfo[pass][hairnum].wgt[i] = + curveInfo.wgt[i]; + } + } + + // + // Shave's hair number indices include killed + // hairs, so we have to leave room for them in our + // arrays. But we don't need to calculate their + // uvs. + // + if (wt.totalfaces > 0) + { + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to actually be + // on the texture, so we have to nudge it + // in a bit. + // + nodeInfo->u[pass][hairnum] = curveInfo.wgt[0]; + nodeInfo->v[pass][hairnum] = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + // Get the index of the mesh that this + // hair is growing on. + meshIndex = faceToMesh[curveInfo.UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + + int normalizedPolyIdx = curveInfo.UTpid - + sNode->exportData.startFaces[meshIndex]; + + float u = 0.0f; + float v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + curveInfo.pntid, + curveInfo.wgt, + u, + v + ); + + nodeInfo->u[pass][hairnum] = u; + nodeInfo->v[pass][hairnum] = v; + + // Get this hair's displacement. + if (nodeInfo->displacement) + { + getDisplacement( + meshNode, + curveInfo, + wt.v[0], + u, + v, + sNode->exportData.startVerts[meshIndex], + nodeInfo->displacement[pass][hairnum] + ); + } + } + } + else + { + if (nodeInfo->displacement) + { + nodeInfo->displacement[pass][hairnum].x = 0.0f; + nodeInfo->displacement[pass][hairnum].y = 0.0f; + nodeInfo->displacement[pass][hairnum].z = 0.0f; + } + } + } + } + + //printf("07 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + // + // SHAVEmake_a_curveROOT will free up the old WFTYPE's info + // on each call, but we have to free up the the final one + // that it left us with. + // + free_geomWF(&wt); + + // + // If we have any textured parameters then we need to + // iterate through all the passes and determine the texture + // information. + // + if (haveTextures) + { + if (verbose) cerr << "doing uv map preprocess... "; + + MObjectArray textures; + unsigned int ti; // which texture? + unsigned int numTextures = 0; + + // + // Get an array of all the textures being used by this + // shaveHairShape. + // + unsigned int i; + + for(i = 0; i < connectedIndexes.length(); i++) + { + int parmIndex; + parmIndex = (int)connectedIndexes[i]; + + // + // Get the plug which is driving this parameter. + // + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + // + // Grab its node. + // + MObject texture = texPlugArray[0].node(); + + //{ + // MFnDependencyNode dFn(texture); + // printf("texture node name %s\n",dFn.name().asChar() );fflush(stdout); + //} + + // + // If we haven't yet seen this one, add it to the + // array. + // + for (ti = 0; ti < numTextures; ti++) + if (texture == textures[ti]) break; + + if (ti == numTextures) + { + textures.append(texture); + numTextures++; + } + } + + // + // Build a UV map for each texture. Each map will have + // a single entry per hair, per pass. + // + HAIRUVS** textureUVMaps = new HAIRUVS*[numTextures]; + struct HairBaryInfo* hair; + + for (ti = 0; ti < numTextures; ti++) + { + textureUVMaps[ti] = new HAIRUVS[nodeInfo->maxPasses]; + + // + // Find the UV set used by each growth mesh for + // this texture. + // + MStringArray uvSets; + + if (splineNode) + { + // + // UV mapping for spline hair is handled + // specially. + // + uvSets.append(""); + } + else + { + MString uvSet; + + for (unsigned int m = 0; m < numGrowthSurfaces; m++) + { + MDagPath objectPath = + sNode->exportData.meshes[m]; + + if (!objectPath.hasFn(MFn::kMesh)) + { + // + // This growth object is not a mesh, so + // it doesn't support UV sets. + // + // We store a blank UV set name which + // will be an indicator later on to + // perform the default mapping for this + // growth object. + // + uvSet = ""; + } + else + { + // + // We have a mesh. + // + // Get the UV set used by this mesh for + // this texture. + // + uvSet = sNode->getUVSet( + objectPath.node(), + textures[ti] + ); + } + + uvSets.append(uvSet); + + // Cache the uvs for this UV set. + uvInfo[m] = cacheUVs(sNode, m, uvSet, uvInfoCache); + } + } + + for (pass = 0; + pass < nodeInfo->maxPasses; + pass++) + { + textureUVMaps[ti][pass].us = new float[nodeInfo->count]; + textureUVMaps[ti][pass].vs = new float[nodeInfo->count]; + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to + // actually be on the texture, so we + // have to nudge it in a bit. + // + u = hair->wgt[0]; + v = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + meshIndex = faceToMesh[hair->UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + meshNode.extendToShape(); + + // If this growth surface is a not + // really a mesh (i.e. it's a NURBS or + // subd) then it will be using the + // default UV parameterization, which + // we have already stored in nodeInfo + // so we can grab the value from there + // rather than recalculating it here. + // + // Similarly, if it *is* a mesh and we're + // using the same uv set as was cached + // in nodeInfo, then just grab the + // values from there. + if (!meshNode.hasFn(MFn::kMesh) + || (uvSets[meshIndex] == meshUVSet)) + { + u = nodeInfo->u[pass][hairnum]; + v = nodeInfo->v[pass][hairnum]; + } + else + { + int normalizedPolyIdx = hair->UTpid - + sNode->exportData.startFaces[meshIndex]; + + u = 0.0f; + v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + hair->pntid, + hair->wgt, + u, + v + ); + } + } + + textureUVMaps[ti][pass].us[hairnum] = u; + textureUVMaps[ti][pass].vs[hairnum] = v; + } + } + } + + //printf("08 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + if (verbose) + { + cerr << "done." << endl; + cerr << "getting texture values from maya... " << endl; + } + int maxtex=100000; + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + + + // malloc the top level of our lookup; + nodeInfo->textureLookup[pass] = (float**) + calloc(SHAVE_NUM_PARAMS, sizeof(float*)); + + MFloatArray uArray; + MFloatArray vArray; + MFloatPointArray pointArray; + MFloatPointArray pa; + VERT pos[3]; + struct HairBaryInfo* hair; + + // we need to to figure out the uv coords for each + // of the roots for this pass, and also the point + // locations for 3D texture evaluation. + MFloatMatrix camMatrix; + + // if we store the lookups we have done then we + // don't need to do them again. This will save both + // time and memory for high density scenes. + MStringArray doneLookups; + MIntArray doneLookupsIndex; + + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + int ind; + int total; + total=nodeInfo->count; + int parmIndex = (int)connectedIndexes[pi]; + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + MObject texture = texPlugArray[0].node(); + + for (ti = 0; ti < numTextures; ti++) + if (textures[ti] == texture) break; + + + nodeInfo->textureLookup[pass][parmIndex] + = (float*) calloc( + total, sizeof(float) + ); + + + for (ind=0;ind<nodeInfo->count;ind+=maxtex) + { + uArray.clear(); + vArray.clear(); + pointArray.clear(); + int szz=total; + + if (total>=maxtex) szz=total-ind; + if (szz>maxtex) szz=maxtex; + //fprintf (stdout,"ind = %d szz= %d\n",ind,szz);fflush(stdout); + + for (hairnum = ind; hairnum < ind+szz; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + for (int k = 0; k < 3; k++) + { + pos[k] = sNode->memShaveObj + .v[hair->pntid[k]]; + pos[k].x *= hair->wgt[k]; + pos[k].y *= hair->wgt[k]; + pos[k].z *= hair->wgt[k]; + } + + uArray.append( + textureUVMaps[ti][pass].us[hairnum] + ); + vArray.append( + textureUVMaps[ti][pass].vs[hairnum] + ); + + pointArray.append( + pos[0].x+pos[1].x+pos[2].x, + pos[0].y+pos[1].y+pos[2].y, + pos[0].z+pos[1].z+pos[2].z + ); + } + + // set aside a little memory for our lookup results. + + + { + { + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + + //////debug////// + //MFloatVectorArray uTang; + //MFloatVectorArray vTang; + //MFloatArray filter; + //uTang.setLength(szz); + //vTang.setLength(szz); + //filter.setLength(szz); + //for(unsigned int k = 0; k < szz; k++) + //{ + // uTang[k] = MFloatVector(1.0f,0.0f); + // vTang[k] = MFloatVector(0.0f,1.0f); + // filter[k] = 0.1f; + //} + ///////////////////////// + + //printf("texture name %s\n",texPlugArray[0].name().asChar() );fflush(stdout); + + // lets get the lookup values from the maya + // shading engine. + MStatus shRes = + MRenderUtil::sampleShadingNetwork( + texPlugArray[0].name(), //shade node name + szz, //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL /*&uTang*/, //u tang + NULL /*&vTang*/, //v tang + NULL /*&filter*/, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); + //fprintf (stdout,"done with lookup\n");fflush(stdout); + // we need to copy the returned values into the + // lookup array our pointer references. also, + // we'll store the name and parm index of this + // lookup so we can reuse it if the opportunity + // presents itself. + nodeInfo->maxIndex[parmIndex] + = (int)total; + + ///////// debug /////////////// + //if(shRes != MStatus::kSuccess) printf(" MRenderUtil::sampleShadingNetwork failed with result %i\n",shRes); + //for(int k=0; k < (int)szz; k++) + //{ + // printf("uv %f %f rgb %f %f %f t %f\n",uArray[k],vArray[k],vertColorArray[k].x,vertColorArray[k].y,vertColorArray[k].z,vertTranspArray[k]); + //} + //fflush(stdout); + /////////////////////////////// + + for(int k=0; k < (int)szz; k++) + { + nodeInfo->textureLookup[pass][parmIndex][k+ind] + = (float)vertColorArray[k].x; + // nodeInfo->textureLookup[pass][parmIndex][k] + // = (float)vertColorArray[k].x; + } + } + } + } + + } + } + //printf("09 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + if (verbose) + { + cerr << "done." << endl; + cerr << "freeing cached curveinfos and uvlists..." << endl; + } + + for (ti = 0; ti < numTextures; ti++) + { + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + delete [] textureUVMaps[ti][pass].us; + delete [] textureUVMaps[ti][pass].vs; + } + + delete [] textureUVMaps[ti]; + } + + delete [] textureUVMaps; + + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + delete [] hairInfo[pass]; + + delete [] hairInfo; + } + + delete [] faceToMesh; + + UVInfoCache::iterator setIter; + + for (setIter = uvInfoCache.begin(); + setIter != uvInfoCache.end(); + ++setIter) + { + UVSetUVInfo* uvSetUVInfo = (*setIter).second; + + for (unsigned int ui = 0; ui < uvSetUVInfo->size(); ++ui) + delete (*uvSetUVInfo)[ui]; + + delete uvSetUVInfo; + } + + //printf("10 - hasPendingEvetns: %s\n",QCoreApplication::hasPendingEvents()?"yes":"no");fflush(stdout); + + uvInfoCache.clear(); + + // we can't set the textured member true until we've taken + // care of all the passes. Otherwise make a curve will fail + // trying to do a lookup. + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + unsigned int pIndex = connectedIndexes[pi]; + nodeInfo->textured[pIndex] = true; + } + + // remove temporary connections that were made in the + // hypergraph. not strictly necesary, but keeps thing + // looking cleaner for the user. + dgMod.undoIt(); + + // + // Let the node know that its texture cache has changed. + // + MPlug plug( + shaveHairShapes[node], + shaveHairShape::textureCacheUpdatedAttr + ); + plug.setValue(true); + + } //end if(onlyThisShape != NULL && onlyThisShape == sNode) + + } //end for node + } + } + + // + // It's now safe to serve up the texture lookup tables. + // + buildingLookups = false; +#ifdef DO_PROFILE + Profile::ProfileDump("initTexInfoLookup2 - done", NULL); +#endif +} + + +// This evaluates the hair-root textures. These are needed during +// rendering and export, but not during Live Mode. +void initTexInfoLookup( + const MObjectArray& shaveHairShapes, + MString meshUVSet, + bool verbose, + bool displayHairsOnly +) +{ + //if (verbose) cerr << endl << "Beginning texture pre-process." << endl; + + // + // There is a bit of recursion here which we need to be careful with. + // To build the texture lookups, we'll need to get the positions of + // each hair from Shave using either SHAVEmake_a_curve() or + // SHAVEmake_a_curveROOT(). + // + // Both of those functions will call the SHAVEapply_texture() callback + // to try and fill in any textured attributes for the hair. + // + // SHAVEapply_texture() will in turn call getTexInfoLookup() to + // retrieve the shaveHairShape's texture information -- which is exactly + // what we're in the process of building. + // + // To break the cycle, we set a flag which tells getTexInfoLookup() and + // getVertInfoLookup() to return null pointers until we're done. + // + buildingLookups = true; + + freeTextureLookup(); + + shaveHairShape* sNode; + bool splineNode; + MStringArray result; + MStatus status; + + // get our shaveHairShapes in a selection list + nodeCount = (int)shaveHairShapes.length(); + + + // if we have nodes (we always should if we get here) then allocate + // space for the lookup structures. + if (nodeCount > 0) + { + // + // We need a lookup table to translate Shave node ID's into indices + // into our texture tables. + // + int i; + + texIDMapSize = (unsigned)(shaveHairShape::getMaxShaveID() + 1); + texIDMap = new int[texIDMapSize]; + + for (i = 0; i < (int)texIDMapSize; i++) + texIDMap[i] = -1; + + // + // Allocate the top (node) level of the texture tables. + // + texInfoLookup = (NODETEXINFO*)malloc(nodeCount*sizeof(NODETEXINFO)); + + if (texInfoLookup == NULL) + { + cerr << "ERROR- COULD NOT ALLOCATE MEMORY FOR TEXTURE LOOKUP" + << " STRUCTURES, PROCEEDING W/O TEXTURES." << endl; + } + else + { + for (int node = 0; node < nodeCount; node++) + { + NODETEXINFO* nodeInfo = &texInfoLookup[node]; + nodeInfo->displacement = NULL; + nodeInfo->textureLookup = NULL; + nodeInfo->u = NULL; + nodeInfo->v = NULL; + + + // get the shaveHairShape; + MFnDependencyNode shaveDependNode(shaveHairShapes[node]); + sNode = (shaveHairShape*) shaveDependNode.userNode (&status); + + nodeInfo->node = sNode; + + // + // Get Shave's internal SHAVENODE structure. Note that + // getting it this way will force the guides to update if + // the geometry has changed, e.g. because we're on a new + // frame. + // + SHAVENODE* hairNode = sNode->getHairNode(); + int hairGroup = sNode->getHairGroup(); + + // + // Make sure that our parameters are all up to date with + // any changes the user may have made. + // + // %%% Shouldn't the 'getHairNode' call above already have + // taken care of that? + // + sNode->updateParams(); + + // + // Add the node's Shave ID to our map. + // + texIDMap[sNode->getShaveID()] = node; + + // + // Let Shave know which node our calls will be referring + // to. + // + sNode->makeCurrent(); + + int numPasses = hairNode->shavep.passes[hairGroup]; + nodeInfo->maxPasses = numPasses; + + // our lookup will return a float, indexed by + // [pass][parm][hairid], here we allocate the [pass] + // level, now that we know what that number is. + nodeInfo->textureLookup = (float***) +// malloc(1*sizeof(float**)); + malloc(numPasses*sizeof(float**)); +// nodeInfo->u = new float*[1]; +// nodeInfo->v = new float*[1]; + nodeInfo->u = new float*[numPasses]; + nodeInfo->v = new float*[numPasses]; + + // get the haircount for this node + if (displayHairsOnly) + nodeInfo->count = sNode->getNumDisplayHairs(false); + else + nodeInfo->count = hairNode->shavep.haircount[hairGroup]*numPasses; + +numPasses=1; +nodeInfo->maxPasses=1; + + + // init the textured boolean to false; + for(int l = 0; l < SHAVE_NUM_PARAMS; l++) + { + nodeInfo->textured[l] = false; + nodeInfo->maxIndex[l] = -1; + } + + // we've got a bit of work to do to find the uv we want. + // We'll need to get the selection list for this node, then + // we'll need to be able to determing which mesh the + // faceIndex belongs to, and then we'll need to correlate + // the pointids to a local face vertid, in order to get the + // UVs. Yuck. + + + // get the texture node plug. We'll use this array plug in + // the next loop to figure out where we need to do our + // lookups from, and which parms are carrying textures. + MDGModifier dgMod; + MIntArray connectedIndexes; + + MPlug texPlug(shaveHairShapes[node], shaveHairShape::shaveTextureAttr); + MPlugArray texPlugArray; + MString currentNodeName = sNode->name(); + unsigned int numGrowthSurfaces = 0; + + //Only need to do this if the growth type is not Spline. + splineNode = (hairGroup == 4); + + if (!splineNode) + { + short uTess; + short vTess; + MPlug plug(shaveHairShapes[node], shaveHairShape::surfTessU); + plug.getValue(uTess); + plug.setAttribute(shaveHairShape::surfTessV); + plug.getValue(vTess); + + short sDept; + short sSamp; + MPlug plug2(shaveHairShapes[node], shaveHairShape::subdTessDept); + plug2.getValue(sDept); + plug2.setAttribute(shaveHairShape::subdTessSamp); + plug2.getValue(sSamp); + + + tesselateGrowthSurfaces( + sNode->exportData.meshes, uTess, vTess, sDept, sSamp + ); + + numGrowthSurfaces = sNode->exportData.meshes.length(); + } + + // + // Most texture connections are made directly to elements + // of the shaveHairShape's 'shaveTex' array attribute. + // + // However, a colour attribute is a compound, which can + // be connected either as a single compound plug, or as + // individual component plugs. Since the 'shaveTex' array + // doesn't allow for compounds, the shaveHairShape's colour + // attributes have their own separate connections. + // + // To make life easier on ourselves, we'd like everything + // to be in the 'shaveTex' array, so let's break the + // compounds up into their constituent parts and make + // temporary connections to the appropriate 'shaveTex' + // array elements. + // + makeTempColourConnections(shaveHairShapes[node], dgMod); + status = dgMod.doIt(); + + // now we need to make sure that the plug is actually + // connected to something. + // Thanks, Maya. + MPlug checkPlug; + + unsigned int pi = 0; + for (pi = 0; pi < 60; pi++) // was 50 + { + // Filter out those parameters which apply to growth + // vertices, not hairs. + unsigned int j; + for (j = 0; j < numVertParams; ++j) + if (pi == vertParams[j]) break; + + if (j == numVertParams) + { + checkPlug = texPlug.elementByLogicalIndex(pi); + + if(checkPlug.isConnected()) + connectedIndexes.append(pi); + } + } + + bool haveTextures = (connectedIndexes.length() > 0); + + // + // This structure defines those few items of + // information from each hair's CURVEINFO which we need + // to keep track of. + // + struct HairBaryInfo + { + int UTpid; + int pntid[3]; + float wgt[3]; + + } **hairInfo = NULL; + + // + // If we have textures, then we'll be needing the hairInfo + // array. + // + if (haveTextures) + hairInfo = new struct HairBaryInfo*[numPasses]; + + UVInfoCache uvInfoCache; + + nodeInfo->displacement = NULL; + + // Cache the vertex UVs so that we can later generate + // hair-root UVs for use in SHAVEapply_texture(). + unsigned int i; + SurfaceUVInfo** uvInfo = NULL; + + if (!splineNode) + { + uvInfo = new SurfaceUVInfo*[numGrowthSurfaces]; + + for (i = 0; i < numGrowthSurfaces; i++) + { + uvInfo[i] = NULL; + + MDagPath surf = sNode->exportData.meshes[i]; + surf.extendToShape(); + +#ifdef EVALUATE_POSITION_FIXED + if (!surf.hasFn(MFn::kMesh)) +#else + if (!surf.hasFn(MFn::kMesh) + && !surf.hasFn(MFn::kSubdiv)) +#endif + { + // For non-mesh surfaces Shave uses a mesh to + // approximate the surface. To get the hairs + // rooted at the correct point we calculate + // the 'displacement' (i.e. the difference + // between the approximate mesh and the real + // surface) for every hair root and cache it. + if (nodeInfo->displacement == NULL) + nodeInfo->displacement = new VERT*[numPasses]; + + // Non-meshes only have a single, default UV + // parameterization. + uvInfo[i] = cacheUVs(sNode, i, "", uvInfoCache); + } + else + { + uvInfo[i] = cacheUVs(sNode, i, meshUVSet, uvInfoCache); + } + } + } + + // Cache the displacements and barycentric info for each hair. + CURVEINFO curveInfo; + unsigned int* faceToMesh = NULL; + int hairnum; + unsigned int meshIndex; + MDagPath meshNode; + int pass; + float u; + float v; + WFTYPE wt; + + init_geomWF(&wt); + + for (pass = 0; pass < numPasses; pass++) + { + if (nodeInfo->displacement) + nodeInfo->displacement[pass] = new VERT[nodeInfo->count]; + + nodeInfo->u[pass] = new float[nodeInfo->count]; + nodeInfo->v[pass] = new float[nodeInfo->count]; + + if (haveTextures) + { + hairInfo[pass] = + new struct HairBaryInfo[nodeInfo->count]; + } + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + SHAVEmake_a_curveROOT( + pass, + hairGroup, + hairnum, + &wt, + &curveInfo + ); + + if (haveTextures) + { + // Save the barycentric info from curveInfo, + // which we'll need later on. + hairInfo[pass][hairnum].UTpid = curveInfo.UTpid; + + for (int i = 0; i < 3; i++) + { + hairInfo[pass][hairnum].pntid[i] = + curveInfo.pntid[i]; + hairInfo[pass][hairnum].wgt[i] = + curveInfo.wgt[i]; + } + } + + // + // Shave's hair number indices include killed + // hairs, so we have to leave room for them in our + // arrays. But we don't need to calculate their + // uvs. + // + if (wt.totalfaces > 0) + { + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to actually be + // on the texture, so we have to nudge it + // in a bit. + // + nodeInfo->u[pass][hairnum] = curveInfo.wgt[0]; + nodeInfo->v[pass][hairnum] = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + // Get the index of the mesh that this + // hair is growing on. + meshIndex = faceToMesh[curveInfo.UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + + int normalizedPolyIdx = curveInfo.UTpid - + sNode->exportData.startFaces[meshIndex]; + + float u = 0.0f; + float v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + curveInfo.pntid, + curveInfo.wgt, + u, + v + ); + + nodeInfo->u[pass][hairnum] = u; + nodeInfo->v[pass][hairnum] = v; + + // Get this hair's displacement. + if (nodeInfo->displacement) + { + getDisplacement( + meshNode, + curveInfo, + wt.v[0], + u, + v, + sNode->exportData.startVerts[meshIndex], + nodeInfo->displacement[pass][hairnum] + ); + } + } + } + else + { + if (nodeInfo->displacement) + { + nodeInfo->displacement[pass][hairnum].x = 0.0f; + nodeInfo->displacement[pass][hairnum].y = 0.0f; + nodeInfo->displacement[pass][hairnum].z = 0.0f; + } + } + } + } + + // + // SHAVEmake_a_curveROOT will free up the old WFTYPE's info + // on each call, but we have to free up the the final one + // that it left us with. + // + free_geomWF(&wt); + + // + // If we have any textured parameters then we need to + // iterate through all the passes and determine the texture + // information. + // + if (haveTextures) + { + if (verbose) cerr << "doing uv map preprocess... "; + + MObjectArray textures; + unsigned int ti; // which texture? + unsigned int numTextures = 0; + + // + // Get an array of all the textures being used by this + // shaveHairShape. + // + unsigned int i; + + for(i = 0; i < connectedIndexes.length(); i++) + { + int parmIndex; + parmIndex = (int)connectedIndexes[i]; + + // + // Get the plug which is driving this parameter. + // + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + // + // Grab its node. + // + MObject texture = texPlugArray[0].node(); + + //{ + // MFnDependencyNode dFn(texture); + // printf("texture node name %s\n",dFn.name().asChar() );fflush(stdout); + //} + + // + // If we haven't yet seen this one, add it to the + // array. + // + for (ti = 0; ti < numTextures; ti++) + if (texture == textures[ti]) break; + + if (ti == numTextures) + { + textures.append(texture); + numTextures++; + } + } + + // + // Build a UV map for each texture. Each map will have + // a single entry per hair, per pass. + // + HAIRUVS** textureUVMaps = new HAIRUVS*[numTextures]; + struct HairBaryInfo* hair; + + for (ti = 0; ti < numTextures; ti++) + { + textureUVMaps[ti] = new HAIRUVS[nodeInfo->maxPasses]; + + // + // Find the UV set used by each growth mesh for + // this texture. + // + MStringArray uvSets; + + if (splineNode) + { + // + // UV mapping for spline hair is handled + // specially. + // + uvSets.append(""); + } + else + { + MString uvSet; + + for (unsigned int m = 0; m < numGrowthSurfaces; m++) + { + MDagPath objectPath = + sNode->exportData.meshes[m]; + + if (!objectPath.hasFn(MFn::kMesh)) + { + // + // This growth object is not a mesh, so + // it doesn't support UV sets. + // + // We store a blank UV set name which + // will be an indicator later on to + // perform the default mapping for this + // growth object. + // + uvSet = ""; + } + else + { + // + // We have a mesh. + // + // Get the UV set used by this mesh for + // this texture. + // + uvSet = sNode->getUVSet( + objectPath.node(), + textures[ti] + ); + } + + uvSets.append(uvSet); + + // Cache the uvs for this UV set. + uvInfo[m] = cacheUVs(sNode, m, uvSet, uvInfoCache); + } + } + + for (pass = 0; + pass < nodeInfo->maxPasses; + pass++) + { + textureUVMaps[ti][pass].us = new float[nodeInfo->count]; + textureUVMaps[ti][pass].vs = new float[nodeInfo->count]; + + for (hairnum = 0; hairnum < nodeInfo->count; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + if (splineNode) + { + // + // Spline hair is a sheet, so there's a U + // direction but no V. (Keep in mind that + // we're talking about texture coords for + // the *base* of the hair here, not along + // the length of the hair itself.) + // + // Annoyingly, Maya never seems to + // consider a V value of 0.0 to + // actually be on the texture, so we + // have to nudge it in a bit. + // + u = hair->wgt[0]; + v = 0.0001f; + } + else + { + // Create a map from Shave's UTpid to the + // the mesh index, if it doesn't already + // exist. + if (faceToMesh == NULL) + faceToMesh = createFaceIDMap(sNode->exportData.startFaces); + meshIndex = faceToMesh[hair->UTpid]; + meshNode = sNode->exportData.meshes[meshIndex]; + meshNode.extendToShape(); + + // If this growth surface is a not + // really a mesh (i.e. it's a NURBS or + // subd) then it will be using the + // default UV parameterization, which + // we have already stored in nodeInfo + // so we can grab the value from there + // rather than recalculating it here. + // + // Similarly, if it *is* a mesh and we're + // using the same uv set as was cached + // in nodeInfo, then just grab the + // values from there. + if (!meshNode.hasFn(MFn::kMesh) + || (uvSets[meshIndex] == meshUVSet)) + { + u = nodeInfo->u[pass][hairnum]; + v = nodeInfo->v[pass][hairnum]; + } + else + { + int normalizedPolyIdx = hair->UTpid - + sNode->exportData.startFaces[meshIndex]; + + u = 0.0f; + v = 0.0f; + + getHairUV( + *uvInfo[meshIndex], + normalizedPolyIdx, + hair->pntid, + hair->wgt, + u, + v + ); + } + } + + textureUVMaps[ti][pass].us[hairnum] = u; + textureUVMaps[ti][pass].vs[hairnum] = v; + } + } + } + + if (verbose) + { + cerr << "done." << endl; + cerr << "getting texture values from maya... " << endl; + } +int maxtex=100000; + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + + + // malloc the top level of our lookup; + nodeInfo->textureLookup[pass] = (float**) + calloc(SHAVE_NUM_PARAMS, sizeof(float*)); + + MFloatArray uArray; + MFloatArray vArray; + MFloatPointArray pointArray; + MFloatPointArray pa; + VERT pos[3]; + struct HairBaryInfo* hair; + + // we need to to figure out the uv coords for each + // of the roots for this pass, and also the point + // locations for 3D texture evaluation. + MFloatMatrix camMatrix; + + // if we store the lookups we have done then we + // don't need to do them again. This will save both + // time and memory for high density scenes. + MStringArray doneLookups; + MIntArray doneLookupsIndex; + + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + int ind; + int total; + total=nodeInfo->count; + int parmIndex = (int)connectedIndexes[pi]; + texPlug + .elementByLogicalIndex(parmIndex) + .connectedTo(texPlugArray, true, false); + + MObject texture = texPlugArray[0].node(); + + for (ti = 0; ti < numTextures; ti++) + if (textures[ti] == texture) break; + + + nodeInfo->textureLookup[pass][parmIndex] + = (float*) calloc( + total, sizeof(float) + ); + + + for (ind=0;ind<nodeInfo->count;ind+=maxtex) + { + uArray.clear(); + vArray.clear(); + pointArray.clear(); + int szz=total; + + if (total>=maxtex) szz=total-ind; + if (szz>maxtex) szz=maxtex; +//fprintf (stdout,"ind = %d szz= %d\n",ind,szz);fflush(stdout); + + for (hairnum = ind; hairnum < ind+szz; hairnum++) + { + hair = &hairInfo[pass][hairnum]; + + for (int k = 0; k < 3; k++) + { + pos[k] = sNode->memShaveObj + .v[hair->pntid[k]]; + pos[k].x *= hair->wgt[k]; + pos[k].y *= hair->wgt[k]; + pos[k].z *= hair->wgt[k]; + } + + uArray.append( + textureUVMaps[ti][pass].us[hairnum] + ); + vArray.append( + textureUVMaps[ti][pass].vs[hairnum] + ); + + pointArray.append( + pos[0].x+pos[1].x+pos[2].x, + pos[0].y+pos[1].y+pos[2].y, + pos[0].z+pos[1].z+pos[2].z + ); + } + + // set aside a little memory for our lookup results. + + +{ + { + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + + //////debug////// + //MFloatVectorArray uTang; + //MFloatVectorArray vTang; + //MFloatArray filter; + //uTang.setLength(szz); + //vTang.setLength(szz); + //filter.setLength(szz); + //for(unsigned int k = 0; k < szz; k++) + //{ + // uTang[k] = MFloatVector(1.0f,0.0f); + // vTang[k] = MFloatVector(0.0f,1.0f); + // filter[k] = 0.1f; + //} + ///////////////////////// + + //printf("texture name %s\n",texPlugArray[0].name().asChar() );fflush(stdout); + + // lets get the lookup values from the maya + // shading engine. + MStatus shRes = + MRenderUtil::sampleShadingNetwork( + texPlugArray[0].name(), //shade node name + szz, //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL /*&uTang*/, //u tang + NULL /*&vTang*/, //v tang + NULL /*&filter*/, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); +//fprintf (stdout,"done with lookup\n");fflush(stdout); + // we need to copy the returned values into the + // lookup array our pointer references. also, + // we'll store the name and parm index of this + // lookup so we can reuse it if the opportunity + // presents itself. + nodeInfo->maxIndex[parmIndex] + = (int)total; + + ///////// debug /////////////// + //if(shRes != MStatus::kSuccess) printf(" MRenderUtil::sampleShadingNetwork failed with result %i\n",shRes); + //for(int k=0; k < (int)szz; k++) + //{ + // printf("uv %f %f rgb %f %f %f t %f\n",uArray[k],vArray[k],vertColorArray[k].x,vertColorArray[k].y,vertColorArray[k].z,vertTranspArray[k]); + //} + //fflush(stdout); + /////////////////////////////// + + for(int k=0; k < (int)szz; k++) + { + nodeInfo->textureLookup[pass][parmIndex][k+ind] + = (float)vertColorArray[k].x; +// nodeInfo->textureLookup[pass][parmIndex][k] +// = (float)vertColorArray[k].x; + } + } +} + } + + } + } + + if (verbose) + { + cerr << "done." << endl; + cerr << "freeing cached curveinfos and uvlists..." << endl; + } + + for (ti = 0; ti < numTextures; ti++) + { + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + { + delete [] textureUVMaps[ti][pass].us; + delete [] textureUVMaps[ti][pass].vs; + } + + delete [] textureUVMaps[ti]; + } + + delete [] textureUVMaps; + + for (pass = 0; pass < nodeInfo->maxPasses; pass++) + delete [] hairInfo[pass]; + + delete [] hairInfo; + } + + delete [] faceToMesh; + + UVInfoCache::iterator setIter; + + for (setIter = uvInfoCache.begin(); + setIter != uvInfoCache.end(); + ++setIter) + { + UVSetUVInfo* uvSetUVInfo = (*setIter).second; + + for (unsigned int ui = 0; ui < uvSetUVInfo->size(); ++ui) + delete (*uvSetUVInfo)[ui]; + + delete uvSetUVInfo; + } + + uvInfoCache.clear(); + + // we can't set the textured member true until we've taken + // care of all the passes. Otherwise make a curve will fail + // trying to do a lookup. + for (pi = 0; pi < connectedIndexes.length(); pi++) + { + unsigned int pIndex = connectedIndexes[pi]; + nodeInfo->textured[pIndex] = true; + } + + // remove temporary connections that were made in the + // hypergraph. not strictly necesary, but keeps thing + // looking cleaner for the user. + dgMod.undoIt(); + + // + // Let the node know that its texture cache has changed. + // + MPlug plug( + shaveHairShapes[node], + shaveHairShape::textureCacheUpdatedAttr + ); + plug.setValue(true); + } + } + } + + // + // It's now safe to serve up the texture lookup tables. + // + buildingLookups = false; + + //if (verbose) cerr << "done with lookups." << endl << endl; +} + + + +MStatus freeTextureLookup(void) +{ + if (texInfoLookup) + { + for (unsigned int node = 0; node < texInfoLookupSize; node++) { + freeTextureLookup(&texInfoLookup[node]); + } + + free(texInfoLookup); + texInfoLookup = NULL; + texInfoLookupSize = 0; + } + + if (texIDMap) + { + delete [] texIDMap; + texIDMap = NULL; + texIDMapSize = 0; + } + + return MS::kSuccess; +} + +MStatus freeTextureLookup(NODETEXINFO* lookup) +{ + if (lookup) + { + bool istextured = false; + int clearPasses = lookup->maxPasses; + + for(int pass = 0; pass < clearPasses; pass++) + { + if (lookup->displacement) + { + delete [] lookup->displacement[pass]; + lookup->displacement[pass] = NULL; + } + delete [] lookup->u[pass]; + lookup->u[pass] = NULL; + + delete [] lookup->v[pass]; + lookup->v[pass] = NULL; + + for(int parm = 0; parm < SHAVE_NUM_PARAMS; parm++) + { + if(lookup->textured[parm]) + { + free(lookup->textureLookup[pass][parm]); + lookup->textureLookup[pass][parm] = NULL; + istextured = true; + } + } + + if(istextured) + { + free(lookup->textureLookup[pass]); + lookup->textureLookup[pass] = NULL; + } + } + + if (lookup->displacement) + { + delete [] lookup->displacement; + lookup->displacement = NULL; + } + delete [] lookup->u; + lookup->u = NULL; + delete [] lookup->v; + lookup->v = NULL; + + if (lookup->textureLookup) + { + free(lookup->textureLookup); + lookup->textureLookup = NULL; + } + } + + return MS::kSuccess; +} + + +void getDisplacement( + MDagPath& surface, + CURVEINFO& ci, + VERT& rootPos, + float rootU, + float rootV, + int surfaceStartFaceIndex, + VERT& displacement +) +{ + displacement.x = 0; + displacement.y = 0; + displacement.z = 0; + + if (surface.hasFn(MFn::kNurbsSurface)) + { + MPoint actualPoint; + MFnNurbsSurface nurbsFn(surface); + MPoint shavePt(rootPos.x, rootPos.y, rootPos.z); + + actualPoint = nurbsFn.closestPoint( + shavePt, NULL, NULL, true, MPoint_kTol, MSpace::kWorld + ); + + displacement.x = (float)(actualPoint.x - shavePt.x); + displacement.y = (float)(actualPoint.y - shavePt.y); + displacement.z = (float)(actualPoint.z - shavePt.z); + } +#ifdef EVALUATE_POSITION_FIXED + else if (surface.hasFn(MFn::kSubdiv)) + { + int i; + int level1FaceIdx = (ci->UTpid - surfaceStartFaceIndex) / 2; + MFnSubd subdFn(surface); + + int numLevel0Faces = subdFn.polygonCount(0); + + for (i = 0; i < numLevel0Faces; i++) + { + MUint64 level0FaceID; + MUint64Array level1Faces; + + MFnSubdNames::toMUint64(level0FaceID, i, 0, 0, 0, 0); + subdFn.polygonChildren(level0FaceID, level1Faces); + + if ((unsigned int)level1FaceIdx >= level1Faces.length()) + { + level1FaceIdx -= level1Faces.length(); + } + else + { + MPoint actualPoint; + + subdFn.evaluatePosition( + level1Faces[level1FaceIdx], + rootU, + rootV, + false, + actualPoint + ); + + // + // 'actualPoint' is in object space. Convert it to world. + // + MMatrix mat = surface.inclusiveMatrix(); + + actualPoint = actualPoint * mat; + + displacement.x = actualPoint.x - rootPos.x; + displacement.y = actualPoint.y - rootPos.y; + displacement.z = actualPoint.z - rootPos.z; + break; + } + } + } + else if (!surface.hasFn(MFn::kMesh) && !surface.hasFn(MFn::kNurbsCurve)) +#else + else if (!surface.hasFn(MFn::kMesh) + && !surface.hasFn(MFn::kSubdiv) + && !surface.hasFn(MFn::kNurbsCurve)) +#endif + { + MGlobal::displayWarning( + MString("shave: getDisplacement: got request for invalid") + + " growth surface '" + surface.fullPathName() + "'." + ); + } +} + + +NODETEXINFO* getTexInfoLookup(unsigned shaveID) +{ + if (buildingLookups + || (texIDMap == NULL) + || (shaveID < 0) + || (shaveID >= texIDMapSize)) + { + return NULL; + } + + int lookupID = texIDMap[shaveID]; + + if (lookupID == -1) return NULL; + + return &texInfoLookup[lookupID]; +} + + +// +// Connect the source plug to the specified element of the given +// shaveHairShape's texture array plug, using the supplied DGModifier. +// +static MStatus makeTextureConnection( + MPlug sourcePlug, MObject shavenode, int texPlugIndex, MDGModifier& dgMod +) +{ + MPlug texPlugArray(shavenode, shaveHairShape::shaveTextureAttr); + + dgMod.connect(sourcePlug, texPlugArray.elementByLogicalIndex(texPlugIndex)); + + return MS::kSuccess; +} + + +// +// If a colour shaveHairShape parameter has an incoming connection, create +// separate temporary connections to the 'tex' array for each of its RGB +// components. +// +static void makeTempCompoundConnection( + const MObject& shavenode, + MObject& colourAttr, + int startIndex, + MDGModifier& dgMod +) +{ + MPlug colourPlug(shavenode, colourAttr); + + if (colourPlug.isConnected()) + { + // + // Find the node which is feeding this connection. + // + MPlugArray connections; + + colourPlug.connectedTo(connections, true, false); + if(connections.length() > 0) + { + MFnDependencyNode texNode(connections[0].node()); + + // + // Connect the component plugs to the shaveHairShape's 'shaveTex' + // array. + // + colourPlug = texNode.findPlug("outColorR"); + makeTextureConnection(colourPlug, shavenode, startIndex, dgMod); + + colourPlug = texNode.findPlug("outColorG"); + makeTextureConnection(colourPlug, shavenode, startIndex+1, dgMod); + + colourPlug = texNode.findPlug("outColorB"); + makeTextureConnection(colourPlug, shavenode, startIndex+2, dgMod); + } + } +} + + +static void makeTempColourConnections( + const MObject& shavenode, MDGModifier& dgMod +) +{ + // first we need to hook up the 'special' nodes- the color + // ones. These nodes are handled differently than the rest + // because they hook one tex up to a vector value, rather + // than one float. + makeTempCompoundConnection( + shavenode, shaveHairShape::hairColorTexture, 9, dgMod + ); + + makeTempCompoundConnection( + shavenode, shaveHairShape::mutantHairColorTexture, 13, dgMod + ); + + makeTempCompoundConnection( + shavenode, shaveHairShape::rootHairColorTexture, 17, dgMod + ); + + MObject colourAttrs[] = + { + shaveHairShape::hairColorTextureR, + shaveHairShape::hairColorTextureG, + shaveHairShape::hairColorTextureB, + shaveHairShape::mutantHairColorTextureR, + shaveHairShape::mutantHairColorTextureG, + shaveHairShape::mutantHairColorTextureB, + shaveHairShape::rootHairColorTextureR, + shaveHairShape::rootHairColorTextureG, + shaveHairShape::rootHairColorTextureB + }; + + int attrIndices[] = { 9, 10, 11, 13, 14, 15, 17, 18, 19 }; + int numAttrs = sizeof(attrIndices) / sizeof(int); + int i; + MPlugArray texturePlug; + + for (i = 0; i < numAttrs; i++) + { + MPlug colourPlug(shavenode, colourAttrs[i]); + + if (colourPlug.isConnected()) + { + colourPlug.connectedTo(texturePlug, true, false); + + makeTextureConnection( + texturePlug[0], shavenode, attrIndices[i], dgMod + ); + } + } +} + + +MStatus tesselateGrowthSurfaces( + const MDagPathArray& growthSurfaces, + short uTess, short vTess, + short sDept, short sSamp +) +{ + MDagPath surface; + unsigned int i; + + standinMeshes.clear(); + standinMeshOrigSurfaces.clear(); + + for (i = 0; i < growthSurfaces.length(); i++) + { + surface = growthSurfaces[i]; + + // + // Make sure that we've got the shape and not its transform. + // + surface.extendToShape(); + +#ifdef EVALUATE_POSITION_FIXED + // + // If the shape is a NURBS surface, then we have to generate a + // temporary mesh for it. + // + if (surface.hasFn(MFn::kNurbsSurface)) +#else + // + // If the shape is a NURBS or subdivision surface, then we have to + // generate a temporary mesh for it. + // + if (surface.hasFn(MFn::kNurbsSurface) || surface.hasFn(MFn::kSubdiv)) +#endif + { + MObject tempMesh = shaveUtil::getMesh(surface, (int)uTess, (int)vTess, (int)sDept, (int)sSamp); + standinMeshes.append(tempMesh); + standinMeshOrigSurfaces.append(surface); + } + } + + return MS::kSuccess; +} + + +unsigned int* createFaceIDMap(const MIntArray& startIndices) +{ + if (startIndices.length() == 0) return new unsigned int[1]; + + unsigned int numFaces = startIndices[startIndices.length()-1]; + unsigned int* map = new unsigned int[numFaces]; + unsigned int i; + unsigned int j = 0; + + for (i = 1; i < startIndices.length(); i++) + { + while (j < (unsigned int)startIndices[i]) + map[j++] = i-1; + } + + return map; +} + + +const MString ShavePreprocTex::commandName("shaveUpdateTextures"); + +static const char* flVertexParams = "-vertexParams"; +static const char* fsVertexParams = "-vp"; + +ShavePreprocTex::~ShavePreprocTex() {} + +void* ShavePreprocTex::createCmd() +{ + return new ShavePreprocTex(); +} + + +MSyntax ShavePreprocTex::createSyntax() +{ + MSyntax syntax; + + syntax.addFlag(fsVertexParams, flVertexParams); + + return syntax; +} + + +MStatus ShavePreprocTex::doIt( const MArgList& args ) +{ + MStatus res = MS::kSuccess; + MArgDatabase argdb(syntax(), args, &res); + if (!res) return res; + + MObjectArray shaveHairShapes; + shaveUtil::getShaveNodes(shaveHairShapes); + + if (shaveHairShapes.length() > 0) + { + MGlobal::displayInfo("command shaveUpdateTextures"); + + shaveGlobals::Globals globals; + shaveGlobals::getGlobals(globals); + + if (argdb.isFlagSet(fsVertexParams)) + initVertTexInfoLookup(shaveHairShapes); + else +#ifdef PER_NODE_TEXLOOKUP + initTexInfoLookup2(shaveHairShapes, "", globals.verbose, true); +#else + initTexInfoLookup(shaveHairShapes, "", globals.verbose, true); +#endif + + MGlobal::executeCommand("currentTime `currentTime -q`"); + } + + return res; +} + + +// Initialize the per-vertex texture lookup for a single shaveHairShape. +// +// If this method is called during shaveHairShape::compute() then 'block' must +// be provided and must point to the same datablock as was passed to +// shaveHairShape::compute(). +// +void initVertTexInfoLookup(shaveHairShape* sNode, MObject node, MDataBlock* block) +{ + MPlug checkPlug; + MObject nodeObj = sNode->thisMObject(); + MPlug texPlug(nodeObj, shaveHairShape::shaveTextureAttr); + MPlugArray texPlugArray; + unsigned int vertCount = sNode->memShaveObj.totalverts; + + // Get get the hair shape's per-vertex texture data. + ShavePerVertTexInfo* vertInfo = &sNode->vertTexInfo; + + // Prepare it to accept new data. + vertInfo->init(vertCount); + + // Store a pointer to the node's info in a map where it can be + // quickly indexed by Shave ID. + vertTexInfoMap[sNode->getShaveID()] = vertInfo; + + + // Step through each of the per-vertex parameters and gather texture + // data for any which are textured. + unsigned int pi = 0; + for (pi = 0; pi < ShavePerVertTexInfo::numParams(); pi++) + { + int paramID = ShavePerVertTexInfo::getParamNumber(pi); + + checkPlug = texPlug.elementByLogicalIndex(paramID); + + if (checkPlug.isConnected()) + { + // + // Get the texture plug so we can determine what + // node to sample. + // + texPlugArray.clear(); + texPlug + .elementByLogicalIndex(paramID) + .connectedTo(texPlugArray, true, false); + + MFloatArray uArray; + MFloatArray vArray; + MFloatPointArray pointArray; + MFloatVectorArray vertColorArray; + MFloatVectorArray vertTranspArray; + MFloatMatrix camMatrix; + VERT wfpos; + + for (unsigned int vi = 0; vi < vertCount; ++vi) + { + //uArray.append(sNode->memShaveObj.uv[vi].x); + //vArray.append(sNode->memShaveObj.uv[vi].y); + + wfpos = sNode->memShaveObj.v[vi]; + + pointArray.append( + MFloatPoint(wfpos.x, wfpos.y, wfpos.z) + ); + //printf("x %f y %f z %f\n", (float)wfpos.x,(float)wfpos.y,(float)wfpos.z); + } + + /// by some reason memShaveObj contains some weird UVs + /// so vertex maps are not mappad same as others + /// so we are trying to grab UVs from export mesh via Maya api + /// if something is wrong with it, the uvs from memShaveObj are used + unsigned int n = 0; + + //printf("num exp meshes %i\n",sNode->exportData.meshes.length());fflush(stdout); + + if(sNode->exportData.meshes.length() > 0) + { + short uTess; + short vTess; + short sDept; + short sSamp; + + if (block != NULL) + { + uTess = block->inputValue(shaveHairShape::surfTessU).asShort(); + vTess = block->inputValue(shaveHairShape::surfTessV).asShort(); + sDept = block->inputValue(shaveHairShape::subdTessDept).asShort(); + sSamp = block->inputValue(shaveHairShape::subdTessSamp).asShort(); + } + else + { + MPlug plug(node, shaveHairShape::surfTessU); + plug.getValue(uTess); + plug.setAttribute(shaveHairShape::surfTessV); + plug.getValue(vTess); + + plug.setAttribute(shaveHairShape::subdTessDept); + plug.getValue(sDept); + plug.setAttribute(shaveHairShape::subdTessSamp); + plug.getValue(sSamp); + } + + //MItMeshVertex vIter(sNode->exportData.meshes[0]); + + + for(unsigned int oo = 0; oo < sNode->exportData.meshes.length(); oo++) + { + MDagPath surface = sNode->exportData.meshes[oo]; + surface.extendToShape(); + MObject mesh = shaveUtil::getMesh(surface, (int)uTess, (int)vTess, (int)sDept, (int)sSamp); + MItMeshVertex vIter(mesh); + + for (vIter.reset(); !vIter.isDone(); vIter.next()) + { + int numUVs; + vIter.numUVs(numUVs); + if (numUVs > 0) + { + float2 vertUV; + vIter.getUV(vertUV); + uArray.append(vertUV[0]); + vArray.append(vertUV[1]); + //printf("u %f v %f\n", vertUV[0], vertUV[1]); + } + else + { + uArray.append(0.0f); + vArray.append(0.0f); + } + n++; + } + } + } + if(n != vertCount) + { + uArray.clear(); + vArray.clear(); + for (unsigned int vi = 0; vi < vertCount; ++vi) + { + uArray.append(sNode->memShaveObj.uv[vi].x); + vArray.append(sNode->memShaveObj.uv[vi].y); + } + } + //printf("texture %s\n",texPlugArray[0].name().asChar()); + //printf("uvs stored %i vertices %i\n", n, vertCount);fflush(stdout); + //////////////////////////// + + // Sample the texture at all vertices. + MRenderUtil::sampleShadingNetwork( + texPlugArray[0].name(), //shade node name + uArray.length(), //samples + false, //shadows + false, //reuse shad maps + camMatrix, //camMatrix + &pointArray, //points + &uArray, //u coords + &vArray, //v coords + NULL, //normals + &pointArray, //ref points + NULL, //u tang + NULL, //v tang + NULL, //filter size + vertColorArray, //out color + vertTranspArray //out transp + ); + + // Store the sampled texture values. + for (unsigned int k = 0; k < vertColorArray.length(); ++k) + { + vertInfo->setValue(pi, k, (float)vertColorArray[k].x); + + //vertInfo->setValue(pi, k, k < vertColorArray.length()/2 ? 0.0f : 1.0f); //test - OK + //printf("r %f g %f b %f\n", (float)vertColorArray[k].x,(float)vertColorArray[k].y,(float)vertColorArray[k].z);fflush(stdout); + } + } + } +} + + +// Vertex-level textures are used for dynamics runup and Live Mode. +// They're not used in rendering or export. +void initVertTexInfoLookup(MObjectArray& shaveHairShapes) +{ + buildingLookups = true; + + unsigned int numNodes = shaveHairShapes.length(); + + for (unsigned int i = 0; i < numNodes; ++i) + { + // Get the hair shape. + MFnDependencyNode shaveDependNode(shaveHairShapes[i]); + shaveHairShape* node = (shaveHairShape*) shaveDependNode.userNode(); + + // Force the guides to update if the geometry has changed, + // e.g. because we're on a new frame. + node->getHairNode(); + + // Make sure that our parameters are all up to date with + // any changes the user may have made. + // + // %%% Shouldn't the 'getHairNode' call above already have + // taken care of that? + node->updateParams(); + + // Let Shave know which node our calls will be referring to. + node->makeCurrent(); + + // Update the node's per-vertex texture info. + initVertTexInfoLookup(node,shaveHairShapes[i]); + + // + // Let the node know that its texture cache has changed. + // + MPlug plug(shaveHairShapes[i], shaveHairShape::textureCacheUpdatedAttr); + plug.setValue(true); + } + + // It's now safe to serve up the texture lookup data. + buildingLookups = false; +} + + +float applyVertTexValue(long shaveID, int vertID, int paramID, float baseValue) +{ + if (!buildingLookups) + { + ShavePerVertTexInfo* vertTexInfo = vertTexInfoMap[shaveID]; + + if (vertTexInfo) + { + // Find the index of this parameter. + unsigned int paramIdx; + + if (ShavePerVertTexInfo::getParamIdx(paramID, paramIdx)) + return (baseValue * vertTexInfo->getValue(paramIdx, vertID)); + } + } + + return baseValue; +} + + +void cacheSurfaceUVs( + MItMeshPolygon& polyIter, + unsigned int pntidOffset, + const MString& uvSet, + SurfaceUVInfo* surfUVInfo +) +{ + FaceUVInfo faceUVInfo; + VertUVInfo vertUVInfo; + + surfUVInfo->reserve(polyIter.count ()); + + for (polyIter.reset(); !polyIter.isDone(); polyIter.next()) + { + unsigned int numFaceVerts = polyIter.polygonVertexCount(); + unsigned int i; + + faceUVInfo.clear(); + faceUVInfo.reserve(numFaceVerts); + + for (i = 0; i < numFaceVerts; ++i) + { + float2 uv = { 0.0f, 0.0f }; + + // If no UV set is specified, use the mesh's default set. + if (uvSet.length() == 0) + { + polyIter.getUV(i, uv); + } + else + { + MStatus st = polyIter.getUV(i, uv, &uvSet); + + // If we couldn't find a UV it's probably because we have + // an uninitialized UV set. So fall back to the default + // mapping. + if (!st) + { + polyIter.getUV(i, uv); + } + } + + vertUVInfo.u = uv[0]; + vertUVInfo.v = uv[1]; + vertUVInfo.pntid = pntidOffset + polyIter.vertexIndex(i); + + faceUVInfo.push_back(vertUVInfo); + } + + surfUVInfo->push_back(faceUVInfo); + } +} + + +SurfaceUVInfo* cacheUVs( + shaveHairShape* sNode, + unsigned int surfIdx, + const MString& uvSet, + UVInfoCache& uvInfoCache +) +{ + UVSetUVInfo* uvSetUVInfo = NULL; + + // Do we have any data cached for this uv set? + UVInfoCache::iterator it = uvInfoCache.find(uvSet); + + if (it == uvInfoCache.end()) + { + // No data for this uv set yet, so create an entry with empty + // slots for each growth surface. + uvSetUVInfo = new UVSetUVInfo; + + unsigned int numSurfaces = sNode->exportData.meshes.length(); + + uvSetUVInfo->reserve(numSurfaces); + uvInfoCache.insert(UVInfoCache::value_type(uvSet, uvSetUVInfo)); + + for (unsigned int i = 0; i < numSurfaces; ++i) + uvSetUVInfo->push_back(NULL); + } + else + { + uvSetUVInfo = (*it).second; + + // If we've already cached the UVs for this growth mesh, return + // them. + if ((*uvSetUVInfo)[surfIdx] != NULL) return (*uvSetUVInfo)[surfIdx]; + } + + SurfaceUVInfo* surfUVInfo = new SurfaceUVInfo; + MDagPath surfPath = sNode->exportData.meshes[surfIdx]; + + // If hair we are dealing with is growing from a nurbs surface, + // then we cannot initialize the poly iterator with its path because + // the path doesn't point to a mesh. Instead we must initialize it + // with the tesselated mesh object that we created earlier. +#ifdef EVALUATE_POSITION_FIXED + if (surfPath.hasFn(MFn::kNurbsSurface)) +#else + if (surfPath.hasFn(MFn::kNurbsSurface) || surfPath.hasFn(MFn::kSubdiv)) +#endif + { + unsigned int i; + + for (i = 0; i < standinMeshOrigSurfaces.length(); i++) + { + if (standinMeshOrigSurfaces[i] == surfPath) break; + } + + if (i < standinMeshes.length()) + { + MItMeshPolygon polyIter(standinMeshes[i]); + + cacheSurfaceUVs( + polyIter, + sNode->exportData.startVerts[surfIdx], + uvSet, + surfUVInfo + ); + } + } + else + { + MItMeshPolygon polyIter(surfPath); + + // Meshes with history can sometimes get themselves into a state + // where an MFnMesh created from the dag path (or node) reports + // vertices but no faces. If that happens then we need to pull + // the mesh from its output plug. + if (polyIter.count() == 0) + { + MFnDagNode nodeFn(surfPath); + MPlug plug = nodeFn.findPlug("outMesh"); + MObject meshObj; + + plug.getValue(meshObj); + + MItMeshPolygon polyIter(meshObj); + + cacheSurfaceUVs( + polyIter, + sNode->exportData.startVerts[surfIdx], + uvSet, + surfUVInfo + ); + } + else + { + cacheSurfaceUVs( + polyIter, + sNode->exportData.startVerts[surfIdx], + uvSet, + surfUVInfo + ); + } + } + + (*uvSetUVInfo)[surfIdx] = surfUVInfo; + + return surfUVInfo; +} |