// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 #include #include #include #include #include #include #include "shaveConstant.h" #include "shaveGlobals.h" #include "shaveHairShape.h" #include "shaveMaya.h" #include "shaveMayaRenderer.h" #include "shaveRenderCallback.h" #include "shaveUtil.h" #include "shaveVertexShader.h" shaveMayaRenderer::shaveMayaRenderer() : mHaveVolumeShader(false) , mRenderCallback(NULL) , mRenderCamera("") , mTimeState(shaveMayaRenderer::kAwaitingNothing) , mWarningGivenUnsupportedHairMode(false) , mWarningGivenUnsupportedInstanceMode(false) { } shaveMayaRenderer::~shaveMayaRenderer() {} void shaveMayaRenderer::frameStart(const shaveGlobals::Globals& g) { shaveRenderer::frameStart(g); // // We only need the volume shader if we're doing 3D compositing or handling // reflections/refractions for non-geometry hair. // mHaveVolumeShader = false; if ((g.doCompositing && !g.composite2d) || (g.visibleInReflections && (mNonGeometryNodes.length() > 0))) { // // If the render camera has changed, remove the volume shader from // the old one and add it to the new one. // MString cameraName; MGlobal::executeCommand("shave_getRenderCamera()", cameraName); if (cameraName != mRenderCamera) { MGlobal::executeCommand("shave_cleanupVolumeShader "); MGlobal::executeCommand( MString("shave_setupVolumeShader ") + cameraName ); mHaveVolumeShader = true; mRenderCamera = cameraName; } } // // At the moment, the Maya renderer will have us in the center of the // frame to rendered. If motion blur is off, that's great. But if // motion blur is on, we only care about the shutter open and close // times. So we'll have to wait for them. // if (shaveMaya::doMotionBlur) mTimeState = kAwaitingShutterOpen; else { MTime curTime = MAnimControl::currentTime(); doShutter(curTime, shaveConstant::kShutterBoth); mTimeState = kAwaitingNothing; } } void shaveMayaRenderer::frameEnd(const shaveGlobals::Globals& g) { mRenderCallback->disableCamRender(); shaveRenderer::frameEnd(g); } float shaveMayaRenderer::getPixelAspect() const { return shaveRenderer::getPixelAspect(); } shaveConstant::RenderMode shaveMayaRenderer::getBaseRenderMode(bool* renderInstances) const { // // Let the base class do its stuff first. // shaveConstant::RenderMode hairRenderMode; hairRenderMode = shaveRenderer::getBaseRenderMode(renderInstances); // // The base class will have verified that the render modes are valid // for *some* renderer, but we must make sure that they're valid for // *this* renderer. // switch (hairRenderMode) { case shaveConstant::kNoRender: case shaveConstant::kBufferRender: case shaveConstant::kGeometryRender: break; default: if(!shaveRender::rendererIsPrMan())//not clear why for rendermane we do not have separate renderer class - dub|22Jun2012 { if (!mWarningGivenUnsupportedHairMode) { MGlobal::displayWarning( MString("Shave: The Maya renderer does not support ") + shaveRender::getRenderModeName(hairRenderMode) + " hair render mode. We'll use " + shaveRender::getRenderModeName(shaveConstant::kBufferRender) + " render mode instead." ); mWarningGivenUnsupportedHairMode = true; } } hairRenderMode = shaveConstant::kBufferRender; break; } // // Return the render mode to the caller. // return hairRenderMode; } // // Is this node "rendered" using normal Maya geometry? // bool shaveMayaRenderer::isGeomNode(const shaveHairShape* nodePtr) const { // // Instances in Maya are always rendered using normal geometry, // so if instances are being rendered then this one will. // if (nodePtr->isInstanced()) return true; // // If the render mode is kGeometryRender then this node will be // rendered using Maya geometry, otherwise it will not. // shaveConstant::RenderMode hairRenderMode = getRenderMode(); return (hairRenderMode == shaveConstant::kGeometryRender); } bool shaveMayaRenderer::needVertexColours() const { // // If there are any vertex shaders in the scene, then we'll need vertex // colour info. // MItDependencyNodes iter(MFn::kPluginDependNode); for (; !iter.isDone(); iter.next()) { MObject node = iter.item(); MFnDependencyNode nodeFn(node); if (nodeFn.typeId() == shaveVertexShader::id) return true; } return false; } void shaveMayaRenderer::render( float frame, shaveConstant::ShutterState shutter, const MDagPath& camera ) { if (mNonGeometryNodes.length() > 0) { // // If native illumination is on, the camera render will call // SHAVEapply_illuminationWF() which will need to sample the lights // in the scene. Unfortunately, light sampling only works from // within an MRenderCallback. That means that when native // illumination is on we cannot do the camera render here but must // instead do it in shaveRenderCallback. // // But wait, there's more. Our volumetric compositing runs before // shaveRenderCallback is called and it needs the output from the // render. So if volumetric compositing is on then we need to do // the render now, not later. // // That means that we cannot support both native illumination and // volumetric compositing at the same time. So if they're both on // then volumetric compositing wins. // // That just leaves us with the case where native illumination and // volumetric compositing are both off. In that case we can do the // render either here or in the callback. We choose to do it in the // callback because that way we can progressively display tiles in // the Render View as they render. bool doCamRenderNow = (doShaveCompsGlob && !doShaveComp2dGlob); shaveRender::bufferRender( shutter, camera, frame, true, true, doCamRenderNow, true ); if (!doCamRenderNow && ((shutter == shaveConstant::kShutterClose) || (shutter == shaveConstant::kShutterBoth))) { mRenderCallback->enableCamRender(camera); } // // Set up shaders on the lights to make them cast shadows for our // hair. // if (((shutter == shaveConstant::kShutterOpen) || (shutter == shaveConstant::kShutterBoth)) && (getShadowSource() == shaveConstant::kShaveShadows) && !shaveShadowMatteGlob) { setupShadowShaders(); } } } void shaveMayaRenderer::renderEnd() { if (mHaveVolumeShader) { if (visibleInReflectionsGlob || !doShaveComp2dGlob) MGlobal::executeCommand("shave_cleanupVolumeShader"); } // // Remove the compositing callback. // if (mRenderCallback) { MRenderCallback::removeCallback(mRenderCallback); delete mRenderCallback; mRenderCallback = NULL; } // // Undo the shadow matte setup. // if (shaveShadowMatteGlob && (getRenderMode() == shaveConstant::kBufferRender)) { MGlobal::executeCommand("shave_cleanupShadowMatte"); } // // Reset warning flags so that the next render will get warnings again. // mWarningGivenUnsupportedHairMode= false; mWarningGivenUnsupportedInstanceMode = false; shaveRenderer::renderEnd(); } void shaveMayaRenderer::renderInterrupted() { postCompositeCleanup(); } void shaveMayaRenderer::renderStart() { mTimeState = kAwaitingNothing; shaveRenderer::renderStart(); // // A shadow matte pass is only useful when there is buffered hair in // the scene. If that's the case, and a shadow matte pass has been // requested, then set it up. // if (shaveShadowMatteGlob && (getRenderMode() == shaveConstant::kBufferRender)) { MGlobal::executeCommand("shave_setupShadowMatte"); } // // Set up a render callback to handle compositing buffer renders onto // the final image. // // NOTE: There's a bug in Maya: it will crash if you reuse an // instance of a render callback object. So we have to // allocate a fresh one each time. // mRenderCallback = new shaveRenderCallback(this); MRenderCallback::addCallback(mRenderCallback, 255); } void shaveMayaRenderer::timeChange(const MTime& newTime) { switch (mTimeState) { case kAwaitingShutterOpen: doShutter(newTime, shaveConstant::kShutterOpen); if (shaveRenderCancelled) mTimeState = kAwaitingNothing; else mTimeState = kAwaitingCenterFrame; break; case kAwaitingCenterFrame: mTimeState = kAwaitingShutterClose; break; case kAwaitingShutterClose: doShutter(newTime, shaveConstant::kShutterClose); mTimeState = kAwaitingNothing; break; default: break; } } //************************************************ // // Helper Methods // //************************************************ void shaveMayaRenderer::postCompositeCleanup() { // // Remove any shadow shaders we may have created. // if (!shaveShadowMatteGlob && (getShadowSource() == shaveConstant::kShaveShadows)) { MGlobal::executeCommand("shave_disconnectShadowShaders()"); } // // It's now safe to free up Shave's render buffers. // shaveRender::bufferRenderCleanup(); } // // Hook up shadow handler nodes to the lights. // void shaveMayaRenderer::setupShadowShaders() { unsigned int i; unsigned int numLights = (unsigned int)shaveUtil::globalLightList.size(); for (i = 0; i < numLights; i++) { MDagPath lightPath = shaveUtil::globalLightList[i].path; MString cmd = "shave_connectShadowShader " + lightPath.fullPathName() + " "; // // MString supplies a '+=' operator for ints, but not a '+' operator. // cmd += (int)i; MGlobal::executeCommand(cmd); } }