aboutsummaryrefslogtreecommitdiff
path: root/mayaPlug/shaveRender.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/shaveRender.cpp
downloadarchived-shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.tar.xz
archived-shave-and-a-haircut-bd0027e737c6512397f841c22786274ed74b927f.zip
Adding Shave-and-a-Haircut 9.6
Diffstat (limited to 'mayaPlug/shaveRender.cpp')
-rw-r--r--mayaPlug/shaveRender.cpp2504
1 files changed, 2504 insertions, 0 deletions
diff --git a/mayaPlug/shaveRender.cpp b/mayaPlug/shaveRender.cpp
new file mode 100644
index 0000000..a005cf2
--- /dev/null
+++ b/mayaPlug/shaveRender.cpp
@@ -0,0 +1,2504 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+#include <maya/MAngle.h>
+#include <maya/MAnimControl.h>
+#include <maya/MColorArray.h>
+#include <maya/MDagPath.h>
+#include <maya/MDGMessage.h>
+#include <maya/MFileIO.h>
+#include <maya/MFnCamera.h>
+#include <maya/MFnDagNode.h>
+#include <maya/MFnLight.h>
+#include <maya/MFnMesh.h>
+#include <maya/MFnSpotLight.h>
+#include <maya/MFnTypedAttribute.h>
+#include <maya/MIntArray.h>
+#include <maya/MItDag.h>
+#include <maya/MItDependencyNodes.h>
+#include <maya/MMatrix.h>
+#include <maya/MPlug.h>
+#include <maya/MPlugArray.h>
+#include <maya/MPoint.h>
+#include <maya/MSelectionList.h>
+#include <maya/MString.h>
+#include <maya/MStringArray.h>
+#include <maya/MVector.h>
+
+#include "shaveCheckObjectVisibility.h"
+#include "shaveDebug.h"
+#include "shaveGlobals.h"
+#include "shaveHairShape.h"
+#include "shaveIO.h"
+#include "shaveMaya.h"
+#include "shaveMayaRenderer.h"
+#include "shaveObjExporter.h"
+#include "shaveRender.h"
+#include "shaveRenderCallback.h"
+#include "shaveRenderer.h"
+#include "shaveSDK.h"
+#include "shaveTextureStore.h"
+#include "shaveUtil.h"
+#include "shaveVertexShader.h"
+#include "shaveVrayRenderer.h"
+#include "shaveXPM.h"
+
+
+#ifndef M_PI
+#define M_PI 3.14159926535
+#endif
+
+bool shaveRenderCancelled = false;
+bool shaveEnableProgressBar = true;
+
+
+//
+// Static class members.
+//
+bool shaveRender::mCallbacksSet = false;
+bool shaveRender::mExportActive = false;
+unsigned shaveRender::mExportFileCompression = 0;
+unsigned shaveRender::mExportFileFramePadding = 0;
+MString shaveRender::mExportFileName = "";
+bool shaveRender::mExportFilePerFrame = false;
+
+shaveConstant::FileNameFormat
+ shaveRender::mExportFileNameFormat = shaveConstant::kNoFileNameFormat;
+
+shaveGlobals::Globals shaveRender::mFrameGlobals;
+
+shaveConstant::RenderMode shaveRender::mHairRenderMode = shaveConstant::kNoRender;
+MObjectArray shaveRender::mHiddenNodes;
+bool shaveRender::mRenderInstances = true;
+
+bool shaveRender::mNeedVertexColours = false;
+shaveConstant::ShadowSource
+ shaveRender::mNormalShadows = shaveConstant::kNoShadows;
+
+shaveRenderer* shaveRender::mRenderer = NULL;
+shaveRender::RenderState shaveRender::mRenderState = shaveRender::kRenderNone;
+shaveRender::SceneInfo shaveRender::mSceneInfo;
+MObjectArray shaveRender::mTemplatedNodes;
+
+MCallbackId shaveRender::mAfterFrameRenderCallback;
+MCallbackId shaveRender::mAfterRenderCallback;
+MCallbackId shaveRender::mBeforeFrameRenderCallback;
+MCallbackId shaveRender::mBeforeRenderCallback;
+MCallbackId shaveRender::mRenderInterruptedCallback;
+MCallbackId shaveRender::mTimeChangeCallback;
+shaveRenderCallback* shaveRender::mShaveCallback = NULL;
+
+//
+// Normally, 'mFirstFrame' will be set true in renderStart() and false in
+// frameEnd(). This ensures that it will be true during the first frame
+// and false during subsequent frames within the same render.
+//
+// However, Shave API calls don't go through renderStart() and frameEnd().
+// Since the Shave API currently just does a single frame at a time,
+// 'mFirstFrame' should always be true.
+//
+bool shaveRender::mFirstFrame = true;
+
+
+void shaveRender::initSceneInfo(float currentFrame, bool allocateBuffers)
+{
+ if (mFrameGlobals.verbose) cerr << "setting up render." << endl;
+
+ static int MBAAQ[] = {1,3,8,15,23};
+
+ shaveRenderCallback::setGeomRender(false);
+
+ mSceneInfo.shaveRenderPixels = NULL;
+ mSceneInfo.shaveZBuffer = NULL;
+
+ if (allocateBuffers)
+ {
+ mSceneInfo.shaveRenderPixels = (Pixel*)malloc(
+ (shaveMaya::imageWidth+1) *
+ (shaveMaya::imageHeight+1) *
+ sizeof(Pixel)
+ );
+
+ if (!mSceneInfo.shaveRenderPixels)
+ {
+ MGlobal::displayError(
+ "Shave Render: not enough memory available for pixel buffer."
+ );
+
+ shaveRenderCancelled = true;
+
+ return;
+ }
+
+ mSceneInfo.shaveZBuffer = (float *)malloc(
+ (shaveMaya::imageWidth+1) *
+ (shaveMaya::imageHeight+1) *\
+ sizeof(float)
+ );
+
+ if (!mSceneInfo.shaveZBuffer)
+ {
+ MGlobal::displayError(
+ "Shave Render: not enough memory available for depth buffer."
+ );
+
+ shaveRenderCancelled = true;
+
+ return;
+ }
+ }
+
+ mSceneInfo.aa = MBAAQ[renderQualityGlob];
+ mSceneInfo.currentFrame = currentFrame;
+ mSceneInfo.haveTracedLights = false;
+ mSceneInfo.height = shaveMaya::imageHeight;
+ mSceneInfo.shaveTraceInitialized= false;
+ mSceneInfo.width = shaveMaya::imageWidth;
+
+ return;
+}
+
+
+void shaveRender::doShadows()
+{
+ //
+ // Note that here we check doHairShadowsGlob rather than checking the
+ // hair and instance shadow sources. This is because even if all the
+ // shadow sources are set to kNoShadows, if doHairShadowsGlob is true
+ // then we still want self-shadowing.
+ //
+ if (doHairShadowsGlob)
+ {
+ int renderReturnVal = 0;
+
+ shadowRender = true;
+
+ if (nativeIlluminationGlob)
+ {
+ //
+ // With native illumination, Maya will be rendering the shadows
+ // for those objects which are actually in the scene, so Shave
+ // doesn't have to.
+ //
+ // The only use that SHAVErender_shadows() makes of the WFTYPEs
+ // that we pass down to it is to render shadows for the
+ // geometry that they contain. So by passing down empty
+ // WFTYPEs, we can prevent it from redoing the shadows which
+ // Maya has already done.
+ //
+ WFTYPE empty;
+
+ init_geomWF(&empty);
+
+ renderReturnVal = SHAVErender_shadows(&empty, &empty, 1, 1);
+ }
+ else
+ {
+ renderReturnVal = SHAVErender_shadows(
+ &mSceneInfo.shutterOpenShadowScene,
+ (shaveMaya::doMotionBlur ?
+ &mSceneInfo.shutterCloseShadowScene :
+ &mSceneInfo.shutterOpenShadowScene),
+ 1,
+ 1
+ );
+ }
+
+ //
+ // If there were any traced lights, then the shadow render will have
+ // called SHAVEtrace_init. It's an expensive operation, so let's
+ // set a flag so that we know not to do it in other places, such as
+ // where we set up for reflections and refractions.
+ //
+ mSceneInfo.shaveTraceInitialized |= mSceneInfo.haveTracedLights;
+
+ if (shaveRenderCancelled)
+ {
+ MGlobal::displayInfo("Shave render cancelled by user.");
+ }
+ else if (renderReturnVal != 0)
+ {
+ cerr << "Shave light render failed, return value is "
+ << renderReturnVal << endl;
+
+ shaveRenderCancelled = true;
+ }
+ else if (mFrameGlobals.verbose)
+ {
+ cerr << "Done with Shave light render." << endl;
+ }
+
+ shadowRender = false;
+
+ MGlobal::executeCommand("shave_closeProgressBar()");
+ }
+
+ return;
+}
+
+
+void shaveRender::bufferRender(
+ shaveConstant::ShutterState shutter,
+ const MDagPath& cameraPath,
+ float frame,
+ bool needBuffers,
+ bool doShadowRender,
+ bool doCameraRender,
+ bool doOcclusions
+)
+{
+ //
+ // Do the initialization on shutter open so that everything's there for
+ // the rest of the render.
+ //
+ if (((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ && !shaveRenderCancelled)
+ {
+ initSceneInfo(frame, needBuffers);
+ }
+
+ //
+ // If this is shutter close, then the actual frame time will be the
+ // average of the current frame and what was saved into
+ // mSceneInfo.currentFrame on shutter open. So calculate the real frame
+ // time.
+ //
+ if (shutter == shaveConstant::kShutterClose)
+ mSceneInfo.currentFrame = (mSceneInfo.currentFrame + frame) / 2.0f;
+
+ if (!shaveRenderCancelled
+ && (doCameraRender || doShadowRender || doOcclusions))
+ {
+ buildOcclusionLists(shutter);
+ }
+
+ if (!shaveRenderCancelled) doCamera(cameraPath, shutter, mSceneInfo);
+
+ //
+ // Lights are not motion-blurred, so we only do them on shutter open.
+ //
+ if (((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ && !shaveRenderCancelled)
+ {
+ doLights();
+ }
+
+ //
+ // Shadows & rendering are done on shutter close.
+ //
+ if ((shutter == shaveConstant::kShutterClose)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+ if (!shaveRenderCancelled && doShadowRender)
+ doShadows();
+
+ if (!shaveRenderCancelled && doCameraRender)
+ {
+ if (renderCameraView(cameraPath) != 0)
+ shaveRenderCancelled = true;
+ }
+ }
+
+ //
+ // If something went wrong, clean up. Otherwise cleanup will be done
+ // by shaveRenderCallback::renderCallback(), once it's through with the
+ // compositing.
+ //
+ if (shaveRenderCancelled)
+ {
+ bufferRenderCleanup();
+ }
+
+ return;
+}
+
+
+void shaveRender::bufferRenderCleanup()
+{
+ mSceneInfo.bufferValid = false;
+
+ if (mSceneInfo.shaveRenderPixels)
+ {
+ free(mSceneInfo.shaveRenderPixels);
+ mSceneInfo.shaveRenderPixels = NULL;
+ }
+
+ if (mSceneInfo.shaveZBuffer)
+ {
+ free(mSceneInfo.shaveZBuffer);
+ mSceneInfo.shaveZBuffer = NULL;
+ }
+
+ SHAVEclear_stack();
+
+ return;
+}
+
+
+void shaveRender::geomRender(MObjectArray& shaveHairShapes)
+{
+ //
+ // Let shaveRenderCallback know that this is a geom render.
+ //
+ shaveRenderCallback::setGeomRender(true);
+
+ //
+ // This code used to only set up texture lookups if there were vertex
+ // shaders in the scene, the logic being that they would need updated
+ // vertex colours. However, we still need to evaluate things such as
+ // cutlength and density which might be textured. So we need to do the
+ // lookups *every* time.
+ //
+#ifdef PER_NODE_TEXLOOKUP
+ initTexInfoLookup2(shaveHairShapes, "", mFrameGlobals.verbose);
+#else
+ initTexInfoLookup(shaveHairShapes, "", mFrameGlobals.verbose);
+#endif
+ //
+ // Force each shaveHairShape to update its outputMesh. This is needed for
+ // two reasons:
+ //
+ // 1) When motion blur is off and we're rendering just a single frame,
+ // the last time change will have occurred just prior to
+ // renderStart(). So the outputMeshes won't have been updated since
+ // renderStart() set the mRenderAllNormalHairs/mRenderAllInstanceHairs
+ // flags, which will affect the output meshes.
+ //
+ // 2) We just now built the proper texture lookup for this frame, so
+ // the meshes need to pick up new values from that.
+ //
+ // %%% We could optimize this by only doing the loop below if motion
+ // blur is off and this is the first frame to be rendered, or if
+ // mNeedVertexColours is true.
+ //
+ unsigned int i;
+ unsigned int numShaveNodes = shaveHairShapes.length();
+
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ MFnDependencyNode nodeFn(shaveHairShapes[i]);
+ shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode();
+
+ //
+ // Simply grabbing the hairNode will cause the output mesh to
+ // recompute.
+ //
+ theShaveNode->getHairNode();
+ }
+
+ //
+ // NOTE: We no longer need the texture lookup tables at this point
+ // because any info we needed from them is now embedded in the
+ // output meshes built by the getHairNode() calls in the loop
+ // above. So it's not a problem if someone else now calls
+ // initTexInfoLookup() with a different set of shaveHairShapes.
+ //
+
+ return;
+}
+
+
+//
+// Called at the start of each frame render. Note that for normal Maya
+// renders we get called from a callback, but some renderers might call
+// us via the shaveRender MEL command.
+//
+void shaveRender::frameStart(void * clientData)
+{
+ //
+ // We don't support IPR.
+ //
+ if (isIPRActive()) return;
+
+ mRenderState = kRenderingFrame;
+ mSceneInfo.bufferValid = false;
+ shaveRenderCancelled = false;
+
+ // We are transitioning from using a bunch of global variables to
+ // using a shaveGlobals::Globals instance to pass around the
+ // shaveGlobals values for the frame. During the transition period we
+ // have to continue to support both.
+ shaveGlobals::getGlobals();
+ saveFrameGlobals();
+
+ shaveMaya::getRenderGlobals();
+
+ SHAVEset_verbose(mFrameGlobals.verbose ? 1 : 0);
+ SHAVEclear_stack();
+
+ //
+ // Do renderer-specific setup.
+ //
+ if (mRenderer) mRenderer->frameStart(mFrameGlobals);
+
+ return;
+}
+
+
+//
+// Called at the end of each frame render. Note that for normal Maya
+// renders we get called from a callback but some renderers might call
+// us via the shaveRender MEL command.
+//
+void shaveRender::frameEnd(void * clientData)
+{
+ //
+ // We don't support IPR.
+ //
+ if (isIPRActive()) return;
+
+ //
+ // Do renderer-specific cleanup.
+ //
+ if (mRenderer) mRenderer->frameEnd(mFrameGlobals);
+
+ if (shaveHairShape::getNumShaveNodes() > 0)
+ {
+ SHAVEclear_stack();
+ }
+
+ mRenderState = kRendering;
+ mFirstFrame = false;
+
+ return;
+}
+
+
+
+
+/*************************************************************************
+ * LIGHTS
+ *************************************************************************/
+void shaveRender::doLights()
+{
+ MStatus st;
+
+ MDagPath lightPath;
+ MTransformationMatrix lightWorldMatrix;
+ MVector lightTranslation;
+ double rot[3];
+ VERT shaveDirection;
+
+ MTransformationMatrix::RotationOrder order = MTransformationMatrix::kXYZ;
+
+ //
+ // Get our lights
+ //
+ shaveUtil::buildLightList();
+
+ //
+ // Loop through lights, register them with Shave
+ //
+ unsigned i;
+ unsigned length = (unsigned int)shaveUtil::globalLightList.size();
+
+ for (i = 0; i < length; i++)
+ {
+ lightPath = shaveUtil::globalLightList[i].path;
+
+ MFnLight lightFn(lightPath);
+ MColor lightColor = lightFn.color (&st);
+ float lightIntensity = lightFn.intensity(&st);
+
+ if (nativeIlluminationGlob)
+ {
+ lightColor = MColor(1,1,1);
+ lightIntensity = 1;
+ }
+
+ //
+ // Look for the per-light shadow parameters. Use defaults if
+ // this light doesn't have the parameters.
+ //
+ MPlug plug;
+ float shadowFuzz = 8.0f;
+ short shadowQuality = shaveGlobals::kHighShadowQuality;
+ int shadowRes = 450;
+ bool shadowTrace = false;
+
+ if (lightPath.hasFn(MFn::kSpotLight)) shadowRes = 600;
+
+ plug = lightFn.findPlug("shaveShadowFuzz", &st);
+ if (st) plug.getValue(shadowFuzz);
+
+ plug = lightFn.findPlug("shaveShadowResolution", &st);
+ if (st) plug.getValue(shadowRes);
+
+ plug = lightFn.findPlug("shaveShadowTrace", &st);
+ if (st) plug.getValue(shadowTrace);
+
+ //
+ // Get the position of the light, in World space.
+ //
+ lightWorldMatrix = lightPath.inclusiveMatrix();
+ lightTranslation = lightWorldMatrix.translation(MSpace::kWorld);
+
+ VERT shaveLightPosition;
+ shaveLightPosition.x = (float)lightTranslation.x;
+ shaveLightPosition.y = (float)lightTranslation.y;
+ shaveLightPosition.z = (float)lightTranslation.z;
+
+ MColor shadowColour = lightFn.shadowColor();
+
+ //
+ // Handle spotlights.
+ //
+ if (lightPath.hasFn(MFn::kSpotLight))
+ {
+ //
+ // DIRECTION VECTOR
+ //
+ lightWorldMatrix.getRotation(rot, order);
+
+ MVector mayaDirectionVector = MVector::zAxis.rotateBy(rot, order);
+
+ shaveDirection.x = (float) -mayaDirectionVector.x;
+ shaveDirection.y = (float) -mayaDirectionVector.y;
+ shaveDirection.z = (float) -mayaDirectionVector.z;
+
+ //
+ // UP VECTOR
+ //
+ MVector mayaUpVector = MVector::yAxis.rotateBy(rot, order);
+
+ VERT shaveUpVector;
+ shaveUpVector.x = (float) mayaUpVector.x;
+ shaveUpVector.y = (float) mayaUpVector.y;
+ shaveUpVector.z = (float) mayaUpVector.z;
+
+ //
+ // Cone Angle
+ //
+ const float kRad2degrees = (float)(180.0 / M_PI);
+ MFnSpotLight spotFn(lightPath);
+
+ float lightConeAngle = (float)
+ (spotFn.coneAngle() + spotFn.penumbraAngle() * 2.0);
+ lightConeAngle = ((float)lightConeAngle) * kRad2degrees;
+
+ Matrix shaveLightMatrix;
+
+ SHAVEmake_view_matrix(
+ shaveLightMatrix,
+ shaveUpVector,
+ shaveDirection,
+ shaveLightPosition,
+ lightConeAngle
+ );
+
+ shaveUtil::globalLightList[i].minId =
+ SHAVEadd_light(
+ lightColor.r*lightIntensity,
+ lightColor.g*lightIntensity,
+ lightColor.b*lightIntensity,
+ shaveLightMatrix,
+ shaveLightPosition,
+ shadowRes,
+ shadowRes,
+ 1.0,
+ lightConeAngle,
+ shadowFuzz,
+ shadowQuality,
+ (float)shadowColour.r,
+ (float)shadowColour.g,
+ (float)shadowColour.b,
+ (shadowTrace ? 1 : 0),
+ 0.01f
+ );
+ shaveUtil::globalLightList[i].maxId = shaveUtil::globalLightList[i].minId;
+ }
+ //
+ // Handle all other light types.
+ //
+ else
+ {
+ shaveUtil::globalLightList[i].minId =
+ SHAVEadd_point_light(
+ lightColor.r*lightIntensity,
+ lightColor.g*lightIntensity,
+ lightColor.b*lightIntensity,
+ shaveLightPosition,
+ shadowRes,
+ shadowRes,
+ shadowFuzz,
+ shadowQuality,
+ (float)shadowColour.r,
+ (float)shadowColour.g,
+ (float)shadowColour.b,
+ (shadowTrace ? 1 : 0),
+ 0.01f
+ );
+
+ //
+ // Internally, Shave generates 6 lights for each non-spotlight
+ // we register using the above function.
+ //
+ shaveUtil::globalLightList[i].maxId = shaveUtil::globalLightList[i].minId + 5;
+ }
+
+ //
+ // Keep track of if we have any traced lights.
+ //
+ if (shadowTrace) mSceneInfo.haveTracedLights = true;
+ }
+
+ return;
+}
+
+
+/*************************************************************************
+ * CAMERA
+ *
+ * A *LOT* of voodoo here. Maya Z and Shave Z are opposites. So we flip
+ * it on our end. This affects the handed-ness of rotations along
+ * X and Y (but not Z). Maya's FOV is horizontal, Shave's camera is vertical.
+ * Maya has the concept of a camera's viewing fustrum (aspect, FOV) being
+ * different than the rendering (globals) fustrum. This is because Maya looks
+ * at the FILM aperture (width & height) for calculating its camera FOV and
+ * aspect, while Shave uses the rendering globals. Can't we all just get along?
+ *************************************************************************/
+
+
+
+/*************************************************************************
+ * SCENE GEOMETRY
+ *************************************************************************/
+void shaveRender::buildOcclusionLists(shaveConstant::ShutterState shutter)
+{
+ static bool firstTime = true;
+
+ if (firstTime)
+ {
+ init_geomWF(&mSceneInfo.shutterOpenCamScene);
+ init_geomWF(&mSceneInfo.shutterCloseCamScene);
+ init_geomWF(&mSceneInfo.shutterOpenShadowScene);
+ init_geomWF(&mSceneInfo.shutterCloseShadowScene);
+ firstTime = false;
+ }
+
+ static MDagPathArray camOcclusions;
+ static MDagPathArray shadowOcclusions;
+ shaveObjTranslator x;
+
+ if ((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+ camOcclusions.clear();
+ shadowOcclusions.clear();
+
+ //
+ // When doing 2D compositing, we ignore transparency: the user must
+ // switch to 3D if zie wants to see hair through glass.
+ //
+ bool ignoreTransparency = doShaveComp2dGlob;
+ unsigned int i;
+ MStringArray occlusionNames;
+ MDagPath occlusionPath;
+ MSelectionList tmpSel;
+
+ if (shaveSceneObjectsGlob == "all")
+ {
+ MGlobal::executeCommand("ls -g", occlusionNames);
+
+ for (i = 0; i < occlusionNames.length(); i++)
+ {
+ tmpSel.clear();
+ tmpSel.add(occlusionNames[i]);
+ tmpSel.getDagPath(0, occlusionPath);
+
+ //
+ // If native illumination is on, then we only do
+ // occlusions for the cam pass, not the shadow pass.
+ //
+ if (nativeIlluminationGlob)
+ {
+ if (areObjectAndParentsVisible(
+ occlusionPath, true, false, ignoreTransparency))
+ {
+ camOcclusions.append(occlusionPath);
+ }
+ }
+ else
+ {
+ //
+ // For shadows, we ignore transparency and primary
+ // visibility.
+ //
+ if (areObjectAndParentsVisible(
+ occlusionPath, true, true, true))
+ {
+ shadowOcclusions.append(occlusionPath);
+
+ //
+ // For the cam pass, we don't want objects whose
+ // primary visibility is off or which have any
+ // transparency to be treated as occlusions.
+ //
+ if (areObjectAndParentsVisible(
+ occlusionPath, true, false, ignoreTransparency))
+ {
+ camOcclusions.append(occlusionPath);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ shaveSceneObjectsGlob.split(' ', occlusionNames);
+
+ for (i = 0; i < occlusionNames.length(); i++)
+ {
+ tmpSel.clear();
+ tmpSel.add(occlusionNames[i]);
+ tmpSel.getDagPath(0, occlusionPath);
+
+ camOcclusions.append(occlusionPath);
+
+ if (!nativeIlluminationGlob)
+ shadowOcclusions.append(occlusionPath);
+ }
+ }
+
+ if (mFrameGlobals.verbose) cerr << "built occlusion list." << endl;
+
+ x.exportOcclusion(camOcclusions, &mSceneInfo.shutterOpenCamScene);
+
+ if (!nativeIlluminationGlob)
+ {
+ x.exportOcclusion(
+ shadowOcclusions, &mSceneInfo.shutterOpenShadowScene
+ );
+ }
+ }
+
+ //
+ // If there's no motion blur then shutter open and close are identical
+ // and we can just use the same data for both. So we only need to
+ // gather occlusions for shutter close if motion blur is on.
+ //
+ if ((shaveMaya::doMotionBlur)
+ && ((shutter == shaveConstant::kShutterClose)
+ || (shutter == shaveConstant::kShutterBoth)))
+ {
+ x.exportOcclusion(camOcclusions, &mSceneInfo.shutterCloseCamScene);
+
+ if (!nativeIlluminationGlob)
+ {
+ x.exportOcclusion(
+ shadowOcclusions, &mSceneInfo.shutterCloseShadowScene
+ );
+ }
+ }
+
+ return;
+}
+
+
+/*
+ * Modify our color projection map (for hair-to-object shadows) with the
+ * color of the light. I thought intensity would also need to be
+ * multiplied in, but I was wrong.
+ */
+void shaveRender::doColorCorrection (MColor colorMod, short mapWidth, short mapHeight, unsigned char* mapBuffer)
+{
+
+ for (int x=0; x < mapWidth*mapHeight*4; x += 4)
+ {
+ // RED
+ mapBuffer[x+0] = (unsigned char) ((float) mapBuffer[x+0] * colorMod.r );
+ //GREEN
+ mapBuffer[x+1] = (unsigned char) ((float) mapBuffer[x+1] * colorMod.g );
+ //BLUE
+ mapBuffer[x+2] = (unsigned char) ((float) mapBuffer[x+2] * colorMod.b );
+ }
+
+}
+
+/*
+ * Correct black levels, to control hair-on-skin shadow density.
+ */
+void shaveRender::doDensityCorrection (float density, short mapWidth, short mapHeight, unsigned char* mapBuffer)
+{
+ /*
+ * Tint values towards white.
+ * Density range is 0.0-1.0
+ */
+ for (int x=0; x < mapWidth*mapHeight*4; x += 4)
+ {
+ mapBuffer[x+0] = (unsigned char)
+ ((float)mapBuffer[x+0] * density + 255.*(1.0-density));
+
+ mapBuffer[x+1] = (unsigned char)
+ ((float)mapBuffer[x+1] * density + 255.*(1.0-density));
+
+ mapBuffer[x+2] = (unsigned char)
+ ((float)mapBuffer[x+2] * density + 255.*(1.0-density));
+ }
+}
+
+
+//
+// Called at the start of a render of a group of one or more frames. Note
+// that for normal Maya renders we get called from a callback but some
+// renderers may call us via the shaveRender MEL command.
+//
+void shaveRender::renderStart(void* clientData)
+{
+ //
+ // We don't support IPR.
+ //
+ if (isIPRActive()) return;
+
+ //
+ // If a render crashes (e.g. due to insufficient memory) Maya won't
+ // send us the renderEnd event. So let's make sure that there isn't
+ // anything lying around from a previous render.
+ //
+ bufferRenderCleanup();
+
+ mRenderState = kRendering;
+ mFirstFrame = true;
+
+ //
+ // Make sure we start out with a fresh renderer instance.
+ //
+ if (mRenderer != NULL)
+ {
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+
+ getRenderer();
+
+ //
+ // Make sure that the display nodes are all in the same render layer as
+ // at least on of their growth surfaces.
+ //
+ MGlobal::executeCommand("shave_assignRenderLayers");
+
+
+ //*******************************************************************
+ //
+ // Get The Shave Nodes
+ //
+ //*******************************************************************
+
+ shaveGlobals::getGlobals();
+
+ MObjectArray shaveHairShapes;
+ shaveUtil::getShaveNodes(shaveHairShapes);
+
+ unsigned int numShaveNodes = shaveHairShapes.length();
+
+
+ //*******************************************************************
+ //
+ // Get The Render Modes
+ //
+ //*******************************************************************
+
+ mHairRenderMode = mRenderer->getFilteredRenderMode(
+ shaveHairShapes, &mRenderInstances
+ );
+
+ //
+ // Go no further if there's nothing to render.
+ //
+ if ((mHairRenderMode == shaveConstant::kNoRender) && !mRenderInstances)
+ {
+ delete mRenderer;
+ mRenderer = NULL;
+
+ return;
+ }
+
+
+ //*******************************************************************
+ //
+ // Determine Shadow Sources
+ //
+ //*******************************************************************
+
+ //
+ // Let's figure out where our shadows are coming from.
+ //
+ mNormalShadows = mRenderer->getShadowSource();
+
+
+ //*******************************************************************
+ //
+ // Set Templating & Visibility
+ //
+ //*******************************************************************
+
+ //
+ // We may not want display geometry showing up in the render, or we
+ // might only want it showing up in secondary effects, such as shadows,
+ // so ask the renderer what we should do with it.
+ //
+ // Note that we need to do it now, before the first call to frameStart,
+ // because we want the full geometry available at the start of the
+ // render, when the renderer does its automatic clipping plane
+ // calculations. Otherwise it might end up clipping some of our
+ // geometry later on.
+ //
+ bool hairPrimaryVisibility;
+ bool hairSecondaryVisibility;
+ bool instancePrimaryVisibility;
+ bool instanceSecondaryVisibility;
+
+ mRenderer->getGeomVisibility(
+ hairPrimaryVisibility,
+ hairSecondaryVisibility,
+ instancePrimaryVisibility,
+ instanceSecondaryVisibility
+ );
+
+ unsigned int i;
+ bool shouldBeTemplated;
+ bool shouldBeVisible;
+
+ mHiddenNodes.clear();
+ mTemplatedNodes.clear();
+
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ //
+ // Find the shaveHairShape's display node.
+ //
+ MFnDependencyNode nodeFn(shaveHairShapes[i]);
+ shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode();
+
+ MObject displayNode = nodePtr->getDisplayShape();
+
+ if (!displayNode.isNull())
+ {
+ //
+ // Set the display node's templating, as appropriate.
+ //
+ nodeFn.setObject(displayNode);
+
+ if (nodePtr->isInstanced())
+ {
+ shouldBeVisible = instancePrimaryVisibility;
+ shouldBeTemplated = (!instancePrimaryVisibility
+ && !instanceSecondaryVisibility);
+ }
+ else
+ {
+ shouldBeVisible = hairPrimaryVisibility;
+ shouldBeTemplated = (!hairPrimaryVisibility
+ && !hairSecondaryVisibility);
+ }
+
+ bool isTemplated = false;
+ bool isVisible = false;
+ MPlug plug;
+
+ plug = nodeFn.findPlug("template");
+ plug.getValue(isTemplated);
+
+ if (isTemplated != shouldBeTemplated)
+ {
+ plug.setValue(shouldBeTemplated);
+ mTemplatedNodes.append(displayNode);
+ }
+
+ //
+ // If the display node is not templated we may still need to
+ // adjust its primary visibility.
+ //
+ if (!shouldBeTemplated)
+ {
+ plug = nodeFn.findPlug("primaryVisibility");
+ plug.getValue(isVisible);
+
+ if (isVisible != shouldBeVisible)
+ {
+ plug.setValue(shouldBeVisible);
+ mHiddenNodes.append(displayNode);
+ }
+ }
+ }
+
+ //
+ // Let the shaveHairShape know about the new render state.
+ //
+ MPlug renderStatePlug(
+ shaveHairShapes[i], shaveHairShape::renderStateAttr
+ );
+
+ renderStatePlug.setValue(mRenderState);
+ }
+
+
+ //*******************************************************************
+ //
+ // Set Up Vertex Shaders
+ //
+ //*******************************************************************
+
+ //
+ // If we're using geometry for hair, for shadows, or for instances,
+ // then we'll need to create/update vertex shaders for those shaveHairShapes
+ // with their geom shader override turned on.
+ //
+ if ((mHairRenderMode == shaveConstant::kGeometryRender)
+ || (mNormalShadows == shaveConstant::kMayaGeomShadows)
+ || mRenderInstances)
+ {
+ MGlobal::executeCommand("shave_prepareVertexShaders");
+ }
+
+ //
+ // Do we need to set up vertex colours?
+ //
+ mNeedVertexColours = mRenderer->needVertexColours();
+
+
+ //*******************************************************************
+ //
+ // Turn Off Automatic Clipping Planes
+ //
+ //*******************************************************************
+
+ //
+ // Automatic clipping planes cause us all kinds of problems because
+ // they're evaluated before frameStart() gets called, which means that
+ // any geometry created on a per-frame basis (e.g. the camera's render
+ // cone) may end up getting clipped.
+ //
+ // So let's just turn it off. We turn it off on all cameras just in
+ // case the render camera changes in mid-sequence.
+ //
+ mSceneInfo.autoClipCameras.clear();
+
+ if (numShaveNodes > 0)
+ {
+ MItDag dagIter(MItDag::kDepthFirst, MFn::kCamera);
+ AutoClipInfo camInfo;
+
+ camInfo.nearClipChanged = false;
+ camInfo.farClipChanged = false;
+
+ for (; !dagIter.isDone(); dagIter.next())
+ {
+ dagIter.getPath(camInfo.cameraPath);
+
+ MFnCamera cameraFn(camInfo.cameraPath);
+ MPlug plug = cameraFn.findPlug("bestFitClippingPlanes");
+ bool autoClipOn;
+
+ plug.getValue(autoClipOn);
+
+ if (autoClipOn)
+ {
+ plug.setValue(false);
+
+ camInfo.origNearClip = cameraFn.nearClippingPlane();
+ camInfo.origFarClip = cameraFn.farClippingPlane();
+
+ //
+ // We shouldn't be doing this here since we don't know
+ // the render cam yet, but Joe doesn't want to pay to do
+ // it properly, so we're stuck with issuing spurious
+ // warnings.
+ //
+ if (!cameraFn.isOrtho())
+ {
+ float diff;
+
+ diff = (float)(camInfo.origFarClip - camInfo.origNearClip);
+
+ if (diff == camInfo.origFarClip)
+ {
+ MGlobal::displayWarning(
+ "Shave does not support autoclip. The clipping"
+ " range is larger than Maya can support. Your"
+ " surfaces may appear black as a result."
+ );
+ }
+ }
+
+ mSceneInfo.autoClipCameras.push_front(camInfo);
+ }
+ }
+ }
+
+ //
+ // Do any renderer-specific setup.
+ //
+ mRenderer->renderStart();
+
+ //
+ // Setup up a callback for time changes.
+ //
+ mTimeChangeCallback = MDGMessage::addForceUpdateCallback(timeChanged);
+
+ return;
+}
+
+
+//
+// Called at the end of a render of a group of one or more frames. Note
+// that for normal Maya renders we get called from a callback but some
+// renderers may call us via the shaveRender MEL command.
+//
+void shaveRender::renderEnd(void* clientData)
+{
+ //
+ // We don't support IPR.
+ //
+ if (isIPRActive()) return;
+
+ mRenderState = kRenderNone;
+
+ //
+ // If there's nothing to render, then there's nothing to undo.
+ //
+ if ((mHairRenderMode == shaveConstant::kNoRender) && !mRenderInstances)
+ {
+ return;
+ }
+
+ MDGMessage::removeCallback(mTimeChangeCallback);
+
+ //
+ // Do renderer-specific cleanup.
+ //
+ if (mRenderer) mRenderer->renderEnd();
+
+ //
+ // Restore automatic clipping planes to those cameras which had them
+ // turned on.
+ //
+ MPlug plug;
+ AutoClipListIter iter;
+
+ for (iter = mSceneInfo.autoClipCameras.begin();
+ iter != mSceneInfo.autoClipCameras.end();
+ iter++)
+ {
+ MFnCamera cameraFn(iter->cameraPath);
+
+ if (iter->nearClipChanged)
+ cameraFn.setNearClippingPlane(iter->origNearClip);
+
+ if (iter->farClipChanged)
+ cameraFn.setFarClippingPlane(iter->origFarClip);
+
+ //
+ // If we turn the camera's auto clipping back on right now, either
+ // by setting its plug or executing a 'setAttr' command, then for
+ // some reason Maya will reset both of the camera's clipping planes
+ // to 0.1.
+ //
+ // But if we do it as a deferred command, then it retains the
+ // clipping planes.
+ //
+ // Just more of the magic of Maya...
+ //
+ MGlobal::executeCommand(
+ MString("evalDeferred \"setAttr ")
+ + iter->cameraPath.fullPathName()
+ + ".bestFitClippingPlanes true \""
+ );
+ }
+
+ mSceneInfo.autoClipCameras.clear();
+
+ shaveGlobals::getGlobals();
+
+ //
+ // Remove any vertex shaders we set up.
+ //
+ if ((mHairRenderMode == shaveConstant::kGeometryRender)
+ || (mNormalShadows == shaveConstant::kMayaGeomShadows)
+ || mRenderInstances)
+ {
+ MGlobal::executeCommand("shave_destroyVertexShaders");
+ }
+
+ //
+ // Toggle the templating of any display nodes which we changed,
+ // back to their original settings.
+ //
+ unsigned int i;
+ unsigned int numTemplatedNodes = mTemplatedNodes.length();
+
+ for (i = 0; i < numTemplatedNodes; i++)
+ {
+ MFnDependencyNode nodeFn(mTemplatedNodes[i]);
+ MPlug plug = nodeFn.findPlug("template");
+ bool isTemplated;
+
+ plug.getValue(isTemplated);
+ plug.setValue(!isTemplated);
+ }
+
+ mTemplatedNodes.clear();
+
+ //
+ // Toggle the primary visibility of any display nodes which we changed,
+ // back to their original settings.
+ //
+ unsigned int numHiddenNodes = mHiddenNodes.length();
+
+ for (i = 0; i < numHiddenNodes; i++)
+ {
+ MFnDependencyNode nodeFn(mHiddenNodes[i]);
+ MPlug plug = nodeFn.findPlug("primaryVisibility");
+ bool isHidden;
+
+ plug.getValue(isHidden);
+ plug.setValue(!isHidden);
+ }
+
+ mHiddenNodes.clear();
+
+ //
+ // Reset the first frame flag to true for the sake of Shave API calls.
+ //
+ // See the initialization of 'mFirstFrame' near the top of this file
+ // for more details on why we do this here.
+ //
+ mFirstFrame = true;
+
+ //
+ // Let the shaveHairShapes know about the new render state.
+ //
+ MObjectArray shaveHairShapes;
+ shaveUtil::getShaveNodes(shaveHairShapes);
+
+ unsigned int numShaveNodes = shaveHairShapes.length();
+
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ MPlug plug(shaveHairShapes[i], shaveHairShape::renderStateAttr);
+
+ plug.setValue(mRenderState);
+ }
+
+ //
+ // Get rid of the renderer instance.
+ //
+ delete mRenderer;
+ mRenderer = NULL;
+
+ return;
+}
+
+
+void shaveRender::renderInterrupted(void* clientData)
+{
+ //
+ // We don't support IPR.
+ //
+ if (isIPRActive()) return;
+
+ if (mRenderer) mRenderer->renderInterrupted();
+
+ if (mRenderState == kRendering)
+ renderEnd(NULL);
+ else if (mRenderState != kRenderNone)
+ {
+ frameEnd(NULL);
+ renderEnd(NULL);
+ }
+
+ return;
+}
+
+
+void shaveRender::setupCallbacks()
+{
+ if (!mCallbacksSet)
+ {
+ mBeforeRenderCallback =
+ MSceneMessage::addCallback(
+ MSceneMessage::kBeforeSoftwareRender,
+ renderStart
+ );
+
+ mAfterRenderCallback =
+ MSceneMessage::addCallback(
+ MSceneMessage::kAfterSoftwareRender,
+ renderEnd
+ );
+
+ mBeforeFrameRenderCallback =
+ MSceneMessage::addCallback(
+ MSceneMessage::kBeforeSoftwareFrameRender,
+ frameStart
+ );
+
+ mAfterFrameRenderCallback =
+ MSceneMessage::addCallback(
+ MSceneMessage::kAfterSoftwareFrameRender,
+ frameEnd
+ );
+
+ mRenderInterruptedCallback =
+ MSceneMessage::addCallback(
+ MSceneMessage::kSoftwareRenderInterrupted,
+ renderInterrupted
+ );
+
+ mCallbacksSet = true;
+ }
+}
+
+
+void shaveRender::cleanupCallbacks()
+{
+ if (mCallbacksSet)
+ {
+ MSceneMessage::removeCallback(mBeforeRenderCallback);
+ MSceneMessage::removeCallback(mAfterRenderCallback);
+ MSceneMessage::removeCallback(mBeforeFrameRenderCallback);
+ MSceneMessage::removeCallback(mAfterFrameRenderCallback);
+ MSceneMessage::removeCallback(mRenderInterruptedCallback);
+
+ mCallbacksSet = false;
+ }
+}
+
+
+// vlad|15Apr2010
+//
+bool shaveRender::rendererIsVray()
+{
+ MStatus status;
+ int isVray = false;
+
+ status = MGlobal::executeCommand("shave_rendererIsVray", isVray);
+
+ return (status && isVray);
+}
+
+bool shaveRender::rendererIsPrMan()
+{
+ MStatus status;
+ int isPrMan = 0;
+ status = MGlobal::executeCommand("shave_curRendererIsPrMan", isPrMan);
+
+ return (status && isPrMan);
+}
+
+
+int shaveRender::renderCameraView(const MDagPath& cameraPath)
+{
+ int renderReturnVal = 0;
+
+ shaveGlobals::getGlobals();
+
+ SceneInfo* shaveData = getSceneInfo();
+
+ if (doShaveCompsGlob || keepHairRenderFilesGlob)
+ {
+ shadowRender = false;
+
+ SHAVEcalibrate(0.0f, -.05f);
+// SHAVEcalibrate(-.25f,0.0f);
+
+ if (shaveMaya::doMotionBlur)
+ {
+ renderReturnVal = SHAVErender_cam(
+ &mSceneInfo.shutterOpenCamScene,
+ &mSceneInfo.shutterCloseCamScene,
+ mSceneInfo.aa,
+ (unsigned char*)mSceneInfo.shaveRenderPixels,
+ mSceneInfo.shaveZBuffer,
+ 0,
+ 0,
+ mSceneInfo.width-1,
+ mSceneInfo.height-1,
+ mSceneInfo.width,
+ mSceneInfo.height,
+ 3
+ );
+ }
+ else
+ {
+ renderReturnVal = SHAVErender_camNOBLUR(
+ &mSceneInfo.shutterOpenCamScene,
+ &mSceneInfo.shutterOpenCamScene,
+ mSceneInfo.aa,
+ (unsigned char*)mSceneInfo.shaveRenderPixels,
+ mSceneInfo.shaveZBuffer,
+ 0,
+ 0,
+ mSceneInfo.width-1,
+ mSceneInfo.height-1,
+ mSceneInfo.width,
+ mSceneInfo.height,
+ 3
+ );
+ }
+
+ if (mFrameGlobals.verbose)
+ cerr << "Pixel render returned: " << renderReturnVal << endl;
+
+ //
+ // Did this camera have automatic clipping planes on?
+ //
+ AutoClipListIter iter;
+
+ for (iter = mSceneInfo.autoClipCameras.begin();
+ iter != mSceneInfo.autoClipCameras.end();
+ iter++)
+ {
+ if (iter->cameraPath == cameraPath) break;
+ }
+
+ if (iter != mSceneInfo.autoClipCameras.end())
+ {
+ //
+ // Find the min and max depth values in the image.
+ //
+ bool gotDepth = false;
+ int i;
+ int numPixels = mSceneInfo.height * mSceneInfo.width;
+ float depth;
+ float minDepth = 0.0f;
+ float maxDepth = 0.0f;
+
+ for (i = 0; i < numPixels; i++)
+ {
+ depth = mSceneInfo.shaveZBuffer[i];
+
+ if (depth != SHAVE_FAR_CLIP)
+ {
+ if (!gotDepth)
+ {
+ minDepth = maxDepth = depth;
+ gotDepth = true;
+ }
+ else if (depth < minDepth)
+ minDepth = depth;
+ else if (depth > maxDepth)
+ maxDepth = depth;
+ }
+ }
+
+ if (gotDepth)
+ {
+ //
+ // We want to make sure that the camera's clipping planes
+ // catch all of our generated pixels. So if a pixel lies
+ // outside of the volume bounded by the near and far
+ // clipping planes, then we will need to move them so that
+ // the pixel is included.
+ //
+ // Shave's depths represent linear distance from the
+ // camera while Maya's clip values are the Z coordinates of
+ // the clipping planes in camera space.
+ //
+ // Now let's look at two points in camera space: [0, 0, 5]
+ // and [4, 2, 4]. The first point is 5 units away from the
+ // camera while the second point is 6 units away from the
+ // camera. So the first point is closer to the camera.
+ //
+ // However, if we set Maya's near clipping plane to a value
+ // of 5, it will still clip the second point. That's
+ // because while the second point is farther from the
+ // camera than the first, it is still closer along the
+ // camera's Z-axis, and that's what counts when clipping.
+ // The camera does not clip at a uniform distance but
+ // rather at a uniform Z coordinate, which means that the
+ // clipping distance increases the further you get from the
+ // camera's Z-axis.
+ //
+ // To do the job properly, we should convert each non-empty
+ // Shave pixel to a camera-space position, then find the
+ // one with the lowest Z coord. However, that's a bit
+ // expensive in terms of computation, so instead we'll
+ // cheat a bit.
+ //
+ // The points on the clipping plane which are most distant
+ // from the camera are at the far corners of the plane. If
+ // we pull the near clipping plane in so that even those
+ // corner points are as close as the minimum depth in
+ // Shave's depth buffer, then we'll be guaranteed that our
+ // nearest pixel doesn't get clipped.
+ //
+ // For the far clipping plane, our job is easier. We want
+ // to push the far clipping plane out until its point
+ // nearest the camera is at least as far away as Shave's
+ // largest depth value. The point nearest the camera on
+ // the far clipping plane is directly along the Z-axis. So
+ // we can just move the far clipping plane to Shave's max
+ // depth value and that will work.
+ //
+ // I call all of this "cheating" because we'll frequently
+ // end up pulling the near clipping plane in closer to the
+ // camera, and pushing the far plane farther from the
+ // camera, than we really need to. But it's unlikely that
+ // anyone will care. If they do, then we can switch to the
+ // full-blown approach.
+ //
+
+ //
+ // The first step is to get the camera's near and far
+ // clipping planes.
+ //
+ MFnCamera cameraFn(cameraPath);
+ double nearClip = cameraFn.nearClippingPlane();
+ double farClip = cameraFn.farClippingPlane();
+
+ //
+ // If Shave's max depth is greater than the camera's far
+ // clip, then reset the clip.
+ //
+ if (maxDepth > farClip)
+ {
+ cameraFn.setFarClippingPlane((double)maxDepth);
+ iter->farClipChanged = true;
+ }
+
+ //
+ // For the near clipping plane, find a corner of the
+ // frustum.
+ //
+ MPoint frustumCorners[4];
+
+ cameraFn.getFilmFrustum(1.0, frustumCorners);
+
+ //
+ // Turn it into a normalized vector.
+ //
+ MVector vec(
+ frustumCorners[0].x,
+ frustumCorners[0].y,
+ frustumCorners[0].z
+ );
+
+ vec.normalize();
+
+ //
+ // Multiply it by our minimum depth value. This will give
+ // us the position vector for a corner point at the minimum
+ // depth.
+ //
+ vec *= minDepth;
+
+ //
+ // If the resulting z-coord is smaller than the near clip
+ // value, then set the near clip to the z-coord value.
+ //
+ if (vec.z < nearClip)
+ {
+ cameraFn.setNearClippingPlane((double)vec.z);
+ iter->nearClipChanged = true;
+ }
+ }
+ }
+
+ shaveData->bufferValid = true;
+ }
+
+ MGlobal::executeCommand("shave_closeProgressBar()");
+
+ if (keepHairRenderFilesGlob)
+ {
+ MString outFilename;
+ int intFrame;
+
+ if (mSceneInfo.currentFrame < 0)
+ intFrame = (int)(mSceneInfo.currentFrame - 0.5f);
+ else
+ intFrame = (int)(mSceneInfo.currentFrame + 0.5f);
+
+ outFilename = hairFileNameGlob + ".";
+ outFilename += intFrame;
+ outFilename += ".tga";
+
+ if (mFrameGlobals.verbose) {
+#if defined(OSMac_) && (MAYA_API_VERSION >= 201600) && (MAYA_API_VERSION < 201700)
+ cerr << "Outputting to " << outFilename.asChar() << endl;
+#else
+ cerr << "Outputting to " << outFilename << endl;
+#endif
+ }
+
+ SHAVEwrite_targa(
+ (char *)outFilename.asChar(),
+ (short)mSceneInfo.width,
+ (short)mSceneInfo.height,
+ (unsigned char*)mSceneInfo.shaveRenderPixels
+ );
+ }
+
+ return renderReturnVal;
+}
+
+
+void shaveRender::doCamera(
+ const MDagPath& camPath,
+ shaveConstant::ShutterState shutter,
+ shaveRender::SceneInfo& shaveData
+)
+{
+ Matrix camvm;
+ MVector mayaUpVector, mayaDirectionVector;
+ double rotationVector[3];
+ VERT shaveUpVector, shaveDirection, shavePosition;
+ MVector unitDirectionVector(0., 0., 1.);
+ MVector unitUpVector(0., 1., 0.);
+
+ MTransformationMatrix::RotationOrder order = MTransformationMatrix::kXYZ;
+
+ MTransformationMatrix camWorldMatrix = camPath.inclusiveMatrix();
+ MFnCamera fnCamera(camPath);
+
+ //
+ // Get MAYA camera translation and rotation info
+ //
+ MVector translation = camWorldMatrix.translation(MSpace::kWorld);
+
+ camWorldMatrix.getRotation(rotationVector, order);
+
+ //
+ // Set SHAVE camera position in World space
+ //
+ shavePosition.x = (float) translation.x;
+ shavePosition.y = (float) translation.y;
+ shavePosition.z = (float) translation.z;
+
+ //
+ // UP VECTOR
+ //
+ mayaUpVector = unitUpVector.rotateBy(rotationVector, order);
+ shaveUpVector.x = (float) mayaUpVector.x;
+ shaveUpVector.y = (float) mayaUpVector.y;
+ shaveUpVector.z = (float) mayaUpVector.z;
+
+ //
+ // DIRECTION VECTOR
+ //
+ mayaDirectionVector = unitDirectionVector.rotateBy(rotationVector, order);
+ shaveDirection.x = (float) -mayaDirectionVector.x;
+ shaveDirection.y = (float) -mayaDirectionVector.y;
+ shaveDirection.z = (float) -mayaDirectionVector.z;
+
+ //
+ // FIELD OF VIEW
+ //
+ float pixelAspect = mRenderer ? mRenderer->getPixelAspect() : 1.0f;
+ double hfovagain, vfovagain;
+
+ portFieldOfView(
+ shaveMaya::imageWidth,
+ shaveMaya::imageHeight,
+ pixelAspect,
+ hfovagain,
+ vfovagain,
+ fnCamera
+ );
+
+ const float rad2degrees = (180.0f / (float)M_PI);
+
+ vfovagain *= rad2degrees;
+
+ if (mFrameGlobals.verbose)
+ {
+#if 0
+#ifdef OSMac_
+ //
+ // Panther broke cerr somehow so that it crashes if you use it on
+ // non-zero float or double values. So let's try our old pal
+ // stderr, instead.
+ //
+ if (stderr) fprintf(stderr, "pixel aspect is: %f\n", pixelAspect);
+#else
+ cerr << "pixel aspect is: " << pixelAspect << endl;
+#endif
+#endif
+ }
+
+ SHAVEmake_view_matrix(
+ camvm, shaveUpVector, shaveDirection, shavePosition, (float)vfovagain
+ );
+
+
+ /*
+ * Register the camera with Shave
+ */
+ if ((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+ SHAVEset_cameraOPEN(
+ camvm,
+ shavePosition,
+ shaveData.width,
+ shaveData.height,
+ pixelAspect,
+ (float)vfovagain,
+ 0.01f
+ );
+ }
+
+ if ((shutter == shaveConstant::kShutterClose)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+ SHAVEset_cameraCLOSE(camvm, shavePosition);
+ }
+
+ return;
+}
+
+
+void shaveRender::computeViewingFrustum(
+ double image_aspect,
+ double& left,
+ double& right,
+ double& bottom,
+ double& top,
+ MFnCamera& cam
+)
+{
+ double film_aspect = cam.aspectRatio();
+ double aperture_x = cam.horizontalFilmAperture();
+ double aperture_y = cam.verticalFilmAperture();
+ double offset_x = cam.horizontalFilmOffset();
+ double offset_y = cam.verticalFilmOffset();
+ double focal_to_near = cam.nearClippingPlane() /
+ (cam.focalLength() * MM_TO_INCH);
+// 88360
+
+ focal_to_near *= cam.cameraScale();
+
+ double scale_x = 1.0;
+ double scale_y = 1.0;
+ double translate_x = 0.0;
+ double translate_y = 0.0;
+
+ switch (cam.filmFit()) {
+ case MFnCamera::kFillFilmFit:
+ // In Fill mode we want to keep the entire image within the
+ // viewing frustum. If the image's aspect ratio is greater
+ // than that of the film then we'll do a horizontal fit,
+ // otherwise we'll do a vertical fit.
+ if (image_aspect > film_aspect)
+ scale_y = film_aspect / image_aspect;
+ else
+ scale_x = image_aspect / film_aspect;
+ break;
+
+ case MFnCamera::kHorizontalFilmFit:
+ scale_y = film_aspect / image_aspect;
+
+ if (scale_y > 1.0)
+ {
+ translate_y = cam.filmFitOffset() *
+ (aperture_y - (aperture_y * scale_y)) / 2.0;
+ }
+ break;
+
+ case MFnCamera::kVerticalFilmFit:
+ scale_x = image_aspect / film_aspect;
+
+ if (scale_x > 1.0)
+ {
+ translate_x = cam.filmFitOffset() *
+ (aperture_x - (aperture_x * scale_x)) / 2.0;
+ }
+ break;
+
+ case MFnCamera::kOverscanFilmFit:
+ // In Overscan mode we allow the edges of the image to extend
+ // beyond the viewing frustum. If the image's aspect ratio is
+ // greater than that of the film then we'll do a vertical fit,
+ // allowing the left and right edges to extend beyond the
+ // frustum. Otherwise we'll do a horizontal fit, allowing the
+ // top and bottom edges to extend beyond the frustum.
+ if (image_aspect > film_aspect)
+ scale_x = image_aspect / film_aspect;
+ else
+ scale_y = film_aspect / image_aspect;
+ break;
+
+ case MFnCamera::kInvalid:
+ break;
+ }
+
+
+ left = focal_to_near * (-.5*aperture_x*scale_x+offset_x+translate_x);
+ right = focal_to_near * ( .5*aperture_x*scale_x+offset_x+translate_x);
+ bottom = focal_to_near * (-.5*aperture_y*scale_y+offset_y+translate_y);
+ top = focal_to_near * ( .5*aperture_y*scale_y+offset_y+translate_y);
+}
+
+
+void shaveRender::portFieldOfView(
+ int port_width,
+ int port_height,
+ double pixelAspect,
+ double& horizontal,
+ double& vertical,
+ MFnCamera& fnCamera
+)
+{
+ double left, right, bottom, top;
+ double aspect = pixelAspect * (double)port_width / (double)port_height;
+ computeViewingFrustum(aspect,left,right,bottom,top,fnCamera);
+
+ double neardb = fnCamera.nearClippingPlane();
+ horizontal = atan(((right - left) * 0.5) / neardb) * 2.0;
+ vertical = atan(((top - bottom) * 0.5) / neardb) * 2.0;
+}
+
+
+void shaveRender::buildHairStack(
+ MObjectArray& shaveHairShapes, shaveConstant::ShutterState shutter
+)
+{
+ unsigned int i;
+ unsigned int numShaveNodes = shaveHairShapes.length();
+
+ //
+ // Register the hair nodes.
+ //
+ if ((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ SHAVEclear_stack();
+
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ MFnDependencyNode nodeFn(shaveHairShapes[i]);
+ shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode();
+ SHAVENODE* hairNode = hairShape->getHairNode();
+
+ hairShape->setStackIndex(i);
+
+ if ((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+ //
+ // Remove any old UV sets and add the current ones.
+ // Note that this *must* be done on the add_hairOPEN call:
+ // add_hairCLOSE is too late.
+ //
+ hairShape->removeShaveUVSets();
+ hairShape->addShaveUVSets();
+ SHAVEadd_hairOPEN2(hairNode, (int)i);
+ }
+
+ if ((shutter == shaveConstant::kShutterClose)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+ SHAVEadd_hairCLOSE2(hairNode, (int)i);
+ }
+ }
+
+ SHAVEset_stack_max(numShaveNodes);
+
+ return;
+}
+
+
+void shaveRender::clearHairStack(MObjectArray& shaveHairShapes)
+{
+ unsigned int i;
+ unsigned int numShaveNodes = shaveHairShapes.length();
+
+ //
+ // Remove the UV sets from the nodes, so that they don't waste mem.
+ //
+ for (i = 0; i < numShaveNodes; i++)
+ {
+ MFnDependencyNode nodeFn(shaveHairShapes[i]);
+ shaveHairShape* hairShape = (shaveHairShape*)nodeFn.userNode();
+
+ hairShape->removeShaveUVSets();
+ }
+
+ SHAVEclear_stack();
+
+ return;
+}
+
+
+void shaveRender::timeChanged(MTime& newTime, void* clientData)
+{
+ if (mRenderer) mRenderer->timeChange(newTime);
+
+ return;
+}
+
+
+MStatus shaveRender::renderSwatch(
+ MObject shaveHairShapeObj, unsigned int res, MString fileName
+)
+{
+ unsigned char* image = new unsigned char[res*res*4];
+ float* zbuff = new float[res*res];
+
+ MFnDependencyNode nodeFn(shaveHairShapeObj);
+ shaveHairShape* theShaveNode = (shaveHairShape*)nodeFn.userNode();
+ SHAVENODE* hairNode = theShaveNode->getHairNode();
+
+ theShaveNode->makeCurrent();
+
+ //
+ // Let's not have the progress bar for such a short render.
+ //
+ shaveRenderCancelled = false;
+ shaveEnableProgressBar = false;
+ SHAVErender_swatch(image, zbuff, res, &hairNode->shavep);
+ shaveEnableProgressBar = true;
+
+ delete [] zbuff;
+
+ bool success = shaveXPM::write(res, res, image, fileName);
+
+ delete [] image;
+
+ if (!success) return MS::kFailure;
+
+ return MS::kSuccess;
+}
+
+
+MStatus shaveRender::getShutterTimes(float& shutterOpen, float& shutterClose)
+{
+ //
+ // Get the current render camera.
+ //
+ MDagPath cameraPath = getRenderCamPath();
+
+ if (!cameraPath.isValid()) return MS::kUnknownParameter;
+
+ //
+ // Get the camera's shutter angle.
+ //
+ MFnCamera cameraFn(cameraPath);
+ MAngle shutterAngle(cameraFn.shutterAngle(), MAngle::kRadians);
+
+ //
+ // Calculate the amount of slider time needed for blurring.
+ //
+ float frameDelta = (float)
+ (shutterAngle.asDegrees()/360.0 * shaveMaya::motionBlurByFrame/2.0);
+
+ float midFrame = 0.0f;
+ //if(rendererIsVray())
+ //{
+ // double open = 0.0;
+ // double close = 0.0;
+ // MGlobal::executeCommand("eval(\"shaveVrayShader -q -shutterOpen\")",open);
+ // MGlobal::executeCommand("eval(\"shaveVrayShader -q -shutterClose\")",close);
+ // shutterOpen = open;
+ // shutterClose= close;
+ //}
+ //else
+ //{
+ midFrame = (float)MAnimControl::currentTime().value();
+ shutterOpen = midFrame - frameDelta;
+ shutterClose = midFrame + frameDelta;
+ //}
+ MGlobal::displayInfo(MString(" sutterOpen ") + shutterOpen + MString(" shutterClose ") + shutterClose);
+
+ return MS::kSuccess;
+}
+
+
+MStatus shaveRender::renderToDRAFile(
+ SHAVE_RENDER_HOST renderHost,
+ const MString& fileName,
+ int voxelResolution,
+ bool includeOcclusions
+)
+{
+ MStatus st;
+ bool mustFreeRenderer = false;
+ MObjectArray shaveHairShapes;
+ float shutterOpen = 0.0f;
+ float shutterClose = 0.0f;
+
+ shaveGlobals::getGlobals();
+
+ if (mRenderer == NULL)
+ {
+ getRenderer();
+ mustFreeRenderer = true;
+ }
+
+ mRenderer->getRenderableShaveNodesByRenderMode(NULL, &shaveHairShapes);
+
+ bool doMotionBlur = shaveMaya::doMotionBlur;
+
+ if (doMotionBlur)
+ {
+ st = getShutterTimes(shutterOpen, shutterClose);
+ if (!st) doMotionBlur = false;
+ }
+
+ MGlobal::displayInfo(MString("blur ") + doMotionBlur + MString("sutterOpen ") + shutterOpen + MString(" shutterClose ") + shutterClose);
+
+ st = renderToDRAFile(
+ renderHost,
+ fileName,
+ voxelResolution,
+ shaveHairShapes,
+ doMotionBlur,
+ shutterOpen,
+ shutterClose,
+ true,
+ includeOcclusions
+ );
+
+ if (mustFreeRenderer)
+ {
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+
+ return st;
+}
+
+
+MStatus shaveRender::renderToDRAFile(
+ SHAVE_RENDER_HOST renderHost,
+ const MString& fileName,
+ int voxelResolution,
+ MObjectArray shaveHairShapes,
+ bool doMotionBlur,
+ float shutterOpen,
+ float shutterClose,
+ bool updateTextureCache,
+ bool includeOcclusions
+)
+{
+ bool mustFreeRenderer = false;
+ MStatus st;
+
+ if (shaveHairShapes.length() == 0) return MS::kNotFound;
+
+ saveFrameGlobals();
+ shaveMaya::getRenderGlobals();
+
+ SHAVEset_verbose(mFrameGlobals.verbose ? 1 : 0);
+ SHAVEclear_stack();
+
+ SHAVEset_tile_limit(
+ mFrameGlobals.tileMemoryLimit, mFrameGlobals.transparencyDepth
+ );
+
+ if (mRenderer == NULL)
+ {
+ getRenderer();
+ mustFreeRenderer = true;
+ }
+
+ mHairRenderMode = mRenderer->getFilteredRenderMode(
+ shaveHairShapes, &mRenderInstances
+ );
+ mNormalShadows = mRenderer->getShadowSource();
+
+ //
+ // Build the texture lookup tables.
+ //
+ if (updateTextureCache)
+#ifdef PER_NODE_TEXLOOKUP
+ initTexInfoLookup2(shaveHairShapes, "", mFrameGlobals.verbose);
+#else
+ initTexInfoLookup(shaveHairShapes, "", mFrameGlobals.verbose);
+#endif
+
+ //
+ // Do everything except the camera renders, as we won't be needing the
+ // rendered images.
+ //
+ shaveRenderCancelled = false;
+ mSceneInfo.bufferValid = false;
+
+ MDagPath cameraPath = getRenderCamPath();
+
+
+
+ if (doMotionBlur)
+ {
+ //if(rendererIsVray()) //vlad|15Apr2010
+ //{
+ // shutterOpen += 0.5;
+ // shutterClose += 0.5;
+ //}
+
+ float curFrame = (float)MAnimControl::currentTime().as(MTime::uiUnit());
+
+ if (curFrame != shutterOpen) shaveUtil::setFrame(shutterOpen);
+
+ buildHairStack(shaveHairShapes, shaveConstant::kShutterOpen);
+
+ bufferRender(
+ shaveConstant::kShutterOpen,
+ cameraPath,
+ shutterOpen,
+ false,
+ false,
+ false,
+ includeOcclusions
+ );
+
+ shaveUtil::setFrame(shutterClose);
+ buildHairStack(shaveHairShapes, shaveConstant::kShutterClose);
+
+ bufferRender(
+ shaveConstant::kShutterClose,
+ cameraPath,
+ shutterClose,
+ false,
+ false,
+ false,
+ includeOcclusions
+ );
+ }
+ else
+ {
+ //MTime ct = MAnimControl::currentTime();
+ //if(rendererIsVray()) //vlad|15Apr2010
+ //{
+ // shaveUtil::setFrame((ct + 1.0).value());
+ //}
+ buildHairStack(shaveHairShapes, shaveConstant::kShutterBoth);
+
+ bufferRender(
+ shaveConstant::kShutterBoth,
+ cameraPath,
+ shutterClose,
+ false,
+ false,
+ false,
+ includeOcclusions
+ );
+ //if(rendererIsVray()) //vlad|15Apr2010
+ //{
+ // shaveUtil::setFrame(ct.value());
+ //}
+ }
+
+#if OCCLUSIONS_IN_DRA
+ if (includeOcclusions)
+ {
+ shaveUtil::setWFTYPEVelocities(
+ &mSceneInfo.shutterOpenCamScene, &mSceneInfo.shutterCloseCamScene
+ );
+
+ SHAVEadd_occlusions(&mSceneInfo.shutterOpenCamScene);
+ }
+#endif
+
+ //
+ // Export the current Shave state to a DRA file.
+ //
+ SHAVEset_render_host(renderHost);
+ SHAVEmult_vox_size(mFrameGlobals.voxelScaling);
+ SHAVEexport_archive((char*)fileName.asChar(), voxelResolution);
+
+#if OCCLUSIONS_IN_DRA
+ if (includeOcclusions)
+ {
+ free_geomWF(&mSceneInfo.shutterOpenCamScene);
+ free_geomWF(&mSceneInfo.shutterCloseCamScene);
+ }
+#endif
+
+ clearHairStack(shaveHairShapes);
+
+ if (mustFreeRenderer)
+ {
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+
+ return MS::kSuccess;
+}
+
+
+MDagPath shaveRender::getRenderCamPath()
+{
+ //
+ // We have a dandy MEL proc which determines the current render camera
+ // for us, so let's use it.
+ //
+ MString cameraName;
+ MGlobal::executeCommand("shave_getRenderCamera()", cameraName);
+
+ MDagPath cameraPath;
+ MSelectionList list;
+
+ list.add(cameraName);
+ list.getDagPath(0, cameraPath);
+
+ return cameraPath;
+}
+
+
+MString shaveRender::getRenderModeName(shaveConstant::RenderMode mode)
+{
+ //
+ // Yes, it would be nice to switch to some kind of dynamic registration
+ // system rather than these hard-coded strings, but that's not likely
+ // to happen unless we start supporting a new renderer, or I have a lot
+ // of idle time on my hands.
+ //
+ switch (mode)
+ {
+ case shaveConstant::kNoRender:
+ return "None";
+
+ case shaveConstant::kBufferRender:
+ return "Buffer";
+
+ case shaveConstant::kGeometryRender:
+ return "Geometry";
+
+ default:
+ break;
+ }
+
+ return "";
+}
+
+
+//
+// The caller must not free the returned renderer.
+//
+shaveRenderer* shaveRender::getRenderer()
+{
+ //
+ // If we're in the middle of a render, then return the existing
+ // renderer (if there is one), otherwise return a new renderer.
+ //
+ if (mRenderState == kRenderNone)
+ {
+ if (mRenderer != NULL)
+ {
+ delete mRenderer;
+ mRenderer = NULL;
+ }
+ }
+
+ if (mRenderer == NULL)
+ {
+#ifdef USE_VRAY
+ if(rendererIsVray()) //vlad|16Apr2010
+ mRenderer = new shaveVrayRenderer;
+ else
+#endif
+ mRenderer = new shaveMayaRenderer;
+
+ ///I do not see tha rpman case is trapped here -- vald|19Jan2012
+ //int isPrMan = 0;
+ //status = MGlobal::executeCommand("shave_curRendererIsPrMan", isPrMan);
+ //if(isPrMan)
+ //{
+ //}
+ }
+
+ return mRenderer;
+}
+
+
+bool shaveRender::isIPRActive()
+{
+ bool isActive = false;
+
+ MGlobal::executeCommand("shave_isIPRActive", isActive);
+
+ return isActive;
+}
+
+
+void shaveRender::exportEnd()
+{
+ mExportActive = false;
+
+ //
+ // Let the MEL scripts know that the export is over.
+ //
+ MGlobal::executeCommand("shave_exportEnd");
+
+ //
+ // Clear out the export filename info.
+ //
+ mExportFileFramePadding = 0;
+ mExportFileNameFormat = shaveConstant::kNoFileNameFormat;
+ mExportFileName = "";
+ mExportFilePerFrame = false;
+ mExportFileCompression = 0;
+}
+
+
+void shaveRender::exportStart()
+{
+ //
+ // The caller didn't give us a name for the export file, so let's see
+ // if Maya can.
+ //
+ exportStart(MFileIO::beforeExportFilename());
+}
+
+
+void shaveRender::exportStart(MString fileName)
+{
+ // These parameters were used for special handling of Mental Ray. We
+ // no longer support Mental Ray and none of the renderers we do
+ // support have need of these, so we just set them to default values
+ // for now.
+ //
+ bool filePerFrame = false;
+ shaveConstant::FileNameFormat nameFormat = shaveConstant::kNoFileNameFormat;
+ unsigned framePadding = 0;
+ unsigned compression = 0;
+
+ exportStart(fileName, filePerFrame, nameFormat, framePadding, compression);
+}
+
+
+void shaveRender::exportStart(
+ MString exportFile,
+ bool filePerFrame,
+ shaveConstant::FileNameFormat nameFormat,
+ unsigned framePadding,
+ unsigned compression
+)
+{
+ mExportFileFramePadding = framePadding;
+ mExportFileName = exportFile;
+ mExportFileNameFormat = nameFormat;
+ mExportFilePerFrame = filePerFrame;
+ mExportFileCompression = compression;
+
+ mExportActive = true;
+
+ //
+ // Let the MEL scripts know that an export has started.
+ //
+ MGlobal::executeCommand("shave_exportStart");
+}
+
+
+void shaveRender::getExportFileInfo(
+ MString& exportFile,
+ bool& filePerFrame,
+ shaveConstant::FileNameFormat& nameFormat,
+ unsigned& framePadding,
+ unsigned& compression
+)
+{
+ exportFile = mExportFileName;
+ filePerFrame = mExportFilePerFrame;
+ nameFormat = mExportFileNameFormat;
+ framePadding = mExportFileFramePadding;
+ compression = mExportFileCompression;
+
+ return;
+}
+
+void shaveRender::vrayKeyFrame()
+{
+ if (mRenderer)
+ {
+#ifdef USE_VRAY
+ if(rendererIsVray())
+ {
+ shaveVrayRenderer* vr = static_cast<shaveVrayRenderer*>( mRenderer );
+ vr->vrayKeyframeCallback();
+ }
+#endif
+ }
+}
+
+///////////////////////
+//void shaveRender::shutterOpen()
+//{
+// shaveGlobals::getGlobals();
+// saveFrameGlobals();
+//
+// shaveMaya::getRenderGlobals();
+//
+// SHAVEset_verbose(mFrameGlobals.verbose ? 1 : 0);
+// SHAVEclear_stack();
+//
+// MTime curTime = MAnimControl::currentTime();
+// float frame = (float)curTime.as(MTime::uiUnit());
+// MGlobal::displayInfo(MString("shutter open: ") + frame);
+//
+// bool mustFreeRenderer = false;
+// if (mRenderer == NULL)
+// {
+// getRenderer();
+// mustFreeRenderer = true;
+// }
+//
+// MObjectArray shaveHairShapes;
+// mRenderer->getRenderableShaveNodes(shaveHairShapes);
+// MGlobal::displayInfo(MString("num nodes: ") + shaveHairShapes.length() );
+// buildHairStack(shaveHairShapes, shaveConstant::kShutterOpen);
+//
+// //if(mustFreeRenderer)
+// //{
+// // delete mRenderer;
+// // mRenderer = NULL;
+// //}
+// MGlobal::displayInfo("shaveRender::shutterOpen - OK");
+//
+//}
+//void shaveRender::shutterClose()
+//{
+// shaveGlobals::getGlobals();
+// saveFrameGlobals();
+//
+// shaveMaya::getRenderGlobals();
+//
+// MTime curTime = MAnimControl::currentTime();
+// float frame = (float)curTime.as(MTime::uiUnit());
+// MGlobal::displayInfo(MString("shutter close: ") + frame);
+//
+//
+// //bool mustFreeRenderer = false;
+// //if (mRenderer == NULL)
+// //{
+// // getRenderer();
+// // mustFreeRenderer = true;
+// //}
+// MObjectArray shaveHairShapes;
+// mRenderer->getRenderableShaveNodes(shaveHairShapes);
+// buildHairStack(shaveHairShapes, shaveConstant::kShutterClose);
+//
+// //if(mustFreeRenderer)
+// //{
+// // delete mRenderer;
+// // mRenderer = NULL;
+// //}
+//}
+//void shaveRender::shutterExport(MString fileName)
+//{
+// SHAVEmult_vox_size(mFrameGlobals.voxelScaling);
+// SHAVEexport_archive((char*)fileName.asChar(), voxelResolutionGlob);
+//
+// //bool mustFreeRenderer = false;
+// //if (mRenderer == NULL)
+// //{
+// // getRenderer();
+// // mustFreeRenderer = true;
+// //}
+// MObjectArray shaveHairShapes;
+// mRenderer->getRenderableShaveNodes(shaveHairShapes);
+// clearHairStack(shaveHairShapes);
+//
+// //if(mustFreeRenderer)
+// //{
+// if(mRenderer)
+// {
+// delete mRenderer;
+// mRenderer = NULL;
+// }
+// //}
+//}