// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 #include #include #include #include #include #include #include #include #include #include #include "shaveAPI.h" #include "shaveAPIimpl.h" #include "shaveCheckObjectVisibility.h" #include "shaveConstant.h" #include "shaveGlobals.h" #include "shaveHairShape.h" #include "shaveIO.h" #include "shaveMaya.h" #include "shaveRender.h" #include "shaveRenderer.h" #include "shaveSDK.h" #include "shaveTextureStore.h" #include "shaveUtil.h" #include std::vector shaveAPIimpl::mShaveNodes; MStatus shaveAPIimpl::exportAllHair( shaveAPI::HairInfo* hairInfo, bool renderableOnly ) { MObjectArray shaveNodes; shaveRender::saveFrameGlobals(); if (renderableOnly) { shaveRenderer* renderer = shaveRender::getRenderer(); renderer->getRenderableShaveNodes(shaveNodes); } else shaveUtil::getShaveNodes(shaveNodes); if (shaveNodes.length() == 0) return MS::kNotFound; return doExport(shaveNodes, hairInfo, shaveRender::getFrameGlobals()); } MStatus shaveAPIimpl::exportDRAFile(MString filename) { MStatus st = MS::kSuccess; return st; } MStatus shaveAPIimpl::exportHair( MObjectArray& shaveNodes, shaveAPI::HairInfo* hairInfo ) { ////////// //MGlobal::displayInfo(MString("shaveAPIimpl::exportHair number of nodes :") + shaveNodes.length()); ///////// if (shaveNodes.length() < 1) return MS::kNotFound; unsigned int i; for (i = 0; i < shaveNodes.length(); i++) { MFnDependencyNode nodeFn(shaveNodes[i]); ////////////// //MGlobal::displayInfo(MString("node: ") + nodeFn.name()); ////////////// if (nodeFn.typeId() != shaveHairShape::id) return MS::kInvalidParameter; } shaveRender::saveFrameGlobals(); MStatus stat = doExport(shaveNodes, hairInfo, shaveRender::getFrameGlobals()); #ifdef _WIN32 assert(_CrtCheckMemory()); #endif return stat; } MStatus shaveAPIimpl::exportOcclusions( shaveAPI::SceneGeom* hairOcclusions, shaveAPI::SceneGeom* shadowOcclusions ) { MStatus st; if ((hairOcclusions == NULL) && (shadowOcclusions == NULL)) return MS::kSuccess; shaveRender::saveFrameGlobals(); shaveMaya::getRenderGlobals(); freeSceneGeom(hairOcclusions); freeSceneGeom(shadowOcclusions); shaveRender::SceneInfo* sceneInfo = shaveRender::getSceneInfo(); // // If the caller only wants shadows and native illumination is on, then // just return the empty list. // if ((hairOcclusions == NULL) && nativeIlluminationGlob) return MS::kSuccess; // // If motion blur is on, get the shutter open and close times. If that // fails for some reason, then treat it as it motion blur is off but // return a status indicating what happened. // bool doMotionBlur = shaveMaya::doMotionBlur; float shutterOpen = 0.0f; float shutterClose = 0.0f; if (doMotionBlur) { st = shaveRender::getShutterTimes(shutterOpen, shutterClose); if (!st) doMotionBlur = false; } if (doMotionBlur) { int i; shaveUtil::setFrame(shutterOpen); shaveRender::buildOcclusionLists(shaveConstant::kShutterOpen); shaveUtil::setFrame(shutterClose); shaveRender::buildOcclusionLists(shaveConstant::kShutterClose); // // Calculate velocities. // if (hairOcclusions) { int numVerts = sceneInfo->shutterOpenCamScene.totalverts; if (sceneInfo->shutterCloseCamScene.totalverts < numVerts) numVerts = sceneInfo->shutterCloseCamScene.totalverts; for (i = 0; i < numVerts; i++) { sceneInfo->shutterOpenCamScene.velocity[i].x = sceneInfo->shutterCloseCamScene.v[i].x - sceneInfo->shutterOpenCamScene.v[i].x; sceneInfo->shutterOpenCamScene.velocity[i].y = sceneInfo->shutterCloseCamScene.v[i].y - sceneInfo->shutterOpenCamScene.v[i].y; sceneInfo->shutterOpenCamScene.velocity[i].z = sceneInfo->shutterCloseCamScene.v[i].z - sceneInfo->shutterOpenCamScene.v[i].z; } } if (shadowOcclusions) { int numVerts = sceneInfo->shutterOpenShadowScene.totalverts; if (sceneInfo->shutterCloseShadowScene.totalverts < numVerts) numVerts = sceneInfo->shutterCloseShadowScene.totalverts; for (i = 0; i < numVerts; i++) { sceneInfo->shutterOpenShadowScene.velocity[i].x = sceneInfo->shutterCloseShadowScene.v[i].x - sceneInfo->shutterOpenShadowScene.v[i].x; sceneInfo->shutterOpenShadowScene.velocity[i].y = sceneInfo->shutterCloseShadowScene.v[i].y - sceneInfo->shutterOpenShadowScene.v[i].y; sceneInfo->shutterOpenShadowScene.velocity[i].z = sceneInfo->shutterCloseShadowScene.v[i].z - sceneInfo->shutterOpenShadowScene.v[i].z; } } } else shaveRender::buildOcclusionLists(shaveConstant::kShutterOpen); if (hairOcclusions) copyWFTYPEToSceneGeom(hairOcclusions, sceneInfo->shutterOpenCamScene); if (shadowOcclusions) { copyWFTYPEToSceneGeom( shadowOcclusions, sceneInfo->shutterOpenShadowScene ); } return st; } void shaveAPIimpl::freeHairInfo(shaveAPI::HairInfo* hairInfo) { if (hairInfo) { SHAVEfree_hairtype((HAIRTYPE*)hairInfo); SHAVEinit_hairtype((HAIRTYPE*)hairInfo); } } void shaveAPIimpl::initHairInfo(shaveAPI::HairInfo* hairInfo) { if (hairInfo) SHAVEinit_hairtype((HAIRTYPE*)hairInfo); } //****************************************** // // SceneGeom Interfaces // //****************************************** void shaveAPIimpl::freeSceneGeom(shaveAPI::SceneGeom* sceneGeom) { if (sceneGeom) { delete sceneGeom->faceVertices; delete sceneGeom->faceStartIndices; delete sceneGeom->faceEndIndices; delete sceneGeom->vertices; delete sceneGeom->velocities; initSceneGeom(sceneGeom); } } void shaveAPIimpl::initSceneGeom(shaveAPI::SceneGeom* sceneGeom) { if (sceneGeom) { sceneGeom->numVertices = 0; sceneGeom->numFaces = 0; sceneGeom->numFaceVertices = 0; sceneGeom->faceVertices = NULL; sceneGeom->faceStartIndices = NULL; sceneGeom->faceEndIndices = NULL; sceneGeom->vertices = NULL; sceneGeom->velocities = NULL; } } //****************************************** // // Utility Methods // //****************************************** void shaveAPIimpl::copyWFTYPEToSceneGeom( shaveAPI::SceneGeom* sceneGeom, WFTYPE& wf ) { // // 'sceneGeom' MUST have been cleared before calling this method. // sceneGeom->numVertices = wf.totalverts; sceneGeom->numFaces = wf.totalfaces; sceneGeom->numFaceVertices = wf.totalfverts; if (wf.totalverts > 0) { // // For performance, we *could* just move the ptrs from the WFTYPE // over to the SceneGeom rather than allocating and copying. // However, that would leave us mixing 'new' and 'malloc' memory // allocations. We could handle that by adding a flag to SceneInfo // to tell us where the memory came from, so that we know whether // to 'delete' or 'free' it. But that's a bit messy, so for now // we'll just copy the data. If performance becomes an issue then // we can get fancy later. // sceneGeom->vertices = new shaveAPI::Vertex[wf.totalverts]; sceneGeom->velocities = new shaveAPI::Vertex[wf.totalverts]; memcpy( sceneGeom->vertices, wf.v, sizeof(shaveAPI::Vertex) * wf.totalverts ); memcpy( sceneGeom->velocities, wf.velocity, sizeof(shaveAPI::Vertex) * wf.totalverts ); } if (wf.totalfaces > 0) { sceneGeom->faceStartIndices = new int[wf.totalfaces]; sceneGeom->faceEndIndices = new int[wf.totalfaces]; memcpy( sceneGeom->faceStartIndices, wf.face_start, sizeof(int) * wf.totalfaces ); memcpy( sceneGeom->faceEndIndices, wf.face_end, sizeof(int) * wf.totalfaces ); } if (wf.totalfverts > 0) { sceneGeom->faceVertices = new int[wf.totalfverts]; memcpy( sceneGeom->faceVertices, wf.facelist, sizeof(int) * wf.totalfverts ); } } MStatus shaveAPIimpl::clearHairStack() { SHAVEclear_stack(); mShaveNodes.clear(); return MS::kSuccess; } MStatus shaveAPIimpl::createHairStack( MObjectArray& shaveNodes, const shaveGlobals::Globals& globals ) { MStatus st; MTime curTime = MAnimControl::currentTime(); clearHairStack(); SHAVEset_tile_limit( (int)globals.tileMemoryLimit, (int)globals.transparencyDepth ); // // Save the shaveNode pointers. // unsigned int i; for (i = 0; i < shaveNodes.length(); i++) { MFnDependencyNode nodeFn(shaveNodes[i]); mShaveNodes.push_back((shaveHairShape*)nodeFn.userNode()); } shaveMaya::getRenderGlobals(); if (shaveMaya::doMotionBlur) { float shutterOpen; float shutterClose; st = shaveRender::getShutterTimes(shutterOpen, shutterClose); if (st) { // // Do shutter open. // shaveUtil::setFrame(shutterOpen); #ifdef PER_NODE_TEXLOOKUP initTexInfoLookup2(shaveNodes, "", globals.verbose); #else initTexInfoLookup(shaveNodes, "", globals.verbose); #endif shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterOpen); // // Do shutter close. // shaveUtil::setFrame(shutterClose); shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterClose); } else { // // We couldn't find a valid render camera, so go ahead and // render without motion blur, but return a status code to let // the caller know what happened. // st = MS::kUnknownParameter; #ifdef PER_NODE_TEXLOOKUP initTexInfoLookup2(shaveNodes, "", globals.verbose); #else initTexInfoLookup(shaveNodes, "", globals.verbose); #endif shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterBoth); } } else { // // No motion blur, so shutter open and close occur at the same // time. // #ifdef PER_NODE_TEXLOOKUP initTexInfoLookup2(shaveNodes, "", globals.verbose); #else initTexInfoLookup(shaveNodes, "", globals.verbose); #endif shaveRender::buildHairStack(shaveNodes, shaveConstant::kShutterBoth); } return st; } MStatus shaveAPIimpl::doExport( MObjectArray& shaveNodes, shaveAPI::HairInfo* hairInfo, const shaveGlobals::Globals& globals ) { #ifdef _WIN32 assert(_CrtCheckMemory()); #endif ////////// //MGlobal::displayInfo(MString("shaveAPIimpl::doExport")); ///////// MStatus st; SHAVEset_verbose(globals.verbose ? 1 : 0); createHairStack(shaveNodes, globals); #ifdef _WIN32 assert(_CrtCheckMemory()); #endif if (st) { ////////// //MGlobal::displayInfo(MString("stack created")); ///////// freeHairInfo(hairInfo); #ifdef _WIN32 assert(_CrtCheckMemory()); #endif ////////// //MGlobal::displayInfo(MString("freeHairInfo")); ///////// int res = SHAVEexport_hairtype((HAIRTYPE*)hairInfo); #ifdef _WIN32 assert(_CrtCheckMemory()); #endif #ifdef _DEBUG MGlobal::displayInfo(MString("SHAVEexport_hairtype result :") + res); MGlobal::displayInfo(MString("hairInfo->numHairs :") + hairInfo->numHairs); MGlobal::displayInfo(MString("hairInfo->numHairVertices :") + hairInfo->numHairVertices); MGlobal::displayInfo(MString("hairInfo->numVertices :") + hairInfo->numVertices); #endif SHAVEclear_stack(); #ifdef _WIN32 assert(_CrtCheckMemory()); #endif ////////// //MGlobal::displayInfo(MString("SHAVEclear_stack")); ///////// if (shaveMaya::doMotionBlur) { ////////// //MGlobal::displayInfo(MString("shaveUtil::setFrame")); ///////// MTime curTime = MAnimControl::currentTime(); shaveUtil::setFrame((float)curTime.value()); } ////////// //MGlobal::displayInfo(MString("done")); ///////// } #ifdef _WIN32 assert(_CrtCheckMemory()); #endif return st; } shaveHairShape* shaveAPIimpl::getNodeFromStack(unsigned int nodeIndex) { if (nodeIndex >= mShaveNodes.size()) return NULL; return mShaveNodes[nodeIndex]; }