void vixo_hairCacheExport::getInCurveInfo(map<int,int>& plugMapVID,MString vixoHairNode,map<int,MVectorArray>& inCurveShape) { MSelectionList slist; MGlobal::getSelectionListByName(vixoHairNode,slist); MDagPath vixoHairNodeDag; slist.getDagPath(0,vixoHairNodeDag); vixoHairNodeDag.extendToShape(); MFnDagNode fnVixoHair(vixoHairNodeDag); MPlug plugInCtrlCurveData=fnVixoHair.findPlug("inCtrlCurveData"); map<int,int>::iterator iterPlugMapVID; for(iterPlugMapVID=plugMapVID.begin();iterPlugMapVID!=plugMapVID.end();iterPlugMapVID++) { MPlug inCurvePlug=plugInCtrlCurveData.elementByLogicalIndex(iterPlugMapVID->first); MFnVectorArrayData fnVector(inCurvePlug.asMObject()); MVectorArray data=fnVector.array(); MVectorArray tangent(data.length()-1); MPlugArray tempArr; inCurvePlug.connectedTo(tempArr,true,false); MFnDependencyNode fnHairSystem(tempArr[0].node()); MPlug inputHairs=fnHairSystem.findPlug("inputHair"); tempArr.clear(); inputHairs.elementByLogicalIndex(iterPlugMapVID->first).connectedTo(tempArr,true,false); MFnDependencyNode fnFolli(tempArr[0].node()); MFnMatrixData fnMatData(fnFolli.findPlug("worldMatrix[0]").asMObject()); MMatrix mat=fnMatData.matrix(); for(int i=0;i<data.length()-1;i++) { tangent[i]=data[i+1]*mat-data[i]*mat; } inCurveShape.insert(pair<int,MVectorArray>(iterPlugMapVID->second,tangent)); } }
MStatus vixo_hairCacheExport::doIt(const MArgList& args) { MString vixoHairNode; int startFrame,endFrame; unsigned index; index=args.flagIndex("vhn","vixoHairNode"); if(MArgList::kInvalidArgIndex!=index) { args.get(index+1,vixoHairNode); } index=args.flagIndex("sf","startFrame"); if(MArgList::kInvalidArgIndex!=index) { args.get(index+1,startFrame); } index=args.flagIndex("ef","endFrame"); if(MArgList::kInvalidArgIndex!=index) { args.get(index+1,endFrame); } //get hairCacheFileName MSelectionList slist; MGlobal::getSelectionListByName(vixoHairNode,slist); MDagPath vixoHairNodeDag; slist.getDagPath(0,vixoHairNodeDag); vixoHairNodeDag.extendToShape(); MFnDagNode fnVixoHair(vixoHairNodeDag); MPlug plugCacheFileName=fnVixoHair.findPlug("hairCacheFileName"); MString cacheFileName=plugCacheFileName.asString(); //~get hairCacheFileName //get staticMesh MPlug plugStaticMesh=fnVixoHair.findPlug("staticInMesh"); MObject staticMeshObj=MFnMeshData(plugStaticMesh.asMObject()).object(); //~get staticMesh //build out dataBase MFnMesh fnStaticMesh(staticMeshObj); map<int,set<outInVertexRelationInfo>> outVertexDataBase; map<int,vector<outVIDCtrlInfo>> outVertexControlData; for(int i=0;i<fnStaticMesh.numVertices();i++) { set<outInVertexRelationInfo> temp; temp.clear(); outVertexDataBase.insert(pair<int,set<outInVertexRelationInfo>>(i,temp)); } //build out dataBase //get inCurveVID map<int,int> plugMapVID; map<int,int> VIDMapPlug; map<int,inVertexBasicInfo> inVIDMapInVertexDataBase; MPlug plugAllInCurve=fnVixoHair.findPlug("inCtrlCurveData"); MIntArray existIndex; plugAllInCurve.getExistingArrayAttributeIndices(existIndex); for(int i=0;i<existIndex.length();i++) { MPlugArray arr; plugAllInCurve.elementByLogicalIndex(existIndex[i]).connectedTo(arr,true,false); MFnDependencyNode fnHairSystem(arr[0].node()); MPlug inputHairs=fnHairSystem.findPlug("inputHair"); arr.clear(); inputHairs.elementByLogicalIndex(existIndex[i]).connectedTo(arr,true,false); MFnDependencyNode fnFolli(arr[0].node()); int vid=fnFolli.findPlug("vertexID").asInt(); plugMapVID.insert(pair<int,int>(existIndex[i],vid)); VIDMapPlug.insert(pair<int,int>(vid,existIndex[i])); initBasicStepInfo(vid,staticMeshObj,inVIDMapInVertexDataBase); } //~get inCurveVID //build in out relation map<int,inVertexBasicInfo>::iterator inDBIter; for(inDBIter=inVIDMapInVertexDataBase.begin();inDBIter!=inVIDMapInVertexDataBase.end();inDBIter++) { buileRelationBetweenInOut(inDBIter->first,inVIDMapInVertexDataBase,outVertexDataBase); } map<int,set<outInVertexRelationInfo>>::iterator outDBIter; for(outDBIter=outVertexDataBase.begin();outDBIter!=outVertexDataBase.end();outDBIter++) { sortControlOrder(outDBIter->first,outVertexDataBase,outVertexControlData); } //~build in out relation for(int i=startFrame;i<=endFrame;i++) { MString currentTime; currentTime.set(i); MGlobal::executeCommand("currentTime "+currentTime); MGlobal::executeCommand("dgeval "+vixoHairNode+".hiddenOutput"); //get dynamic mesh MPlug plugDynamicMesh=fnVixoHair.findPlug("dynamicInMesh"); MObject dynamicMeshObj=MFnMeshData(plugDynamicMesh.asMObject()).object(); //~get dynamic mesh //export cache //faceid triid vid position normal tangent vector<forExportHairCache> exportData; exportBasicData(dynamicMeshObj,exportData); //curve tangent //get in curve infos map<int,MVectorArray> inCurveShape; getInCurveInfo(plugMapVID,vixoHairNode,inCurveShape); //~get in curve infos vector<vec3> outCurveCacheData; vec3 init; init.x=0; init.y=0; init.z=0; outCurveCacheData.resize(fnStaticMesh.numVertices()*inCurveShape.begin()->second.length(),init); buildCache(inCurveShape.begin()->second.length(),dynamicMeshObj,outCurveCacheData,inCurveShape,outVertexControlData); //~export cache //write to file MString fileName=getFileName(cacheFileName,i); fstream fout(fileName.asChar(),ios_base::out|ios_base::binary); int triNumvertexNum[3]; triNumvertexNum[0]=exportData.size(); triNumvertexNum[1]=fnStaticMesh.numVertices(); triNumvertexNum[2]=inCurveShape.begin()->second.length(); fout.write((char*)triNumvertexNum,sizeof(int)*3); fout.write((char*)&exportData[0],sizeof(forExportHairCache)*exportData.size()); fout.write((char*)&outCurveCacheData[0],sizeof(vec3)*outCurveCacheData.size()); fout.flush(); fout.close(); /* MString fileNameDebug=fileName+".dbg"; fout.open(fileNameDebug.asChar(),ios_base::out); for(int i=0;i<outCurveCacheData.size();i++) { fout<<outCurveCacheData[i].x<<" "<<outCurveCacheData[i].y<<" "<<outCurveCacheData[i].z<<endl; } fout.flush(); fout.close(); */ //~write to file } return MS::kSuccess; }
bool preFrame( const MObject hairSystem, const double curTime, void **privateData ) { MStatus status; // If you need want to perform any preprocessing on your collision // objects pre-frame, do it here. One option for storing the pre- // processed data is on a typed attribute on the hairSystem node. // That data could be fetched and updated here. // // In our example, we'll just compute a bounding box here and NOT use // attribute storage. That is an exercise for the reader. // MFnDependencyNode fnHairSystem( hairSystem, &status ); CHECK_MSTATUS_AND_RETURN( status, false ); fprintf( stderr, "preFrame: calling hairSystem node=`%s', time=%g\n", fnHairSystem.name().asChar(), curTime ); MObjectArray cols; MIntArray logIdxs; CHECK_MSTATUS_AND_RETURN( MHairSystem::getCollisionObject( hairSystem, cols, logIdxs ), false ); int nobj = cols.length(); // Allocate private data. // This allows us to pre-process data on a pre-frame basis to avoid // calculating it per hair inside the collide() call. As noted earlier // we could allocate this in preFrame() and hang it off the hairSystem // node via a dynamic attribute. // Instead we'll allocate it here. // COLLISION_INFO *collisionInfo = (COLLISION_INFO *) malloc( sizeof( COLLISION_INFO ) ); collisionInfo->objs = (COLLISION_OBJ *) malloc( nobj * sizeof( COLLISION_OBJ ) ); collisionInfo->numObjs = nobj; // Create the private data that we'll make available to the collide // method. The data should actually be stored in a way that it can // be cleaned up (such as storing the pointer on the hairSystem node // using a dynamic attribute). As it stands right now, there is a // memory leak with this plug-in because the memory we're allocating // for the private data is never cleaned up. // // Note that when using the dynamic attribute approach, it is still // wise to set *privateData because this avoids the need to look up // the plug inside the collide() routine which is a high-traffic // method. // *privateData = (void *) collisionInfo; // Loop through the collision objects and pre-process, storing the // results in the collisionInfo structure. // int obj; for ( obj = 0; obj < nobj; ++obj ) { // Get the ith collision geometry we are connected to. // MObject colObj = cols[obj]; // Get the DAG path for the collision object so we can transform // the vertices to world space. // MFnDagNode fnDagNode( colObj, &status ); CHECK_MSTATUS_AND_RETURN( status, false ); MDagPath path; status = fnDagNode.getPath( path ); CHECK_MSTATUS_AND_RETURN( status, false ); MFnMesh fnMesh( path, &status ); if ( MS::kSuccess != status ) { fprintf( stderr, "%s:%d: collide was not passed a valid mesh shape\n", __FILE__, __LINE__ ); return( false ); } // Get the vertices of the object transformed to world space. // MFloatPointArray verts; status = fnMesh.getPoints( verts, MSpace::kWorld ); CHECK_MSTATUS_AND_RETURN( status, false ); // Compute the bounding box for the collision object. // As this is a quick and dirty demo, we'll just support collisions // between hair and the bbox. // double minx, miny, minz, maxx, maxy, maxz, x, y, z; minx = maxx = verts[0].x; miny = maxy = verts[0].y; minz = maxz = verts[0].z; int nv = verts.length(); int i; for ( i = 1; i < nv; ++i ) { x = verts[i].x; y = verts[i].y; z = verts[i].z; if ( x < minx ) { minx = x; } if ( y < miny ) { miny = y; } if ( z < minz ) { minz = z; } if ( x > maxx ) { maxx = x; } if ( y > maxy ) { maxy = y; } if ( z > maxz ) { maxz = z; } } // Store this precomputed informantion into our private data // structure. // collisionInfo->objs[obj].numVerts = nv; collisionInfo->objs[obj].minx = minx; collisionInfo->objs[obj].miny = miny; collisionInfo->objs[obj].minz = minz; collisionInfo->objs[obj].maxx = maxx; collisionInfo->objs[obj].maxy = maxy; collisionInfo->objs[obj].maxz = maxz; fprintf( stderr, "Inside preFrameInit, bbox=%g %g %g %g %g %g\n", minx,miny,minz,maxx,maxy,maxz); } return( true ); }