geometryOverrideHighPerformance/geometryOverrideHighPerformance.cpp

geometryOverrideHighPerformance/geometryOverrideHighPerformance.cpp
//-
// ==========================================================================
// Copyright 2018 Autodesk, Inc. All rights reserved.
//
// Use of this software is subject to the terms of the Autodesk
// license agreement provided at the time of installation or download,
// or which otherwise accompanies this software in either electronic
// or hard copy form.
// ==========================================================================
//+
//
// This plug-in shows how to use MPxGeometryOverride to render geometry with the best performance
// as possible. It will register a new type of node "geometryOverrideHighPerformance_shape" and associate it to
// a MPxGeometryOverride.
//
// The GeometryOverrideHighPerformance_shape class derives from MPxSurfaceShape and add two attributes to the node.
// The first attribute is the size of the shape that will be used by the geometry override to scale
// the vertex position data. The second attribute is a input mesh that will be use by the geometry
// override to render. The GeometryOverrideHighPerformance_shape also enable the shape to be selectable in
// Viewport 2.0.
//
// The GeometryOverrideHighPerformance class derives from MPxGeometryOverride and shows how to
// create / update different render items and fill vertex / index buffers to render the geometry
// of geometryOverrideHighPerformance_shape. This class try to be smart as possible to only update the geometry
// data as needed.
//
// This plugin uses two different techniques to optimize the performance of the GeometryOverrideHighPerformance_shape
// draw in VP2.
//
// Technique 1:
// In order to know when the GeometryOverrideHighPerformance render items need to be updated
// the factors affecting how render items are drawn are stored. When updateRenderItems()
// is called the current values can be compared against the previously used values,
// allowing Maya to call updateRenderItems() only when necessary.
//
// Technique 2:
// In order to know when the GeometryOverrideHighPerformance geometry needs to be updated the
// GeometryOverrideHighPerformance_shape node look into dirty propagation and the evaluation manager
// to track when attributes which affect geometry change. GeometryOverrideHighPerformance can then query
// GeometryOverrideHighPerformance_shape to find out if a geometry update is necessary.
//
// Evaluation Manager Parallel Update:
// This plugin enable the Evaluation Manager Parallel Update by returning true from
// supportsEvaluationManagerParallelUpdate(). Evaluation Manager Parallel Update is a
// performance optimization which allows the updateDG() and populateGeometry() step
// of the draw preparation to occur during Evaluation Graph Execution rather than during
// VP2 refresh. That's means the Maya nodes begin preparing render data for
// VP2 immediately after a node is evaluated, and before the rest of the graph has
// finished evaluation. This improves performance by allowing updateDG() and
// populateGeometry() calls of several plug-ins to run in parallel with each other
// and with Evaluation Manager dependency graph evaluation.
//
// Evaluation Caching:
// GeometryOverrideHighPerformance is fully compatible with Evaluation Caching. Supporting Evaluation Caching
// adds some additional, subtle requirements on the plug-in node. Evaluation Caching
// automatically caches data for two types of attributes: output attributes and dynamic
// attributes, where output attributes are defined as any attribute which is affected by
// another attribute on the node. The affects relationship can be created either by
// calling MPxNode::attributeAffects() or by returning affected attributes from
// MPxNode::setDependentsDirty().
//
// By default, Maya doesn't add a custom shape (MPxSurfaceShape) into the cache system.
// So, that means we need to add the node type into the system ourselves
// with mel command. See geometryOverrideHighPerformance.mel.
//
// Using Evaluation Caching with Evaluation Manager Parallel Update has an additional issue
// to be aware of. When using Evaluation Manager Parallel Update some MPxGeometryOverride
// methods are called after the corresponding DAG node has been evaluated but before the
// full evaluation graph has been evaluated. When Evaluation Caching is enabled only
// cached DG values may be read before the full evaluation graph has been evaluated. Therefore,
// when using Evaluation Manager Parallel Update and Evaluation Caching only cached DG values
// may be read. Attempting to read an uncached value before evaluation finishes results in
// undefined behavior (incorrect data or crashes).
//
// VP2 Custom Caching:
// GeometryOverrideHighPerformance is fully compatible with VP2 Custom Caching. When using VP2
// custom caching the MPxGeometryOverride may be invoked in the normal context or in a
// background thread using another context. If playback and background evaluation occur
// concurrently Maya guarantees that all of the MPxGeoemtryOverride methods called for a
// context occur atomically without being interleaved with MPxGeometryOverride methods
// for the same DAG object in a different context.
//
// To use this plug-in run the following mel or python commands once the plugin is loaded
//
// Mel script:
//
// //
// // Add the geometryOverrideHighPerformance_shape node type in evaluation caching
// //
// cacheEvaluator -newFilter "nodeTypes"
// -newFilterParam "types=+geometryOverrideHighPerformance_shape"
// -newAction "enableEvaluationCache";
//
// //
// // Add geometryOverrideHighPerformance_shape node type in VP2 SW Cache
// //
// cacheEvaluator -newFilter "nodeTypes"
// -newFilterParam "types=+geometryOverrideHighPerformance_shape"
// -newAction "enableVP2Cache"
// -newActionParam "useHardware=0";
//
// //
// // Add geometryOverrideHighPerformance_shape node type in VP2 HW Cache
// //
// cacheEvaluator -newFilter "nodeTypes"
// -newFilterParam "types=+geometryOverrideHighPerformance_shape"
// -newAction "enableVP2Cache"
// -newActionParam "useHardware=1";
//
// $shapeNodeName = `createNode geometryOverrideHighPerformance_shape`;
// sets -add initialShadingGroup $shapeNodeName;
//
// Python script:
//
// maya.cmds.cacheEvaluator(newFilter="nodeTypes", newFilterParam = "types=+geometryOverrideHighPerformance_shape" , newAction = "enableEvaluationCache")
// maya.cmds.cacheEvaluator(newFilter="nodeTypes", newFilterParam = "types=+geometryOverrideHighPerformance_shape" , newAction = "enableVP2Cache", newActionParam = "useHardware=0")
// maya.cmds.cacheEvaluator(newFilter="nodeTypes", newFilterParam = "types=+geometryOverrideHighPerformance_shape" , newAction = "enableVP2Cache", newActionParam = "useHardware=1")
//
// shapeNodeName = maya.cmds.createNode('geometryOverrideHighPerformance_shape')
// maya.cmds.sets(shapeNodeName, add = 'initialShadingGroup')
//
// Or run the geometryOverrideHighPerformance_shape.mel
//
#include "geometryOverrideHighPerformance.h"
#include "geometryOverrideHighPerformanceHelper.h"
#include <maya/MFnPlugin.h>
#include <maya/MDrawRegistry.h>
#include <maya/MShaderManager.h>
#include <maya/MSelectionMask.h>
#include <maya/MEvaluationNode.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MFnDagNode.h>
#include <maya/MDistance.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MPlugArray.h>
#include <maya/MGeometryExtractor.h>
#include <maya/MGlobal.h>
#include <maya/MDGModifier.h>
static void instancingChangedCallback(MDagPath& child, MDagPath& parent, void* clientData)
{
MGlobal::displayWarning("GeometryOverrideHighPerformance does't support instancing !");
}
//===========================================================================
//
// GeometryOverrideHighPerformance_shape implementation
//
//===========================================================================
MTypeId GeometryOverrideHighPerformance_shape::fsId = MTypeId(0x8003F);
MObject GeometryOverrideHighPerformance_shape::fsSize;
MObject GeometryOverrideHighPerformance_shape::fsInputMesh;
MObject GeometryOverrideHighPerformance_shape::fsInputMeshBoundingBoxMin;
MObject GeometryOverrideHighPerformance_shape::fsInputMeshBoundingBoxMax;
MObject GeometryOverrideHighPerformance_shape::fsSizeChangedSinceVP2Update;
MObject GeometryOverrideHighPerformance_shape::fsInputMeshChanged;
MObject GeometryOverrideHighPerformance_shape::fsInputMeshGeoChanged;
MObject GeometryOverrideHighPerformance_shape::fsMaterialChanged;
Float3Array GeometryOverrideHighPerformance_shape::fPositions;
Float3Array GeometryOverrideHighPerformance_shape::fNormals;
Float3Array GeometryOverrideHighPerformance_shape::fTangents;
Float3Array GeometryOverrideHighPerformance_shape::fBiTangents;
Float2Array GeometryOverrideHighPerformance_shape::fTextureCoords;
IndexList GeometryOverrideHighPerformance_shape::fIndices;
MBoundingBox GeometryOverrideHighPerformance_shape::fBoundingBox;
GeometryOverrideHighPerformance_shape::GeometryOverrideHighPerformance_shape()
{
}
MStatus GeometryOverrideHighPerformance_shape::initialize()
{
MStatus stat;
//
// Create the size attribute
//
fsSize = unitFn.create("size", "sz", MFnUnitAttribute::kDistance);
unitFn.setDefault(1.0);
stat = addAttribute(fsSize);
if (!stat) {
stat.perror("Failed to create the size attribute !");
return stat;
}
//
// Create the input mesh attribute
//
fsInputMesh = typedFn.create("inputMesh", "inm", MFnData::kMesh, MObject::kNullObj);
stat = addAttribute(fsInputMesh);
if (!stat) {
stat.perror("Failed to create the inputMesh attribute !");
return stat;
}
//
// bbox attributes
//
MFnNumericAttribute numericAttributeFn;
fsInputMeshBoundingBoxMin = numericAttributeFn.create("inputBoundMin", "inbn", MFnNumericData::k3Double, 0);
numericAttributeFn.setArray(false);
numericAttributeFn.setUsesArrayDataBuilder(false);
numericAttributeFn.setHidden(false);
numericAttributeFn.setKeyable(false);
addAttribute(fsInputMeshBoundingBoxMin);
fsInputMeshBoundingBoxMax = numericAttributeFn.create("inputBoundMax", "inbm", MFnNumericData::k3Double, 0);
numericAttributeFn.setArray(false);
numericAttributeFn.setUsesArrayDataBuilder(false);
numericAttributeFn.setHidden(false);
numericAttributeFn.setKeyable(false);
addAttribute(fsInputMeshBoundingBoxMax);
attributeAffects(fsInputMeshBoundingBoxMin, MPxSurfaceShape::nodeBoundingBoxMin);
attributeAffects(fsInputMeshBoundingBoxMax, MPxSurfaceShape::nodeBoundingBoxMax);
attributeAffects(fsSize, MPxSurfaceShape::nodeBoundingBoxMin);
attributeAffects(fsSize, MPxSurfaceShape::nodeBoundingBoxMax);
/*
VP2 Custom Caching: When using VP2 custom caching the MPxGeometryOverride associated
with the node might be invoked in the normal context or in the background
context. If playback and background evaluation occur concurrently Maya guarantees
that all of the MPxGeometryOverride methods called for a context occur atomically
without being interleaved with MPxGeometryOverride methods for the same object in
a different context.
However, Maya does not make any timing guarantee between the call(s) to evaluate the
MPxNode and calls to the MPxGeometryOverride. For example, a postEvaluation call
in the background evaluation thread may occur at the same time that the foreground
thread is using the MPxGeometryOverride.
This means that any communication which occurs between the MPxNode during evaluation
and the MPxGeometryOverride must be context aware. The communication channel must
use different storage for each context. The easiest way to implement this to use
internal attributes on the MPxNode that the MPxGeometryOverride has access to.
Don't create any affects relationships because those attribute doesn't use any
Maya dirty management or evaluation. They are used only by getting the MDataHandle
directly from the MDataBlock using outputValue().
*/
fsSizeChangedSinceVP2Update = attrFn.create("sizeChangedSinceVP2Update", "sd", MFnNumericData::kBoolean, true);
attrFn.setStorable(false);
attrFn.setHidden(true);
attrFn.setConnectable(false);
stat = addAttribute(fsSizeChangedSinceVP2Update);
if (!stat) {
stat.perror("Failed to create the sizeChangedSinceVP2Update attribute !");
return stat;
}
fsInputMeshChanged = attrFn.create("inputMeshChangedSinceVP2Update", "imd", MFnNumericData::kBoolean, true);
attrFn.setStorable(false);
attrFn.setHidden(true);
attrFn.setConnectable(false);
stat = addAttribute(fsInputMeshChanged);
if (!stat) {
stat.perror("Failed to create the inputMeshChangedSinceVP2Update attribute !");
return stat;
}
fsInputMeshGeoChanged = attrFn.create("inputMeshGeoChangedSinceVP2Update", "img", MFnNumericData::kBoolean, true);
attrFn.setStorable(false);
attrFn.setHidden(true);
attrFn.setConnectable(false);
stat = addAttribute(fsInputMeshGeoChanged);
if (!stat) {
stat.perror("Failed to create the inputMeshGeoChangedSinceVP2Update attribute !");
return stat;
}
fsMaterialChanged = attrFn.create("materialChangedSinceVP2Update", "mcd", MFnNumericData::kBoolean, true);
attrFn.setStorable(false);
attrFn.setHidden(true);
attrFn.setConnectable(false);
stat = addAttribute(fsMaterialChanged);
if (!stat) {
stat.perror("Failed to create the materialChangedSinceVP2Update attribute !");
return stat;
}
createSphere(1, 50, 50,
GeometryOverrideHighPerformance_shape::fPositions,
GeometryOverrideHighPerformance_shape::fNormals,
GeometryOverrideHighPerformance_shape::fTangents,
GeometryOverrideHighPerformance_shape::fBiTangents,
GeometryOverrideHighPerformance_shape::fTextureCoords,
GeometryOverrideHighPerformance_shape::fIndices,
GeometryOverrideHighPerformance_shape::fBoundingBox);
return MS::kSuccess;
}
GeometryOverrideHighPerformance_shape::~GeometryOverrideHighPerformance_shape()
{
MMessage::removeCallback(mInstanceAddedCallbackId);
}
// ============================================================
//
// When instances of this node are created internally, the MObject associated
// with the instance is not created until after the constructor of this class
// is called. This means that no member functions of MPxSurfaceShape can
// be called in the constructor.
// The postConstructor solves this problem. Maya will call this function
// after the internal object has been created.
// As a general rule do all of your initialization in the postConstructor.
//
// Note : For more information, see MPxSurfaceShape::postConstructor()
//
// ============================================================
void GeometryOverrideHighPerformance_shape::postConstructor()
{
// This call allows the shape to have shading groups assigned
setRenderable(true);
//
// Add a callback that will be called when instance are added.
//
MDagPath path;
MDagPath::getAPathTo(thisMObject(), path);
mInstanceAddedCallbackId = MDagMessage::addInstanceAddedDagPathCallback(const_cast<MDagPath&>(path), &instancingChangedCallback);
}
// ============================================================
//
// This method is overriden to support interactive object selection in Viewport 2.0
//
// Returns The selection mask of the shaper.
//
// Note : For more information, see MPxSurfaceShape::getShapeSelectionMask()
//
// ============================================================
MSelectionMask GeometryOverrideHighPerformance_shape::getShapeSelectionMask() const
{
return MSelectionMask(selType);
}
// ============================================================
//
// Returns the bounding box for this object.
// It is a good idea not to recompute here as this funcion is called often.
//
// Note : This function is only called if MPxSurfaceShape::isBounded() return true.
// For more information, see MPxSurfaceShape::boundingBox()
//
// ============================================================
MBoundingBox GeometryOverrideHighPerformance_shape::boundingBox() const
{
MObject thisNode = thisMObject();
//
// Get the value of the size attribute
//
MPlug sizePlug(thisNode, fsSize);
MDistance sizeVal;
sizePlug.getValue(sizeVal);
const float multiplier = sizeVal.asCentimeters();
//
// Return the scaled input mesh bounding box if there is a input
// mesh connected to the node
//
if(fUseInputMesh)
{
MDataBlock block = const_cast<GeometryOverrideHighPerformance_shape*>(this)->forceCache();
double3& boundingBoxMin = block.inputValue(fsInputMeshBoundingBoxMin).asDouble3();
double3& boundingBoxMax = block.inputValue(fsInputMeshBoundingBoxMax).asDouble3();
return MBoundingBox(MPoint(boundingBoxMin) * multiplier, MPoint(boundingBoxMax) * multiplier);
}
//
// Return the scaled hardcoded mesh bounding box
//
return MBoundingBox(fBoundingBox.min() * multiplier, fBoundingBox.max() * multiplier);
}
// ============================================================
//
// This method gets called when connections are made to attributes
// of this node.
//
// Note : For more information, see MPxNode::connectionMade()
//
// ============================================================
MStatus GeometryOverrideHighPerformance_shape::connectionMade(const MPlug& plug, const MPlug& otherPlug, bool asSrc)
{
// If the plug get connected with a shading engine, set fMaterialChanged to true,
// so in the geometry override updateRenderItems() we can return true to force
// the render items to be updated.
if (asSrc &&
otherPlug.node().hasFn(MFn::kShadingEngine) &&
(plug.attribute() == instObjGroups || plug.attribute() == objectGroups))
{
setMaterialChangedSinceVP2Update(true);
}
// When a input mesh is connected to the node, we must also connect the input mesh
// bounding box attribute to this node to be able to get easly the mesh bounding box.
if (!asSrc &&
otherPlug.node().hasFn(MFn::kMesh) &&
(plug.attribute() == fsInputMesh))
{
fUseInputMesh = true;
setInputMeshChangedSinceVP2Update(true);
MObject thisNode = thisMObject();
MPlug boundingBoxMinPlug(thisNode, fsInputMeshBoundingBoxMin);
MPlug boundingBoxMaxPlug(thisNode, fsInputMeshBoundingBoxMax);
MPlug inputMeshBoundingBoxMinPlug(otherPlug.node(), MPxSurfaceShape::nodeBoundingBoxMin);
MPlug inputMeshBoundingBoxMaxPlug(otherPlug.node(), MPxSurfaceShape::nodeBoundingBoxMax);
MDGModifier modifier;
modifier.connect(inputMeshBoundingBoxMinPlug, boundingBoxMinPlug);
modifier.connect(inputMeshBoundingBoxMaxPlug, boundingBoxMaxPlug);
modifier.doIt();
}
return MPxNode::connectionMade(plug, otherPlug, asSrc);
}
// ============================================================
//
// This method gets called when connections are broken with attributes
// of this node.
//
// Note : For more information, see MPxNode::connectionBroken()
//
// ============================================================
MStatus GeometryOverrideHighPerformance_shape::connectionBroken(const MPlug& plug, const MPlug& otherPlug, bool asSrc)
{
// If the plug get disconnected with a shading engine, set fMaterialChanged to true,
// so in the geometry override updateRenderItems() we can return true to force
// the render items to be updated.
if (asSrc &&
otherPlug.node().hasFn(MFn::kShadingEngine) &&
(plug.attribute() == instObjGroups || plug.attribute() == objectGroups))
{
setMaterialChangedSinceVP2Update(true);
}
// When a input mesh is disconnected from the node,
// we also must disconnect the input mesh bounding box attribute from this node
if (!asSrc &&
otherPlug.node().hasFn(MFn::kMesh) &&
(plug.attribute() == fsInputMesh))
{
fUseInputMesh = false;
setInputMeshChangedSinceVP2Update(true);
MObject thisNode = thisMObject();
MPlug boundingBoxMinPlug(thisNode, fsInputMeshBoundingBoxMin);
MPlug boundingBoxMaxPlug(thisNode, fsInputMeshBoundingBoxMax);
MPlug inputMeshBoundingBoxMinPlug(otherPlug.node(), MPxSurfaceShape::nodeBoundingBoxMin);
MPlug inputMeshBoundingBoxMaxPlug(otherPlug.node(), MPxSurfaceShape::nodeBoundingBoxMax);
MDGModifier modifier;
modifier.disconnect(inputMeshBoundingBoxMinPlug, boundingBoxMinPlug);
modifier.disconnect(inputMeshBoundingBoxMaxPlug, boundingBoxMaxPlug);
modifier.doIt();
}
return MPxNode::connectionBroken(plug, otherPlug, asSrc);
}
// ============================================================
//
// This method can be overridden in user defined nodes to specify
// which plugs should be set dirty based upon an input plug
//
// Warning : any time you implement setDependentsDirty you probably also need to implement
// something similar in preEvaluation() or postEvaluation() so the code works correctly
// with Evaluation Manager enabled.
//
// Note : For more information, see MPxNode::setDependentsDirty()
//
// ============================================================
MStatus GeometryOverrideHighPerformance_shape::setDependentsDirty(const MPlug& plug, MPlugArray& plugArray)
{
if (plug.attribute() == GeometryOverrideHighPerformance_shape::fsSize)
{
setSizeChangedSinceVP2Update(true);
}
else if (plug.attribute() == GeometryOverrideHighPerformance_shape::fsInputMesh)
{
setInputMeshGeoChangedSinceVP2Update(true);
}
}
// ============================================================
//
// After the evaluation graph execution, each node gets a chance to
// restore / update its internal states.For example, resetting
// draw state.
//
// This code has to be thread safe, non - blocking and work only on
// data owned by the node.
//
// This call will most likely happen from a worker thread.
//
// Note : For more information, see MPxNode::postEvaluation
//
// Evaluation Caching : It is critical for Evaluation Caching that the EM dirty information
// is accessed from postEvaluation rather than preEvaluation. During Evaluation
// Caching restore(or VP2 Custom Caching restore) preEvaluation will not be called,
//
// preEvaluation should be used to prepare for the drawing override calls
// postEvaluation should be used to notify consumers of the data(VP2) that new data is ready.
//
// Warning: any time you implement preEvaluation or postEvaluation and use dirtyPlugExists
// you probably also need to implement something similar in setDependentsDirty() so
// the code works correctly without Evaluation Manager.
//
// ============================================================
MStatus GeometryOverrideHighPerformance_shape::postEvaluation(const MDGContext& context, const MEvaluationNode& evaluationNode, PostEvaluationType evalType)
{
if (evaluationNode.dirtyPlugExists(GeometryOverrideHighPerformance_shape::fsSize, &status) && status)
{
setSizeChangedSinceVP2Update(true);
}
if (evaluationNode.dirtyPlugExists(GeometryOverrideHighPerformance_shape::fsInputMesh, &status) && status)
{
setInputMeshGeoChangedSinceVP2Update(true);
}
}
void GeometryOverrideHighPerformance_shape::setSizeChangedSinceVP2Update(bool sizeChanged)
{
/*
Calling forceCache here should be fast. Possible calling sites are:
- setDependentsDirty() -> the normal context is current.
- preparing the draw in VP2 -> the normal context is current.
- background evaluation postEvaluation() -> datablock for background context already exists.
- background evaluation for VP2 Custom Caching -> datablock for background context already exists.
*/
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsSizeChangedSinceVP2Update);
dataHandle.setBool(sizeChanged);
}
bool GeometryOverrideHighPerformance_shape::evalSizeChangedSinceVP2Update()
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsSizeChangedSinceVP2Update);
return dataHandle.asBool();
}
void GeometryOverrideHighPerformance_shape::setInputMeshChangedSinceVP2Update(bool value)
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsInputMeshChanged);
dataHandle.setBool(value);
}
bool GeometryOverrideHighPerformance_shape::evalInputMeshChangedSinceVP2Update()
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsInputMeshChanged);
return dataHandle.asBool();
}
void GeometryOverrideHighPerformance_shape::setInputMeshGeoChangedSinceVP2Update(bool value)
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsInputMeshGeoChanged);
return dataHandle.setBool(value);
}
bool GeometryOverrideHighPerformance_shape::evalInputMeshGeoChangedSinceVP2Update()
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsInputMeshGeoChanged);
return dataHandle.asBool();
}
bool GeometryOverrideHighPerformance_shape::evalMaterialChangedSinceVP2Update()
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsMaterialChanged);
return dataHandle.asBool();
}
void GeometryOverrideHighPerformance_shape::setMaterialChangedSinceVP2Update(bool value)
{
MDataBlock block = forceCache();
MDataHandle dataHandle = block.outputValue(fsMaterialChanged);
return dataHandle.setBool(value);
}
//===========================================================================
//
// GeometryOverrideHighPerformance implementation
//
//===========================================================================
const char* GeometryOverrideHighPerformance::sActiveWireframeRenderItemName = "GeometryOverrideHighPerformance_ActiveWireframe";
const char* GeometryOverrideHighPerformance::sDormantWireframeRenderItemName = "GeometryOverrideHighPerformance_DormantWireframe";
const char* GeometryOverrideHighPerformance::sCustomShadedItemName = "GeometryOverrideHighPerformance_shaded";
GeometryOverrideHighPerformance::GeometryOverrideHighPerformance(const MObject& obj)
: MHWRender::MPxGeometryOverride(obj)
, fSurfaceNode(obj)
{
// get the real mesh object from the MObject
MStatus status;
MFnDependencyNode node(obj, &status);
if (status)
{
fMesh = dynamic_cast<GeometryOverrideHighPerformance_shape*>(node.userNode());
}
}
GeometryOverrideHighPerformance::~GeometryOverrideHighPerformance()
{
}
// ============================================================
//
// This function return draw API that is supported by this plugin.
//
// Note : For more information, see MPxGeometryOverride::supportedDrawAPIs()
//
// ============================================================
MHWRender::DrawAPI GeometryOverrideHighPerformance::supportedDrawAPIs() const
{
}
// ============================================================
//
// Perform any work required to translate the geometry data that needs to get
// information from the dependency graph.This should be the only place that
// dependency graph evaluation occurs. Any data retrieved should be cached for
// later stages.
//
// Note : For more information, see MPxGeometryOverride::updateDG()
//
// ============================================================
void GeometryOverrideHighPerformance::updateDG()
{
//
// Cache the input mesh dagpath if the input mesh has changed
//
if (fMesh && fMesh->evalInputMeshChangedSinceVP2Update())
{
MPlug plug(fSurfaceNode, GeometryOverrideHighPerformance_shape::fsInputMesh);
MPlugArray connections;
if (plug.connectedTo(connections, true/*asDst*/, false/*asSrc*/))
{
for (unsigned int i = 0; i < connections.length(); ++i)
{
MObject node = connections[i].node();
if (node.hasFn(MFn::kMesh))
{
MDagPath::getAPathTo(node, fInputMeshPath);
break;
}
fInputMeshPath = MDagPath();
}
}
else
{
fInputMeshPath = MDagPath();
}
}
//
// Only update fSizeMultiplier if the size changed
//
if (fMesh && fMesh->evalSizeChangedSinceVP2Update())
{
MPlug plug(fSurfaceNode, GeometryOverrideHighPerformance_shape::fsSize);
if (!plug.isNull())
{
MDistance sizeVal;
if (plug.getValue(sizeVal))
{
fSizeMultiplier = static_cast<float>(sizeVal.asCentimeters());
}
}
}
}
// ============================================================
//
// This method is called once during each draw-preparation phase. If this method returns true
// then the associated DAG object will have a chance to update its render geometry this frame.
// (Maya will call populateGeometry())
//
// This code has to be thread safe, non - blocking and work only on
// data owned by the associated DAG object.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::requiresGeometryUpdate()
//
// ============================================================
bool GeometryOverrideHighPerformance::requiresGeometryUpdate() const
{
// In this example, there is only two situation where we need update the geometry on the GPU
// 1-) The input mesh connection changed, so we need to update all the vertex data on the gpu.
// 2-) The input mesh geometry changed, so we need to update all the vertex data on the gpu.
// 3-) The size attribute changed, so we need to scale the vertex position on the CPU and
// update the vertex position on the GPU.
return fMesh &&
(fMesh->evalInputMeshChangedSinceVP2Update() ||
fMesh->evalInputMeshGeoChangedSinceVP2Update() ||
fMesh->evalSizeChangedSinceVP2Update());
}
// ============================================================
//
// This method is called for each geometry stream on the assocated DAG object
// whenever the object changes.This method is passed a vertex buffer
// descriptor representing one stream on the object to be updated.This
// method should return false if it is safe to reuse the existing buffer rather
// than filling a new buffer with data. Note that returning false from
// isStreamDirty may NOT prevent populateGeometry from requiring that a stream
// be updated.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::isStreamDirty()
//
// ============================================================
bool GeometryOverrideHighPerformance::isStreamDirty(const MHWRender::MVertexBufferDescriptor &desc)
{
if(fInputMeshPath.isValid())
{
if (fMesh && fMesh->evalInputMeshChangedSinceVP2Update())
{
// The input mesh changed since the last VP2 update (populate geometry).
// That means during the last VP2 update we were using the default mesh
// or another input mesh so, make all the stream dirty to be able to
// populate them with the default mesh data.
return true;
}
else if (fMesh &&
( fMesh->evalInputMeshGeoChangedSinceVP2Update() ||
fMesh->evalSizeChangedSinceVP2Update()))
{
// When the input mesh geometry change or when the size attribute change,
// we need to mark all the streams as dirty because MGeometryExtractor is used
// to get the input mesh data and MGeometryExtractor return different result
// depending on stream combination (Number of vertex for each stream).
return true;
}
else
{
return true;
}
}
else
{
if(fMesh && fMesh->evalInputMeshChangedSinceVP2Update())
{
// The input mesh changed since the last VP2 update (populate geometry).
// That means during the last VP2 update we were using a input mesh that
// have been disconnected so, make all the stream dirty to be able to
// populate them with the default mesh data.
return true;
}
else if (desc.semantic() == MGeometry::kPosition)
{
// We only need to make the position stream dirty if the size attribute changed
return fMesh && fMesh->evalSizeChangedSinceVP2Update();
}
else
{
// For the default mesh, we assume that all the others stream (Normal, Tangent ...)
// does't change so mark them all as not dirty.
return false;
}
}
}
// ============================================================
//
// This method is called for each render item on the assocated DAG object
// whenever the object changes.This method is passed a render item.This
// method should return true if the indexing for the render item has changed
// since the last frame.Note that returning false from isIndexingDirty may
// NOT prevent populate geometry from requiring that an index buffer is
// updated.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::isIndexingDirty()
//
// ============================================================
bool GeometryOverrideHighPerformance::isIndexingDirty(const MRenderItem& item)
{
// In this example, we only need to invalidate the index data when the input mesh changed
return fMesh && fMesh->evalInputMeshChangedSinceVP2Update();
}
// ============================================================
//
// If this method returns false for an MDagPath instance
// of the associated DAG object then updateRenderItems() will not be called for that MDagPath
// draw-preparation phase. If requiresUpdateRenderItems() returns false for all MDagPaths
// then updateRenderItems() will not be called at all for the draw-preparation phase.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::requiresUpdateRenderItems()
//
// ============================================================
bool GeometryOverrideHighPerformance::requiresUpdateRenderItems(const MDagPath& path) const
{
if(fMesh && fMesh->evalMaterialChangedSinceVP2Update())
{
fMesh->setMaterialChangedSinceVP2Update(false);
return true;
}
// If the display status not changed, then don't do any render item updates.
//
// Note : In a real situation, you will probably want to check if fDisplayStatus changed
// per dag path instance. In this example, we try to keep thing as simple as possible.
if (currentDisplayStatus == fDisplayStatus)
return false;
return true;
}
// ============================================================
//
// If this method returns true then the MPxGeometryOverride will be considered
// for Evaluation Manager Parallel Update.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::supportsEvaluationManagerParallelUpdate()
//
// ============================================================
bool GeometryOverrideHighPerformance::supportsEvaluationManagerParallelUpdate() const
{
return true;
}
// ============================================================
//
// This method is called at any time during evaluation to determine if VP2
// Custom Caching is supported.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::supportsVP2CustomCaching()
//
// ============================================================
bool GeometryOverrideHighPerformance::supportsVP2CustomCaching() const
{
// This method must return the same value for the entire lifetime of the object.
return true;
}
// ============================================================
//
// This method is called for each instance of the associated DAG object whenever
// the object changes and receive the path to the instance and the current list
// of render items associated with that instance. Implementations of this method
// may add, remove or modify items in the list. As an alternative this method
// can enable or disable items that must be used or not based on some properties.
//
// A render item represents a single renderable entity and contain many properties
// to let the Viewport 2.0 to know how to render the entity. By example, A render
// item contain a name, a type, the geometry primitive type, a set of geometry buffers
// and a shader instance.
//
// In this example, this functions will create 3 render items to render the object's
// geometry. The first render item will be use to display the oject with a single color
// only when the Viewport 2.0 is in shaded or textured mode. The second render item
// will be use to display the object in wireframe with a single color only when the
// Viewport 2.0 is in wireframe mode. The last render item will be use to render the
// object with a single color in wireframe only when the object is selected independing
// of the Viewport 2.0 display mode. Both wireframe render item will be enable or disable
// depending of the object selection state.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::updateRenderItems()
//
// ============================================================
void GeometryOverrideHighPerformance::updateRenderItems(const MDagPath& path, MHWRender::MRenderItemList& renderItems)
{
if (!path.isValid())
return;
MRenderer* renderer = MRenderer::theRenderer();
if (!renderer)
return;
const MShaderManager* shaderManager = renderer->getShaderManager();
if (!shaderManager)
return;
//
// Look if there is a "StandardShadedItem" render item in the list.
// This render item is created by maya when the object is attached
// to a shader network node.
//
auto standardShadedItemIndex = renderItems.indexOf("StandardShadedItem");
auto renderItemIndex = renderItems.indexOf(sCustomShadedItemName);
//
// Create a render item that will render geometry with a single color
// if it didn't already exist and there is not shader network attached
// to the object.
//
// This render item will be used only in shaded and textured mode.
//
if (standardShadedItemIndex < 0 && renderItemIndex < 0)
{
// Create the new render item with the given name.
// We designate this item as a UI Material.
// The topology for the render item is a triangle list.
MHWRender::MRenderItem* shadedRenderItem =
MHWRender::MRenderItem::Create(sCustomShadedItemName,
// We want this render item to only show up when in shaded or textured mode
// The the depth priority of this render item to sDormantFilledDepthPriority (The default value).
// Depth priority is usefull when an object is displayed with many render items.
// Viewport 2.0 will use the depth priority to know which render item must be draw on top
// of others.
// Enable the render item so it will be use for rendering
shadedRenderItem->enable(true);
// Get an instance of a "mayaLambertSurface" from the shader manager.
// The shader tells the graphics hardware how to draw the geometry.
// The MShaderInstance is a reference to a shader along with the values for the shader parameters.
MShaderInstance* shader = shaderManager->getFragmentShader("mayaLambertSurface", "outSurfaceFinal", true);
if (shader)
{
// Set the shader color parameter
const float blueColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
shader->setParameter("color", blueColor);
// Assign the shader to the render item. This adds a reference to that
// shader.
shadedRenderItem->setShader(shader);
// Once assigned, no need to hold on to shader instance
shaderManager->releaseShader(shader);
}
// The item must be added to the persistent list to be considered
// for update / rendering
renderItems.append(shadedRenderItem);
}
//
// If the 'StandardShadedItem' and the custom render item exists,
// enable the 'StandardShadedItem' render item and disable the custom render item.
//
// This situation can happen if the geometry override was first created without a
// shading engine, than a shading enine was attached to the node.
//
if(standardShadedItemIndex >= 0 && renderItemIndex >= 0)
{
MHWRender::MRenderItem* renderItem = renderItems.itemAt(renderItemIndex);
if (renderItem)
renderItem->enable(false);
renderItem = renderItems.itemAt(standardShadedItemIndex);
if (renderItem)
renderItem->enable(true);
}
//
// Make sure the custom render item is enable if the 'StandardShadedItem' render item
// does not exists.
//
if (standardShadedItemIndex < 0 && renderItemIndex >= 0)
{
MHWRender::MRenderItem* renderItem = renderItems.itemAt(renderItemIndex);
if (renderItem)
renderItem->enable(true);
}
// Get the inherited DAG display properties.
fWireframeDisplayColor = MHWRender::MGeometryUtilities::wireframeColor(path);
// Update the wireframe render item used when the object will be selected
bool isWireFrameRenderItemEnabled = fDisplayStatus == MHWRender::kLead || fDisplayStatus == MHWRender::kActive;
updateWireframeItems(sActiveWireframeRenderItemName,
fWireframeDisplayColor,
isWireFrameRenderItemEnabled,
renderItems,
*shaderManager);
// Update the wireframe render item used when the object will not be selected
isWireFrameRenderItemEnabled = fDisplayStatus == MHWRender::kDormant;
updateWireframeItems(sDormantWireframeRenderItemName,
fWireframeDisplayColor,
isWireFrameRenderItemEnabled,
renderItems,
*shaderManager);
}
// ============================================================
//
// Fill in data and index streams based on the requirements passed in.
// Associate indexing with the render items passed in.
//
// Note : Dependency graph evaluation is not allowed in this function.
// Note : For more information, see MPxGeometryOverride::populateGeometry()
//
// ============================================================
void GeometryOverrideHighPerformance::populateGeometry(const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& renderItems, MHWRender::MGeometry& data)
{
if (!fMesh)
return;
if(fInputMeshPath.isValid())
{
populateInputMeshGeometry(requirements, renderItems, data);
}
else
{
populateDefaultMeshGeometry(requirements, renderItems, data);
}
// Now that the current geometry have been updated
// clear the signal flags.
fMesh->setSizeChangedSinceVP2Update(false);
fMesh->setInputMeshChangedSinceVP2Update(false);
fMesh->setInputMeshGeoChangedSinceVP2Update(false);
}
// ============================================================
//
// Clean up any cached data stored from the updateDG() phase.
//
// Note : For more information, see MPxGeometryOverride::cleanUp()
//
// ============================================================
void GeometryOverrideHighPerformance::cleanUp()
{
// Nothing here because the plugin doesn't keep any temporary data.
}
// ============================================================
//
// Update the wireframe render item named 'renderItemName' or create it
// if it doesn't exists.
//
// Note : Arguments drawMode and depthPriority are only used for creation of
// the render item.
//
// ============================================================
void GeometryOverrideHighPerformance::updateWireframeItems(const char* renderItemName, MGeometry::DrawMode drawMode,
unsigned int depthPriority, MColor color, bool isEnabled,
MHWRender::MRenderItemList& renderItemList,
const MHWRender::MShaderManager& shaderManager)
{
MHWRender::MRenderItem* renderItem = nullptr;
// Try to find the active wireframe render item.
// If the returning index is smaller than 0, that means
// the render item does't exists yet. So, create it.
auto renderItemIndex = renderItemList.indexOf(renderItemName);
if (renderItemIndex < 0)
{
// Create the new render item with the given name.
// We designate this item as a UI decoration and will not be
// involved in rendering aspects such as casting shadows
// The topology for the render item is a line list.
renderItem = MHWRender::MRenderItem::Create(renderItemName,
// We want this render item to show up when in all mode ( Wireframe, Shaded, Textured and BoundingBox)
renderItem->setDrawMode(drawMode);
// Set selection priority: on top of everything
renderItem->depthPriority(depthPriority);
// Get an instance of a 3dSolidShader from the shader manager.
// The shader tells the graphics hardware how to draw the geometry.
// The MShaderInstance is a reference to a shader along with the values for the shader parameters.
MShaderInstance* shader = shaderManager.getStockShader(MShaderManager::k3dSolidShader);
if (shader)
{
// Assign the shader to the render item. This adds a reference to that
// shader.
renderItem->setShader(shader);
// Once assigned, no need to hold on to shader instance
shaderManager.releaseShader(shader);
}
// The item must be added to the persistent list to be considered
// for update / rendering
renderItemList.append(renderItem);
}
else
{
renderItem = renderItemList.itemAt(renderItemIndex);
}
if(renderItem)
{
MHWRender::MShaderInstance* shader = renderItem->getShader();
if (shader)
{
// Set the shader color parameter
shader->setParameter("solidColor", &color.r);
}
renderItem->enable(isEnabled);
}
}
// ============================================================
//
// This function is used to populate the geometry data from the
// input mesh. It used the MGeometryExtractor to extract
// the data.
//
// ============================================================
void GeometryOverrideHighPerformance::populateInputMeshGeometry(const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& renderItems, MHWRender::MGeometry& data)
{
//
// Use MGeometryExtractor to extract geometry from the input mesh path
//
MStatus status;
MGeometryExtractor extractor(requirements, fInputMeshPath, MHWRender::kPolyGeom_NotSharing, &status);
if (MS::kFailure == status)
return;
const MVertexBufferDescriptorList& vertexBufferDescriptorList = requirements.vertexRequirements();
for (int i = 0; i < vertexBufferDescriptorList.length(); i++)
{
MVertexBufferDescriptor desc{};
if (!vertexBufferDescriptorList.getDescriptor(i, desc))
continue;
MVertexBuffer* vertexBuffer = data.createVertexBuffer(desc);
if (vertexBuffer)
{
unsigned int vertexCount = extractor.vertexCount();
float* data = (float*)vertexBuffer->acquire(vertexCount, true /*writeOnly - we don't need the current buffer values*/);
if (data)
{
status = extractor.populateVertexBuffer(data, vertexCount, desc);
if (MS::kFailure == status)
return;
// For vertex position, scale the position with the size multiplicator
if (desc.semantic() == MGeometry::kPosition)
{
for (int i = 0; i < vertexCount; i++)
{
data[i * 3] *= fSizeMultiplier;
data[i * 3 + 1] *= fSizeMultiplier;
data[i * 3 + 2] *= fSizeMultiplier;
}
}
// Transfer from CPU to GPU memory.
vertexBuffer->commit(data);
}
}
}
// Update indexing data for all appropriate render items
for (int i = 0; i < renderItems.length(); ++i)
{
const MRenderItem* item = renderItems.itemAt(i);
if (!item) continue;
MIndexBuffer* indexBuffer = data.createIndexBuffer(MGeometry::kUnsignedInt32);
if (!indexBuffer) continue;
if (item->primitive() == MGeometry::kTriangles)
{
MIndexBufferDescriptor triangleDesc(MIndexBufferDescriptor::kTriangle, MString(), MGeometry::kTriangles, 3);
unsigned int numTriangles = extractor.primitiveCount(triangleDesc);
unsigned int* indices = (unsigned int*)indexBuffer->acquire(3 * numTriangles, true /*writeOnly*/);
// Fill a buffer with geometry indexing data
status = extractor.populateIndexBuffer(indices, numTriangles, triangleDesc);
if (MS::kFailure == status)
return;
// Transfer from CPU to GPU memory.
indexBuffer->commit(indices);
}
else if (item->primitive() == MGeometry::kLines)
{
MIndexBufferDescriptor edgeDesc(MIndexBufferDescriptor::kEdgeLine, MString(), MGeometry::kLines, 2);
unsigned int numEdges = extractor.primitiveCount(edgeDesc);
unsigned int* indices = (unsigned int*)indexBuffer->acquire(2 * numEdges, true /*writeOnly*/);
// Fill a buffer with geometry indexing data
status = extractor.populateIndexBuffer(indices, numEdges, edgeDesc);
if (MS::kFailure == status)
return;
// Transfer from CPU to GPU memory.
indexBuffer->commit(indices);
}
item->associateWithIndexBuffer(indexBuffer);
}
}
// ============================================================
//
// This function is used to populate the geometry data from the
// default mesh (Hardcoded mesh) in case there is no input mesh
// attached to the node.
//
// ============================================================
void GeometryOverrideHighPerformance::populateDefaultMeshGeometry(const MHWRender::MGeometryRequirements& requirements, const MHWRender::MRenderItemList& renderItems, MHWRender::MGeometry& data)
{
const MVertexBufferDescriptorList& vertexBufferDescriptorList = requirements.vertexRequirements();
for (int i = 0; i < vertexBufferDescriptorList.length(); i++)
{
MVertexBufferDescriptor desc{};
if (!vertexBufferDescriptorList.getDescriptor(i, desc))
continue;
switch (desc.semantic())
{
//
// Create and fill the vertex position buffer
//
case MGeometry::kPosition:
{
MHWRender::MVertexBuffer* positionBuffer = data.createVertexBuffer(desc);
if (positionBuffer)
{
const Float3Array& positions = fMesh->getPositions();
Float3* buffer = static_cast<Float3*>(positionBuffer->acquire(positions.size(), true /*writeOnly */));
if (buffer)
{
// copie the cube vertices and scale them by the size multiplier
for (std::size_t i = 0; i < positions.size(); i++)
{
buffer[i].x = positions[i].x * fSizeMultiplier;
buffer[i].y = positions[i].y * fSizeMultiplier;
buffer[i].z = positions[i].z * fSizeMultiplier;
}
// Transfer from CPU to GPU memory.
positionBuffer->commit(buffer);
}
}
}
break;
//
// Create and fill the vertex normal buffer
//
case MGeometry::kNormal:
{
MHWRender::MVertexBuffer* normalsBuffer = data.createVertexBuffer(desc);
if (normalsBuffer)
{
const Float3Array& normals = fMesh->getNormals();
void* buffer = normalsBuffer->acquire(normals.size(), true /*writeOnly*/);
if (buffer)
{
const std::size_t bufferSizeInByte =
sizeof(Float3Array::value_type) * normals.size();
memcpy(buffer, normals.data(), bufferSizeInByte);
// Transfer from CPU to GPU memory.
normalsBuffer->commit(buffer);
}
}
}
break;
//
// Create and fill the vertex tangent buffer
//
case MGeometry::kTangent:
{
MHWRender::MVertexBuffer* tangentBuffer = data.createVertexBuffer(desc);
if (tangentBuffer)
{
const Float3Array& tangents = fMesh->getTangents();
void* buffer = tangentBuffer->acquire(tangents.size(), true /*writeOnly*/);
if (buffer)
{
const std::size_t bufferSizeInByte =
sizeof(Float3Array::value_type) * tangents.size();
memcpy(buffer, tangents.data(), bufferSizeInByte);
// Transfer from CPU to GPU memory.
tangentBuffer->commit(buffer);
}
}
}
break;
//
// Create and fill the vertex bitangent buffer
//
case MGeometry::kBitangent:
{
MHWRender::MVertexBuffer* tangentBuffer = data.createVertexBuffer(desc);
if (tangentBuffer)
{
const Float3Array& tangents = fMesh->getBiTangents();
void* buffer = tangentBuffer->acquire(tangents.size(), true /*writeOnly*/);
if (buffer)
{
const std::size_t bufferSizeInByte =
sizeof(Float3Array::value_type) * tangents.size();
memcpy(buffer, tangents.data(), bufferSizeInByte);
// Transfer from CPU to GPU memory.
tangentBuffer->commit(buffer);
}
}
}
break;
//
// Create and fill the vertex texture coords buffer
//
case MGeometry::kTexture:
{
MHWRender::MVertexBuffer* vertexBuffer = data.createVertexBuffer(desc);
if (vertexBuffer)
{
const Float2Array& texCoords = fMesh->getTexCoords();
void* vertexBufferData = vertexBuffer->acquire(texCoords.size(), true /*writeOnly*/);
if (vertexBufferData)
{
const std::size_t bufferSizeInByte =
sizeof(Float2Array::value_type) * texCoords.size();
memcpy(vertexBufferData, texCoords.data(), bufferSizeInByte);
// Transfer from CPU to GPU memory.
vertexBuffer->commit(vertexBufferData);
}
}
}
break;
case MGeometry::kColor:
case MGeometry::kTangentWithSign:
case MGeometry::kInvalidSemantic: // avoid compiling error
//
// In this example, we don't need to used those vertex informantions.
//
break;
}
}
// Update indexing data for all appropriate render items
const int numItems = renderItems.length();
for (int i = 0; i < numItems; i++)
{
const MHWRender::MRenderItem* item = renderItems.itemAt(i);
if (!item)
continue;
{
//
// Create and fill the index buffer used to render triangles
//
if (indexBuffer)
{
IndexList indices = fMesh->getIndices();
void* buffer = indexBuffer->acquire(indices.size(), true /*writeOnly*/);
if (buffer)
{
const std::size_t bufferSizeInByte =
sizeof(IndexList::value_type) * indices.size();
memcpy(buffer, indices.data(), bufferSizeInByte);
// Transfer from CPU to GPU memory.
indexBuffer->commit(buffer);
// Associate index buffer with render item
item->associateWithIndexBuffer(indexBuffer);
}
}
}
}
}
//===========================================================================
//
// initialize / uninitialize plugin
//
//===========================================================================
namespace
{
MString sDrawDbClassification("drawdb/geometry/geometryOverrideHighPerformance");
MString sDrawRegistrantId("geometryOverrideHighPerformancePlugin");
}
MStatus initializePlugin(MObject obj)
{
MFnPlugin plugin(obj, PLUGIN_COMPANY, "1.0", "Any");
MStatus status;
status = plugin.registerShape("geometryOverrideHighPerformance_shape", // the name of the new type of user defined shape node
GeometryOverrideHighPerformance_shape::fsId, // a unique id that identifies this node
&GeometryOverrideHighPerformance_shape::creator, // function that will return a pointer to a new instance of the class
// (derived from MPxSurfaceNode) that implements the new shape node type
&GeometryOverrideHighPerformance_shape::initialize, // function that will initialize all the attributes of the new shape node type
nullptr,
&sDrawDbClassification);
if (!status)
{
cerr << "Failed to register geometryOverrideHighPerformance_shape." << std::endl;
return status;
}
sDrawRegistrantId,
GeometryOverrideHighPerformance::Creator);
if (!status)
{
std::cerr << "Failed to register Viewport 2.0 geometry override." << std::endl;
return status;
}
return status;
}
MStatus uninitializePlugin(MObject obj)
{
MFnPlugin plugin(obj);
MStatus status;
status = MHWRender::MDrawRegistry::deregisterGeometryOverrideCreator(sDrawDbClassification, sDrawRegistrantId);
if (!status)
{
std::cerr << "Failed to deregister geometry override." << std::endl;
}
status = plugin.deregisterNode(GeometryOverrideHighPerformance_shape::fsId);
if (!status)
{
cerr << "Failed to deregister GeometryOverrideHighPerformance_shape." << std::endl;
}
return status;
}