aboutsummaryrefslogtreecommitdiff
path: root/mayaPlug/shaveWriteRib.cpp
diff options
context:
space:
mode:
authorBen Marsh <[email protected]>2019-10-22 09:07:59 -0400
committerBen Marsh <[email protected]>2019-10-22 09:07:59 -0400
commitbd0027e737c6512397f841c22786274ed74b927f (patch)
treef7ffbdb8f3741bb7f24635616cc189cba5cb865c /mayaPlug/shaveWriteRib.cpp
downloadshave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.tar.xz
shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.zip
Adding Shave-and-a-Haircut 9.6
Diffstat (limited to 'mayaPlug/shaveWriteRib.cpp')
-rw-r--r--mayaPlug/shaveWriteRib.cpp3307
1 files changed, 3307 insertions, 0 deletions
diff --git a/mayaPlug/shaveWriteRib.cpp b/mayaPlug/shaveWriteRib.cpp
new file mode 100644
index 0000000..4d3bcbb
--- /dev/null
+++ b/mayaPlug/shaveWriteRib.cpp
@@ -0,0 +1,3307 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+#include "shaveIO.h"
+
+#include <maya/MAngle.h>
+#include <maya/MArgDatabase.h>
+#include <maya/MArgList.h>
+#include <maya/MAnimControl.h>
+#include <maya/MColor.h>
+#include <maya/MColorArray.h>
+#include <maya/MDagPath.h>
+#include <maya/MFnCamera.h>
+#include <maya/MFnDagNode.h>
+#include <maya/MFnDependencyNode.h>
+#include <maya/MFileObject.h>
+#include <maya/MFloatVector.h>
+#include <maya/MFnMesh.h>
+#include <maya/MGlobal.h>
+#include <maya/MIntArray.h>
+#include <maya/MItMeshFaceVertex.h>
+#include <maya/MObjectArray.h>
+#include <maya/MPlug.h>
+#include <maya/MPxCommand.h>
+#include <maya/MSelectionList.h>
+#include <maya/MString.h>
+#include <maya/MSyntax.h>
+
+#include <assert.h>
+#include <string.h>
+
+# 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 <ri.h>
+
+# 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 <dlfcn.h>
+# 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<NodeInfo> 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;qq<wf.totalverts;qq++)
+ snorm[qq+ff*hair]=curveInfo.norm;
+free_geomWF(&wf);
+ }
+int list_total=0;
+int c;
+int *inlist;
+inlist=(int *) malloc (np*numPasses*sizeof(int));
+// list_total=0;
+ for (c=0;c<np*numPasses;c++)
+ {
+ inlist[c]=c;
+ list_total++;
+ }
+
+ MTbunch_of_hairs(list_total, inlist,hairGroup,0, &wf,&curveInfo);
+free(inlist);
+
+ //
+ // Note that currently motion blur is not supported
+ // when doing geometry output in non-voxel mode,
+ // because the overhead is simply too great.
+ //
+ // So the second arg in the call below is
+ // meaningless. Which is a good thing because
+ // otherwise we would end up generating a whole
+ // bunch of RiMotionBegin's followed by a bunch of
+ // RiMotionEnd's, rather than having them be
+ // properly paired up.
+ //
+ if (wf.totalfaces>0)
+ 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 (b<nh)
+ {
+ int inlist[1001];
+ int list_total=0;
+ chunk=1;
+ if (nh>1000)
+ {
+ chunk=1000;
+ }
+ if (chunk>nh-b)
+ chunk=nh-b;
+
+// SHAVEmake_a_curve(
+// pass, hairGroup, hair, numSegs, &wf, &curveInfo
+// );
+ list_total=0;
+ for (c=b;c<b+chunk;c++)
+ {
+ inlist[list_total]=c;
+ list_total++;
+ }
+
+ MTbunch_of_hairs(list_total, inlist,hairGroup,pass, &wf,&curveInfo);
+
+ int face;
+
+ for (face = 0; face < wf.totalfaces; face++)
+ {
+ //
+ // If this is spline hair, then the first two
+ // weights in the barycentric coords in
+ // curveInfo are the UV coords we want.
+ //
+ float u;
+ float v;
+
+// if (theShaveNode->getHairGroup() == 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