diff options
| author | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
|---|---|---|
| committer | Ben Marsh <[email protected]> | 2019-10-22 09:07:59 -0400 |
| commit | bd0027e737c6512397f841c22786274ed74b927f (patch) | |
| tree | f7ffbdb8f3741bb7f24635616cc189cba5cb865c /mayaPlug/shaveRenderer.cpp | |
| download | shave-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.cpp | 541 |
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; +} + + + |