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();
~footPrint() override;
MStatus compute( const MPlug& plug, MDataBlock& data ) override;
void draw( M3dView & view, const MDagPath & path,
M3dView::DisplayStatus status ) override;
bool isBounded() const override;
MBoundingBox boundingBox() const override;
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*/ )
{
return MS::kUnknownParameter;
}
// 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);
}
~FootPrintGeometryOverride() override;
bool hasUIDrawables() const override { return false; }
void updateDG() override;
bool isIndexingDirty(const MHWRender::MRenderItem &item) override { return false; }
bool isStreamDirty(const MHWRender::MVertexBufferDescriptor &desc) override { return mMultiplierChanged; }
void updateRenderItems(const MDagPath &path, MHWRender::MRenderItemList& list) override;
void populateGeometry(const MHWRender::MGeometryRequirements &requirements, const MHWRender::MRenderItemList &renderItems, MHWRender::MGeometry &data) override;
void cleanUp() override {};
/*
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.
*/
bool traceCallSequence() const override
{
// Return true if internal tracing is desired.
return false;
}
void handleTraceMessage( const MString &message ) const override
{
MGlobal::displayInfo("footPrintGeometryOverride: " + message);
// Some simple custom message formatting.
fputs("footPrintGeometryOverride: ",stderr);
fputs(message.asChar(),stderr);
fputs("\n",stderr);
}
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;
unsigned int depthPriority;
{
break;
default:
break;
}
MHWRender::MRenderItem* wireframeItem = NULL;
int index = list.indexOf(wireframeItemName_);
if (index < 0)
{
wireframeItemName_,
list.append(wireframeItem);
}
else
{
wireframeItem = list.itemAt(index);
}
if(wireframeItem)
{
wireframeItem->setShader(shader);
wireframeItem->depthPriority(depthPriority);
wireframeItem->enable(true);
}
MHWRender::MRenderItem* shadedItem = NULL;
index = list.indexOf(shadedItemName_);
if (index < 0)
{
shadedItemName_,
list.append(shadedItem);
}
else
{
shadedItem = list.itemAt(index);
}
if(shadedItem)
{
shadedItem->setShader(shader);
shadedItem->depthPriority(depthPriority);
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;
}