aboutsummaryrefslogtreecommitdiff
path: root/mayaPlug/shaveRenderer.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/shaveRenderer.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/shaveRenderer.cpp')
-rw-r--r--mayaPlug/shaveRenderer.cpp541
1 files changed, 541 insertions, 0 deletions
diff --git a/mayaPlug/shaveRenderer.cpp b/mayaPlug/shaveRenderer.cpp
new file mode 100644
index 0000000..6ebf6d1
--- /dev/null
+++ b/mayaPlug/shaveRenderer.cpp
@@ -0,0 +1,541 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+#include <maya/MFnDependencyNode.h>
+#include <maya/MGlobal.h>
+#include <maya/MItDependencyNodes.h>
+#include <maya/MObjectArray.h>
+
+#include "shaveDebug.h"
+
+#include "shaveCheckObjectVisibility.h"
+#include "shaveConstant.h"
+#include "shaveGlobals.h"
+#include "shaveHairShape.h"
+#include "shaveMaya.h"
+#include "shaveRender.h"
+#include "shaveRenderer.h"
+#include "shaveSDKFUNCS.h"
+#include "shaveTextureStore.h"
+#include "shaveUtil.h"
+
+
+shaveRenderer::shaveRenderer()
+: mRendering(false)
+, mWarningGivenInvalidHairMode(false)
+, mWarningGivenInvalidInstanceMode(false)
+{}
+
+
+shaveRenderer::~shaveRenderer()
+{}
+
+
+void shaveRenderer::doShutter(
+ const MTime& curTime, shaveConstant::ShutterState shutter
+)
+{
+ ENTER();
+
+ if ((mGeometryNodes.length() > 0) || (mNonGeometryNodes.length() > 0))
+ {
+ float frame = (float)curTime.as(MTime::uiUnit());
+
+ shaveGlobals::getGlobals();
+ shaveMaya::getRenderGlobals();
+
+ //
+ // We "render" geometry by having the shaveHairShape create its entire
+ // mesh. As such, we want it in the scene before anything else
+ // happens so that Maya will take it into account when doing
+ // bounding box calculations for the render.
+ //
+ if (mGeometryNodes.length() > 0)
+ shaveRender::geomRender(mGeometryNodes);
+
+ MDagPath cameraPath = shaveRender::getRenderCamPath();
+
+ if (mNonGeometryNodes.length() > 0)
+ {
+ //
+ // Create the texture lookup table for this frame.
+ //
+ // We only do this on shutterOpen, not shutterClose. The
+ // reason for this is that Shave doesn't handle animation of
+ // any of its parameters: it simply takes their value at
+ // shutter close. So we only need to create the lookups once
+ // per frame, even when motion blur is on.
+ //
+ // That would argue that we should create the lookup on
+ // shutterClose, not shutterOpen. However, two things mitigate
+ // against that. First, to properly support vertex shaders, we
+ // need to have vertex-level colour info available at both
+ // shutterOpen and shutterClose. Second, there is a bug in
+ // Maya that if you sample a shading network on shutterClose,
+ // it screws up other, unrelated textures in the scene.
+ //
+ // Okay, so the last thing to discuss is why we don't create
+ // the lookup table in frameStart(). That's because there's a
+ // bug in Mental Ray For Maya such that the frame number at
+ // frameStart() is not correct during a batch render. So
+ // instead we have to wait for the actual time change before we
+ // can know which frame we are rendering. We no longer support
+ // Mental Ray, but it seems safer to leave it as-is in case
+ // any other renderers suffer from the same problem.
+ //
+ if ((shutter == shaveConstant::kShutterOpen)
+ || (shutter == shaveConstant::kShutterBoth))
+ {
+#ifdef PER_NODE_TEXLOOKUP
+ initTexInfoLookup2(
+ mNonGeometryNodes,
+ "",
+ shaveRender::getFrameGlobals().verbose
+ );
+#else
+ initTexInfoLookup(
+ mNonGeometryNodes,
+ "",
+ shaveRender::getFrameGlobals().verbose
+ );
+#endif
+ }
+
+ shaveRender::buildHairStack(mNonGeometryNodes, shutter);
+ }
+
+ //
+ // Call the overridden render() method.
+ //
+ render(frame, shutter, cameraPath);
+ }
+
+ LEAVE();
+}
+
+
+void shaveRenderer::frameEnd(const shaveGlobals::Globals& g)
+{
+ shaveRender::clearHairStack(mNonGeometryNodes);
+ mGeometryNodes.clear();
+ mNonGeometryNodes.clear();
+}
+
+
+void shaveRenderer::frameStart(const shaveGlobals::Globals& g)
+{
+ SHAVEset_tile_limit((int)g.tileMemoryLimit, (int)g.transparencyDepth);
+
+ //
+ // Get the nodes to be rendered as geometry, and those to be rendered
+ // not as geometry, into two separate lists.
+ //
+ mGeometryNodes.clear();
+ mNonGeometryNodes.clear();
+
+ getRenderableShaveNodesByRenderMode(&mGeometryNodes, &mNonGeometryNodes);
+
+ //
+ // Changes in animated attributes may have changed the list of
+ // renderable shaveHairShapes. Let's update the flags telling us which
+ // types of shaveHairShapes we have.
+ //
+ bool tempHaveHair;
+ bool tempHaveInstances;
+
+ shaveUtil::classifyShaveNodes(mGeometryNodes, tempHaveHair, tempHaveInstances);
+ shaveUtil::classifyShaveNodes(mNonGeometryNodes, mHaveHair, mHaveInstances);
+
+ mHaveHair = (mHaveHair || tempHaveHair);
+ mHaveInstances = (mHaveInstances || tempHaveInstances);
+}
+
+
+//
+// Return the base render modes, without caring if we actually have any of
+// the relevant types of shaveHairShape.
+//
+shaveConstant::RenderMode shaveRenderer::getBaseRenderMode(
+ bool* renderInstances
+) const
+{
+ shaveConstant::RenderMode hairRenderMode;
+
+ hairRenderMode = normalRenderModeGlob;
+
+ if (renderInstances) *renderInstances = enableInstanceGeometryGlob;
+
+ //
+ // If the specified render mode does not exist, then switch to buffer.
+ //
+ if (shaveRender::getRenderModeName(hairRenderMode) == "")
+ {
+ if (!mWarningGivenInvalidHairMode)
+ {
+ MGlobal::displayWarning(
+ MString("Shave: Hair render mode ") + (double)hairRenderMode
+ + " is invalid. Using "
+ + shaveRender::getRenderModeName(shaveConstant::kBufferRender)
+ + " render mode instead."
+ );
+
+ mWarningGivenInvalidHairMode = true;
+ }
+
+ hairRenderMode = shaveConstant::kBufferRender;
+ }
+
+ return hairRenderMode;
+}
+
+
+shaveConstant::ShadowSource shaveRenderer::getBaseShadowSource() const
+{
+ ENTER();
+
+ shaveConstant::ShadowSource hairShadowSource = shaveConstant::kNoShadows;
+ shaveConstant::RenderMode hairRenderMode = getRenderMode();
+
+ if (doHairShadowsGlob)
+ {
+ switch (hairRenderMode)
+ {
+ case shaveConstant::kNoRender:
+ break;
+
+ case shaveConstant::kGeometryRender:
+ hairShadowSource = shaveConstant::kMayaGeomShadows;
+ break;
+
+ default:
+ if (shaveUseGeomShadowsGlob)
+ hairShadowSource = shaveConstant::kMayaGeomShadows;
+ else
+ hairShadowSource = shaveConstant::kShaveShadows;
+ break;
+ }
+ }
+
+ //
+ // Return the shadow sources to the caller.
+ //
+ RETURN(hairShadowSource);
+}
+
+
+//
+// Return the render modes applicable to the given array of shaveHairShapes.
+//
+shaveConstant::RenderMode shaveRenderer::getFilteredRenderMode(
+ const MObjectArray& shaveHairShapes, bool* renderInstances
+) const
+{
+ //
+ // Get the base mode.
+ //
+ shaveConstant::RenderMode hairRenderMode = getRenderMode(renderInstances);
+
+ //
+ // Do we have any hair? Any instances?
+ //
+ bool haveHair = false;
+ bool haveInstances = false;
+
+ shaveUtil::classifyShaveNodes(shaveHairShapes, haveHair, haveInstances);
+
+ if (!haveInstances && renderInstances) *renderInstances = false;
+ if (!haveHair) hairRenderMode = shaveConstant::kNoRender;
+
+ return hairRenderMode;
+}
+
+
+void shaveRenderer::getGeomVisibility(
+ bool& hairPrimaryVisibility,
+ bool& hairSecondaryVisibility,
+ bool& instancePrimaryVisibility,
+ bool& instanceSecondaryVisibility
+)
+{
+ bool renderInstances;
+ shaveConstant::RenderMode hairRenderMode;
+ shaveConstant::ShadowSource hairShadowSource;
+
+ hairRenderMode = getRenderMode(&renderInstances);
+ hairShadowSource = getShadowSource();
+
+ switch (hairRenderMode)
+ {
+ case shaveConstant::kGeometryRender:
+ hairPrimaryVisibility = true;
+ hairSecondaryVisibility = true;
+ break;
+
+ case shaveConstant::kBufferRender:
+ hairPrimaryVisibility = false;
+ hairSecondaryVisibility =
+ (hairShadowSource == shaveConstant::kMayaGeomShadows);
+ break;
+
+ case shaveConstant::kNoRender:
+ default:
+ hairPrimaryVisibility = false;
+ hairSecondaryVisibility = false;
+ break;
+ }
+
+ instancePrimaryVisibility = renderInstances;
+ instanceSecondaryVisibility = renderInstances;
+}
+
+
+float shaveRenderer::getPixelAspect() const
+{
+ return ((float)shaveMaya::imageHeight
+ / (float)shaveMaya::imageWidth)
+ * shaveMaya::deviceAspectRatio;
+}
+
+
+// Returns an array of those shaveHairShapes which are in an appropriate state
+// to be rendered (e.g. active, visible, etc).
+//
+void shaveRenderer::getRenderableShaveNodes(MObjectArray& shaveHairShapes)
+{
+ shaveHairShapes.clear();
+
+ // Get the base render modes.
+ //
+ bool includeInstances;
+ shaveConstant::RenderMode renderMode = getRenderMode(&includeInstances);
+
+ const bool includeHair = (renderMode != shaveConstant::kNoRender);
+
+ // If the render modes are excluding both hair and instances, then we're
+ // done.
+ //
+ if (!includeHair && !includeInstances) return;
+
+ // Run through all the shaveHairShapes in the scene and save the ones which
+ // are renderable.
+ //
+ MItDependencyNodes iter;
+
+ for (; !iter.isDone(); iter.next())
+ {
+ MObject node = iter.item();
+ MFnDependencyNode nodeFn(node);
+
+ if (nodeFn.typeId() != shaveHairShape::id) continue;
+
+ shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode();
+
+ //
+ // Skip any nodes whose render mode means that they're not being
+ // rendered.
+ //
+ bool isInstanced = nodePtr->isInstanced();
+
+ if ((isInstanced && !includeInstances)
+ || (!isInstanced && !includeHair))
+ {
+ continue;
+ }
+
+ // Skip any nodes which are not visible.
+ //
+ // TODO: The user may have turned off visibility on the
+ // hair node or its display node. The display node
+ // only matters when we're displaying hair as geometry,
+ // but it would be confusing to force our users to
+ // switch back and forth between the hair and display
+ // nodes, depending upon which render mode they were
+ // using. To avoid that we go with a least common
+ // denominator approach: both the hair and display
+ // nodes must be visible for the hair to render.
+ //
+ MDagPath path;
+ MDagPath::getAPathTo(node, path);
+
+ if (areObjectAndParentsVisible(path, false, true))
+ {
+ // Find the shaveHairShape's display node.
+ //
+ MObject displayNode = nodePtr->getDisplayShape();
+
+ if (displayNode.isNull()) continue;
+
+ // Is the display node in a renderable layer?
+ //
+ MFnDagNode dagNodeFn(displayNode);
+ MPlug plug = dagNodeFn.findPlug("layerRenderable");
+ bool layerIsRenderable;
+
+ plug.getValue(layerIsRenderable);
+
+ if (!layerIsRenderable) continue;
+
+ // Get a DAG path to the display node. At the moment we don't
+ // support instancing of shaveHairShapes, or their display
+ // nodes, so any path will do.
+ //
+ dagNodeFn.getPath(path);
+
+ // We only render shaveHairShapes whose display nodes are
+ // visible.
+ //
+ // This method used to discard those shaveHairShapes whose
+ // display nodes had their primary visibility off. However, if
+ // someone turns off a node's primary visibility while leaving
+ // its regular visibility on, that means that they still want
+ // it to show up in shadows, reflections, refractions, etc. So
+ // we really do still need to render it. Admittedly we might
+ // not want it to be seen in a buffer render, but that's a
+ // larger issue than we can resolve here with a simply binary
+ // check of primary visibility. So now we ignore primary
+ // visibility when checking display nodes.
+ //
+ if (areObjectAndParentsVisible(path, false, true))
+ shaveHairShapes.append(node);
+ }
+ }
+}
+
+
+//
+// Returns those shaveHairShapes which are in an appropriate state to be
+// rendered (e.g. active, visible, etc). The result is returned as two
+// arrays, one containing those shaveHairShapes which are to be rendered as
+// normal Maya geometry, and another containing those which require
+// rendering by Shave. (The latter includes geometry shaders using Shave
+// calls to generate render geometry.)
+//
+void shaveRenderer::getRenderableShaveNodesByRenderMode(
+ MObjectArray* geomNodes, MObjectArray* nonGeomNodes
+)
+{
+ MObjectArray shaveHairShapes;
+
+ getRenderableShaveNodes(shaveHairShapes);
+
+ unsigned int i;
+
+ for (i = 0; i < shaveHairShapes.length(); i++)
+ {
+ MFnDependencyNode nodeFn(shaveHairShapes[i]);
+ shaveHairShape* nodePtr = (shaveHairShape*)nodeFn.userNode();
+
+ //
+ // Ask the renderer-specific subclass whether this node should be
+ // put onto the geomNodes list.
+ //
+ if (isGeomNode(nodePtr))
+ {
+ if (geomNodes) geomNodes->append(shaveHairShapes[i]);
+ }
+ else
+ {
+ if (nonGeomNodes) nonGeomNodes->append(shaveHairShapes[i]);
+ }
+ }
+}
+
+
+shaveConstant::RenderMode shaveRenderer::getRenderMode(bool* renderInstances)
+ const
+{
+ //
+ // If we're in the middle of a render, return the cached values.
+ //
+ if (mRendering)
+ {
+ if (renderInstances) *renderInstances = mRenderInstances;
+
+ return mHairRenderMode;
+ }
+
+ return getBaseRenderMode(renderInstances);
+}
+
+
+shaveConstant::ShadowSource shaveRenderer::getShadowSource() const
+{
+ //
+ // If we're in the middle of a render, return the cached value.
+ //
+ if (mRendering) return mHairShadowSource;
+
+ return getBaseShadowSource();
+}
+
+
+bool shaveRenderer::needVertexColours() const
+{
+ return false;
+}
+
+
+void shaveRenderer::renderEnd()
+{
+ //
+ // Clear the various warning flags so that the warnings will be issued
+ // on the next render.
+ //
+ mWarningGivenInvalidHairMode = false;
+ mWarningGivenInvalidInstanceMode = false;
+
+ mRendering = false;
+}
+
+
+void shaveRenderer::renderInterrupted()
+{}
+
+
+void shaveRenderer::renderStart()
+{
+ //
+ // Cache the render and shadow modes.
+ //
+ MObjectArray shaveHairShapes;
+
+ mHairRenderMode = getRenderMode(&mRenderInstances);
+ mHairShadowSource = getShadowSource();
+
+ //
+ // We have something of a problem here. We don't want to create, say,
+ // a buffer render shader if there are only instances in the scene.
+ // It's easy enough to check the list of shaveHairShapes in the scene, but
+ // it's possible that there might be shaveHairShapes which are present but
+ // not being rendered, because their 'active' flag is false, their
+ // instance geom is invisible, etc. We can't take any of that into
+ // account because the affect may be animated: thus an instance which
+ // is invisible now might be visible on the next frame, so we have to
+ // make sure that the shader for it is already in place.
+ //
+ // So we're stuck with just a static check of the nodes currently
+ // available. If subsequently during a frame we find that a particular
+ // type of shader is not needed we'll have to give it a flag telling it
+ // to act as a no-op for that frame.
+ //
+ shaveUtil::getShaveNodes(shaveHairShapes);
+ shaveUtil::classifyShaveNodes(shaveHairShapes, mHaveHair, mHaveInstances);
+
+ if (!mHaveHair)
+ {
+ mHairRenderMode = shaveConstant::kNoRender;
+ mHairShadowSource = shaveConstant::kNoShadows;
+ }
+
+ if (!mHaveInstances) mRenderInstances = false;
+
+ //
+ // This will trigger getRenderMode() and getShadowSource() to use the
+ // cached values.
+ //
+ mRendering = true;
+}
+
+
+