You can create a plug-in with an MRenderOverride to completely replace the rendering pipeline. The plug-in can use standard operations (or passes) in the pipeline, as well as add other custom operations. To do this, the plug-in should first obtain the operation set that represents the current pipeline, then modify it by adding, inserting, or removing operations.
The standard operations are controlled by a variety of settings and filters that determine how the pass operates. This provides ultimate control over the viewport, and provides a few conveniences for filtering the objects displayed, the display modes used, the material overrides, the post processes, and so forth.
The advantage of creating an MRenderOverride plug-in by populating the operation list is that the plug-in does not need to include target creation or management in its implementation. This is handled automatically by the system and pushed down to the operation level.
The drawback to using an MRenderOverride is that it must override the entire pipeline, and any customization to the pipeline must appear as a whole new renderer. For more information about MRenderOverride, see Rendering Overrides.
The following classes and interfaces enable you to create an MRenderOverride as outlined above.
The MRenderOperationList class holds and takes ownership of a collection of MRenderOperations. This class has standard list methods for indexing, adding, removing, and replacing operations in the list, and also includes methods to take ownership of operations from a list.
The MRenderer::getStandardViewportOperations() interface allows you to fill a list with the standard viewport operations used for non-override drawing.
The MRenderOverride protected member:
MRenderOperationList mOperations
A class deriving from MRenderOverride simply needs to populate the list with operations, either by getting the standard list of operations, by adding their own operations, or a combination of both.
//Get the standard list of operations MHWRender::MRenderer::theRenderer()->getStandardViewportOperations(mOperations); //Create a custom quad render operation PostQuadRender* swirlOp = new PostQuadRender( kSwirlPassName, "FilterSwirl", "" ); swirlOp->setEnabled(false); // swirl is disabled by default //Insert swirlOp in the pipeline after the kStandardSceneName operation mOperations.insertAfter(MHWRender::MRenderOperation::kStandardSceneName, swirlOp);
A few standard operation names have been predefined for ease of use. These names can be used to locate operations in a standard operation list.
Target management is handled at the operation level to allow operations to be more autonomous and be self-describing when added to a larger operation list.
A class deriving from MRenderOperation must do the following to complete its implementation:
MRenderOperation has two protected member variables that allow you to perform the first step above:
MStringArray mInputTargetNames
MStringArray mOutputTargetNames
To declare the inputs and outputs, an operation must add the name (that is, the semantic) of the targets it want to use. A few common target types have been predefined in the MRenderOperation class:
static const MString kColorTargetName; static const MString kDepthTargetName; static const MString kAuxiliaryTargetName; static const MString kAuxiliaryTarget2Name; static const MString kAuxiliaryTarget3Name; static const MString kAuxiliaryTarget4Name; static const MString kAuxiliaryDepthTargetName;
By default, all MRenderOperations use the standard color and depth target names for their inputs, so that they connect automatically to other operations in the list that write to or read from the standard targets. This is all that is required for operations that simply write to the currently active target, and is implemented for you in the base class.
For example, the following code snippet declares two default inputs and two default outputs:
mInputTargetNames.append(kColorTargetName); mInputTargetNames.append(kDepthTargetName); mOutputTargetNames.append(kColorTargetName); mOutputTargetNames.append(kDepthTargetName);
MRenderOperation classes that want to read from or write to multiple targets can override the default inputs and outputs by clearing the lists and adding their own.
mInputTargetNames.append(kAuxiliaryTargetName); mInputTargetNames.append(kAuxiliaryDepthTargetName); mInputTargetNames.append(“sceneTarget”); mInputTargetNames.append(“sceneDepthTarget”); mOutputTargetNames.append(kColorTargetName); mOutputTargetNames.append(kDepthTargetName);
They can then supply descriptions for the named input targets in their custom passes. In the following example, the descriptions are copied from the auxiliary targets in order for the multi-sample anti-aliasing properties and size to match.
bool PostQuadRender::getInputTargetDescription(const MString& name, MHWRender::MRenderTargetDescription& description) { if (name == “sceneTarget”) { MHWRender::MRenderTarget* outTarget = getInputTarget(kAuxiliaryTargetName); if (outTarget) outTarget->targetDescription(description); description.setName("_post_target_1"); return true; } else if (name == “sceneDepthTarget”) { MHWRender::MRenderTarget* outTarget = getInputTarget(kAuxiliaryDepthTargetName); if (outTarget) outTarget->targetDescription(description); description.setName("_post_target_depth"); return true; } return false; }
An MRenderOverride plug-in can then connect operations by ensuring that the names match.
//Get MRenderOperationList of the standard viewport operations MHWRender::MRenderer::theRenderer()->getStandardViewportOperations(mOperations); //Get the index of the operation kStandardSceneName int sceneOpID = mOperations.indexOf(MHWRender::MRenderOperation::kStandardSceneName); //Set sceneOp to point to the kStandardSceneName operation in the MRenderOperationList MRenderOperation* sceneOp = mOperations[sceneOpID]; //Rename the output target of the kStandardSceneName operation from kColorTargetName to sceneTarget sceneOp->renameOutputTarget(MHWRender::MRenderOperation::kColorTargetName, “sceneTarget”); //Create the new MRenderOperation swirlOp PostQuadRender* swirlOp = new PostQuadRender( kSwirlPassName, "FilterSwirl", "" ); // Insert swirlOp after the operation named kStandardSceneName // Because swirlOp has an input named sceneTarget, and you have renamed the output target // of kStandardSceneName to sceneTarget, the two operations will share the same target. // The target is constructed from the description provided by the swirl // operation and is the target that the scene operation should render to. mOperations.insertAfter(MHWRender::MRenderOperation::kStandardSceneName, swirlOp);
To specify the start index and number of targets an MRenderOperation should write to, do as follows:
int PostQuadRender::writableTargets(unsigned int& count) { count = 2; return 0; }