aboutsummaryrefslogtreecommitdiff
path: root/mayaPlug/shaveTextureStore.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mayaPlug/shaveTextureStore.cpp')
-rw-r--r--mayaPlug/shaveTextureStore.cpp2812
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;
+}