footPrintNode_GeometryOverride/footPrintNode_GeometryOverride.cpp

footPrintNode_GeometryOverride/footPrintNode_GeometryOverride.cpp
//-
// ==========================================================================
// Copyright 2015 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.
// ==========================================================================
//+
// DESCRIPTION:
//
// This plug-in demonstrates how to draw a simple mesh like foot Print in an efficient way.
//
// This efficient way is supported in Viewport 2.0.
//
// For comparison, you can also reference a Maya Developer Kit sample named footPrintNode.
// In that sample, we draw the footPrint using the MUIDrawManager primitives in footPrintDrawOverride::addUIDrawables()
//
// For comparison, you can also reference another Maya Developer Kit sample named rawfootPrintNode.
// In that sample, we draw the footPrint with OpenGL\DX in method rawFootPrintDrawOverride::draw().
//
// Note the method:
// footPrint::draw()
// is only called in legacy default viewport to draw the foot print.
// while the methods:
// FootPrintGeometryOverride::updateDG()
// FootPrintGeometryOverride::updateRenderItems()
// FootPrintGeometryOverride::populateGeometry()
// are only called in Viewport 2.0 to prepare and draw the foot print.
//
// Example usage is to load the plug-in and create the node using
// the createNode command:
//
// loadPlugin footPrint_GeometryOverride;
// createNode footPrint_GeometryOverride;
//
#include <maya/MPxLocatorNode.h>
#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MVector.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MColor.h>
#include <maya/M3dView.h>
#include <maya/MFnPlugin.h>
#include <maya/MDistance.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MGlobal.h>
#include <maya/MEvaluationNode.h>
// Viewport 2.0 includes
#include <maya/MDrawRegistry.h>
#include <maya/MPxGeometryOverride.h>
#include <maya/MUserData.h>
#include <maya/MDrawContext.h>
#include <maya/MShaderManager.h>
#include <maya/MHWGeometry.h>
#include <maya/MHWGeometryUtilities.h>
#include <maya/MPointArray.h>
#include <unordered_map>
namespace
{
// Foot Data
//
float sole[][3] = { { 0.00f, 0.0f, -0.70f },
{ 0.04f, 0.0f, -0.69f },
{ 0.09f, 0.0f, -0.65f },
{ 0.13f, 0.0f, -0.61f },
{ 0.16f, 0.0f, -0.54f },
{ 0.17f, 0.0f, -0.46f },
{ 0.17f, 0.0f, -0.35f },
{ 0.16f, 0.0f, -0.25f },
{ 0.15f, 0.0f, -0.14f },
{ 0.13f, 0.0f, 0.00f },
{ 0.00f, 0.0f, 0.00f },
{ -0.13f, 0.0f, 0.00f },
{ -0.15f, 0.0f, -0.14f },
{ -0.16f, 0.0f, -0.25f },
{ -0.17f, 0.0f, -0.35f },
{ -0.17f, 0.0f, -0.46f },
{ -0.16f, 0.0f, -0.54f },
{ -0.13f, 0.0f, -0.61f },
{ -0.09f, 0.0f, -0.65f },
{ -0.04f, 0.0f, -0.69f },
{ -0.00f, 0.0f, -0.70f } };
float heel[][3] = { { 0.00f, 0.0f, 0.06f },
{ 0.13f, 0.0f, 0.06f },
{ 0.14f, 0.0f, 0.15f },
{ 0.14f, 0.0f, 0.21f },
{ 0.13f, 0.0f, 0.25f },
{ 0.11f, 0.0f, 0.28f },
{ 0.09f, 0.0f, 0.29f },
{ 0.04f, 0.0f, 0.30f },
{ 0.00f, 0.0f, 0.30f },
{ -0.04f, 0.0f, 0.30f },
{ -0.09f, 0.0f, 0.29f },
{ -0.11f, 0.0f, 0.28f },
{ -0.13f, 0.0f, 0.25f },
{ -0.14f, 0.0f, 0.21f },
{ -0.14f, 0.0f, 0.15f },
{ -0.13f, 0.0f, 0.06f },
{ -0.00f, 0.0f, 0.06f } };
int soleCount = 21;
int heelCount = 17;
// Viewport 2.0 specific data
//
const MString colorParameterName_ = "solidColor";
const MString wireframeItemName_ = "footPrintLocatorWires";
const MString shadedItemName_ = "footPrintLocatorTriangles";
// Maintain a mini cache for 3d solid shaders in order to reuse the shader
// instance whenever possible. This can allow Viewport 2.0 optimization e.g.
// the GPU instancing system and the consolidation system to be leveraged.
//
struct MColorHash
{
std::size_t operator()(const MColor& color) const
{
std::size_t seed = 0;
CombineHashCode(seed, color.r);
CombineHashCode(seed, color.g);
CombineHashCode(seed, color.b);
CombineHashCode(seed, color.a);
return seed;
}
void CombineHashCode(std::size_t& seed, float v) const
{
std::hash<float> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
};
std::unordered_map<MColor, MHWRender::MShaderInstance*, MColorHash> the3dSolidShaders;
MHWRender::MShaderInstance* get3dSolidShader(const MColor& color)
{
// Return the shader instance if exists.
auto it = the3dSolidShaders.find(color);
if (it != the3dSolidShaders.end())
{
return it->second;
}
if (renderer)
{
const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager();
if (shaderMgr)
{
}
}
if (shader)
{
float solidColor[] = { color.r, color.g, color.b, 1.0f };
shader->setParameter(colorParameterName_, solidColor);
the3dSolidShaders[color] = shader;
}
return shader;
}
MStatus releaseShaders()
{
if (renderer)
{
const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager();
if (shaderMgr)
{
for (auto it = the3dSolidShaders.begin(); it != the3dSolidShaders.end(); it++)
{
shaderMgr->releaseShader(it->second);
}
the3dSolidShaders.clear();
return MS::kSuccess;
}
}
return MS::kFailure;
}
} // anonymous namespace
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Node implementation with standard viewport draw
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class footPrint : public MPxLocatorNode
{
public:
footPrint();
virtual ~footPrint();
virtual MStatus compute( const MPlug& plug, MDataBlock& data );
virtual void draw( M3dView & view, const MDagPath & path,
virtual bool isBounded() const;
virtual MBoundingBox boundingBox() const;
MStatus preEvaluation(const MDGContext& context, const MEvaluationNode& evaluationNode) override;
static void * creator();
static MStatus initialize();
static MObject size; // The size of the foot
public:
static MTypeId id;
static MString drawDbClassification;
static MString drawRegistrantId;
};
MObject footPrint::size;
MTypeId footPrint::id( 0x00080033 );
MString footPrint::drawDbClassification("drawdb/geometry/light/footPrint_GeometryOverride");
static bool sMakeFootPrintDirLight = (getenv("MAYA_FOOTPRINT_GEOMETRY_OVERRIDE_AS_DIRLIGHT") != NULL);
static MString lightClassification("light:drawdb/geometry/light/footPrint_GeometryOverride:drawdb/light/directionalLight");
MString footPrint::drawRegistrantId("FootprintNode_GeometryOverridePlugin");
static bool sUseLegacyDraw = (getenv("MAYA_ENABLE_VP2_PLUGIN_LOCATOR_LEGACY_DRAW") != NULL);
footPrint::footPrint() {}
footPrint::~footPrint() {}
MStatus footPrint::compute( const MPlug& /*plug*/, MDataBlock& /*data*/ )
{
}
// called by legacy default viewport
void footPrint::draw( M3dView & view, const MDagPath & /*path*/,
{
// Get the size
//
MObject thisNode = thisMObject();
MPlug plug( thisNode, size );
MDistance sizeVal;
plug.getValue( sizeVal );
float multiplier = (float) sizeVal.asCentimeters();
view.beginGL();
if ( ( style == M3dView::kFlatShaded ) ||
( style == M3dView::kGouraudShaded ) )
{
// Push the color settings
//
glPushAttrib( GL_CURRENT_BIT );
if ( status == M3dView::kActive ) {
} else {
}
glBegin( GL_TRIANGLE_FAN );
int i;
int last = soleCount - 1;
for ( i = 0; i < last; ++i ) {
glVertex3f( sole[i][0] * multiplier,
sole[i][1] * multiplier,
sole[i][2] * multiplier );
}
glEnd();
glBegin( GL_TRIANGLE_FAN );
last = heelCount - 1;
for ( i = 0; i < last; ++i ) {
glVertex3f( heel[i][0] * multiplier,
heel[i][1] * multiplier,
heel[i][2] * multiplier );
}
glEnd();
glPopAttrib();
}
// Draw the outline of the foot
//
glBegin( GL_LINES );
int i;
int last = soleCount - 1;
for ( i = 0; i < last; ++i ) {
glVertex3f( sole[i][0] * multiplier,
sole[i][1] * multiplier,
sole[i][2] * multiplier );
glVertex3f( sole[i+1][0] * multiplier,
sole[i+1][1] * multiplier,
sole[i+1][2] * multiplier );
}
last = heelCount - 1;
for ( i = 0; i < last; ++i ) {
glVertex3f( heel[i][0] * multiplier,
heel[i][1] * multiplier,
heel[i][2] * multiplier );
glVertex3f( heel[i+1][0] * multiplier,
heel[i+1][1] * multiplier,
heel[i+1][2] * multiplier );
}
glEnd();
view.endGL();
}
bool footPrint::isBounded() const
{
return true;
}
MBoundingBox footPrint::boundingBox() const
{
// Get the size
//
MObject thisNode = thisMObject();
MPlug plug( thisNode, size );
MDistance sizeVal;
plug.getValue( sizeVal );
double multiplier = sizeVal.asCentimeters();
MPoint corner1( -0.17, 0.0, -0.7 );
MPoint corner2( 0.17, 0.0, 0.3 );
corner1 = corner1 * multiplier;
corner2 = corner2 * multiplier;
return MBoundingBox( corner1, corner2 );
}
MSelectionMask footPrint::getShapeSelectionMask() const
{
return MSelectionMask("footPrintSelection");
}
// Called before this node is evaluated by Evaluation Manager
MStatus footPrint::preEvaluation(
const MDGContext& context,
const MEvaluationNode& evaluationNode)
{
if (context.isNormal())
{
MStatus status;
if (evaluationNode.dirtyPlugExists(size, &status) && status)
{
}
}
}
void* footPrint::creator()
{
return new footPrint();
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Viewport 2.0 override implementation
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
class FootPrintGeometryOverride : public MHWRender::MPxGeometryOverride
{
public:
static MHWRender::MPxGeometryOverride* Creator(const MObject& obj)
{
return new FootPrintGeometryOverride(obj);
}
virtual ~FootPrintGeometryOverride();
virtual bool hasUIDrawables() const { return false; }
virtual void updateDG();
virtual bool isIndexingDirty(const MHWRender::MRenderItem &item) { return false; }
virtual bool isStreamDirty(const MHWRender::MVertexBufferDescriptor &desc) { return mMultiplierChanged; }
virtual void updateRenderItems(const MDagPath &path, MHWRender::MRenderItemList& list);
virtual void populateGeometry(const MHWRender::MGeometryRequirements &requirements, const MHWRender::MRenderItemList &renderItems, MHWRender::MGeometry &data);
virtual void cleanUp() {};
/*
Tracing will look something like the following when in shaded mode:
footPrintGeometryOverride: Geometry override DG update: footPrint1
footPrintGeometryOverride: Start geometry override render item update: |transform1|footPrint1
footPrintGeometryOverride: - Call API to update render items
footPrintGeometryOverride: End geometry override render item update: |transform1|footPrint1
footPrintGeometryOverride: Start geometry override update stream and indexing data: footPrint1
footPrintGeometryOverride: - Update render item: soleLocatorTriangles
footPrintGeometryOverride: - Update render item: heelLocatorTriangles
footPrintGeometryOverride: End geometry override stream and indexing data: footPrint1
footPrintGeometryOverride: End geometry override clean up: footPrint1
at creation time.
footPrintGeometryOverride: Geometry override DG update: footPrint1
footPrintGeometryOverride: Start geometry override render item update: |transform1|footPrint1
footPrintGeometryOverride: - Call API to update render items
footPrintGeometryOverride: End geometry override render item update: |transform1|footPrint1
footPrintGeometryOverride: End geometry override clean up: footPrint1
on selection change.
footPrintGeometryOverride: Geometry override DG update: footPrint1
footPrintGeometryOverride: Start geometry override render item update: |transform1|footPrint1
footPrintGeometryOverride: - Call API to update render items
footPrintGeometryOverride: End geometry override render item update: |transform1|footPrint1
footPrintGeometryOverride: Geometry override dirty stream check: footPrint1
footPrintGeometryOverride: Start geometry override update stream and indexing data: footPrint1
footPrintGeometryOverride: End geometry override stream and indexing data: footPrint1
footPrintGeometryOverride: End geometry override clean up: footPrint1
for footprint size change.
This is based on the existing stream and indexing dirty flags being used
which attempts to minimize the amount of render item, vertex buffer and indexing update.
*/
virtual bool traceCallSequence() const
{
// Return true if internal tracing is desired.
return false;
}
virtual void handleTraceMessage( const MString &message ) const
{
MGlobal::displayInfo("footPrintGeometryOverride: " + message);
// Some simple custom message formatting.
fprintf(stderr, "footPrintGeometryOverride: ");
fprintf(stderr, message.asChar());
fprintf(stderr, "\n");
}
private:
FootPrintGeometryOverride(const MObject& obj);
MObject mLocatorNode;
float mMultiplier;
bool mMultiplierChanged;
};
FootPrintGeometryOverride::FootPrintGeometryOverride(const MObject& obj)
: MHWRender::MPxGeometryOverride(obj)
, mLocatorNode(obj)
, mMultiplier(0.0f)
, mMultiplierChanged(true)
{
}
FootPrintGeometryOverride::~FootPrintGeometryOverride()
{
}
MHWRender::DrawAPI FootPrintGeometryOverride::supportedDrawAPIs() const
{
// this plugin supports both GL and DX
}
void FootPrintGeometryOverride::updateDG()
{
MPlug plug(mLocatorNode, footPrint::size);
float newScale = 1.0f;
if (!plug.isNull())
{
MDistance sizeVal;
if (plug.getValue(sizeVal))
{
newScale = (float)sizeVal.asCentimeters();
}
}
if (newScale != mMultiplier)
{
mMultiplier = newScale;
mMultiplierChanged = true;
}
}
void FootPrintGeometryOverride::updateRenderItems( const MDagPath& path, MHWRender::MRenderItemList& list )
{
MHWRender::MShaderInstance* shader = get3dSolidShader(color);
if (!shader) return;
MHWRender::MRenderItem* wireframeItem = NULL;
int index = list.indexOf(wireframeItemName_);
if (index < 0)
{
wireframeItemName_,
wireframeItem->depthPriority(5);
list.append(wireframeItem);
}
else
{
wireframeItem = list.itemAt(index);
}
if(wireframeItem)
{
wireframeItem->setShader(shader);
wireframeItem->enable(true);
}
MHWRender::MRenderItem* shadedItem = NULL;
index = list.indexOf(shadedItemName_);
if (index < 0)
{
shadedItemName_,
shadedItem->depthPriority(5);
list.append(shadedItem);
}
else
{
shadedItem = list.itemAt(index);
}
if(shadedItem)
{
shadedItem->setShader(shader);
shadedItem->enable(true);
}
}
void FootPrintGeometryOverride::populateGeometry(
const MHWRender::MGeometryRequirements& requirements,
const MHWRender::MRenderItemList& renderItems,
{
MHWRender::MVertexBuffer* verticesBuffer = NULL;
float* vertices = NULL;
const MHWRender::MVertexBufferDescriptorList& vertexBufferDescriptorList = requirements.vertexRequirements();
const int numberOfVertexRequirments = vertexBufferDescriptorList.length();
MHWRender::MVertexBufferDescriptor vertexBufferDescriptor;
for (int requirmentNumber = 0; requirmentNumber < numberOfVertexRequirments; ++requirmentNumber)
{
if (!vertexBufferDescriptorList.getDescriptor(requirmentNumber, vertexBufferDescriptor))
{
continue;
}
switch (vertexBufferDescriptor.semantic())
{
{
if (!verticesBuffer)
{
verticesBuffer = data.createVertexBuffer(vertexBufferDescriptor);
if (verticesBuffer)
{
vertices = (float*)verticesBuffer->acquire(soleCount+heelCount);
}
}
}
break;
default:
// do nothing for stuff we don't understand
break;
}
}
int verticesPointerOffset = 0;
// We concatenate the heel and sole positions into a single vertex buffer.
// The index buffers will decide which positions will be selected for each render items.
for (int currentVertex = 0 ; currentVertex < soleCount+heelCount; ++currentVertex)
{
if(vertices)
{
if (currentVertex < heelCount)
{
int heelVtx = currentVertex;
vertices[verticesPointerOffset++] = heel[heelVtx][0] * mMultiplier;
vertices[verticesPointerOffset++] = heel[heelVtx][1] * mMultiplier;
vertices[verticesPointerOffset++] = heel[heelVtx][2] * mMultiplier;
}
else
{
int soleVtx = currentVertex - heelCount;
vertices[verticesPointerOffset++] = sole[soleVtx][0] * mMultiplier;
vertices[verticesPointerOffset++] = sole[soleVtx][1] * mMultiplier;
vertices[verticesPointerOffset++] = sole[soleVtx][2] * mMultiplier;
}
}
}
if(verticesBuffer && vertices)
{
verticesBuffer->commit(vertices);
}
for (int i=0; i < renderItems.length(); ++i)
{
const MHWRender::MRenderItem* item = renderItems.itemAt(i);
if (!item)
{
continue;
}
if (item->name() == wireframeItemName_ )
{
int primitiveIndex = 0;
int startIndex = 0;
int numPrimitive = heelCount + soleCount - 2;
int numIndex = numPrimitive * 2;
unsigned int* indices = (unsigned int*)indexBuffer->acquire(numIndex);
for (int i = 0; i < numIndex; )
{
if (i < (heelCount - 1) * 2)
{
startIndex = 0;
primitiveIndex = i / 2;
}
else
{
startIndex = heelCount;
primitiveIndex = i / 2 - heelCount + 1;
}
indices[i++] = startIndex + primitiveIndex;
indices[i++] = startIndex + primitiveIndex + 1;
}
indexBuffer->commit(indices);
}
else if (item->name() == shadedItemName_ )
{
int primitiveIndex = 0;
int startIndex = 0;
int numPrimitive = heelCount + soleCount - 4;
int numIndex = numPrimitive * 3;
unsigned int* indices = (unsigned int*)indexBuffer->acquire(numIndex);
for (int i = 0; i < numIndex; )
{
if (i < (heelCount - 2) * 3)
{
startIndex = 0;
primitiveIndex = i / 3;
}
else
{
startIndex = heelCount;
primitiveIndex = i / 3 - heelCount + 2;
}
indices[i++] = startIndex;
indices[i++] = startIndex + primitiveIndex + 1;
indices[i++] = startIndex + primitiveIndex + 2;
}
indexBuffer->commit(indices);
}
item->associateWithIndexBuffer(indexBuffer);
}
mMultiplierChanged = false;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// Plugin Registration
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
MStatus footPrint::initialize()
{
MStatus stat;
size = unitFn.create( "size", "sz", MFnUnitAttribute::kDistance );
unitFn.setDefault( 1.0 );
stat = addAttribute( size );
if (!stat) {
stat.perror("addAttribute");
return stat;
}
return MS::kSuccess;
}
MStatus initializePlugin( MObject obj )
{
MStatus status;
MFnPlugin plugin( obj, PLUGIN_COMPANY, "3.0", "Any");
status = plugin.registerNode(
"footPrint_GeometryOverride",
footPrint::id,
&footPrint::creator,
&footPrint::initialize,
sUseLegacyDraw ? NULL : (sMakeFootPrintDirLight ? &lightClassification : &footPrint::drawDbClassification));
if (!status) {
status.perror("registerNode");
return status;
}
if (!sUseLegacyDraw)
{
footPrint::drawDbClassification,
footPrint::drawRegistrantId,
FootPrintGeometryOverride::Creator);
if (!status) {
status.perror("registerDrawOverrideCreator");
return status;
}
}
// Register a custom selection mask with priority 2 (same as locators
// by default).
MSelectionMask::registerSelectionType("footPrintSelection", 2);
status = MGlobal::executeCommand("selectType -byName \"footPrintSelection\" 1");
return status;
}
MStatus uninitializePlugin( MObject obj)
{
MStatus status;
MFnPlugin plugin( obj );
if (!sUseLegacyDraw)
{
footPrint::drawDbClassification,
footPrint::drawRegistrantId);
if (!status) {
status.perror("deregisterDrawOverrideCreator");
return status;
}
status = releaseShaders();
if (!status) {
status.perror("releaseShaders");
return status;
}
}
status = plugin.deregisterNode( footPrint::id );
if (!status) {
status.perror("deregisterNode");
return status;
}
// Deregister custom selection mask
return status;
}