// Shave and a Haircut // (c) 2019 Epic Games // US Patent 6720962 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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( 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; // } // //} //}