aboutsummaryrefslogtreecommitdiff
path: root/mayaPlug/shaveRenderCallback.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/shaveRenderCallback.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/shaveRenderCallback.cpp')
-rw-r--r--mayaPlug/shaveRenderCallback.cpp581
1 files changed, 581 insertions, 0 deletions
diff --git a/mayaPlug/shaveRenderCallback.cpp b/mayaPlug/shaveRenderCallback.cpp
new file mode 100644
index 0000000..2665824
--- /dev/null
+++ b/mayaPlug/shaveRenderCallback.cpp
@@ -0,0 +1,581 @@
+// Shave and a Haircut
+// (c) 2019 Epic Games
+// US Patent 6720962
+
+#include <float.h>
+#include <math.h>
+
+#include <maya/MDagPath.h>
+#include <maya/MFnDependencyNode.h>
+#include <maya/MFnDagNode.h>
+#include <maya/MFnTransform.h>
+#include <maya/MGlobal.h>
+#include <maya/MItDag.h>
+#include <maya/MMatrix.h>
+#include <maya/MPoint.h>
+#include <maya/MRenderUtil.h>
+#include <maya/MRenderView.h>
+#include <maya/MVector.h>
+
+#include "shaveDebug.h"
+#include "shaveGlobals.h"
+#include "shaveIO.h"
+#include "shaveMaya.h"
+#include "shaveMayaRenderer.h"
+#include "shaveRender.h"
+#include "shaveRenderCallback.h"
+
+
+extern void lightBufferFileCleanup();
+extern void lightProjectionNodeCleanup();
+
+// The different order of bytes passed by Maya to renderCallback() is
+// OSX-specific and is not an artifact of the byte-ordering on different
+// processors because both PPC and Intel on OSX use the same ordering,
+// which is different from that used by Linux and Windows.
+#ifdef OSMac_
+#define ALPHA 0
+#define BLUE 1
+#define GREEN 2
+#define RED 3
+#else
+#define BLUE 0
+#define GREEN 1
+#define RED 2
+#define ALPHA 3
+#endif
+
+#ifndef M_PI
+#define M_PI 3.14159926535
+#endif
+
+
+bool shaveRenderCallback::mDoingGeomRender = false;
+bool shaveRenderCallback::mDoTiles = false;
+const MRenderData* shaveRenderCallback::mMayaRenderData = 0;
+const shaveRender::Pixel* shaveRenderCallback::mShavePixels = 0;
+
+
+void shaveRenderCallback::disableCamRender()
+{
+ ENTER();
+ mDoingCamRender = false;
+ LEAVE();
+}
+
+
+void shaveRenderCallback::enableCamRender(const MDagPath& renderCam)
+{
+ ENTER();
+ mDoingCamRender = true;
+ mRenderCamera = renderCam;
+
+ LEAVE();
+}
+
+
+shaveRenderCallback::shaveRenderCallback(shaveMayaRenderer* renderer)
+: mDoingCamRender(false)
+, mRenderer(renderer)
+{}
+
+
+#if !defined(max)
+inline float max(float first, float second)
+{
+ return(first >= second ? first : second);
+}
+#endif
+
+#if !defined(min)
+inline float min(float first, float second)
+{
+ return(first <= second ? first : second);
+}
+#endif
+
+
+bool shaveRenderCallback::shadowCastCallback (const MRenderShadowData &data)
+{
+ return true;
+}
+
+
+bool shaveRenderCallback::renderCallback (const MRenderData &data)
+{
+ ENTER();
+
+ if (mDoingGeomRender)
+ {
+ mDoingGeomRender = false;
+ }
+ else
+ {
+ shaveRender::SceneInfo* shaveData = shaveRender::getSceneInfo();
+ const shaveGlobals::Globals& globals = shaveRender::getFrameGlobals();
+
+ if (mDoingCamRender && !shaveRenderCancelled)
+ {
+ initTileCallback(data, shaveData);
+
+ if (shaveRender::renderCameraView(mRenderCamera) != 0)
+ shaveRenderCancelled = true;
+
+ cleanupTileCallback();
+ }
+
+ if (globals.doCompositing
+ && !shaveRenderCancelled
+ && !shaveGlobals::getDefaultNode().isNull())
+ {
+ float maxSZ = -SHAVE_FAR_CLIP;
+ float minSZ = 0.0f;
+ float maxMZ = -SHAVE_FAR_CLIP;
+ float minMZ = 0.0f;
+ float* depthData = data.depthArr;
+
+ if (globals.composite2d)
+ {
+ unsigned char * imageData = data.rgbaArr;
+
+ shaveMaya::getRenderGlobals();
+
+ shaveRender::Pixel* sPixel = NULL;
+
+ if ((depthData != NULL) && (shaveData->shaveZBuffer != NULL))
+ {
+ float* MZ;
+ float SZ;
+ float nSZ;
+ float tmp;
+ MFloatPoint newPoint;
+ for(int y = 0; y < data.ysize; y++)
+ {
+ for(int x = 0; x < data.xsize; x++)
+ {
+ MZ = &depthData[data.xsize*y+x];
+ SZ = (float)shaveData->shaveZBuffer[
+ data.resX*(y + data.bottom) + x + data.left];
+ nSZ = shaveZtoMaya(SZ);
+ tmp = (*MZ == 0 ? 0:1.0f/(*MZ));
+ if(tmp != 0)
+ {
+ maxMZ = max(maxMZ, tmp);
+ minMZ = min(minMZ, tmp);
+ }
+ if(nSZ != 0)
+ {
+ maxSZ = max(maxSZ, -SZ);
+ minSZ = min(minSZ, -SZ);
+ }
+ if((nSZ < *MZ && nSZ != 0.0f) || *MZ == 0.0f)
+ *MZ = (float)nSZ;
+ }
+ }
+ }
+
+ if (shaveData->shaveRenderPixels)
+ {
+ unsigned int carryA;
+ unsigned int carryB;
+ unsigned int carryG;
+ unsigned int carryR;
+ int i;
+ unsigned int iALPHA = ALPHA * data.bytesPerChannel;
+ unsigned int iBLUE = BLUE * data.bytesPerChannel;
+ unsigned int iGREEN = GREEN * data.bytesPerChannel;
+ unsigned int iRED = RED * data.bytesPerChannel;
+ unsigned char* rPixel;
+ unsigned int temp;
+
+ for (int y = 0; y < data.ysize; y++)
+ {
+ for (int x=0; x < data.xsize; x++)
+ {
+ rPixel = imageData + ((data.xsize*y+x)*4*data.bytesPerChannel);
+ sPixel = &shaveData->shaveRenderPixels[data.resX
+ * (y + data.bottom) + x + data.left];
+
+ float sAlpha = (float)sPixel->a / 255.0f;
+ float sTransp = 1.0f - sAlpha;
+
+ //
+ // Our own version of infinite precision
+ // arithmetic.
+ //
+ carryA = carryB = carryG = carryR = 0;
+
+#if defined(IRIX) || defined(__ppc__)
+ for (i = data.bytesPerChannel-1; i >= 0; i--)
+#else
+ for (i = 0; i < data.bytesPerChannel; i++)
+#endif
+ {
+ temp = (unsigned int)sPixel->r
+ + (unsigned int)(sTransp*(float)rPixel[iRED+i])
+ + carryR;
+
+ carryR = temp >> 8;
+
+ if ((data.bytesPerChannel == 1) && (temp > 255))
+ rPixel[iRED+i] = 255;
+ else
+ rPixel[iRED+i] = (unsigned char)(temp & 0xff);
+
+ temp = (unsigned int)sPixel->g
+ + (unsigned int)(sTransp*(float)rPixel[iGREEN+i])
+ + carryG;
+
+ carryG = temp >> 8;
+
+ if ((data.bytesPerChannel == 1) && (temp > 255))
+ rPixel[iGREEN+i] = 255;
+ else
+ rPixel[iGREEN+i] = (unsigned char)(temp & 0xff);
+
+ temp = (unsigned int)sPixel->b
+ + (unsigned int)(sTransp*(float)rPixel[iBLUE+i])
+ + carryB;
+
+ carryB = temp >> 8;
+
+ if ((data.bytesPerChannel == 1) && (temp > 255))
+ rPixel[iBLUE+i] = 255;
+ else
+ rPixel[iBLUE+i] = (unsigned char)(temp & 0xff);
+
+ temp = (unsigned int)sPixel->a
+ + (unsigned int)rPixel[iALPHA+i]
+ + carryA;
+
+ carryA = temp >> 8;
+
+ if ((data.bytesPerChannel == 1) && (temp > 255))
+ rPixel[iALPHA+i] = 255;
+ else
+ rPixel[iALPHA+i] = (unsigned char)(temp & 0xff);
+ }
+
+ //
+ // In theory, all of Shave's color values will
+ // lie in the range 0 - alpha, so it should be
+ // impossible for any of the values to
+ // overflow. However, let's be paranoid: if we
+ // got a carry out the MSB of any channel, then
+ // set that channel's MSB to its max value of
+ // 255. (We could set all of the bytes in the
+ // channel, but MSB should be sufficient.)
+ //
+#if defined(IRIX) || defined(__ppc__)
+ i++;
+#else
+ i--;
+#endif
+#if 0
+ //
+ // This *should* have had the same effect as
+ // the more expensive 'temp > 255' checks in
+ // the loop above, but Joe says that replacing
+ // these with the checks got rid of 'pookies':
+ // multicolored artifacts at the ends of
+ // semi-transparent hairs.
+ //
+ if (carryR) rPixel[iRED+i] = 255;
+ if (carryG) rPixel[iGREEN+i] = 255;
+ if (carryB) rPixel[iBLUE+i] = 255;
+ if (carryA) rPixel[iALPHA+i] = 255;
+#endif
+ }
+ }
+ }
+ }
+ //
+ // Even if the hair itself is composited into the image within
+ // a 3d volumetric shader, we still need to give Maya our depth
+ // values so that post-processes such as depth of field will
+ // work.
+ //
+ else
+ {
+ if ((depthData != NULL)
+ && (shaveData->shaveZBuffer != NULL)
+ && globals.doCompositing)
+ {
+ float* MZ;
+ float SZ;
+ float nSZ;
+ float tmp;
+
+ for(int y = 0; y < data.ysize; y++)
+ {
+ for(int x = 0; x < data.xsize; x++)
+ {
+ MZ = &depthData[data.xsize*y+x];
+ SZ = (float)shaveData->shaveZBuffer[
+ data.resX*(y + data.bottom) + x + data.left];
+ nSZ = shaveZtoMaya(SZ);
+ tmp = (*MZ == 0 ? 0:1.0f/(*MZ));
+ if(tmp != 0)
+ {
+ maxMZ = max(maxMZ, tmp);
+ minMZ = min(minMZ, tmp);
+ }
+ if(nSZ != 0)
+ {
+ maxSZ = max(maxSZ, -SZ);
+ minSZ = min(minSZ, -SZ);
+ }
+ if((nSZ < *MZ && nSZ != 0.0f) || *MZ == 0.0f)
+ *MZ = (float)nSZ;
+ }
+ }
+ }
+ }
+
+ if (globals.verbose)
+ {
+#ifdef OSMac_
+ //
+ // Panther broke cerr somehow so that it crashes if you use
+ // it to non-zero float or double values. So let's try our
+ // old pal stderr, instead.
+ //
+ if (stderr)
+ {
+ fprintf(
+ stderr,
+ "Z Bounds: Maya: %f, %f, Shave: %f, %f\n",
+ -maxMZ, -minMZ, -maxSZ, -minSZ
+ );
+ }
+#else
+ cerr << "Z Bounds: Maya: " << -maxMZ << ", " << -minMZ
+ << ", Shave: " << -maxSZ << ", " << -minSZ << endl;
+#endif
+ }
+ }
+ }
+
+ if (mRenderer) mRenderer->postCompositeCleanup();
+
+ RETURN(true);
+}
+
+
+bool shaveRenderCallback::postProcessCallback (const MRenderData &data)
+{
+ return true;
+}
+
+
+float shaveZtoMaya(float shaveVal)
+{
+ if(shaveVal == SHAVE_FAR_CLIP)
+ return 0.0f;
+ else
+ return -1.0f/shaveVal;
+}
+
+
+MStatus shaveRenderCallback::getRenderCamera(
+ const MRenderData& renderData, MDagPath& renderCamPath
+)
+{
+ //
+ // Unfortunately, there's no way to simply ask Maya which camera is
+ // currently being rendered. So instead we have to walk through all
+ // available cameras and find out which one's parameters most closely
+ // match those which are available to use through the passed-in
+ // MRenderData object.
+ //
+ MItDag iter(MItDag::kDepthFirst, MFn::kCamera);
+ MDagPath testCamPath;
+ float aspectDiff;
+ float fovDiff;
+ const float kTolerance = 0.001f;
+
+ for (; !iter.isDone(); iter.next())
+ {
+ iter.getPath(testCamPath);
+
+ MFnCamera cameraFn(testCamPath);
+
+ //
+ // Make sure that the perspective/ortho and other settings match.
+ //
+ aspectDiff = renderData.aspectRatio - (float)cameraFn.aspectRatio();
+
+ fovDiff = renderData.fieldOfView
+ - (float)cameraFn.horizontalFieldOfView();
+
+ if ((cameraFn.isOrtho() != renderData.perspective)
+ && (fabs(aspectDiff) < kTolerance)
+ && (fabs(fovDiff) < kTolerance))
+ {
+ //
+ // The preliminaries look good, now let's see if the camera's
+ // eye point is in the right place.
+ //
+ MPoint tempPoint = cameraFn.eyePoint(MSpace::kWorld);
+
+ MFloatPoint eyePoint(
+ (float)tempPoint.x,
+ (float)tempPoint.y,
+ (float)tempPoint.z
+ );
+
+ //
+ // For some inexplicable reason, MRenderData::worldToEyeMatrix
+ // is an MFloatMatrix rather than a normal MMatrix. That means
+ // that we lose a lot of precision in the calculations below.
+ // Enough so that even our relatively generous kTolerance value
+ // may end up rejecting valid matches.
+ //
+ // So let's instead calculate a tolerance which is in keeping
+ // with the overall scale of the vectors involved.
+ //
+ float scaledTolerance = eyePoint.distanceTo(MFloatPoint::origin)
+ / 1000000.0f;
+
+ //
+ // Convert it to the render camera's local space.
+ //
+ eyePoint *= renderData.worldToEyeMatrix;
+ if (eyePoint.isEquivalent(renderData.eyePoint, scaledTolerance))
+ {
+ //
+ // The eye point is right, now let's check the viewing
+ // direction.
+ //
+ MFloatVector viewDir(cameraFn.viewDirection());
+
+ if (viewDir.isEquivalent(
+ renderData.viewDirection, scaledTolerance))
+ {
+ renderCamPath = testCamPath;
+
+ return MS::kSuccess;
+ }
+ }
+ }
+ }
+
+ MGlobal::displayError(
+ "shaveRenderCallback::getRenderCamera - could not find the render camera."
+ );
+
+ return MS::kFailure;
+}
+
+
+void shaveRenderCallback::initTileCallback(
+ const MRenderData& mayaRenderData, shaveRender::SceneInfo* shaveRenderData
+)
+{
+ mMayaRenderData = &mayaRenderData;
+ mShavePixels = shaveRenderData->shaveRenderPixels;
+ mDoTiles = true;
+}
+
+
+void shaveRenderCallback::cleanupTileCallback()
+{
+ mMayaRenderData = 0;
+ mShavePixels = 0;
+ mDoTiles = false;
+}
+
+
+// This gets called every time Shave has finished rendering a tile.
+void shaveRenderCallback::tileRendered(
+ unsigned int left, unsigned int right, unsigned int bottom, unsigned int top
+)
+{
+ if (mDoTiles && MRenderView::doesRenderEditorExist())
+ {
+ // Find the intersection between Shave's tile and Maya's render
+ // region.
+ if (left < mMayaRenderData->left)
+ left = mMayaRenderData->left;
+ if (right >= (unsigned int)(mMayaRenderData->left + mMayaRenderData->xsize))
+ right = mMayaRenderData->left + mMayaRenderData->xsize - 1;
+ if (bottom < mMayaRenderData->bottom)
+ bottom = mMayaRenderData->bottom;
+ if (top >= (unsigned int)(mMayaRenderData->bottom + mMayaRenderData->ysize))
+ top = mMayaRenderData->bottom + mMayaRenderData->ysize - 1;
+
+ if ((right < left) || (top < bottom)) return;
+
+ const unsigned xsize = right - left + 1;
+ const unsigned ysize = top - bottom + 1;
+
+ // Create an image buffer for the render view.
+ RV_PIXEL* viewPixels = new RV_PIXEL[xsize * ysize];
+
+ //---------------------------------------------------------------
+ // Compose the shave pixels onto Maya's pixels and store in the
+ // render view buffer.
+ //---------------------------------------------------------------
+ const unsigned int iALPHA = ALPHA * mMayaRenderData->bytesPerChannel;
+ const unsigned int iBLUE = BLUE * mMayaRenderData->bytesPerChannel;
+ const unsigned int iGREEN = GREEN * mMayaRenderData->bytesPerChannel;
+ const unsigned int iRED = RED * mMayaRenderData->bytesPerChannel;
+ const unsigned int mayaBytesPerPixel = 4 * mMayaRenderData->bytesPerChannel;
+
+ // If there are multiple bytes per channel, we only care about the
+ // most significant byte.
+#if defined(__ppc__)
+ unsigned int mayaMSBOffset = 0;
+#else
+ unsigned int mayaMSBOffset = mMayaRenderData->bytesPerChannel - 1;
+#endif
+ unsigned int mayaByteIdx = 0;
+ unsigned char* mayaBytes = mMayaRenderData->rgbaArr;
+ unsigned int shavePixelIdx = 0;
+ float temp;
+ unsigned int viewPixelIdx = 0;
+
+ for (unsigned int y = bottom; y <= top; ++y)
+ {
+ shavePixelIdx = mMayaRenderData->resX * y + left;
+ mayaByteIdx = (mMayaRenderData->xsize * (y - mMayaRenderData->bottom)
+ + left - mMayaRenderData->left) * mayaBytesPerPixel
+ + mayaMSBOffset;
+
+ for (unsigned int x = left; x <= right; ++x)
+ {
+ float shaveAlpha = (float)mShavePixels[shavePixelIdx].a / 255.0f;
+ float shaveTransp = 1.0f - shaveAlpha;
+
+ temp = (float)mShavePixels[shavePixelIdx].r
+ + shaveTransp * (float)mayaBytes[mayaByteIdx + iRED];
+ if (temp > 255.0f) temp = 255.0f;
+ viewPixels[viewPixelIdx].r = temp;
+
+ temp = (float)mShavePixels[shavePixelIdx].g
+ + shaveTransp * (float)mayaBytes[mayaByteIdx + iGREEN];
+ if (temp > 255.0f) temp = 255.0f;
+ viewPixels[viewPixelIdx].g = temp;
+
+ temp = (float)mShavePixels[shavePixelIdx].b
+ + shaveTransp * (float)mayaBytes[mayaByteIdx + iBLUE];
+ if (temp > 255.0f) temp = 255.0f;
+ viewPixels[viewPixelIdx].b = temp;
+
+ temp = shaveAlpha + (float)mayaBytes[mayaByteIdx + iALPHA];
+ if (temp > 255.0f) temp = 255.0f;
+ viewPixels[viewPixelIdx].a = temp;
+
+ mayaByteIdx += mayaBytesPerPixel;
+ ++shavePixelIdx;
+ ++viewPixelIdx;
+ }
+ }
+
+ // Update the render view with the new buffer.
+ MRenderView::updatePixels(left, right, bottom, top, viewPixels);
+ MRenderView::refresh(left, right, bottom, top);
+
+ delete [] viewPixels;
+ }
+}