// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 #include "shaveIO.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include # ifdef _WIN32 // ri.h contains a lot of float/double mismatches. Let's stop VC++ from warning us about them. # pragma warning(push) # pragma warning(disable : 4244 ) # endif # include # ifdef _WIN32 # pragma warning(pop) # endif #include "shaveEngine.h" #include "shaveGlobals.h" #include "shaveHairShape.h" #include "shaveMaya.h" #include "shaveRender.h" #include "shaveRenderer.h" #include "shaveRenderman.h" #include "shaveSDK.h" #include "shaveTextureStore.h" #include "shaveUtil.h" #include "shaveWriteRib.h" // Helper class to load symbols from RenderMan_for_Maya mll. // class RfMFuncs { public: RfMFuncs(const shaveGlobals::Globals&); ~RfMFuncs(); bool initialized; #if defined(__linux__) || defined(OSMac_) void *rfmDLL; #elif defined(_WIN32) HMODULE rfmDLL; #endif // Functions void (*RiArchiveRecord)(RtToken type, char* format, ...); void (*RiBasis)(RtBasis u, int ustep, RtBasis v, int vstep); void (*RiBegin)(RtToken name); void (*RiCurvesV)( RtToken type, int ncurves, int* nvertices, RtToken wrap, int n, RtToken nms[], RtPointer vals[]); RtToken (*RiDeclare)(char*, char*); void (*RiEnd)(); void (*RiMotionBegin)(int n, ...); void (*RiMotionEnd)(); void (*RiOption)(RtToken, ...); void (*RiPointsPolygonsV)( int npolys, int* nverts, int* verts, int n, RtToken nms[], RtPointer vals[]); void (*RiProcDelayedReadArchive)(RtPointer data, float detail); void (*RiProcedural)( RtPointer data, RtBound bound, RtProcSubdivFunc sfunc, RtProcFreeFunc ffunc); void (*RiShadingInterpolation)(RtToken type); void (*RiTransformBegin)(); void (*RiTransformEnd)(); // Variables RtBasis *RiCatmullRomBasis; RtToken *RI_CS; RtToken *RI_CUBIC; RtToken *RI_LINEAR; RtToken *RI_N; RtToken *RI_NONPERIODIC; RtToken *RI_OS; RtToken *RI_P; RtToken *RI_S; RtToken *RI_SMOOTH; RtToken *RI_T; RtToken *RI_VERBATIM; RtToken *RI_WIDTH; }; #if defined(__linux__) || defined(OSMac_) # include # define getDLLSymbol(hdl,name) dlsym(hdl,name) #elif defined(_WIN32) # define getDLLSymbol(hdl,name) GetProcAddress(hdl,name) #else # error Platform not supported. #endif RfMFuncs::RfMFuncs(const shaveGlobals::Globals &g) : initialized(false) { // If a RIB library override has been provided, use that. // MString dllPath = g.rib.libOverride; if (dllPath.length() == 0) { int rfmLoaded = 0; MGlobal::executeCommand("pluginInfo -q -l RenderMan_for_Maya", rfmLoaded); if (rfmLoaded == 0) { MGlobal::displayError( "No access to RenderMan functions. Please load the " "RenderMan_for_Maya plugin, or provide" ); MGlobal::displayError( "the path to an alternate RIB library in the shaveGlobals " "'RIB Library Override' field." ); return; } #if defined(__linux__) || defined(OSMac_) MGlobal::executeCommand("pluginInfo -q -p RenderMan_for_Maya", dllPath); #elif defined(_WIN32) // If no path is provided Windows will look for a match based just // on the name, which improves our chances of finding it. // dllPath = "RenderMan_for_Maya.mll"; #endif } #if defined(__linux__) || defined(OSMac_) rfmDLL = dlopen(dllPath.asChar(), RTLD_LAZY); if (rfmDLL == nullptr) #elif defined(_WIN32) // Make sure the path uses backslashes as directory separators. // dllPath.substitute("/", "\\"); rfmDLL = LoadLibrary(dllPath.asChar()); if (rfmDLL == 0) #endif { MGlobal::displayError( MString("Could not access shared library '") + dllPath + "'. Please ensure that it exists and is readable and executable." ); return; } // Functions this->RiArchiveRecord = (void (*)(RtToken, char*, ...))getDLLSymbol(rfmDLL, "RiArchiveRecord"); if (this->RiArchiveRecord == nullptr) { MGlobal::displayError( MString("Could not find the 'RiArchiveRecord' function in '") + dllPath + "'." ); if (g.rib.libOverride.length() > 0) { MGlobal::displayError( "Please check that the 'RIB Library Override' field in " "shaveGlobals is correct and gives the path to a RIB-compliant " "shared library." ); } else { MGlobal::displayError( "Please check that RenderMan_for_Maya is correctly installed." ); } return; } this->RiBasis = (void (*)(RtBasis, int, RtBasis, int))getDLLSymbol(rfmDLL, "RiBasis"); assert(this->RiBasis != nullptr); this->RiBegin = (void (*)(RtToken name))getDLLSymbol(rfmDLL, "RiBegin"); assert(this->RiBegin != nullptr); this->RiCurvesV = (void (*)(RtToken, int, int*, RtToken, int, RtToken[], RtPointer[]))getDLLSymbol(rfmDLL, "RiCurvesV"); assert(this->RiCurvesV != nullptr); this->RiDeclare = (RtToken (*)(char*, char*))getDLLSymbol(rfmDLL, "RiDeclare"); assert(this->RiDeclare != nullptr); this->RiEnd = (void (*)())getDLLSymbol(rfmDLL, "RiEnd"); assert(this->RiEnd != nullptr); this->RiMotionBegin = (void (*)(int, ...))getDLLSymbol(rfmDLL, "RiMotionBegin"); assert(this->RiMotionBegin != nullptr); this->RiMotionEnd = (void (*)())getDLLSymbol(rfmDLL, "RiMotionEnd"); assert(this->RiMotionEnd != nullptr); this->RiOption = (void (*)(RtToken, ...))getDLLSymbol(rfmDLL, "RiOption"); assert(this->RiOption != nullptr); this->RiPointsPolygonsV = (void (*)(int, int*, int*, int, RtToken[], RtPointer[]))getDLLSymbol(rfmDLL, "RiPointsPolygonsV"); assert(this->RiPointsPolygonsV != nullptr); this->RiProcDelayedReadArchive = (void (*)(RtPointer, float))getDLLSymbol(rfmDLL, "RiProcDelayedReadArchive"); assert(this->RiProcDelayedReadArchive != nullptr); this->RiProcedural = (void (*)(RtPointer, RtBound, RtProcSubdivFunc, RtProcFreeFunc))getDLLSymbol(rfmDLL, "RiProcedural"); assert(this->RiProcedural != nullptr); this->RiShadingInterpolation = (void (*)(RtToken))getDLLSymbol(rfmDLL, "RiShadingInterpolation"); assert(this->RiShadingInterpolation != nullptr); this->RiTransformBegin = (void (*)())getDLLSymbol(rfmDLL, "RiTransformBegin"); assert(this->RiTransformBegin != nullptr); this->RiTransformEnd = (void (*)())getDLLSymbol(rfmDLL, "RiTransformEnd"); assert(this->RiTransformEnd != nullptr); // Variables this->RiCatmullRomBasis = (RtBasis*)getDLLSymbol(rfmDLL, "RiCatmullRomBasis"); assert(this->RiCatmullRomBasis != nullptr); this->RI_CS = (RtToken*)getDLLSymbol(rfmDLL, "RI_CS"); assert(this->RI_CS != nullptr); this->RI_CUBIC = (RtToken*)getDLLSymbol(rfmDLL, "RI_CUBIC"); assert(this->RI_CUBIC != nullptr); this->RI_LINEAR = (RtToken*)getDLLSymbol(rfmDLL, "RI_LINEAR"); assert(this->RI_LINEAR != nullptr); this->RI_NONPERIODIC = (RtToken*)getDLLSymbol(rfmDLL, "RI_NONPERIODIC"); assert(this->RI_NONPERIODIC != nullptr); this->RI_OS = (RtToken*)getDLLSymbol(rfmDLL, "RI_OS"); assert(this->RI_OS != nullptr); this->RI_P = (RtToken*)getDLLSymbol(rfmDLL, "RI_P"); assert(this->RI_P != nullptr); this->RI_S = (RtToken*)getDLLSymbol(rfmDLL, "RI_S"); assert(this->RI_S != nullptr); this->RI_SMOOTH = (RtToken*)getDLLSymbol(rfmDLL, "RI_SMOOTH"); assert(this->RI_SMOOTH != nullptr); this->RI_T = (RtToken*)getDLLSymbol(rfmDLL, "RI_T"); assert(this->RI_T != nullptr); this->RI_VERBATIM = (RtToken*)getDLLSymbol(rfmDLL, "RI_VERBATIM"); assert(this->RI_VERBATIM != nullptr); this->RI_WIDTH = (RtToken*)getDLLSymbol(rfmDLL, "RI_WIDTH"); assert(this->RI_WIDTH != nullptr); initialized = true; } RfMFuncs::~RfMFuncs() { #if defined(__linux__) || defined(OSMac_) if (rfmDLL != nullptr) dlclose(rfmDLL); #elif defined(_WIN32) if (rfmDLL != 0) FreeLibrary(rfmDLL); #endif } const MString shaveWriteRib::commandName("shaveWriteRib"); static const char* fsBinary = "-b"; static const char* flBinary = "-binary"; static const char* fsCleanup = "-cln"; static const char* flCleanup = "-cleanup"; static const char* fsFrame = "-f"; static const char* flFrame = "-frame"; static const char* fsFrameRelativeTime = "-frt"; static const char* flFrameRelativeTime = "-frameRelativeTime"; static const char* fsFullPaths = "-fp"; static const char* flFullPaths = "-fullPaths"; static const char* fsGzip = "-gz"; static const char* flGzip = "-gzip"; static const char* fsHairNode = "-hn"; static const char* flHairNode = "-hairNode"; static const char* fsHelp = "-h"; static const char* flHelp = "-help"; static const char* fsIgnoreShaveGlobals = "-isg"; static const char* flIgnoreShaveGlobals = "-ignoreShaveGlobals"; static const char* fsMotionBlur = "-mb"; static const char* flMotionBlur = "-motionBlur"; static const char* fsNormals = "-n"; static const char* flNormals = "-normals"; static const char* fsOpacities = "-opa"; static const char* flOpacities = "-opacities"; static const char* fsRestoreFrame = "-rf"; static const char* flRestoreFrame = "-restoreFrame"; static const char* fsRootTipColors = "-rtc"; static const char* flRootTipColors = "-rootTipColors"; static const char* fsRootPositions = "-rp"; static const char* flRootPositions = "-rootPositions"; static const char* fsShutterClose = "-sc"; static const char* flShutterClose = "-shutterClose"; static const char* fsShutterCloseOffset = "-sco"; static const char* flShutterCloseOffset = "-shutterCloseOffset"; static const char* fsShutterOpen = "-so"; static const char* flShutterOpen = "-shutterOpen"; static const char* fsShutterOpenOffset = "-soo"; static const char* flShutterOpenOffset = "-shutterOpenOffset"; static const char* fsSurfaceNormals = "-sn"; static const char* flSurfaceNormals = "-surfaceNormals"; static const char* fsTimeUnits = "-tu"; static const char* flTimeUnits = "-timeUnits"; static const char* fsUVSet = "-uv"; static const char* flUVSet = "-uvSet"; static const char* fsVertexColors = "-vc"; static const char* flVertexColors = "-vertexColors"; static const char* fsVoxels = "-vox"; static const char* flVoxels = "-voxels"; static const char* fsWCoords = "-wc"; static const char* flWCoords = "-wCoords"; static struct { const char* shortName; const char* longName; bool multiUse; MSyntax::MArgType argType; const char* argName; const char* description; } kFlags[] = { { fsBinary, flBinary, false, MSyntax::kNoArg, 0, "output file as binary rather than text." }, { fsCleanup, flCleanup, false, MSyntax::kNoArg, 0, "don't dump any RIBs but instead delete the files generated by a" " previous dump. You must specify all the same parameters as the dump" " you want to clean up." }, { fsFrame, flFrame, false, MSyntax::kDouble, "frame", "number of frame to output. If not specified then the current frame" " is used." }, { fsFrameRelativeTime, flFrameRelativeTime, false, MSyntax::kNoArg, 0, "when motion blur is enabled this causes all times in motion blocks" " to be written as being relative offsets to the frame time rather than" " as absolute time values. This is for use with the 'offset' shutter" " option in Renderman." }, { fsFullPaths, flFullPaths, false, MSyntax::kNoArg, 0, "use in conjunction with -voxels. If this flag is present then the" " main RIB file will refer to the per-voxel RIB files using full" " pathnames. If not present then just the filename will be used, with" " no path." }, { fsGzip, flGzip, false, MSyntax::kNoArg, 0, "compress the output file using 'gzip' format. Only valid with '-b'." }, { fsHairNode, flHairNode, true, MSyntax::kString, "nodeName", "only write RIB for the specified shaveHair node." }, { fsHelp, flHelp, false, MSyntax::kNoArg, 0, "display this help message." }, { fsIgnoreShaveGlobals, flIgnoreShaveGlobals, false, MSyntax::kNoArg, 0, "ignore any defaults set in Shave Globals and only use those flags and" " values which are explicitly specified on the command line." }, { fsMotionBlur, flMotionBlur, false, MSyntax::kNoArg, 0, "write motion blur info to the file." }, { fsNormals, flNormals, false, MSyntax::kNoArg, 0, "for instance geometry, output surface normals for each vertex of each" " instance." }, { fsOpacities, flOpacities, false, MSyntax::kNoArg, 0, "write opacities ('Os' parameter)." }, { fsRestoreFrame, flRestoreFrame, false, MSyntax::kBoolean, "yes|no", "if this flag is set to 'no' then when motion blur is enabled the" " current time will be left at the shutter close time rather than" " being restored to the time at the start of the command. This is" " useful for scripts which are processing a series of frames and wish" " to avoid redundant frame changes. The default is 'yes'." }, { fsRootPositions, flRootPositions, false, MSyntax::kNoArg, 0, "write per-hair root positions as the 'P_srf' parameter." }, { fsRootTipColors, flRootTipColors, false, MSyntax::kNoArg, 0, "write root and tip colors." }, { fsSurfaceNormals, flSurfaceNormals, false, MSyntax::kNoArg, 0, "for each hair generate an 'N_srf' parameter which is the normal of" " the growth surface at the root of the hair." }, { fsShutterOpen, flShutterOpen, false, MSyntax::kDouble, "time", "when motion blur is enabled, use this as the exact shutter open time." }, { fsShutterOpenOffset, flShutterOpenOffset, false, MSyntax::kDouble, "offset", "same as '-shutterOpen' except this is an offset which is added to the" " frame time. If both this and -shutterOpen are given the latter will" " be used." }, { fsShutterClose, flShutterClose, false, MSyntax::kDouble, "time", "when motion blur is enabled, use this as the exact shutter close time." }, { fsShutterCloseOffset, flShutterCloseOffset, false, MSyntax::kDouble, "offset", "same as '-shutterClose' except this is an offset which is added to" " the frame time. If both this and -shutterClose are given the latter" " will be used." }, { fsTimeUnits, flTimeUnits, false, MSyntax::kString, "frames|seconds", "the units to be used when writing times to the RIB file, for example" " in MotionBegin statements. \"frames\" uses Maya frame numbers, which" " is the default, \"seconds\" converts frame numbers to seconds using" " the currently specified frame rate. Note that this does not change" " the units used by other command flags, such as -shutterClose: those" " continue to be in frames." }, { fsUVSet, flUVSet, false, MSyntax::kString, "uvSetName", "write texture coords for the given uv set." }, { fsVertexColors, flVertexColors, false, MSyntax::kNoArg, 0, "write vertex colors ('Cs' parameter)." }, { fsVoxels, flVoxels, false, MSyntax::kNoArg, 0, "if present, the hair for each shaveHairShape will be divided into" " several voxels and each voxel will be written to a numbered file." " The main output file will then load them as delayed read archives." }, { fsWCoords, flWCoords, false, MSyntax::kNoArg, 0, "write 'w' texture coords, which give the parametric length along each" " hair." } }; static char* ktAmbDiff = "SHAVEambdiff"; static char* ktGloss = "SHAVEgloss"; static char* ktOpacity = "SHAVEopacity"; static char* ktRootColor = "rootcolor"; static char* ktRootPosition = "P_Srf"; static char* ktSelfShadow = "SHAVEselfshad"; static char* ktSpecular = "SHAVEspec"; static char* ktSquirrel = "SHAVEsquirrel"; static char* ktSpecularColor = "SHAVEspec_color"; static char* ktSpecularColor2 = "SHAVEspec_color2"; static char* ktSurfaceNormal = "N_Srf"; static char* ktTipColor = "tipcolor"; static char* ktWCoord = "w"; static char* ktIndex = "index"; shaveWriteRib::shaveWriteRib() : fDoBinary(false) , fDoFullPaths(false) , fDoGzip(false) , fDoMotionBlur(false) , fDoNormals(false) , fDoOpacities(false) , fRestoreFrame(true) , fDoRootPositions(false) , fDoRootTipColors(false) , fDoSurfaceNormals(false) , fDoUVs(false) , fDoVertexColors(false) , fDoVoxels(false) , fDoWCoords(false) #ifndef NO_PRMAN , fFrameRelativeTime(false) , fTimeFactor(1.0) , fTimeUnits(shaveGlobals::kFrames) #endif { } shaveWriteRib::~shaveWriteRib() {} #ifndef NO_PRMAN void shaveWriteRib::addCurve( int firstVertIdxIdx, int lastVertIdxIdx, const int* vertIndices, const VERT* verts, const VERT* vertColours, const VERT* rootColour, const VERT* tipColour, float hairU, float hairV, float rootRadius, float tipRadius, const VERT* hairNormal, bool tipFade, int indx ) { int numHairVerts = lastVertIdxIdx - firstVertIdxIdx + 1; fVertsPerHair[fCurveIdx] = numHairVerts; if (fUseCubicCurves) fVertsPerHair[fCurveIdx] += 2; // // If per-vertex colours were not supplied then we'll have to // interpolate them from the root and tip colours. // VERT colourPerVert = { 0.0f, 0.0f, 0.0f }; float numSegs = (float)(numHairVerts - 1); if ((vertColours == NULL) && (rootColour != NULL) && (tipColour != NULL)) { colourPerVert.x = (tipColour->x - rootColour->x) / numSegs; colourPerVert.y = (tipColour->y - rootColour->y) / numSegs; colourPerVert.z = (tipColour->z - rootColour->z) / numSegs; } // // Set up all the per-vertex parameter values. // int i; int j; for (i = firstVertIdxIdx; i <= lastVertIdxIdx; i++) { int vertIdx = vertIndices[i]; // // We use a loop to double up on the first and last CVs when // generating cubic curves. // for (j = 0; j < 2; j++) { fVert[fVertIdx][0] = verts[vertIdx].x; fVert[fVertIdx][1] = verts[vertIdx].y; fVert[fVertIdx][2] = verts[vertIdx].z; if (fDoVertexColors) { if (vertColours) { fVertexColor[fVertIdx][0] = vertColours[vertIdx].x; fVertexColor[fVertIdx][1] = vertColours[vertIdx].y; fVertexColor[fVertIdx][2] = vertColours[vertIdx].z; } else if (rootColour && tipColour) { float portion = (float)(i - firstVertIdxIdx); fVertexColor[fVertIdx][0] = rootColour->x + colourPerVert.x * portion; fVertexColor[fVertIdx][1] = rootColour->y + colourPerVert.y * portion; fVertexColor[fVertIdx][2] = rootColour->z + colourPerVert.z * portion; } else { fVertexColor[fVertIdx][0] = 0.5; fVertexColor[fVertIdx][1] = 0.5; fVertexColor[fVertIdx][2] = 0.5; } } fVertIdx++; // // If this isn't the first or last cv of a // cubic curve then don't double up on it. // if (!fUseCubicCurves || ((i != firstVertIdxIdx) && (i != lastVertIdxIdx))) { break; } } } // // We need one width, opacity and w-coord for each unique cv in the // curve. So it doesn't matter whether we're doing linear or cubic // curves as we output the same number of widths in both cases. // float radiusPerVert = (tipRadius - rootRadius) / numSegs; float opacityPerVert = fBaseOpacity / numSegs; for (j = 0; j < numHairVerts; j++) { fWidth[fWidthIdx] = (rootRadius + radiusPerVert * (float)j) * 2.0f; if (fDoOpacities) { float opacity = fBaseOpacity; if (tipFade) { if (j == numHairVerts - 1) opacity = 0.0f; else { opacity -= opacityPerVert * (float)j; } } fOpacity[fWidthIdx][0] = opacity; fOpacity[fWidthIdx][1] = opacity; fOpacity[fWidthIdx][2] = opacity; } if (fDoWCoords) { // // It should be impossible to have just one vert as that would // imply no segments in the curve. Still, better safe than // sorry. // if (numHairVerts == 1) fWCoord[fWidthIdx] = 0.5; else { fWCoord[fWidthIdx] = (float)j / (float)(numHairVerts - 1); } } fWidthIdx++; } // // Put the hair-specific info into their arrays. // if (fDoRootPositions) { int firstVertIdx = vertIndices[firstVertIdxIdx]; fRootPosition[fCurveIdx][0] = verts[firstVertIdx].x; fRootPosition[fCurveIdx][1] = verts[firstVertIdx].y; fRootPosition[fCurveIdx][2] = verts[firstVertIdx].z; } if (fDoRootTipColors) { if (rootColour) { fRootColor[fCurveIdx][0] = rootColour->x; fRootColor[fCurveIdx][1] = rootColour->y; fRootColor[fCurveIdx][2] = rootColour->z; } else if (vertColours) { int firstVertIdx = vertIndices[firstVertIdxIdx]; fRootColor[fCurveIdx][0] = vertColours[firstVertIdx].x; fRootColor[fCurveIdx][1] = vertColours[firstVertIdx].y; fRootColor[fCurveIdx][2] = vertColours[firstVertIdx].z; } if (tipColour) { fTipColor[fCurveIdx][0] = tipColour->x; fTipColor[fCurveIdx][1] = tipColour->y; fTipColor[fCurveIdx][2] = tipColour->z; } else { int lastVertIdx = vertIndices[lastVertIdxIdx]; fTipColor[fCurveIdx][0] = vertColours[lastVertIdx].x; fTipColor[fCurveIdx][1] = vertColours[lastVertIdx].y; fTipColor[fCurveIdx][2] = vertColours[lastVertIdx].z; } } if (fDoSurfaceNormals && hairNormal) { fSurfaceNormal[fCurveIdx][0] = hairNormal->x; fSurfaceNormal[fCurveIdx][1] = hairNormal->y; fSurfaceNormal[fCurveIdx][2] = hairNormal->z; } if (fDoUVs) { // // For curves, UVs are defined to be per-hair. // fSCoord[fCurveIdx] = hairU; fTCoord[fCurveIdx] = hairV; } fIndex[fCurveIdx]=indx; fCurveIdx++; } void shaveWriteRib::beginFile(RfMFuncs &rfm, MString filename, MString verbatim) const { // // Set up the RIB file's formatting. // if (fDoBinary) { RtString format[1] = {"binary"}; rfm.RiOption("rib", "format", (RtPointer)format, RI_NULL); } else { RtString format[1] = {"ascii"}; rfm.RiOption("rib", "format", (RtPointer)format, RI_NULL); } if (fDoGzip) { RtString str[1] = {"gzip"}; rfm.RiOption("rib", "compression", (RtPointer)str, RI_NULL); } else { RtString str[1] = {"none"}; rfm.RiOption("rib", "compression", (RtPointer)str, RI_NULL); } // // Begin the RIB file. // if (filename == "") rfm.RiBegin(RI_NULL); else rfm.RiBegin ((char *)filename.asChar()); // // If there is global RIB text, output it as a verbatim record. // if (verbatim != "") { rfm.RiArchiveRecord(*rfm.RI_VERBATIM, "%s\n", verbatim.asChar()); // If the global RIB text contains the definition for our // standard Reyes shader, then output the patterns and Bxdf // declaration needed for our standard RIS shader as well. // // NOTE: Currently we only generate ShaveHairBxdf on Windows, // but a RIB generated on Linux or OSX could be rendered // on a Windows machine, so we want to include the Bxdf // declarations on all platforms. // const MString reyesShaderDecl("Surface \"Shave\""); if (verbatim.indexW(reyesShaderDecl) != -1) { rfm.RiArchiveRecord( *rfm.RI_VERBATIM, "Pattern \"PxrPrimvar\" \"shave_OsVar\" \"string varname\" [\"Os\"] \"string type\" [\"color\"]\n" "Pattern \"PxrPrimvar\" \"shave_rootcolorVar\" \"string varname\" [\"rootcolor\"] \"string type\" [\"color\"]\n" "Pattern \"PxrPrimvar\" \"shave_tipcolorVar\" \"string varname\" [\"tipcolor\"] \"string type\" [\"color\"]\n" "Pattern \"PxrPrimvar\" \"shave_ambdiffVar\" \"string varname\" [\"SHAVEambdiff\"] \"string type\" [\"float\"]\n" "Pattern \"PxrPrimvar\" \"shave_opacityVar\" \"string varname\" [\"SHAVEopacity\"] \"string type\" [\"float\"]\n" "Pattern \"PxrPrimvar\" \"shave_glossVar\" \"string varname\" [\"SHAVEgloss\"] \"string type\" [\"float\"]\n" "Pattern \"PxrPrimvar\" \"shave_selfshadVar\" \"string varname\" [\"SHAVEselfshad\"] \"string type\" [\"float\"]\n" "Pattern \"PxrPrimvar\" \"shave_specVar\" \"string varname\" [\"SHAVEspec\"] \"string type\" [\"float\"]\n" "Pattern \"PxrPrimvar\" \"shave_specColorVar\" \"string varname\" [\"SHAVEspec_color\"] \"string type\" [\"color\"]\n" "Pattern \"PxrPrimvar\" \"shave_specColor2Var\" \"string varname\" [\"SHAVEspec_color2\"] \"string type\" [\"color\"]\n" "Bxdf \"ShaveHairBxdf\" \"shaveMaterial\"\n" " \"reference color Os\" [\"shave_OsVar:resultRGB\"]\n" " \"reference color rootcolor\" [\"shave_rootcolorVar:resultRGB\"]\n" " \"reference color tipcolor\" [\"shave_tipcolorVar:resultRGB\"]\n" " \"reference float SHAVEambdiff\" [\"shave_ambdiffVar:resultF\"]\n" " \"reference float SHAVEopacity\" [\"shave_opacityVar:resultF\"]\n" " \"reference float SHAVEgloss\" [\"shave_glossVar:resultF\"]\n" " \"reference float SHAVEselfshad\" [\"shave_selfshadVar:resultF\"]\n" " \"reference float SHAVEspec\" [\"shave_specVar:resultF\"]\n" " \"reference color SHAVEspec_color\" [\"shave_specColorVar:resultRGB\"]\n" " \"reference color SHAVEspec_color2\" [\"shave_specColor2Var:resultRGB\"]\n" ); } } // // Begin the scene description. // rfm.RiTransformBegin(); } void shaveWriteRib::cleanup( const MObjectArray& hairShapes, const shaveGlobals::Globals& g ) const { if (fDoVoxels) { MString baseFilename = getBaseFilename(); int i; int v; int numVoxels = g.rib.voxels.resolution * g.rib.voxels.resolution * g.rib.voxels.resolution; MString voxelFile; for (i = 0; i < (int)hairShapes.length(); i++) { MFnDependencyNode nodeFn(hairShapes[i]); for (v = 0; v < numVoxels; v++) { voxelFile = getVoxelFilename(baseFilename, nodeFn.name(), v); shaveUtil::fileDelete(voxelFile); } } } shaveUtil::fileDelete(fFilename); } void shaveWriteRib::clearCurves() { fCurveIdx = 0; fVertIdx = 0; fWidthIdx = 0; } #endif void* shaveWriteRib::createCmd() { return new shaveWriteRib(); } MSyntax shaveWriteRib::createSyntax() { MSyntax syntax; syntax.enableEdit(false); syntax.enableQuery(false); int numFlags = sizeof(kFlags) / sizeof(kFlags[0]); int i; for (i = 0; i < numFlags; i++) { syntax.addFlag( kFlags[i].shortName, kFlags[i].longName, kFlags[i].argType ); if (kFlags[i].multiUse) syntax.makeFlagMultiUse(kFlags[i].shortName); } syntax.setObjectType(MSyntax::kStringObjects, 0, 1); return syntax; } #ifdef NO_PRMAN static RtToken (*zot)(char *name, char *decl) = nullptr; static GLuint (*blat)(GLenum shaderType) = nullptr; MStatus shaveWriteRib::doIt(const MArgList& args) { MGlobal::displayError( "shaveWriteRib: command is not supported on this platform." ); return MS::kFailure; } #else MStatus shaveWriteRib::doIt(const MArgList& args) { MStatus st; MArgDatabase argdb(syntax(), args, &st); if (!st) return st; if (argdb.isFlagSet(fsHelp)) { showHelp(); return MS::kSuccess; } // Are we doing cleanup of an earlier dump? bool doCleanup = argdb.isFlagSet(fsCleanup); // // Make sure that we have the default shaveGlobals node. // MObject shaveGlobalsNode = shaveGlobals::getDefaultNode(); if (shaveGlobalsNode.isNull()) { MGlobal::displayError( commandName + ": Could not find default shaveGlobals node." ); return MS::kFailure; } shaveGlobals::Globals g; shaveGlobals::getGlobals(g); // This second call is still needed because some of the external // methods we call still expect to find their shaveGlobals values in // global variables. shaveGlobals::getGlobals(); // fetch the offsets openoffset=g.rib.blur.shutterOpenOffset; closeoffset=g.rib.blur.shutterCloseOffset; // If we're not ignoring defaults from Shave Globals, then pull those // in first. bool ignoreDefaults = argdb.isFlagSet(fsIgnoreShaveGlobals); if (!ignoreDefaults) { fDoBinary = g.rib.binary; fDoGzip = g.rib.compress; fDoNormals = g.rib.normals; fDoOpacities = g.rib.opacities; fDoRootPositions = g.rib.rootPositions; fDoRootTipColors = g.rib.rootTipColors; fDoSurfaceNormals = g.rib.normals; fDoUVs = g.rib.uvCoords; fDoVertexColors = g.rib.vertexColors; fDoWCoords = g.rib.wCoords; fDoMotionBlur = g.rib.blur.enable; fDoVoxels = g.rib.voxels.enable; fDoFullPaths = g.rib.voxels.fullPaths; fFrameRelativeTime = (g.rib.blur.timeBasis == shaveConstant::kTimeRelative); fRestoreFrame = g.rib.blur.restoreFrame; fTimeUnits = g.rib.timeUnits; // If we're inheriting settings from Renderman then see if it has // blur enabled. If we can't find the renderManGlobals node then // we will fall back to inheriting from Maya. if (g.rib.blur.inheritSettings == shaveConstant::kRibBlurInheritRenderman) { // Find the renderManGlobals node. MObject globalsNode = shaveUtil::getNode("renderManGlobals"); if (globalsNode.isNull()) g.rib.blur.inheritSettings = shaveConstant::kRibBlurInheritMaya; else { MFnDependencyNode nodeFn(globalsNode); MPlug plug = nodeFn.findPlug("rman__torattr___motionBlur"); MPlug plug2 = nodeFn.findPlug("rman__torattr___cameraBlur"); if (plug.isNull() || plug2.isNull()) g.rib.blur.inheritSettings = shaveConstant::kRibBlurInheritMaya; else { int temp, temp2; plug.getValue(temp); plug2.getValue(temp2); fDoMotionBlur = (temp != 0) || (temp2 != 0); } } } if (g.rib.blur.inheritSettings == shaveConstant::kRibBlurInheritMaya) { shaveMaya::getRenderGlobals(); fDoMotionBlur = shaveMaya::doMotionBlur; } } fDoBinary |= argdb.isFlagSet(fsBinary); fDoGzip |= argdb.isFlagSet(fsGzip); if (fDoGzip && !fDoBinary) { MGlobal::displayWarning( commandName + ": the " + fsGzip + "/" + flGzip + " flag is only valid with " + fsBinary + "/" + flBinary ); fDoGzip = false; } fDoFullPaths |= argdb.isFlagSet(fsFullPaths); fDoMotionBlur |= argdb.isFlagSet(fsMotionBlur); fDoNormals |= argdb.isFlagSet(fsNormals); fDoOpacities |= argdb.isFlagSet(fsOpacities); fDoRootPositions |= argdb.isFlagSet(fsRootPositions); fDoRootTipColors |= argdb.isFlagSet(fsRootTipColors); fDoSurfaceNormals |= argdb.isFlagSet(fsSurfaceNormals); fDoVertexColors |= argdb.isFlagSet(fsVertexColors); fDoVoxels |= argdb.isFlagSet(fsVoxels); fDoWCoords |= argdb.isFlagSet(fsWCoords); fFrameRelativeTime |= argdb.isFlagSet(fsFrameRelativeTime); if (argdb.isFlagSet(fsRestoreFrame)) argdb.getFlagArgument(fsRestoreFrame, 0, fRestoreFrame); if (argdb.isFlagSet(fsTimeUnits)) { MString timeUnitStr; argdb.getFlagArgument(fsTimeUnits, 0, timeUnitStr); if (timeUnitStr == "frames") fTimeUnits = shaveGlobals::kFrames; else if (timeUnitStr == "seconds") fTimeUnits = shaveGlobals::kSeconds; else { MGlobal::displayError( commandName + ": '" + timeUnitStr + "' is not a valid time unit. Must be one of" " \"frames\" or \"seconds\"." ); return MS::kInvalidParameter; } } if (fTimeUnits == shaveGlobals::kSeconds) { // Set up a time variable containing a single frame of time at // the current frame rate. MTime timeConv(1.0, MTime::uiUnit()); // Time in seconds. fTimeFactor = timeConv.as(MTime::kSeconds); } if (argdb.isFlagSet(fsUVSet)) { fDoUVs = true; argdb.getFlagArgument(fsUVSet, 0, fUVSet); } // // Get the various frames times we need. // double curFrame = MAnimControl::currentTime().as(MTime::uiUnit()); getFrameTimes(argdb, g, curFrame); // Get the shaveHair nodes for which we're writing RIB. MObjectArray hairNodes; st = getHairShapes(argdb, hairNodes); if (!st || (hairNodes.length() == 0)) return st; // // Get the name of the output file. // MStringArray optArgs; argdb.getObjects(optArgs); if (optArgs.length() > 0) { fFilename = optArgs[0]; //check write permissions FILE* fp = fopen(fFilename.asChar(),"wb"); if (fp == NULL) { MGlobal::displayError(MString("Can't write rib '") + fFilename + "'. Directory is write protected."); return MStatus::kFailure; } else fclose(fp); #if 0 unsigned int flen = fFilename.length(); if(flen > 0) { bool found = false; unsigned int pos = 0; std::string fn(fFilename.asChar()); for(unsigned int i = flen-1; i > 0; i--) { if(fn[i] == '/' || fn[i] == '\\') { found = true; pos = i; break; } } if(found) { int res = 0; MString dir = fFilename.substring(0,pos); /// debug /// //MGlobal::displayInfo(dir); ///////////// //test fails if backslash is used \\, need / //I really do not like this approach std::string dn(dir.asChar()); for(unsigned int i = 0; i < dn.length(); i++) { if(dn[i] == '\\') dn[i] = '/'; } MString dir2(dn.data()); MGlobal::executeCommand(MString("filetest -w \"") + dir2 + MString("\""),res); if(res == 0) { MGlobal::displayError(MString("Can't write rib. Directory '") + dir + "' is write protected"); return MStatus::kFailure; } } } #endif if (g.verbose && !doCleanup) { #if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700) cerr << "Writing rib to file '" << fFilename.asChar() << "' ..." << endl; #else cerr << "Writing rib to file '" << fFilename << "' ..." << endl; #endif } } else fFilename = ""; // // How should we represent the hairs: curves or polys? // fUseCubicCurves = (g.rib.primitiveType == 1); if (doCleanup) { cleanup(hairNodes, g); } else { RfMFuncs rfm(g); if (!rfm.initialized) return MS::kFailure; // // If we're not already there, move to the frame we're writing. // if (fShutterOpen != curFrame) shaveUtil::setFrame((float)fShutterOpen); // // Output RIB info for all of the renderable shaveHairShapes. // st = writeRib(rfm, hairNodes, g); // // If motion blur is on we will be left at the shutter close time // which will be between frames, so jump back to the actual frame // time. // if (fDoMotionBlur && fRestoreFrame) shaveUtil::setFrame((float)fFrameCenter); if (g.verbose && (fFilename != "")) cerr << "Done writing rib file." << endl; } SHAVEclear_stack(); return st; } void shaveWriteRib::endFile(RfMFuncs &rfm) const { rfm.RiTransformEnd(); rfm.RiEnd(); } void shaveWriteRib::freeBuffers(bool isInstanced) { delete [] fVert; delete [] fVertsPerHair; if (fDoRootTipColors) { delete [] fRootColor; delete [] fTipColor; } if (!isInstanced) if (fDoSurfaceNormals) delete [] fSurfaceNormal; if (!isInstanced) if (fDoVertexColors) delete [] fVertexColor; if (fDoUVs) { delete [] fSCoord; delete [] fTCoord; } delete [] fIndex; if (isInstanced) { delete [] fVertIndices; delete [] fNormal; delete [] fVertexColor; } else { delete [] fWidth; if (fDoOpacities) delete [] fOpacity; if (!isInstanced) if (fDoRootPositions) delete [] fRootPosition; if (fDoWCoords) delete [] fWCoord; } // // Free up the parameter list buffers. // delete [] fParams; delete [] fTokens; } MString shaveWriteRib::getBaseFilename() const { MString baseFilename = fFilename; int lastDot = baseFilename.rindex('.'); int lastSlash = baseFilename.rindex('/'); int lastBSlash = baseFilename.rindex('\\'); if ((lastDot > lastSlash) && (lastDot > lastBSlash)) baseFilename = baseFilename.substring(0, lastDot-1); return baseFilename; } void shaveWriteRib::getFrameTimes( const MArgDatabase& argdb, const shaveGlobals::Globals& g, double curFrame ) { bool ignoreGlobalDefaults = argdb.isFlagSet(fsIgnoreShaveGlobals); // If -frame was specified, use that as frame center, otherwise use // the current frame. fFrameCenter = curFrame; { openoffset=g.rib.blur.shutterOpenOffset; closeoffset=g.rib.blur.shutterCloseOffset; } if (argdb.isFlagSet(fsFrame)) argdb.getFlagArgument(fsFrame, 0, fFrameCenter); // Determine the shutter open and close times, if explicitly // specified. bool useShaveGlobals = fDoMotionBlur && !ignoreGlobalDefaults && (g.rib.blur.inheritSettings == shaveConstant::kRibBlurInheritOff); bool explicitShutterOpen = true; if (argdb.isFlagSet(fsShutterOpen)) argdb.getFlagArgument(fsShutterOpen, 0, fShutterOpen); else if (argdb.isFlagSet(fsShutterOpenOffset)) { double offset; argdb.getFlagArgument(fsShutterOpenOffset, 0, offset); fShutterOpen = fFrameCenter + offset; } else if (useShaveGlobals) fShutterOpen = fFrameCenter + g.rib.blur.shutterOpenOffset; else explicitShutterOpen = false; bool explicitShutterClose = true; if (argdb.isFlagSet(fsShutterClose)) argdb.getFlagArgument(fsShutterClose, 0, fShutterClose); else if (argdb.isFlagSet(fsShutterCloseOffset)) { double offset; argdb.getFlagArgument(fsShutterCloseOffset, 0, offset); fShutterClose = fFrameCenter + offset; } else if (useShaveGlobals) fShutterClose = fFrameCenter + g.rib.blur.shutterCloseOffset; else explicitShutterClose = false; if (fDoMotionBlur) { // Get the shutter open and close times. There are several // possibilities here: // // 1) We have -shutterOpen and -shutterClose: we use those values and // ignore -frame, if present. // // 2) We have -shutterOpen and -frame but no -shutterClose: we use // -shutterOpen as the open time and treat the difference between // it and -frame as half the blur length. // // 3) We have -shutterClose and -frame but no -shutterOpen: we use // -shutterClose as the close time and treat the difference between // it and -frame as half the blur length. // // 4) We have just one of -frame with no -shutterOpen or // -shutterClose: calculate the blur delta from the camera's // shutter angle and apply it to whichever value was provided. if (explicitShutterOpen && explicitShutterClose) { // If we picked up absolute shutter open/close times from the // command line, as opposed to relative ones, then the -frame // value will be ignored, so warn the user. if (argdb.isFlagSet(fsShutterOpen) && argdb.isFlagSet(fsShutterClose) && argdb.isFlagSet(fsFrame)) { displayWarning( commandName + ": both " + flShutterOpen + " and " + flShutterClose + " were specified so " + flFrame + " will be ignored." ); } } else if (explicitShutterOpen) { fShutterClose = fFrameCenter + (fFrameCenter - fShutterOpen); } else if (explicitShutterClose) { fShutterOpen = fFrameCenter - (fShutterClose - fFrameCenter); } else { if (g.rib.blur.inheritSettings == shaveConstant::kRibBlurInheritRenderman) { getRendermanShutterTimes(); } else getMayaShutterTimes(); } } else { // If we have -frame, use it. If not, then use -shutterOpen. If // we don't have that either, then use the current frame. if (argdb.isFlagSet(fsFrame)) fShutterOpen = fFrameCenter; else if (!argdb.isFlagSet(fsShutterOpen)) fShutterOpen = curFrame; // -shutterClose is meaningless when motion blur is off, so warn // the user if they specified it. if (argdb.isFlagSet(fsShutterClose)) { displayWarning( commandName + ": motion blur not enabled: ignoring " + flShutterClose ); } } } MStatus shaveWriteRib::getHairShapes( const MArgDatabase& argdb, MObjectArray& hairShapes ) const { // If shaveHair nodes were explicitly supplied in the command line, // return those, otherwise return all renderable shaveHair nodes in // the scene. if (argdb.isFlagSet(fsHairNode)) { unsigned int numNodes = argdb.numberOfFlagUses(fsHairNode); unsigned int i; for (i = 0; i < numNodes; ++i) { MArgList flagArgs; argdb.getFlagArgumentList(fsHairNode, i, flagArgs); MString nodeName = flagArgs.asString(0); MSelectionList list; MDagPath path; list.add(nodeName); list.getDagPath(0, path); if (path.hasFn(MFn::kTransform)) path.extendToShape(); MFnDagNode nodeFn(path); if (nodeFn.typeId() != shaveHairShape::id) { displayError( MString("'") + nodeName + "' is not a shaveHair node." ); return MS::kInvalidParameter; } hairShapes.append(path.node()); } } else { shaveRenderer* renderer = shaveRender::getRenderer(); renderer->getRenderableShaveNodes(hairShapes); unsigned int numShaveNodes = hairShapes.length(); if (numShaveNodes == 0) { MGlobal::displayWarning( commandName + ": There are no renderable shaveHairShapes in the scene." ); return MS::kSuccess; } } return MS::kSuccess; } void shaveWriteRib::getMayaShutterTimes() { shaveMaya::getRenderGlobals(); // Get the current render camera. MDagPath cameraPath = shaveRender::getRenderCamPath(); if (cameraPath.isValid()) { // Get the camera's shutter angle. MFnCamera cameraFn(cameraPath); MAngle shutterAngle(cameraFn.shutterAngle(), MAngle::kRadians); // Calculate the amount of slider time needed for blurring. double blurDelta = shutterAngle.asDegrees() / 360.0 * shaveMaya::motionBlurByFrame / 2.0; // Calculate the shutter open/close times. fShutterOpen = fFrameCenter - blurDelta; fShutterClose = fFrameCenter + blurDelta; } else { // We couldn't find a valid render camera, so turn off motion // blur. displayWarning( commandName + ": could not find a valid render camera." + " Disabling motion blur." ); fDoMotionBlur = false; fShutterOpen = fFrameCenter; } } int shaveWriteRib::getNumParams(bool isInstanced) const { // Allocate the parameter list used to call Ri geometry functions. // int numParams = 0; // // Allocate buffers and fill in the parameter list. // int paramIdx = 0; // RI_P is always param 0. if (!isInstanced) { // RI_WIDTH is always param 1 for hair curves. paramIdx = 2; // Opacity and the W-coord are also "varying" parameters, which // means that there is one per vertex of the curve's linear form. // (Or alternatively, one at the start of each segment, plus one // at the very end of the curve.) if (fDoOpacities) { // RI_OS paramIdx++; } if (fDoWCoords) { // ktWCoord paramIdx++; } } else { paramIdx = 1; } if (fDoRootPositions && !isInstanced) { // ktRootPosition paramIdx++; } if (fDoRootTipColors) { // ktRootColor paramIdx++; // ktTipColor paramIdx++; } // We only do vertex normals for instance geometry. // // TODO: Apparently we now ignore fDoNormals so we // should get rid of it. // if (isInstanced) { // RI_N paramIdx++; } if (fDoSurfaceNormals && !isInstanced) { // ktSurfaceNormal paramIdx++; } // We always do vertex colors for instances, but for hairs // only if we are asked to. // if (isInstanced || fDoVertexColors) { // RI_CS paramIdx++; } if (fDoUVs) { // RI_S paramIdx++; // RI_T paramIdx++; } // ktIndex paramIdx++; // ktAmbDiff paramIdx++; // ktOpacity paramIdx++; // ktGloss paramIdx++; // ktSelfShadow paramIdx++; // ktSpecular paramIdx++; // ktSquirrel paramIdx++; // ktSpecularColor paramIdx++; // ktSpecularColor2 paramIdx++; numParams=paramIdx; return numParams; } void shaveWriteRib::getRendermanShutterTimes() { // Find the renderManGlobals node. MObject globalsNode = shaveUtil::getNode("renderManGlobals"); if (!globalsNode.isNull()) { MFnDependencyNode nodeFn(globalsNode); MPlug plug; plug = nodeFn.findPlug("rman__toropt___motionBlurType"); if (!plug.isNull()) { float blurDelta = 1.0; MString blurType; plug.getValue(blurType); // If 'Motion Blur Type' is 'Frame' then the blur delta is // always one full frame, regardless of camera angle. // // If 'Motion Blur Type' is 'Subframe' then the blur delta is // the camera angle divided by 360. if (blurType == "frame") { blurDelta = 1.0; } else if (blurType == "subframe") { float angle; plug = nodeFn.findPlug("rman__toropt___shutterAngle"); plug.getValue(angle); blurDelta = angle / 360.0f; } else { displayWarning( MString("shaveWriteRib: unrecognized renderManGlobals") + " Motion Blur Type, '" + blurType + "'. Assuming 'frame'." ); blurDelta = 1.0f; } MString shutterTiming; plug = nodeFn.findPlug("rman__toropt___shutterTiming"); plug.getValue(shutterTiming); //new params - 29Jun2012 bool doBlur = false; int prmanMoBlur; plug = nodeFn.findPlug("rman__torattr___motionBlur"); plug.getValue(prmanMoBlur); int prmanCamBlur; plug = nodeFn.findPlug("rman__torattr___cameraBlur"); plug.getValue(prmanCamBlur); doBlur = (prmanMoBlur != 0) || (prmanCamBlur != 0); float prmanSutterOpen; plug = nodeFn.findPlug("rman__riopt__Camera_shutteropening0"); plug.getValue(prmanSutterOpen); float prmanSutterClose; plug = nodeFn.findPlug("rman__riopt__Camera_shutteropening1"); plug.getValue(prmanSutterClose); /////// dump //////////// printf(" --------------- prman ---------------\n"); printf("prman: Motion Blur: %i\n",prmanMoBlur); printf("prman: Camera Blur: %i\n",prmanCamBlur); printf("prman: Sutter Angle/360: %f\n",blurDelta); printf("prman: Sutter Open: %f\n",prmanSutterOpen); printf("prman: Sutter Close: %f\n",prmanSutterClose); printf("prman: Sutter Timing: %s\n",shutterTiming.asChar()); printf("prman: Blur Type: %s\n",blurType.asChar()); fflush(stdout); ///////////////////////// // If 'Shutter Timing' is 'Frame Open' then the open offset is 0 // and the close offset is the blur delta. // // If 'Shutter Timing' is 'Frame Center' then the open offset is // negative half the blur delta and the close offset is // positive half the blur delta. // // If 'Shutter Timing' is 'Frame Close' then the open offset is // negative the blur delta and the close offset is 0. // // The actual shutter open/close times are simply the frame // time with those offsets applied. if (shutterTiming == "frameOpen") { fShutterOpen = fFrameCenter; fShutterClose = fFrameCenter + blurDelta; } else if (shutterTiming == "frameCenter") { fShutterOpen = fFrameCenter - blurDelta / 2.0f; fShutterClose = fFrameCenter + blurDelta / 2.0f; } else if (shutterTiming == "frameClose") { fShutterOpen = fFrameCenter - blurDelta; fShutterClose = fFrameCenter; } else { displayWarning( MString("shaveWriteRib: unrecognized renderManGlobals") + " Shutter Timing, '" + shutterTiming + "'. Assuming 'frameOpen'." ); fShutterOpen = fFrameCenter; fShutterClose = fFrameCenter + blurDelta; } return; } } // We couldn't find the necessary renderManGlobals settings, so print // a warning and use Maya's instead. displayWarning( MString("shaveWriteRib: renderMan blur defaults requested but") + " renderManGlobals are unavailable. Using Maya defaults instead." ); getMayaShutterTimes(); } MString shaveWriteRib::getVoxelFilename( const MString& baseFilename, const MString& nodeName, int voxel ) const { // The node name may contain a ':' which is bad in a filename, so // let's convert those all to '_'. MString modifiedNodeName = shaveUtil::substituteAll(nodeName, ':', '_'); MString voxelFile = baseFilename + "_" + modifiedNodeName + "_"; voxelFile += voxel; voxelFile += ".rib"; return voxelFile; } void shaveWriteRib::initBuffers( RfMFuncs &rfm, MFnDependencyNode &nodeFn, int numHairs, int numVerts, int numFaceVerts, float baseOpacity, bool isInstanced ) { // // Allocate the parameter list used to call Ri geometry functions. // int numParams = getNumParams(isInstanced); fParams = new RtPointer[numParams]; fTokens = new RtToken[numParams]; // // Allocate buffers and fill in the parameter list. // int paramIdx = 0; if (!isInstanced) { int numWidths = numVerts; fWidth = new RtFloat[numWidths]; fTokens[1] = *rfm.RI_WIDTH; fParams[1] = fWidth; paramIdx = 2; // Opacity and the W-coord are also "varying" parameters, which // means that there is one per vertex of the curve's linear form. // (Or alternatively, one at the start of each segment, plus one // at the very end of the curve.) if (fDoOpacities) { fOpacity = new RtColor[numWidths]; fTokens[paramIdx] = *rfm.RI_OS; fParams[paramIdx] = fOpacity; paramIdx++; } if (fDoWCoords) { fWCoord = new RtFloat[numWidths]; fTokens[paramIdx] = ktWCoord; fParams[paramIdx] = fWCoord; paramIdx++; } // // If we are creating cubic curves, then we need to double up on the // start and end cvs to anchor the hair's endpoints. // // Note that we only supply widths for each unique cv, so we do not // want to increment the width count to include these extra cvs. // if (fUseCubicCurves) numVerts += 2 * numHairs; // // For curves, the number of face-verts and the number of verts // should be the same. // numFaceVerts = numVerts; } else { fVertIndices = new RtInt[numFaceVerts]; paramIdx = 1; } fVert = new RtPoint[numVerts]; fTokens[0] = *rfm.RI_P; fParams[0] = fVert; fVertsPerHair = new RtInt[numHairs]; if (fDoRootPositions && !isInstanced) { fRootPosition = new RtPoint[numHairs]; fTokens[paramIdx] = ktRootPosition; fParams[paramIdx] = fRootPosition; paramIdx++; } if (fDoRootTipColors) { fRootColor = new RtColor[numHairs]; fTipColor = new RtColor[numHairs]; fTokens[paramIdx] = ktRootColor; fParams[paramIdx] = fRootColor; paramIdx++; fTokens[paramIdx] = ktTipColor; fParams[paramIdx] = fTipColor; paramIdx++; } // We only do vertex normals for instance geometry. // if (isInstanced) { fNormal = new RtNormal[numVerts]; fTokens[paramIdx] = *rfm.RI_N; fParams[paramIdx] = fNormal; paramIdx++; } if (fDoSurfaceNormals && !isInstanced) { fSurfaceNormal = new RtNormal[numHairs]; fTokens[paramIdx] = ktSurfaceNormal; fParams[paramIdx] = fSurfaceNormal; paramIdx++; } if (fDoVertexColors || isInstanced) { fVertexColor = new RtColor[numVerts]; fTokens[paramIdx] = *rfm.RI_CS; fParams[paramIdx] = fVertexColor; paramIdx++; } if (fDoUVs) { // For instance geometry we output the per face-vert UVs from the // original geometry. // // For hair curves the entire curve acquires the UV of the // point on the growth surface to which it is rooted, so the UVs // are per-curve. // if (isInstanced) { fSCoord = new RtFloat[numFaceVerts]; fTCoord = new RtFloat[numFaceVerts]; } else { fSCoord = new RtFloat[numHairs]; fTCoord = new RtFloat[numHairs]; } fTokens[paramIdx] = *rfm.RI_S; fParams[paramIdx] = fSCoord; paramIdx++; fTokens[paramIdx] = *rfm.RI_T; fParams[paramIdx] = fTCoord; paramIdx++; } fIndex = new RtInt[numHairs]; fTokens[paramIdx] = ktIndex; fParams[paramIdx] = fIndex; paramIdx++; // Set the Shave material constants MPlug plug; fTokens[paramIdx] = ktAmbDiff; plug = nodeFn.findPlug("amb/diff"); plug.getValue(fAmbDiff); fParams[paramIdx] = &fAmbDiff; paramIdx++; fTokens[paramIdx] = ktOpacity; fBaseOpacity = baseOpacity; fParams[paramIdx] = &fBaseOpacity; paramIdx++; fTokens[paramIdx] = ktGloss; plug = nodeFn.findPlug("gloss"); plug.getValue(fGloss); fParams[paramIdx] = &fGloss; paramIdx++; fTokens[paramIdx] = ktSelfShadow; plug = nodeFn.findPlug("selfShadow"); plug.getValue(fSelfShadow); fParams[paramIdx] = &fSelfShadow; paramIdx++; fTokens[paramIdx] = ktSpecular; plug = nodeFn.findPlug("specular"); plug.getValue(fSpecular); fParams[paramIdx] = &fSpecular; paramIdx++; fTokens[paramIdx] = ktSquirrel; plug = nodeFn.findPlug("squirrel"); plug.getValue(fSquirrel); fParams[paramIdx] = &fSquirrel; paramIdx++; fTokens[paramIdx] = ktSpecularColor; plug = nodeFn.findPlug("specularTint"); plug.child(0).getValue(fSpecularColor[0]); plug.child(1).getValue(fSpecularColor[1]); plug.child(2).getValue(fSpecularColor[2]); fParams[paramIdx] = &fSpecularColor; paramIdx++; fTokens[paramIdx] = ktSpecularColor2; plug = nodeFn.findPlug("specularTint2"); plug.child(0).getValue(fSpecularColor2[0]); plug.child(1).getValue(fSpecularColor2[1]); plug.child(2).getValue(fSpecularColor2[2]); fParams[paramIdx] = &fSpecularColor2; paramIdx++; clearCurves(); } static void myFree(void* ptr) { } void shaveWriteRib::outputCurves(RfMFuncs &rfm, int numCurves, bool isShutterOpen) { int numParams = getNumParams(false); if (fDoMotionBlur && isShutterOpen) outputMotionBegin(rfm); rfm.RiCurvesV( (fUseCubicCurves ? *rfm.RI_CUBIC : *rfm.RI_LINEAR), numCurves, fVertsPerHair, *rfm.RI_NONPERIODIC, numParams, fTokens, fParams ); if (fDoMotionBlur && !isShutterOpen) rfm.RiMotionEnd(); } void shaveWriteRib::outputInstanceGeom( RfMFuncs& rfm, MFnDependencyNode& nodeFn, bool isShutterOpen, MObject instanceMesh, int numFaces, int numVerts, int numFaceVerts, const int* faceList, const int* faceStarts, const int* faceEnds, const VERT* verts, const VERT* vertColors, const VERT* vertNormals, const VERT* growthSurfNormal, int index ) { MStatus st; MFnMesh meshFn(instanceMesh); int facesPerInst = meshFn.numPolygons(); int faceVertsPerInst = meshFn.numFaceVertices(); int numInsts = numFaces / facesPerInst; // // We can only do those parameters for which we have been passed // appropriate data. // bool savedDoNormals = fDoNormals; bool savedDoRootTipColors = fDoRootTipColors; bool savedDoSurfaceNormals = fDoSurfaceNormals; bool savedDoUVs = fDoUVs; bool savedDoVertexColors = fDoVertexColors; fDoNormals = fDoNormals && (vertNormals != NULL); fDoRootTipColors = false; fDoSurfaceNormals = fDoSurfaceNormals && (growthSurfNormal != NULL); fDoUVs = fDoUVs && !instanceMesh.isNull(); fDoVertexColors = fDoVertexColors && (!instanceMesh.isNull() || (vertColors != NULL)); // // Now that we've adjusted some of the settings, get the number of // parameters which we'll be dumping. // int numParams = getNumParams(true); initBuffers(rfm, nodeFn, numFaces, numVerts, numFaceVerts, 1.0f, true); int i; for (i = 0; i < numFaces; i++) { fVertsPerHair[i] = faceEnds[i] - faceStarts[i]; } for (i = 0; i < numFaceVerts; i++) fVertIndices[i] = faceList[i]; for (i = 0; i < numVerts; i++) { fVert[i][0] = verts[i].x; fVert[i][1] = verts[i].y; fVert[i][2] = verts[i].z; } // if (fDoNormals) { for (i = 0; i < numVerts; i++) { fNormal[i][0] = vertNormals[i].x; fNormal[i][1] = vertNormals[i].y; fNormal[i][2] = vertNormals[i].z; MFloatVector v(vertNormals[i].x, vertNormals[i].y, vertNormals[i].z); float mag = v.length(); if ((mag < 0.995f) || (mag > 1.005f)) { (std::cerr << "--- normal " << i << " has mag " << mag << std::endl).flush(); } } } if (0==1) if (fDoSurfaceNormals) { // // At the moment, we only ever get passed one surface normal which // must then be applied to all the faces (which will generally be // multiple strands of the same hair). // for (i = 0; i < numFaces; i++) { fSurfaceNormal[i][0] = growthSurfNormal[i].x; fSurfaceNormal[i][1] = growthSurfNormal[i].y; fSurfaceNormal[i][2] = growthSurfNormal[i].z; } } { // if (vertColors) { // for (i = 0; i < numFaceVerts; i++) for (i = 0; i < numVerts; i++) { // fVertexColor[i][0] = vertColors[faceList[i]].x; // fVertexColor[i][1] = vertColors[faceList[i]].y; // fVertexColor[i][2] = vertColors[faceList[i]].z; fVertexColor[i][0] = vertColors[i].x; fVertexColor[i][1] = vertColors[i].y; fVertexColor[i][2] = vertColors[i].z; } } } if (fDoUVs) { // // Get the uvs from the original instance mesh. // MString* uvSet = NULL; if (fUVSet != "") uvSet = &fUVSet; int firstInstFVIndex = 0; int instNum; MItMeshFaceVertex iter(instanceMesh); float2 uv; bool uvSetWarningGiven = false; for (iter.reset(); !iter.isDone(); iter.next()) { int thisInstFVIndex = firstInstFVIndex; st = iter.getUV(uv, uvSet); if (!st && (uvSet != NULL)) { MGlobal::displayWarning( commandName + ": '" + nodeFn.name() + "'s instance mesh does not have a UV set named '" + *uvSet + "'. Using the default UV set instead." ); uvSet = NULL; st = iter.getUV(uv, uvSet); } if (!st && !uvSetWarningGiven) { MGlobal::displayWarning( commandName + ": '" + nodeFn.name() + "'s instance mesh does not have a default UV set." + " All UVs for this mesh will be zero." ); uvSetWarningGiven = true; } for (instNum = 0; instNum < numInsts; instNum++) { if (st) { fSCoord[thisInstFVIndex] = uv[0]; fTCoord[thisInstFVIndex] = uv[1]; } else { fSCoord[thisInstFVIndex] = 0.0f; fTCoord[thisInstFVIndex] = 0.0f; } thisInstFVIndex += faceVertsPerInst; } firstInstFVIndex++; } } int xx; for (xx = 0; xx < numFaces; xx++) { // fprintf (stdout,"xx = %d\n",xx);fflush(stdout); fIndex[xx]=index; } if (fDoMotionBlur && isShutterOpen) outputMotionBegin(rfm); rfm.RiPointsPolygonsV( numFaces, fVertsPerHair, fVertIndices, numParams, fTokens, fParams ); if (fDoMotionBlur && !isShutterOpen) rfm.RiMotionEnd(); freeBuffers(true); fDoNormals = savedDoNormals; fDoRootTipColors = savedDoRootTipColors; fDoSurfaceNormals = savedDoSurfaceNormals; fDoUVs = savedDoUVs; fDoVertexColors = savedDoVertexColors; } // // Output those declarations which will be the same for all objects, // regardless of whether they are hairs or instances. // void shaveWriteRib::outputGlobalDecls(RfMFuncs &rfm) const { // // Output declarations. // if (fUseCubicCurves) rfm.RiBasis(*rfm.RiCatmullRomBasis, 1, *rfm.RiCatmullRomBasis, 1); rfm.RiDeclare(*rfm.RI_WIDTH, "varying float"); rfm.RiDeclare(*rfm.RI_OS, "varying color"); // if (!isInstanced) if (fDoRootPositions) rfm.RiDeclare(ktRootPosition, "uniform point"); if (fDoRootTipColors) { rfm.RiDeclare(ktRootColor, "uniform color"); rfm.RiDeclare(ktTipColor, "uniform color"); } if (fDoSurfaceNormals) rfm.RiDeclare(ktSurfaceNormal, "uniform normal"); // // The w texture coord runs up the length of the hair, so it's // different for each vertex along the hair. // if (fDoWCoords) rfm.RiDeclare(ktWCoord, "varying float"); rfm.RiDeclare(ktIndex, "uniform integer"); // Basic Shave texture params rfm.RiDeclare(ktAmbDiff, "constant float"); rfm.RiDeclare(ktOpacity, "constant float"); rfm.RiDeclare(ktGloss, "constant float"); rfm.RiDeclare(ktSelfShadow, "constant float"); rfm.RiDeclare(ktSpecular, "constant float"); rfm.RiDeclare(ktSquirrel, "constant integer"); rfm.RiDeclare(ktSpecularColor, "constant color"); rfm.RiDeclare(ktSpecularColor2, "constant color"); } void shaveWriteRib::outputMotionBegin(RfMFuncs &rfm) const { float openTime = 0.0f; float closeTime = 0.0f; if (fFrameRelativeTime) { // we put this in because someone was having problems with negative motion blocks // openTime = 0.0; // closeTime = (fShutterClose - fShutterOpen) * fTimeFactor; openTime = openoffset;//*fTimeFactor; closeTime = closeoffset;//*fTimeFactor; // openTime = (-(fShutterClose - fShutterOpen)/2.0f) * fTimeFactor; // closeTime = ((fShutterClose - fShutterOpen)/2.0f) * fTimeFactor; } else { openTime = (float)(fShutterOpen * fTimeFactor); closeTime = (float)(fShutterClose * fTimeFactor); } rfm.RiMotionBegin(2, openTime, closeTime); } void shaveWriteRib::outputObjSpecificDecls(RfMFuncs &rfm, bool isInstanced) const { #if 0 // Opacity ("Os") is a standard geometric parameter so we don't need // to declare it. if (fDoOpacities && !isInstanced) { rfm.RiDeclare(*rfm.RI_OS, "vertex color"); } #endif rfm.RiShadingInterpolation(*rfm.RI_SMOOTH); if (fDoVertexColors) { rfm.RiDeclare(*rfm.RI_CS, "vertex color"); } if (fDoUVs) { // // For instance geometry we output the per face-vert UVs from the // original geometry. // // For hair curves the entire curve acquires the UV of the point on // the growth surface to which it is rooted, so the UVs are per // curve. // if (isInstanced) { rfm.RiDeclare(*rfm.RI_S, "facevarying float"); rfm.RiDeclare(*rfm.RI_T, "facevarying float"); } else { rfm.RiDeclare(*rfm.RI_S, "uniform float"); rfm.RiDeclare(*rfm.RI_T, "uniform float"); } } } void shaveWriteRib::showHelp() const { // Use 'indent1' if you're including the hypen after the indent (e.g. // when the flag name overran where the description should start so // the description starts on the second line). // // Use 'indent2' when just continuing a description. MString indent1 = " "; MString indent2 = indent1 + " "; MGlobal::displayInfo( MString("Usage: ") + commandName + " [flags] [\"ribFileName\"]" ); MGlobal::displayInfo(""); MGlobal::displayInfo("where [flags] are:"); unsigned int numFlags = sizeof(kFlags) / sizeof(kFlags[0]); unsigned int i; const MString indent = " "; unsigned int indentLen = indent.length(); MString line; const unsigned int kMaxLen = 78; for (i = 0; i < numFlags; i++) { line = MString(" ") + kFlags[i].shortName + "/" + kFlags[i].longName; if (kFlags[i].argName) line += MString(" ") + kFlags[i].argName; if (line.length() == indentLen - 2) line += "- "; else if (line.length() < indentLen - 2) line += indent.substring(0, indentLen - line.length() - 3) + "- "; else { MGlobal::displayInfo(line); line = indent.substring(0, indentLen-3) + "- "; } line += kFlags[i].description; if (kFlags[i].multiUse) line += " (multi-use)"; while (line.length() > kMaxLen) { MString temp = line.substring(0, kMaxLen-1); int wordBreak = temp.rindex(' '); temp = temp.substring(0, wordBreak-1); MGlobal::displayInfo(temp); line = indent + line.substring(wordBreak+1, line.length()+1); } if (line.length() > indent.length()) MGlobal::displayInfo(line); } MGlobal::displayInfo(""); MGlobal::displayInfo( " ribFileName - file to send output to. Must be in quotes." ); MGlobal::displayInfo(indent2 + "If not specified then output will be written"); MGlobal::displayInfo(indent2 + "to the console."); MGlobal::displayInfo(""); MGlobal::displayInfo( "Note that most settings will default to those specified in Shave Globals" ); MGlobal::displayInfo( "unless the -isg/-ignoreShaveGlobals flag is used." ); } // // Write out a RIB file for each shaveHairShape's voxels, and have the main file // include them all as delayed read archives. // MStatus shaveWriteRib::writeMultipleRibs( RfMFuncs &rfm, MObjectArray shaveHairShapes, const shaveGlobals::Globals& g ) { //vlad|16jan2013: you can get visibility flags from there //g.cameraVis //g.lightVis //g.giVis MStatus st; // // Create a name for the temporary Shave archive file we'll be using // by stripping the extension from the name of the RIB file and adding // "_shaveTemp.dat". // // %%% If the filename is empty, need to find the temp dir and create // a default name there. // MString baseFilename = getBaseFilename(); MString archiveFile = baseFilename + "_shaveTemp.dat"; // // Create the Shave archive file. // fprintf (stdout, "Creating archive file..\n");fflush(stdout); st = shaveRender::renderToDRAFile( kShaveHostPRMAN, archiveFile, (int)g.rib.voxels.resolution, shaveHairShapes, fDoMotionBlur, (float)fShutterOpen, (float)fShutterClose, true ); fprintf (stdout, "Done Creating archive file..\n");fflush(stdout); // // Initialize Shave's PRIM library from the archive. // PRIMlogin(); int numVoxels = PRIMinit_hairstack((char*)archiveFile.asChar()); // // Process each shaveHairShape separately. // std::vector nodes; const unsigned int numBlurPasses = (fDoMotionBlur ? 2 : 1); unsigned int sn; fprintf (stdout, "Fetching Voxels..\n");fflush(stdout); for (sn = 0; sn < shaveHairShapes.length(); sn++) { MFnDependencyNode nodeFn(shaveHairShapes[sn]); shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode(); MFnMesh instanceMeshFn; MObject instanceMesh; bool isInstanced = nodePtr->isInstanced(); NodeInfo nodeInfo; MString nodeName = nodeFn.name(); MString nodePreamble; MPlug plug = nodeFn.findPlug(shaveHairShape::ribInsert); bool tipFade = false; plug.getValue(nodePreamble); plug = nodeFn.findPlug(shaveHairShape::aTipFade); plug.getValue(tipFade); // // If the node is instanced then we'll need its instance mesh. // if (isInstanced) { plug = nodeFn.findPlug(shaveHairShape::instanceMesh); plug.getValue(instanceMesh); instanceMeshFn.setObject(instanceMesh); } // // Step through each voxel for this shaveHairShape and output its hair // into a separate RIB file. // int i; NodeInfo::VoxelInfo voxel; for (i = 0; i < numVoxels; i++) { // // Get the hair which this shaveHairShape has within this voxel. // HAIRTYPE hair; UVSETS uvSets; UVSETS* pUVSets = (fDoUVs ? &uvSets : 0); PRIMfetch_voxel_by_name2( i, (char*)nodeName.asChar(), &hair, pUVSets, false ); if (hair.totalfaces > 0) { bool haveUVs = (fDoUVs && (uvSets.totalUVSets > 0)); // // Create the name for this voxel's RIB file and save it // for later output to the main RIB file. // voxel.fFilename = getVoxelFilename(baseFilename, nodeName, i); // // Get the voxel's bounding box and save it for later // output to the main RIB file. // VERT bbMin; VERT bbMax; PRIMfetch_bbox(i, &bbMin, &bbMax); voxel.fBBox[0] = bbMin.x; voxel.fBBox[1] = bbMax.x; voxel.fBBox[2] = bbMin.y; voxel.fBBox[3] = bbMax.y; voxel.fBBox[4] = bbMin.z; voxel.fBBox[5] = bbMax.z; // // Add this voxel's info to the shaveHairShape's array. // nodeInfo.fVoxels.push_back(voxel); // // Start the RIB file for this voxel. // beginFile(rfm, (char *)voxel.fFilename.asChar(), nodePreamble); outputGlobalDecls(rfm); outputObjSpecificDecls(rfm, isInstanced); if (!isInstanced) { initBuffers( rfm, nodeFn, hair.totalfaces, hair.totalverts, hair.totalfverts, sqrt(hair.opacity[0]), // Opacity is const per-node false ); } // // If motion blur is on, we need to create separate motion // blocks for shutter open and close. We do this by making // two passes over the data. // unsigned int blurPass; int j; for (blurPass = 0; blurPass < numBlurPasses; blurPass++) { // // If this is the second pass for motion blur, add the // velocities to the vertex positions to get their // positions at shutter close. // if (blurPass == 1) { for (j = 0; j < hair.totalverts; j++) { VERT& vert = hair.v[j]; VERT& vel = hair.velocity[j]; vert.x += vel.x; vert.y += vel.y; vert.z += vel.z; } } // // At this point, curves and instances must part // company as they are handled quite differently. // if (isInstanced) { #ifndef PRIM_BUG_FIXED // // %%% There is a bug in the PRIM functions such that // they don't return the correct face-vertex counts // or lists for instances, so we have to do those // ourselves. // int facesPerInst = instanceMeshFn.numPolygons(); int faceVertsPerInst = instanceMeshFn.numFaceVertices(); int numInsts = hair.totalfaces / facesPerInst; int numFaceVerts = numInsts * faceVertsPerInst; int vertsPerInst = instanceMeshFn.numVertices(); if (hair.totalfverts < numFaceVerts) { if (hair.facelist) free(hair.facelist); if (hair.face_start) free(hair.face_start); if (hair.face_end) free(hair.face_end); hair.totalfverts = numFaceVerts; hair.facelist = (int*)malloc(sizeof(int)*numFaceVerts); hair.face_start = (int*)malloc(sizeof(int)*hair.totalfaces+1); hair.face_end = (int*)malloc(sizeof(int)*hair.totalfaces+1); int faceStart = 0; int faceEnd = 0; int faceNum; int instNum; for (faceNum = 0; faceNum < facesPerInst; faceNum++) { MIntArray vertIds; instanceMeshFn.getPolygonVertices( faceNum, vertIds ); faceEnd = faceStart + (int)vertIds.length(); for (instNum = 0; instNum < numInsts; instNum++) { int vertOffset = instNum * vertsPerInst; int faceVertOffset = instNum * faceVertsPerInst; int fv; for (fv = 0; fv < (int)vertIds.length(); fv++) { hair.facelist[ faceVertOffset + faceStart + fv ] = vertOffset + vertIds[fv]; } hair.face_start[instNum*facesPerInst + faceNum] = faceVertOffset + faceStart; hair.face_end[instNum*facesPerInst + faceNum] = faceVertOffset + faceEnd; } faceStart = faceEnd; } hair.face_start[hair.totalfaces] = hair.face_end[hair.totalfaces-1]; hair.face_end[hair.totalfaces] = hair.face_end[hair.totalfaces-1]; } #endif outputInstanceGeom( rfm, nodeFn, (blurPass == 0), instanceMesh, hair.totalfaces, hair.totalverts, hair.totalfverts, hair.facelist, hair.face_start, hair.face_end, hair.v, NULL, hair.surfNorm, NULL, hair.index[0] ); } else { // // Clear out any curve data from previous passes. // clearCurves(); // // Fill in the arrays for the hairs. // int hairIdx; int uvIdx = 0; for (hairIdx = 0; hairIdx < hair.totalfaces; hairIdx++) { int faceStart = hair.face_start[hairIdx]; int faceEnd = hair.face_end[hairIdx] - 1; addCurve( faceStart, faceEnd, hair.facelist, hair.v, NULL, &hair.colorroot[hairIdx], &hair.colortip[hairIdx], (haveUVs ? uvSets.uvRoot[uvIdx].x : 0.0f), (haveUVs ? uvSets.uvRoot[uvIdx].y : 0.0f), hair.radiusroot[hairIdx], hair.radiustip[hairIdx], &hair.surfNorm[hairIdx], tipFade, hair.index[hairIdx] ); if (haveUVs) uvIdx += uvSets.totalUVSets; } outputCurves(rfm, hair.totalfaces, (blurPass == 0)); } } endFile(rfm); if (!isInstanced) freeBuffers(false); } if (fDoUVs) SHAVEfree_UV(pUVSets); PRIMfree_hairtype(&hair); } nodes.push_back(nodeInfo); } // // Create the main RIB file. // beginFile(rfm, fFilename, g.rib.filePreamble); appendToResult(fFilename); for (sn = 0; sn < nodes.size(); sn++) { NodeInfo& node = nodes[sn]; // // Output delayed read archives for each of the node's voxel // files. // this stuff will need to be inserted at the node level for prman export // come back here for pixar .. RtString args[1]; unsigned int v; for (v = 0; v < node.fVoxels.size(); v++) { NodeInfo::VoxelInfo& voxel = node.fVoxels[v]; MString filename = voxel.fFilename; appendToResult(filename); // // If the -fullPaths option was not specified, then strip the // path from the filename. // if (!fDoFullPaths) { MFileObject fileObj; fileObj.setFullName(filename); filename = fileObj.name(); } #ifdef RiProcedural_BROKEN rfm.RiArchiveRecord( *rfm.RI_VERBATIM, "Procedural \"DelayedReadArchive\" [\"%s\"] [%f %f %f %f %f %f]\n", filename.asChar(), voxel.fBBox[0], voxel.fBBox[1], voxel.fBBox[2], voxel.fBBox[3], voxel.fBBox[4], voxel.fBBox[5] ); #else args[0] = (char*)filename.asChar(); rfm.RiProcedural( (RtPointer)args, voxel.fBBox, rfm.RiProcDelayedReadArchive, myFree ); #endif } } endFile(rfm); PRIMlogout(); shaveUtil::fileDelete(archiveFile); return MS::kSuccess; } MStatus shaveWriteRib::writeRib( RfMFuncs &rfm, const MObjectArray& shaveHairShapes, const shaveGlobals::Globals& g ) { MStatus st; unsigned int numShaveNodes = shaveHairShapes.length(); if (numShaveNodes == 0) { MGlobal::displayWarning( commandName + ": There are no renderable shaveHairShapes in the scene." ); return MS::kSuccess; } // // If we're not doing motion blur, then we can get the texture info for // all of the shaveHairShapes at once. // if (!fDoMotionBlur) #ifdef PER_NODE_TEXLOOKUP initTexInfoLookup2(shaveHairShapes, fUVSet, g.verbose); #else initTexInfoLookup(shaveHairShapes, fUVSet, g.verbose); #endif // // Generate the rib file(s). // if (fDoVoxels) st = writeMultipleRibs(rfm, shaveHairShapes, g); else st = writeSingleRib(rfm, shaveHairShapes, g); return st; } MStatus shaveWriteRib::writeSingleRib( RfMFuncs &rfm, MObjectArray shaveHairShapes, const shaveGlobals::Globals& g ) { //vlad|16jan2013: you can get visibility flags from there //g.cameraVis //g.lightVis //g.giVis MStatus st; // // Start the file. // beginFile(rfm, fFilename, g.rib.filePreamble); outputGlobalDecls(rfm); // We always want the return type to be an array of strings, so even // though we only have one filename in this case, let's put it into an // array. MStringArray fileNames; fileNames.append(fFilename); setResult(fileNames); // // Output the hair for each shave node. // unsigned int sn; unsigned int blurPass; for (sn = 0; sn < shaveHairShapes.length(); sn++) { MFnDependencyNode nodeFn(shaveHairShapes[sn]); shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode(); bool isInstanced = theShaveNode->isInstanced(); bool savedDoMotionBlur = fDoMotionBlur; // Motion blur is not currently supported when doing geometry // output in non-voxel mode, because the overhead is simply too // great: we would either have to store all of the instance // geometry between shutter open and close, thereby defeating the // point of the hair-at-a-time approach, which is to keep memory // overhead low; or we would have to switch frames for every // instance which would be horribly slow. // if (isInstanced) fDoMotionBlur = false; theShaveNode->makeCurrent(); outputObjSpecificDecls(rfm, isInstanced); if (fDoMotionBlur) { // // If motion blur is on, and we have multiple shaveHairShapes, // then the previous shaveHairShape will have left us at the // shutterClose time, in which case we need to move back to // shutterOpen. // if (sn > 0) shaveUtil::setFrame((float)fShutterOpen); // // Generate this shaveHairShape's texture info. // // (When motion blur is off, texture info for all of the nodes // is generated at once, back near the start of this method.) // MObjectArray shaveHairShapeAsAnArray; shaveHairShapeAsAnArray.append(shaveHairShapes[sn]); #ifdef PER_NODE_TEXLOOKUP initTexInfoLookup2(shaveHairShapeAsAnArray, fUVSet, g.verbose); #else initTexInfoLookup(shaveHairShapeAsAnArray, fUVSet, g.verbose); #endif } // // If this shaveHairShape has RIB text, output it as a verbatim // record. // MPlug plug = nodeFn.findPlug(shaveHairShape::ribInsert); MString nodePreamble; plug.getValue(nodePreamble); if (nodePreamble != "") rfm.RiArchiveRecord(*rfm.RI_VERBATIM, "%s\n", nodePreamble.asChar()); bool tipFade = false; plug = nodeFn.findPlug(shaveHairShape::aTipFade); plug.getValue(tipFade); SHAVENODE* hairNode = theShaveNode->getHairNode(); int hairGroup = theShaveNode->getHairGroup(); int numHairs = hairNode->shavep.haircount[hairGroup]; int numPasses = hairNode->shavep.passes[hairGroup]; int numSegs = hairNode->shavep.segs[hairGroup]; int pass; WFTYPE wf; init_geomWF(&wf); // // If we're generating the hair as curves, we need to figure out // how big our arrays have to be. // CURVEINFO curveInfo; MObject instanceMesh; unsigned int totalCurves = 0; unsigned int totalCVs = 0; if (isInstanced) { // // Get the original instance mesh. // plug = nodeFn.findPlug(shaveHairShape::instanceMesh); plug.getValue(instanceMesh); } else { // for (pass = 0; pass < numPasses; pass++) pass=0; { int hair; for (hair = 0; hair < numHairs*(numPasses+1); hair++) { // For speed, we call SHAVEmake_a_curveROOT rather than // SHAVEmake_a_curve. Note, however, that this means // our counts will include some curves which may // eventually be killed. SHAVEmake_a_curveROOT( pass, hairGroup, hair, &wf, &curveInfo ); // // Each face is effectively a separate hair. // totalCurves += wf.totalfaces; } } // // If we ended up with no curves, then skip this shaveHairShape. // if (totalCurves == 0) continue; totalCVs = totalCurves * (numSegs + 1); float np=(float)numPasses; // np=(np)/2; if (np<1.0f) np=1.0f; float opacity = 1.0f / np; opacity=sqrt(opacity); //opacity*=opacity; initBuffers( rfm, nodeFn, totalCurves, totalCVs, totalCVs, opacity, false ); } const unsigned int numBlurPasses = (fDoMotionBlur ? 2 : 1); for (blurPass = 0; blurPass < numBlurPasses; blurPass++) { // // If this is the second pass for motion blur, move to the // shutter close time. // if (blurPass == 1) { shaveUtil::setFrame((float)fShutterClose); // // Changing time causes all shaveHairShapes to reload // themselves, which means that whichever shaveHairShape was // last is the one which will now be loaded into Shave. So // let's make sure that *this* shaveHairShape is the one that's // loaded. // theShaveNode->makeCurrent(); } // // At this point, curves and polys must part company as they are // handled quite differently. // if (isInstanced) { int pass=0; int np; np=numHairs; if (np<1) np=1; // np*=numPasses; // // Generate each instance as a poly. // pass=0; // for (pass = 0; pass < numPasses; pass++) { int hair; int ff; VERT *snorm; SHAVEmake_a_hair(pass, hairGroup, 1, 14, &wf); if (numPasses<=0) numPasses=1; if (wf.totalverts>0) snorm=(VERT *) malloc((np*numPasses+1)*wf.totalverts*sizeof(VERT)); ff=wf.totalverts; free_geomWF(&wf); for (hair = 0; hair < np*numPasses; hair++) { /// SHAVEmake_a_curve( /// pass, hairGroup, hair, 1, &wf, &curveInfo // ); SHAVEmake_a_curveROOT( pass, hairGroup, hair, &wf, &curveInfo ); // if (curveInfo.killme) continue; //if (wf.totalfaces>0) // SHAVEmake_a_hair(pass, hairGroup, hair, numSegs, &wf); int qq; for (qq=0;qq0) outputInstanceGeom( rfm, nodeFn, (blurPass == 0), instanceMesh, wf.totalfaces, wf.totalverts, wf.totalfverts, wf.facelist, wf.face_start, wf.face_end, wf.v, wf.color, wf.vn, snorm, curveInfo.hairID ); if (wf.totalfaces>0) free(snorm); if (wf.totalfaces>0) free_geomWF(&wf); } } else { // // Clear out any curve data from previous passes. // clearCurves(); // Now we get to find out how many curves we *really* // have. totalCurves = 0; // // Fill in the arrays for the hairs. // pass=0; // for (pass = 0; pass < numPasses; pass++) { // int hair; int nh; int b=0,c=0; int chunk=0; int np; np=numPasses; if (np<1) np=1; nh=numHairs*np;//*(np); // for (hair = 0; hair < numHairs; hair++) while (b1000) { chunk=1000; } if (chunk>nh-b) chunk=nh-b; // SHAVEmake_a_curve( // pass, hairGroup, hair, numSegs, &wf, &curveInfo // ); list_total=0; for (c=b;cgetHairGroup() == 4) // { u = wf.uv[wf.facelist[wf.face_start[face]]].x; v = wf.uv[wf.facelist[wf.face_start[face]]].y; // u = curveInfo.wgt[0]; // v = curveInfo.wgt[1]; // } // else // { // u = curveInfo.u; // v = curveInfo.v; // } addCurve( wf.face_start[face], wf.face_end[face] - 1, wf.facelist, wf.v, wf.color, NULL, NULL, u, v, // curveInfo.baserad, // curveInfo.tiprad, wf.r1[face], wf.r2[face], &wf.vn[wf.face_start[face]], tipFade, wf.index[face] ); } // // Each face is effectively a separate hair and // will thus require its own curve. // totalCurves += wf.totalfaces; b+=chunk; free_geomWF(&wf); } } outputCurves(rfm, totalCurves, (blurPass == 0)); } // end of curves vs. polys split } // end of blur pass loop // // If we were generating instances then restore the motion blur // setting, otherwise clean up the buffers used to create curves. // if (isInstanced) fDoMotionBlur = savedDoMotionBlur; else freeBuffers(false); } // end of shaveHairShape loop endFile(rfm); return st; } #endif