apiMeshShape/apiMeshGeometryOverride.cpp

apiMeshShape/apiMeshGeometryOverride.cpp
//-
// ==========================================================================
// Copyright 2010 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.
// ==========================================================================
//+
//
// apiMeshGeometryOverride.cpp
//
#include <apiMeshGeometryOverride.h>
#include <apiMeshShape.h>
#include <apiMeshGeom.h>
#include <maya/MGlobal.h>
#include <maya/MUserData.h>
#include <maya/MSelectionList.h>
#include <maya/MFnDependencyNode.h>
#include <maya/MHWGeometry.h>
#include <maya/MShaderManager.h>
#include <maya/MViewport2Renderer.h>
#include <maya/MDrawContext.h>
#include <maya/MHWGeometryUtilities.h>
#include <maya/MObjectArray.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MFnDagNode.h>
#include <set>
// Custom user data class to attach to render items
class apiMeshUserData : public MUserData
{
public:
apiMeshUserData()
: MUserData(true) // let Maya clean up
, fMessage("")
, fNumModifications(0)
{
}
virtual ~apiMeshUserData()
{
}
MString fMessage;
int fNumModifications;
};
// Pre/Post callback helper
static void callbackDataPrint(
const MHWRender::MRenderItemList& renderItemList)
{
int numItems = renderItemList.length();
const MHWRender::MRenderItem* item = NULL;
for (int i=0; i<numItems; i++)
{
item = renderItemList.itemAt(i);
if (item)
{
MDagPath path = item->sourceDagPath();
printf("\tITEM: '%s' -- SOURCE: '%s'\n", item->name().asChar(), path.fullPathName().asChar());
}
}
const MHWRender::MPassContext & passCtx = context.getPassContext();
const MString & passId = passCtx.passIdentifier();
const MStringArray & passSem = passCtx.passSemantics();
printf("tAPI mesh drawing in pass[%s], semantic[", passId.asChar());
for (unsigned int i=0; i<passSem.length(); i++)
printf(" %s", passSem[i].asChar());
printf("\n");
}
// Custom pre-draw callback
static void apiMeshPreDrawCallback(
const MHWRender::MRenderItemList& renderItemList,
MHWRender::MShaderInstance *shaderInstance)
{
printf("PRE-draw callback triggered for render item list with data:\n");
callbackDataPrint(context, renderItemList);
printf("\n");
}
// Custom post-draw callback
static void apiMeshPostDrawCallback(
const MHWRender::MRenderItemList& renderItemList,
MHWRender::MShaderInstance *shaderInstance)
{
printf("POST-draw callback triggered for render item list with data:\n");
callbackDataPrint(context, renderItemList);
printf("\n");
}
apiMeshGeometryOverride::apiMeshGeometryOverride(const MObject& obj)
: MPxGeometryOverride(obj)
, fMesh(NULL)
, fMeshGeom(NULL)
, fColorRemapTexture(NULL)
, fLinearSampler(NULL)
{
// get the real apiMesh object from the MObject
MStatus status;
MFnDependencyNode node(obj, &status);
if (status)
{
fMesh = dynamic_cast<apiMesh*>(node.userNode());
}
fWireframeItemName = "apiMeshWire";
fShadedTemplateItemName = "apiMeshShadedTemplateWire";
fSelectedWireframeItemName = "apiMeshSelection";
fVertexItemName = "apiMeshVertices";
fActiveVertexItemName = "apiMeshActiveVertices";
fVertexIdItemName = "apiMeshVertexIds";
fVertexPositionItemName = "apiMeshVeretxPositions";
fShadedModeFaceCenterItemName = "apiMeshFaceCenterInShadedMode";
fWireframeModeFaceCenterItemName = "apiMeshFaceCenterInWireframeMode";
fShadedProxyItemName = "apiShadedProxy";
fAffectedEdgeItemName = "apiMeshAffectedEdges";
fAffectedFaceItemName = "apiMeshAffectedFaces";
fDrawSharedActiveVertices = true; // false;
fActiveVertexStreamName = "apiMeshSharedVertexStream";
fDrawFaceCenters = true; // false;
fFaceCenterStreamName = "apiMeshFaceCenterStream";
// Turn on to view active vertices with colours lookedup from a 1D texture.
fDrawActiveVerticesWithRamp = false;
if (fDrawActiveVerticesWithRamp)
{
fDrawFaceCenters = false; // Too cluttered showing the face centers at the same time.
}
// Can set the following to true, but then the logic to
// determine what color to set is the responsibility of the plugin.
//
fUseCustomColors = false;
// Can change this to choose a different shader to use when
// no shader node is assigned to the object.
fProxyShader =
// Uncommenting one of the following will result in a different line
// shader to be used. Note that color-per-vertex (CPV) is provided in populateGeometry()
// to handle shaders which have this geometry requirement.
//
// -1 // Use this to indicate to later code that we want to use a built in fragment shader
// MHWRender::MShaderManager::k3dSolidShader // - Basic line shader
// MHWRender::MShaderManager::k3dStippleShader // - For filled stipple faces (triangles)
// MHWRender::MShaderManager::k3dThickLineShader // For thick solid colored lines
// MHWRender::MShaderManager::k3dCPVThickLineShader // For thick colored lines. Black if no CPV
// MHWRender::MShaderManager::k3dDashLineShader // - For dashed solid color lines
// MHWRender::MShaderManager::k3dCPVDashLineShader //- For dashed coloured lines. Black if no CPV
// MHWRender::MShaderManager::k3dThickDashLineShader // For thick dashed solid color lines. black if no cpv
MHWRender::MShaderManager::k3dCPVThickDashLineShader //- For thick, dashed and coloured lines
;
// Set to true to test that overriding internal items has no effect
// for shadows and effects overrides
fInternalItems_NoShadowCast = false;
fInternalItems_NoShadowReceive = false;
fInternalItems_NoPostEffects = false;
// Use the existing shadow casts / receives flags on the shape
// to drive settings for applicable render items.
fExternalItems_NoShadowCast = false;
fExternalItems_NoShadowReceive = false;
fExternalItemsNonTri_NoShadowCast = false;
fExternalItemsNonTri_NoShadowReceive = false;
// Set to true to ignore post-effects for wireframe items.
// Shaded items will still have effects applied.
fExternalItems_NoPostEffects = true;
fExternalItemsNonTri_NoPostEffects = true;
}
apiMeshGeometryOverride::~apiMeshGeometryOverride()
{
fMesh = NULL;
fMeshGeom = NULL;
if (fColorRemapTexture)
{
MHWRender::MTextureManager* textureMgr = renderer ? renderer->getTextureManager() : NULL;
if (textureMgr)
{
textureMgr->releaseTexture(fColorRemapTexture);
fColorRemapTexture = NULL;
}
}
if (fLinearSampler)
{
fLinearSampler = NULL;
}
}
MHWRender::DrawAPI apiMeshGeometryOverride::supportedDrawAPIs() const
{
// this plugin supports both GL and DX
}
void apiMeshGeometryOverride::updateDG()
{
// Pull the actual outMesh from the shape, as well
// as any active components
fActiveVertices.clear();
if (fMesh)
{
fMeshGeom = fMesh->meshGeom();
if (fMesh->hasActiveComponents())
{
MObjectArray clist = fMesh->activeComponents();
if (clist.length())
{
MFnSingleIndexedComponent fnComponent( clist[0] );
if (fnComponent.elementCount())
{
fnComponent.getElements( fActiveVertices );
}
}
}
}
}
/*
Some example code to print out shader parameters
*/
void apiMeshGeometryOverride::printShader(MHWRender::MShaderInstance* shader)
{
if (!shader)
return;
MStringArray params;
shader->parameterList(params);
unsigned int numParams = params.length();
printf("DEBUGGING SHADER, BEGIN PARAM LIST OF LENGTH %d\n", numParams);
for (unsigned int i=0; i<numParams; i++)
{
printf("ParamName='%s', ParamType=", params[i].asChar());
switch (shader->parameterType(params[i]))
{
printf("'Invalid', ");
break;
printf("'Boolean', ");
break;
printf("'Integer', ");
break;
printf("'Float', ");
break;
printf("'Float2', ");
break;
printf("'Float3', ");
break;
printf("'Float4', ");
break;
printf("'Float4x4Row', ");
break;
printf("'Float4x4Col', ");
break;
printf("'1D Texture', ");
break;
printf("'2D Texture', ");
break;
printf("'3D Texture', ");
break;
printf("'Cube Texture', ");
break;
printf("'Sampler', ");
break;
default:
printf("'Unknown', ");
break;
}
printf("IsArrayParameter='%s'\n", shader->isArrayParameter(params[i]) ? "YES" : "NO");
}
printf("END PARAM LIST\n");
}
/*
Set the solid color for solid color shaders
*/
void apiMeshGeometryOverride::setSolidColor(MHWRender::MShaderInstance* shaderInstance, const float *value)
{
if (!shaderInstance)
return;
const MString colorParameterName = "solidColor";
shaderInstance->setParameter(colorParameterName, value);
}
/*
Set the point size for solid color shaders
*/
void apiMeshGeometryOverride::setSolidPointSize(MHWRender::MShaderInstance* shaderInstance, float pointSize)
{
if (!shaderInstance)
return;
float pointSizeArray[2] = {0};
pointSizeArray[0] = pointSize;
pointSizeArray[1] = pointSize;
const MString pointSizeParameterName = "pointSize";
shaderInstance->setParameter( pointSizeParameterName, pointSizeArray );
}
/*
Set the line width for solid color shaders
*/
void apiMeshGeometryOverride::setLineWidth(MHWRender::MShaderInstance* shaderInstance, float lineWidth)
{
if (!shaderInstance)
return;
float lineWidthArray[2] = {0};
lineWidthArray[0] = lineWidth;
lineWidthArray[1] = lineWidth;
const MString pointSizeParameterName = "lineWidth";
shaderInstance->setParameter( pointSizeParameterName, lineWidthArray );
}
/*
Update render items for dormant and template wireframe drawing.
1) If the object is dormant and not templated then we require
a render item to display when wireframe drawing is required (display modes
is wire or wire-on-shaded)
2a) If the object is templated then we use the same render item as in 1)
when in wireframe drawing is required.
2b) However we also require a render item to display when in shaded mode.
*/
void apiMeshGeometryOverride::updateDormantAndTemplateWireframeItems(
const MDagPath& path,
const MHWRender::MShaderManager* shaderMgr)
{
// Stock colors
static const float dormantColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
static const float templateColor[] = { 0.45f, 0.45f, 0.45f, 1.0f };
static const float activeTemplateColor[] = { 1.0f, 0.5f, 0.5f, 1.0f };
// Some local options to show debug interface
//
static const bool debugShader = false;
static const unsigned int shadedDrawMode = MHWRender::MGeometry::kAll;
// Get render item used for draw in wireframe mode
// (Mode to draw in is MHWRender::MGeometry::kWireframe)
//
MHWRender::MRenderItem* wireframeItem = NULL;
int index = list.indexOf(fWireframeItemName);
if (index < 0)
{
fWireframeItemName,
primitive);
// Set dormant wireframe with appropriate priority to not clash with
// any active wireframe which may overlap in depth.
list.append(wireframeItem);
debugShader ? apiMeshPreDrawCallback : NULL,
debugShader ? apiMeshPostDrawCallback : NULL);
if (shader)
{
// assign shader
wireframeItem->setShader(shader);
// sample debug code
if (debugShader)
{
printShader( shader );
}
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
wireframeItem = list.itemAt(index);
}
// Get render item for handling mode shaded template drawing
//
MHWRender::MRenderItem* shadedTemplateItem = NULL;
int index2 = list.indexOf(fShadedTemplateItemName);
if (index2 < 0)
{
shadedTemplateItem = MHWRender::MRenderItem::Create(
fShadedTemplateItemName,
primitive);
shadedTemplateItem->setDrawMode((MHWRender::MGeometry::DrawMode) shadedDrawMode);
// Set shaded item as being dormant wire since it should still be raised
// above any shaded items, but not as high as active items.
list.append(shadedTemplateItem);
NULL, NULL);
if (shader)
{
// assign shader
shadedTemplateItem->setShader(shader);
// sample debug code
if (debugShader)
{
printShader( shader );
}
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
shadedTemplateItem = list.itemAt(index2);
}
// Sample code to disable cast, receives shadows, and post effects.
if (fExternalItemsNonTri_NoShadowCast)
shadedTemplateItem->castsShadows( false );
if (fExternalItemsNonTri_NoShadowReceive)
shadedTemplateItem->receivesShadows( false );
if (fExternalItemsNonTri_NoPostEffects)
shadedTemplateItem->setExcludedFromPostEffects( true );
MHWRender::DisplayStatus displayStatus =
// Enable / disable wireframe item and update the shader parameters
//
if (wireframeItem)
{
MHWRender::MShaderInstance* shader = wireframeItem->getShader();
switch (displayStatus) {
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : templateColor);
}
wireframeItem->enable(true);
break;
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : activeTemplateColor );
}
wireframeItem->enable(true);
break;
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : dormantColor);
}
wireframeItem->enable(true);
break;
if (shader)
{
static const float theColor[] = { 0.5f, 0.0f, 1.0f, 1.0f };
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
}
wireframeItem->enable(true);
break;
default:
wireframeItem->enable(false);
break;
}
}
// Enable / disable shaded/template item and update the shader parameters
//
bool isTemplate = path.isTemplated();
if (shadedTemplateItem)
{
MHWRender::MShaderInstance* shader = shadedTemplateItem->getShader();
switch (displayStatus) {
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : templateColor );
}
shadedTemplateItem->enable(isTemplate);
break;
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : activeTemplateColor );
}
shadedTemplateItem->enable(isTemplate);
break;
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : dormantColor);
}
shadedTemplateItem->enable(isTemplate);
break;
default:
shadedTemplateItem->enable(false);
break;
}
}
}
/*
Create a render item for active wireframe if it does not exist. Updating
shading parameters as necessary.
*/
void apiMeshGeometryOverride::updateActiveWireframeItem(const MDagPath& path,
const MHWRender::MShaderManager* shaderMgr)
{
MHWRender::MRenderItem* selectItem = NULL;
int index = list.indexOf(fSelectedWireframeItemName);
if (index < 0)
{
fSelectedWireframeItemName,
// This is the same as setting the argument raiseAboveShaded = true,
// since it sets the priority value to be the same. This line is just
// an example of another way to do the same thing after creation of
// the render item.
list.append(selectItem);
// For active wireframe we will use a shader which allows us to draw thick lines
//
static const bool drawThick = false;
if (shader)
{
// assign shader
selectItem->setShader(shader);
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
selectItem = list.itemAt(index);
}
if (selectItem)
{
shader = selectItem->getShader();
}
MHWRender::DisplayStatus displayStatus =
switch (displayStatus) {
selectItem->enable(true);
if (shader)
{
static const float theColor[] = { 0.0f, 0.8f, 0.0f, 1.0f };
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
}
break;
if (shader)
{
static const float theColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
}
selectItem->enable(true);
break;
if (shader)
{
static const float theColor[] = { 0.0f, 0.5f, 0.7f, 1.0f };
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : theColor );
}
selectItem->enable(true);
break;
default:
selectItem->enable(false);
break;
};
// Add custom user data to selection item
apiMeshUserData* myCustomData = dynamic_cast<apiMeshUserData*>(selectItem->customData());
if (!myCustomData)
{
// create the custom data
myCustomData = new apiMeshUserData();
myCustomData->fMessage = "I'm custom data!";
selectItem->setCustomData(myCustomData);
}
else
{
// modify the custom data
myCustomData->fNumModifications++;
}
}
/*
Create render items for numeric display, and update shaders as necessary
*/
void apiMeshGeometryOverride::updateVertexNumericItems(const MDagPath& path, MHWRender::MRenderItemList& list, const MHWRender::MShaderManager* shaderMgr)
{
// Enable to show numeric render items
bool enableNumiercDisplay = false;
// Vertex id item
//
MHWRender::MRenderItem* vertexItem = NULL;
int index = list.indexOf(fVertexIdItemName);
if (index < 0)
{
fVertexIdItemName,
list.append(vertexItem);
// Use single integer numeric shader
if (shader)
{
// Label the fields so that they can be found later on.
vertexItem->setShader(shader, &fVertexIdItemName);
shaderMgr->releaseShader(shader);
}
}
else
{
vertexItem = list.itemAt(index);
}
if (vertexItem)
{
MHWRender::MShaderInstance* shader = vertexItem->getShader();
if (shader)
{
// set color
static const float theColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
setSolidColor( shader, theColor);
}
vertexItem->enable(enableNumiercDisplay);
}
// Vertex position numeric render item
//
MHWRender::MRenderItem* vertexItem2 = NULL;
index = list.indexOf(fVertexPositionItemName);
if (index < 0)
{
fVertexPositionItemName,
list.append(vertexItem2);
// Use triple float numeric shader
if (shader)
{
//vertexItem2->setShader(shader);
vertexItem2->setShader(shader, &fVertexPositionItemName);
shaderMgr->releaseShader(shader);
}
}
else
{
vertexItem2 = list.itemAt(index);
}
if (vertexItem2)
{
MHWRender::MShaderInstance* shader = vertexItem2->getShader();
if (shader)
{
// set color
static const float theColor[] = { 0.0f, 1.0f, 1.0f, 1.0f };
setSolidColor( shader, theColor);
}
vertexItem2->enable(enableNumiercDisplay);
}
}
/*
Create a render item for dormant vertices if it does not exist. Updating
shading parameters as necessary.
*/
void apiMeshGeometryOverride::updateDormantVerticesItem(const MDagPath& path, MHWRender::MRenderItemList& list, const MHWRender::MShaderManager* shaderMgr)
{
MHWRender::MRenderItem* vertexItem = NULL;
int index = list.indexOf(fVertexItemName);
if (index < 0)
{
fVertexItemName,
// Set depth priority higher than wireframe and shaded render items,
// but lower than active points.
// Raising higher than wireframe will make them not seem embedded into the surface
list.append(vertexItem);
if (shader)
{
// Set the point size parameter
static const float pointSize = 3.0f;
setSolidPointSize( shader, pointSize );
// assign shader
vertexItem->setShader(shader);
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
vertexItem = list.itemAt(index);
}
if (vertexItem)
{
MHWRender::MShaderInstance* shader = vertexItem->getShader();
if (shader)
{
// set color
static const float theColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
setSolidColor( shader, theColor);
}
MHWRender::DisplayStatus displayStatus =
// Generally if the display status is hilite then we
// draw components.
if (displayStatus == MHWRender::kHilite)
{
// In case the object is templated
// we will hide the components to be consistent
// with how internal objects behave.
if (path.isTemplated())
vertexItem->enable(false);
else
vertexItem->enable(true);
}
else
{
vertexItem->enable(false);
}
}
}
/*
Create a render item for active vertices if it does not exist. Updating
shading parameters as necessary.
*/
void apiMeshGeometryOverride::updateActiveVerticesItem(const MDagPath& path, MHWRender::MRenderItemList& list, const MHWRender::MShaderManager* shaderMgr, bool enableActiveDisplay)
{
MHWRender::MRenderItem* activeItem = NULL;
int index = list.indexOf(fActiveVertexItemName);
if (index < 0)
{
fActiveVertexItemName,
// Set depth priority to be active point. This should offset it
// to be visible above items with "dormant point" priority.
list.append(activeItem);
if (shader)
{
// Set the point size parameter. Make it slightly larger for active vertices
static const float pointSize = 5.0f;
setSolidPointSize( shader, pointSize );
// 1D Ramp color lookup option
//
if (fDrawActiveVerticesWithRamp )
{
MHWRender::MTextureManager* textureMgr = renderer->getTextureManager();
// Assign dummy ramp lookup
if (!fColorRemapTexture)
{
// Sample 3 colour ramp
float colorArray[12];
colorArray[0] = 1.0f; colorArray[1] = 0.0f; colorArray[2] = 0.0f; colorArray[3] = 1.0f;
colorArray[4] = 0.0f; colorArray[5] = 1.0f; colorArray[6] = 0.0f; colorArray[7] = 1.0f;
colorArray[8] = 0.0f; colorArray[9] = 0.0f; colorArray[10] = 1.0f; colorArray[11] = 1.0f;
unsigned int arrayLen = 3;
textureDesc.setToDefault2DTexture();
textureDesc.fWidth = arrayLen;
textureDesc.fHeight = 1;
textureDesc.fDepth = 1;
textureDesc.fBytesPerSlice = textureDesc.fBytesPerRow = 24*arrayLen;
textureDesc.fMipmaps = 1;
textureDesc.fArraySlices = 1;
fColorRemapTexture =
textureMgr->acquireTexture("", textureDesc, &( colorArray[0] ), false);
}
if (!fLinearSampler)
{
fLinearSampler = MHWRender::MStateManager::acquireSamplerState(samplerDesc);
}
if (fColorRemapTexture && fLinearSampler)
{
// Set up the ramp lookup
texAssignment.texture = fColorRemapTexture;
shader->setParameter("map", texAssignment);
shader->setParameter("samp", *fLinearSampler);
// No remapping. The initial data created in the range 0...1
//
MFloatVector rampValueRange(0.0f, 1.0f);
shader->setParameter("UVRange", (float*)&rampValueRange);
}
}
// Assign shader. Use a named stream if we want to supply a different
// set of "shared" vertices for drawing active vertices
if (fDrawSharedActiveVertices)
{
activeItem->setShader(shader, &fActiveVertexStreamName );
}
else
{
activeItem->setShader(shader, NULL);
}
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
activeItem = list.itemAt(index);
}
if (activeItem)
{
MHWRender::MShaderInstance* shader = activeItem->getShader();
if (shader)
{
// Set active color
static const float theColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
setSolidColor( shader, theColor);
}
activeItem->enable( enableActiveDisplay );
}
}
//Add render item for face centers in wireframe mode, always show face centers in wireframe mode except it is drawn as template.
void apiMeshGeometryOverride::updateWireframeModeFaceCenterItem(const MDagPath& path, MHWRender::MRenderItemList& list, const MHWRender::MShaderManager* shaderMgr)
{
MHWRender::MRenderItem* wireframeModeFaceCenterItem = NULL;
int index = list.indexOf(fWireframeModeFaceCenterItemName);
if (index < 0)
{
wireframeModeFaceCenterItem = MHWRender::MRenderItem::Create(
fWireframeModeFaceCenterItemName,
wireframeModeFaceCenterItem->setDrawMode(MHWRender::MGeometry::kWireframe);
list.append(wireframeModeFaceCenterItem);
MHWRender::MShaderManager::k3dFatPointShader );
if (shader)
{
// Set the point size parameter. Make it slightly larger for face centers
static const float pointSize = 5.0f;
setSolidPointSize( shader, pointSize );
wireframeModeFaceCenterItem->setShader(shader, &fFaceCenterStreamName );
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
wireframeModeFaceCenterItem = list.itemAt(index);
}
if (wireframeModeFaceCenterItem)
{
MHWRender::MShaderInstance* shader = wireframeModeFaceCenterItem->getShader();
if (shader)
{
// Set face center color in wireframe mode
static const float theColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
setSolidColor( shader, theColor);
}
//disable the face center item when template
bool isTemplate = path.isTemplated();
if(isTemplate)
wireframeModeFaceCenterItem->enable( false );
}
}
//Add render item for face centers in shaded mode. If the geometry is not selected, face centers are not drawn.
void apiMeshGeometryOverride::updateShadedModeFaceCenterItem(const MDagPath& path, MHWRender::MRenderItemList& list, const MHWRender::MShaderManager* shaderMgr)
{
static const unsigned int shadedDrawMode =
MHWRender::MRenderItem* shadedModeFaceCenterItem = NULL;
int index = list.indexOf(fShadedModeFaceCenterItemName);
if (index < 0)
{
shadedModeFaceCenterItem = MHWRender::MRenderItem::Create(
fShadedModeFaceCenterItemName,
shadedModeFaceCenterItem->setDrawMode((MHWRender::MGeometry::DrawMode)shadedDrawMode);
list.append(shadedModeFaceCenterItem);
MHWRender::MShaderManager::k3dFatPointShader );
if (shader)
{
// Set the point size parameter. Make it slightly larger for face centers
static const float pointSize = 5.0f;
setSolidPointSize( shader, pointSize );
shadedModeFaceCenterItem->setShader(shader, &fFaceCenterStreamName );
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
shadedModeFaceCenterItem = list.itemAt(index);
}
if (shadedModeFaceCenterItem)
{
shadedModeFaceCenterItem->setExcludedFromPostEffects(true);
MHWRender::MShaderInstance* shader = shadedModeFaceCenterItem->getShader();
if (shader)
{
// Set face center color in shaded mode
setSolidColor( shader, &(wireColor.r));
}
switch(displayStatus){
shadedModeFaceCenterItem->enable(true);
break;
default:
shadedModeFaceCenterItem->enable(false);
break;
}
}
}
/*
Test to see if active components should be enabled.
Based on active vertices + non-template state
*/
bool apiMeshGeometryOverride::enableActiveComponentDisplay(const MDagPath &path) const
{
bool enable = true;
// If no active components then disable the active
// component render item
if (fActiveVertices.length() == 0)
{
enable = false;
}
else
{
// If there are components then we need to check
// either the display status of the object, or
// in the case of a templated object make sure
// to hide components to be consistent with how
// internal objects behave
//
MHWRender::DisplayStatus displayStatus =
if (displayStatus == MHWRender::kTemplate ||
displayStatus == MHWRender::kActiveTemplate)
{
enable = false;
}
else
{
// Do an explicit path test for templated
// since display status does not indicate this.
if (path.isTemplated())
enable = false;
}
}
return enable;
}
/*
Example of adding in items to hilite edges and faces. In this
case these are edges and faces which are connected to vertices
and we thus call them "affected" components.
*/
void apiMeshGeometryOverride::updateAffectedComponentItems(
const MDagPath& path,
const MHWRender::MShaderManager* shaderMgr,
bool enableActiveDisplay)
{
// Create / update "affected" edges component render item.
//
MHWRender::MRenderItem* componentItem = NULL;
int index = list.indexOf(fAffectedEdgeItemName);
if (index < 0)
{
fAffectedEdgeItemName,
// Set depth priority to be active line so that it is above wireframe
// but below dormant and active points.
list.append(componentItem);
MHWRender::MShaderManager::k3dSolidShader );
if (shader)
{
// Set lines a bit thicker to stand out
static const float lineSize = 1.0f;
setLineWidth( shader, lineSize );
// Assign shader.
componentItem->setShader(shader, NULL);
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
componentItem = list.itemAt(index);
}
if (componentItem)
{
MHWRender::MShaderInstance* shader = componentItem->getShader();
if (shader)
{
// Set affected color
static const float theColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
setSolidColor( shader, theColor);
}
componentItem->enable( enableActiveDisplay );
}
// Create / update "affected" faces component render item
//
componentItem = NULL;
index = list.indexOf(fAffectedFaceItemName);
if (index < 0)
{
fAffectedFaceItemName,
// Set depth priority to be dormant wire so that edge and vertices
// show on top.
list.append(componentItem);
if (shader)
{
// Assign shader.
componentItem->setShader(shader, NULL);
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
componentItem = list.itemAt(index);
}
if (componentItem)
{
MHWRender::MShaderInstance* shader = componentItem->getShader();
if (shader)
{
// Set affected color
static const float theColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };
setSolidColor( shader, theColor);
}
componentItem->enable( enableActiveDisplay );
}
}
/*
In the event there are no shaded items we create a proxy
render item so we can still see where the object is.
*/
void apiMeshGeometryOverride::updateProxyShadedItem(
const MDagPath& path,
const MHWRender::MShaderManager* shaderMgr)
{
// Stock colors
static const float dormantColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
static const float templateColor[] = { 0.45f, 0.45f, 0.45f, 1.0f };
static const float activeTemplateColor[] = { 1.0f, 0.5f, 0.5f, 1.0f };
// Note that we still want to raise it above shaded even though
// we don't have a shaded render item for this override.
// This will handle in case where there is another shaded object
// which overlaps this object in depth
//
static const bool raiseAboveShaded = true;
unsigned int shadedDrawMode =
// Mark proxy item as wireframe if not using a material shader
//
bool useFragmentShader = fProxyShader < 0;
if ( !useFragmentShader )
// Fragment + stipple shaders required triangles. All others
// in the possible list requires lines
//
bool filledProxy = ( useFragmentShader
||
if (filledProxy)
{
}
MHWRender::MRenderItem* proxyItem = NULL;
int index = list.indexOf(fShadedProxyItemName);
if (index < 0)
{
fShadedProxyItemName,
primitive);
proxyItem->setDrawMode((MHWRender::MGeometry::DrawMode) shadedDrawMode);
proxyItem->depthPriority( raiseAboveShaded
if (fExternalItems_NoShadowCast)
proxyItem->castsShadows( false );
else
proxyItem->castsShadows( fCastsShadows );
if (fExternalItems_NoShadowReceive)
proxyItem->receivesShadows( false );
else
proxyItem->receivesShadows( fReceivesShadows );
if (fExternalItems_NoPostEffects)
proxyItem->setExcludedFromPostEffects( true );
list.append(proxyItem);
// We'll draw the proxy with a proxy shader as a visual cue
//
if (useFragmentShader)
{
shader = shaderMgr->getFragmentShader("mayaLambertSurface", "outSurfaceFinal", true);
static const float sBlue[] = {0.4f, 0.4f, 1.0f};
shader->setParameter("color", sBlue);
shader->setIsTransparent(false);
}
else
{
shader = shaderMgr->getStockShader( (MHWRender::MShaderManager::MStockShader)fProxyShader );
}
if (shader)
{
if (!filledProxy)
setLineWidth(shader, 10.0f);
// assign shader
proxyItem->setShader(shader);
// once assigned, no need to hold on to shader instance
shaderMgr->releaseShader(shader);
}
}
else
{
proxyItem = list.itemAt(index);
}
// As this is a shaded item it is up to the plug-in to determine
// on each update how to handle shadowing and effects.
// Especially note that shadowing changes on the DAG object will trigger
// a call to updateRenderItems()
//
if (fExternalItems_NoShadowCast)
proxyItem->castsShadows( false );
else
proxyItem->castsShadows( fCastsShadows );
if (fExternalItems_NoShadowReceive)
proxyItem->receivesShadows( false );
else
proxyItem->receivesShadows( fReceivesShadows );
if (fExternalItems_NoPostEffects)
proxyItem->setExcludedFromPostEffects( true );
MHWRender::DisplayStatus displayStatus =
// Check for any shaded render items. A lack of one indicates
// there is no shader assigned to the object.
//
bool haveShadedItems = false;
for (int i=0; i<list.length(); i++)
{
MHWRender::MRenderItem *item = list.itemAt(i);
if (!item)
continue;
)
{
if (item->name() != fShadedTemplateItemName)
{
haveShadedItems = true;
break;
}
}
}
// If we are missing shaded render items then enable
// the proxy. Otherwise disable it.
//
if (filledProxy)
{
// If templated then hide filled proxy
if (path.isTemplated())
proxyItem->enable(false);
else
proxyItem->enable(!haveShadedItems);
}
else
proxyItem->enable(!haveShadedItems);
// Note that we do not toggle the item on and off just based on
// display state. If this was so then call to MRenderer::setLightsAndShadowsDirty()
// would be required as shadow map update does not monitor display state.
//
if (proxyItem)
{
MHWRender::MShaderInstance* shader = proxyItem->getShader();
switch (displayStatus) {
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : templateColor);
}
break;
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : activeTemplateColor );
}
break;
if (shader)
{
setSolidColor( shader, !fUseCustomColors ? &(wireColor.r) : dormantColor);
}
break;
default:
break;
}
}
}
/*
Update render items. Shaded render item is provided so this
method will be adding and updating UI render items only.
*/
void apiMeshGeometryOverride::updateRenderItems(
const MDagPath& path,
{
if (!renderer) return;
const MHWRender::MShaderManager* shaderMgr = renderer->getShaderManager();
if (!shaderMgr) return;
MFnDagNode dagNode(path);
MPlug castsShadowsPlug = dagNode.findPlug("castsShadows", false);
fCastsShadows = castsShadowsPlug.asBool();
MPlug receiveShadowsPlug = dagNode.findPlug("receiveShadows", false);
fReceivesShadows = receiveShadowsPlug.asBool();
// Update wireframe render items
updateDormantAndTemplateWireframeItems(path, list, shaderMgr);
updateActiveWireframeItem(path, list, shaderMgr);
bool enableActiveDisplay = enableActiveComponentDisplay(path);
// Update vertex render items
updateDormantVerticesItem(path, list, shaderMgr);
updateActiveVerticesItem(path, list, shaderMgr, enableActiveDisplay);
// Update vertex numeric render items
updateVertexNumericItems(path, list, shaderMgr);
//Update face center item
if(fDrawFaceCenters)
{
updateWireframeModeFaceCenterItem(path, list, shaderMgr);
updateShadedModeFaceCenterItem(path, list, shaderMgr);
}
// Update "affected" edge and face render items
updateAffectedComponentItems(path, list, shaderMgr, enableActiveDisplay);
// Update proxy shaded render item
updateProxyShadedItem(path, list, shaderMgr);
// Test overrides on existing shaded items.
// In this case it is not valid to override these states
// so there should be no change in behaviour.
//
const bool testShadedOverrides = fInternalItems_NoShadowCast || fInternalItems_NoShadowReceive || fInternalItems_NoPostEffects;
if (testShadedOverrides)
{
for (int i=0; i<list.length(); i++)
{
MHWRender::MRenderItem *item = list.itemAt(i);
if (!item)
continue;
)
{
if (item->name() != fShadedTemplateItemName
//&& item->name() != fShadedProxyItemName
)
{
if (fInternalItems_NoShadowCast)
item->castsShadows( false );
else
item->castsShadows( fCastsShadows );
if (fInternalItems_NoShadowReceive)
item->receivesShadows( false );
else
item->receivesShadows( fReceivesShadows );
if (fInternalItems_NoPostEffects)
}
}
}
}
}
/*
Examine the geometry requirements and create / update the
appropriate data streams. As render items specify both named and
unnamed data streams, both need to be handled here.
*/
void apiMeshGeometryOverride::updateGeometryRequirements(
const MHWRender::MGeometryRequirements& requirements,
unsigned int activeVertexCount,
unsigned int totalVerts,
bool debugPopulateGeometry)
{
// Vertex data
MHWRender::MVertexBuffer* positionBuffer = NULL;
float* positions = NULL;
MHWRender::MVertexBuffer* vertexNumericIdBuffer = NULL;
float* vertexNumericIds = NULL;
MHWRender::MVertexBuffer* vertexNumericIdPositionBuffer = NULL;
float* vertexNumericIdPositions = NULL;
MHWRender::MVertexBuffer* vertexNumericLocationBuffer = NULL;
float* vertexNumericLocations = NULL;
MHWRender::MVertexBuffer* vertexNumericLocationPositionBuffer = NULL;
float* vertexNumericLocationPositions = NULL;
MHWRender::MVertexBuffer* activeVertexPositionBuffer = NULL;
float* activeVertexPositions = NULL;
MHWRender::MVertexBuffer* activeVertexUVBuffer = NULL;
float* activeVertexUVs = NULL;
MHWRender::MVertexBuffer* faceCenterPositionBuffer = NULL;
float* faceCenterPositions = NULL;
MHWRender::MVertexBuffer* normalBuffer = NULL;
float* normals = NULL;
MHWRender::MVertexBuffer* cpvBuffer = NULL;
float* cpv = NULL;
MHWRender::MVertexBuffer* uvBuffer = NULL;
float* uvs = NULL;
int numUVs = fMeshGeom->uvcoords.uvcount();
requirements.vertexRequirements();
int numVertexReqs = descList.length();
for (int reqNum=0; reqNum<numVertexReqs; reqNum++)
{
if (!descList.getDescriptor(reqNum, desc))
{
continue;
}
// Fill in vertex data for drawing active vertex components (if drawSharedActiveVertices=true)
//
if (fDrawSharedActiveVertices && (desc.name() == fActiveVertexStreamName))
{
switch (desc.semantic())
{
{
if (!activeVertexPositionBuffer)
{
activeVertexPositionBuffer = data.createVertexBuffer(desc);
if (activeVertexPositionBuffer)
{
if (debugPopulateGeometry)
{
printf(">>> Fill in data for active vertex requirement[%d] with name %s. Semantic = %d\n",
reqNum, desc.name().asChar(), desc.semantic() );
}
activeVertexPositions = (float*)activeVertexPositionBuffer->acquire(activeVertexCount, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
break;
{
if (!activeVertexUVBuffer)
{
activeVertexUVBuffer = data.createVertexBuffer(desc);
if (activeVertexUVBuffer)
{
if (debugPopulateGeometry)
{
printf(">>> Fill in data for active vertex requirement[%d] with name %s. Semantic = %d\n",
reqNum, desc.name().asChar(), desc.semantic() );
}
activeVertexUVs = (float*)activeVertexUVBuffer->acquire(activeVertexCount, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
default:
// do nothing for stuff we don't understand
break;
}
}
// Fill in vertex data for drawing face center components (if fDrawFaceCenters=true)
//
else if (fDrawFaceCenters && (desc.name() == fFaceCenterStreamName))
{
switch (desc.semantic())
{
{
if (!faceCenterPositionBuffer)
{
faceCenterPositionBuffer = data.createVertexBuffer(desc);
if (faceCenterPositionBuffer)
{
if (debugPopulateGeometry)
{
printf(">>> Fill in data for face center requirement[%d] with name %s. Semantic = %d\n",
reqNum, desc.name().asChar(), desc.semantic() );
}
faceCenterPositions = (float*)faceCenterPositionBuffer->acquire(fMeshGeom->faceCount, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
break;
default:
// do nothing for stuff we don't understand
break;
}
}
// Fill vertex stream data used for dormant vertex, wireframe and shaded drawing.
// Fill also for active vertices if (fDrawSharedActiveVertices=false)
else
{
if (debugPopulateGeometry)
{
printf(">>> Fill in data for requirement[%d] with name %s. Semantic = %d\n",
reqNum, desc.name().asChar(), desc.semantic() );
}
switch (desc.semantic())
{
{
if (desc.name() == fVertexIdItemName)
{
if (!vertexNumericIdPositionBuffer)
{
vertexNumericIdPositionBuffer = data.createVertexBuffer(desc);
if (vertexNumericIdPositionBuffer)
{
if (debugPopulateGeometry)
printf("Acquire 1float-numeric position buffer\n");
vertexNumericIdPositions = (float*)vertexNumericIdPositionBuffer->acquire(totalVerts, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
else if (desc.name() == fVertexPositionItemName)
{
if (!vertexNumericLocationPositionBuffer)
{
vertexNumericLocationPositionBuffer = data.createVertexBuffer(desc);
if (vertexNumericLocationPositionBuffer)
{
if (debugPopulateGeometry)
printf("Acquire 3float-numeric position buffer\n");
vertexNumericLocationPositions = (float*)vertexNumericLocationPositionBuffer->acquire(totalVerts, true /*writeOnly */);
}
}
}
else
{
if (!positionBuffer)
{
positionBuffer = data.createVertexBuffer(desc);
if (positionBuffer)
{
if (debugPopulateGeometry)
printf("Acquire unnamed position buffer\n");
positions = (float*)positionBuffer->acquire(totalVerts, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
}
break;
{
if (!normalBuffer)
{
normalBuffer = data.createVertexBuffer(desc);
if (normalBuffer)
{
normals = (float*)normalBuffer->acquire(totalVerts, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
break;
{
static const MString numericValue("numericvalue");
static const MString numeric3Value("numeric3value");
// Fill in single numeric field
if ((desc.semanticName().toLowerCase() == numericValue) && (desc.name() == fVertexIdItemName))
{
if (!vertexNumericIdBuffer)
{
vertexNumericIdBuffer = data.createVertexBuffer(desc);
if (vertexNumericIdBuffer)
{
if (debugPopulateGeometry)
printf("Acquire 1float numeric buffer\n");
vertexNumericIds = (float*)vertexNumericIdBuffer->acquire(totalVerts, true /*writeOnly */);
}
}
}
// Fill in triple numeric field
else if ((desc.semanticName().toLowerCase() == numeric3Value) && (desc.name() == fVertexPositionItemName))
{
if (!vertexNumericLocationBuffer)
{
vertexNumericLocationBuffer = data.createVertexBuffer(desc);
if (vertexNumericLocationBuffer)
{
if (debugPopulateGeometry)
printf("Acquire 3float numeric location buffer\n");
vertexNumericLocations = (float*)vertexNumericLocationBuffer->acquire(totalVerts, true /*writeOnly */);
}
}
}
// Fill in uv values
else if (desc.name() != fVertexIdItemName &&
desc.name() != fVertexPositionItemName)
{
if (!uvBuffer)
{
uvBuffer = data.createVertexBuffer(desc);
if (uvBuffer)
{
if (debugPopulateGeometry)
printf("Acquire a uv buffer\n");
uvs = (float*)uvBuffer->acquire(totalVerts, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
}
break;
{
if (!cpvBuffer)
{
cpvBuffer = data.createVertexBuffer(desc);
if (cpvBuffer)
{
cpv = (float*)cpvBuffer->acquire(totalVerts, true /*writeOnly - we don't need the current buffer values*/);
}
}
}
break;
default:
// do nothing for stuff we don't understand
break;
}
}
}
int vid = 0;
int pid = 0;
int nid = 0;
int uvid = 0;
int cid = 0;
for (int i=0; i<fMeshGeom->faceCount; i++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[i];
if (numVerts > 2)
{
for (int j=0; j<numVerts; j++)
{
if (positions || vertexNumericIdPositions ||
vertexNumericLocationPositions || vertexNumericLocations)
{
MPoint position = fMeshGeom->vertices[fMeshGeom->face_connects[vid]];
// Position used as position
if (positions)
{
positions[pid] = (float)position[0];
positions[pid+1] = (float)position[1];
positions[pid+2] = (float)position[2];
}
// Move the id's a bit to avoid overlap. Position used as position.
if (vertexNumericIdPositions)
{
vertexNumericIdPositions[pid] = (float)(position[0])+1.0f;
vertexNumericIdPositions[pid+1] = (float)(position[1])+1.0f;
vertexNumericIdPositions[pid+2] = (float)(position[2])+1.0f;
}
// Move the locations a bit to avoid overlap. Position used as position.
if (vertexNumericLocationPositions)
{
vertexNumericLocationPositions[pid] = (float)(position[0])+3.0f;
vertexNumericLocationPositions[pid+1] = (float)(position[1])+3.0f;
vertexNumericLocationPositions[pid+2] = (float)(position[2])+3.0f;
}
// Position used as numeric display.
if (vertexNumericLocations)
{
vertexNumericLocations[pid] = (float)position[0];
vertexNumericLocations[pid+1] = (float)position[1];
vertexNumericLocations[pid+2] = (float)position[2];
}
pid += 3;
}
if (normals)
{
MVector normal = fMeshGeom->normals[fMeshGeom->face_connects[vid]];
normals[nid++] = (float)normal[0];
normals[nid++] = (float)normal[1];
normals[nid++] = (float)normal[2];
}
if (uvs)
{
float u = 0.0f;
float v = 0.0f;
if (numUVs > 0)
{
int uvNum = fMeshGeom->uvcoords.uvId(vid);
fMeshGeom->uvcoords.getUV(uvNum, u, v);
}
uvs[uvid++] = u;
uvs[uvid++] = v;
}
// Just same fake colors to show filling in requirements for
// color-per-vertex (CPV)
if (cpv)
{
MPoint position = fMeshGeom->vertices[fMeshGeom->face_connects[vid]];
cpv[cid++] = (float)position[0];
cpv[cid++] = (float)position[1];
cpv[cid++] = (float)position[2];
cpv[cid++] = 1.0f;
}
// Vertex id's used for numeric display
if (vertexNumericIds)
{
vertexNumericIds[vid] = (float)(fMeshGeom->face_connects[vid]);
}
vid++;
}
}
else if (numVerts > 0)
{
vid += numVerts;
}
}
if (positions)
{
positionBuffer->commit(positions);
}
if (normals)
{
normalBuffer->commit(normals);
}
if (uvs)
{
uvBuffer->commit(uvs);
}
if (cpv)
{
cpvBuffer->commit(cpv);
}
if (vertexNumericIds)
{
vertexNumericIdBuffer->commit(vertexNumericIds);
}
if (vertexNumericIdPositions)
{
vertexNumericIdPositionBuffer->commit(vertexNumericIdPositions);
}
if (vertexNumericLocations)
{
vertexNumericLocationBuffer->commit(vertexNumericLocations);
}
if (vertexNumericLocationPositions)
{
vertexNumericLocationPositionBuffer->commit(vertexNumericLocationPositions);
}
// Fill in active vertex data buffer (only when fDrawSharedActiveVertices=true
// which results in activeVertexPositions and activeVertexPositionBuffer being non-NULL)
//
if (activeVertexPositions && activeVertexPositionBuffer)
{
if (debugPopulateGeometry)
{
printf(">>> Fill in the data for active vertex position buffer base on component list\n");
}
// Fill in position buffer with positions based on active vertex indexing list
//
pid = 0;
if (activeVertexCount > fMeshGeom->vertices.length())
activeVertexCount = fMeshGeom->vertices.length();
for (unsigned int i=0; i<activeVertexCount; i++)
{
MPoint position = fMeshGeom->vertices[ fActiveVertices[i] ];
activeVertexPositions[pid++] = (float)position[0];
activeVertexPositions[pid++] = (float)position[1];
activeVertexPositions[pid++] = (float)position[2];
}
activeVertexPositionBuffer->commit(activeVertexPositions);
}
if (activeVertexUVs && activeVertexUVBuffer)
{
if (debugPopulateGeometry)
{
printf(">>> Fill in the data for active vertex uv buffer base on component list\n");
}
// Fill in position buffer with positions based on active vertex indexing list
//
pid = 0;
if (activeVertexCount > fMeshGeom->vertices.length())
activeVertexCount = fMeshGeom->vertices.length();
for (unsigned int i=0; i<activeVertexCount; i++)
{
activeVertexUVs[pid++] = (float)i/ (float)activeVertexCount;
}
activeVertexUVBuffer->commit(activeVertexUVs);
}
// Fill in face center data buffer (only when fDrawFaceCenter=true
// which results in faceCenterPositions and faceCenterPositionBuffer being non-NULL)
//
if (faceCenterPositions && faceCenterPositionBuffer)
{
if (debugPopulateGeometry)
{
printf(">>> Fill in the data for face center position buffer\n");
}
// Fill in face center buffer with positions based on realtime calculations.
//
pid = 0;
vid = 0;
for (int faceId=0; faceId < fMeshGeom->faceCount; faceId++)
{
//tmp variables for calculating the face center position.
double x = 0.0;
double y = 0.0;
double z = 0.0;
MPoint faceCenterPosition;
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceId];
if (numVerts > 2){
for (int v=0; v<numVerts; v++)
{
MPoint face_vertex_position = fMeshGeom->vertices[fMeshGeom->face_connects[vid]];
x += face_vertex_position[0];
y += face_vertex_position[1];
z += face_vertex_position[2];
vid++;
}
faceCenterPosition[0] = (float)x/numVerts;
faceCenterPosition[1] = (float)y/numVerts;
faceCenterPosition[2] = (float)z/numVerts;
faceCenterPositions[pid++] = (float)faceCenterPosition[0];
faceCenterPositions[pid++] = (float)faceCenterPosition[1];
faceCenterPositions[pid++] = (float)faceCenterPosition[2];
}
else if(numVerts > 0)
{
vid += numVerts;
}
}
faceCenterPositionBuffer->commit(faceCenterPositions);
}
}
/*
Create / update indexing required to draw wireframe render items.
There can be more than one render item using the same wireframe indexing
so it is passed in as an argument. If it is not null then we can
reuse it instead of creating new indexing.
*/
void apiMeshGeometryOverride::updateIndexingForWireframeItems(MHWRender::MIndexBuffer* wireIndexBuffer,
unsigned int totalVerts)
{
// Wireframe index buffer is same for both wireframe and selected render item
// so we only compute and allocate it once, but reuse it for both render items
if (!wireIndexBuffer)
{
if (wireIndexBuffer)
{
unsigned int* buffer = (unsigned int*)wireIndexBuffer->acquire(2*totalVerts, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
int vid = 0;
int first = 0;
unsigned int idx = 0;
for (int faceIdx=0; faceIdx<fMeshGeom->faceCount; faceIdx++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceIdx];
if (numVerts > 2)
{
first = vid;
for (int v=0; v<numVerts-1; v++)
{
buffer[idx++] = vid++;
buffer[idx++] = vid;
}
buffer[idx++] = vid++;
buffer[idx++] = first;
}
else
{
vid += numVerts;
}
}
wireIndexBuffer->commit(buffer);
}
}
}
// Associate same index buffer with either render item
if (wireIndexBuffer)
{
item->associateWithIndexBuffer(wireIndexBuffer);
}
}
/*
Create / update indexing for render items which draw dormant vertices
*/
void apiMeshGeometryOverride::updateIndexingForDormantVertices(const MHWRender::MRenderItem* item,
unsigned int numTriangles)
{
MHWRender::MIndexBuffer* indexBuffer =
if (indexBuffer)
{
unsigned int* buffer = (unsigned int*)indexBuffer->acquire(3*numTriangles, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
// compute index data for triangulated convex polygons sharing
// poly vertex data among triangles
unsigned int base = 0;
unsigned int idx = 0;
for (int faceIdx=0; faceIdx<fMeshGeom->faceCount; faceIdx++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceIdx];
if (numVerts > 2)
{
for (int v=1; v<numVerts-1; v++)
{
buffer[idx++] = base;
buffer[idx++] = base+v;
buffer[idx++] = base+v+1;
}
base += numVerts;
}
}
indexBuffer->commit(buffer);
item->associateWithIndexBuffer(indexBuffer);
}
}
}
/*
Create / update indexing for render items which draw active vertices
*/
void apiMeshGeometryOverride::updateIndexingForActiveVertices(const MHWRender::MRenderItem* item,
unsigned int numTriangles,
unsigned int activeVertexCount,
std::set<int> & vertexIdSet,
bool debugPopulateGeometry)
{
std::set<int>::iterator vertexIdSetIter;
MHWRender::MIndexBuffer* indexBuffer =
if (indexBuffer)
{
unsigned int* buffer = NULL;
// If drawing shared active vertices then the indexing degenerates into
// a numerically increasing index value. Otherwise a remapping from
// the active vertex list indexing to the unshared position stream is required.
//
// 1. Create indexing for shared positions. In this case it
// is a degenerate list since the position buffer was created
// in linear ascending order.
//
if (fDrawSharedActiveVertices)
{
buffer = (unsigned int*)indexBuffer->acquire(activeVertexCount, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
if (debugPopulateGeometry)
printf(">>> Set up indexing for shared vertices\n");
for (unsigned int i=0; i<activeVertexCount; i++)
{
buffer[i] = i;
}
}
}
// 2. Create indexing to remap to unshared positions
//
else
{
if (debugPopulateGeometry)
printf(">>> Set up indexing for unshared vertices\n");
buffer = (unsigned int*)indexBuffer->acquire(3*numTriangles, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
for (unsigned int i=0; i<3*numTriangles; i++)
{
buffer[i] = 3*numTriangles+1;
}
// compute index data for triangulated convex polygons sharing
// poly vertex data among triangles
unsigned int base = 0;
unsigned int lastFound = 0;
unsigned int idx = 0;
for (int faceIdx=0; faceIdx<fMeshGeom->faceCount; faceIdx++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceIdx];
if (numVerts > 2)
{
for (int v=1; v<numVerts-1; v++)
{
int vertexId = fMeshGeom->face_connects[base];
vertexIdSetIter = vertexIdSet.find( vertexId );
if (vertexIdSetIter != vertexIdSet.end())
{
buffer[idx++] = base;
lastFound = base;
}
vertexId = fMeshGeom->face_connects[base+v];
vertexIdSetIter = vertexIdSet.find( vertexId );
if (vertexIdSetIter != vertexIdSet.end())
{
buffer[idx++] = base+v;
lastFound = base+v;
}
vertexId = fMeshGeom->face_connects[base+v+1];
vertexIdSetIter = vertexIdSet.find( vertexId );
if (vertexIdSetIter != vertexIdSet.end())
{
buffer[idx++] = base+v+1;
lastFound = base+v+1;
}
}
base += numVerts;
}
}
for (unsigned int i=0; i<3*numTriangles; i++)
{
if (buffer[i] == 3*numTriangles+1)
buffer[i] = lastFound;
}
}
}
if (buffer)
indexBuffer->commit(buffer);
item->associateWithIndexBuffer(indexBuffer);
}
}
/*
Create / update indexing for render items which draw face centers
*/
void apiMeshGeometryOverride::updateIndexingForFaceCenters(const MHWRender::MRenderItem* item, MHWRender::MGeometry& data, bool debugPopulateGeometry)
{
MHWRender::MIndexBuffer* indexBuffer =
if (indexBuffer)
{
unsigned int* buffer = NULL;
buffer = (unsigned int*)indexBuffer->acquire(fMeshGeom->faceCount, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
if (debugPopulateGeometry)
printf(">>> Set up indexing for face centers\n");
for (int i=0; i<fMeshGeom->faceCount; i++)
{
buffer[i] = 0;
}
unsigned int idx = 0;
for (int i=0; i<fMeshGeom->faceCount; i++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[i];
if (numVerts > 2)
{
buffer[idx] = idx;
idx++;
}
}
}
if (buffer)
indexBuffer->commit(buffer);
item->associateWithIndexBuffer(indexBuffer);
}
}
/*
Create / update indexing for render items which draw affected edges
*/
void apiMeshGeometryOverride::updateIndexingForAffectedEdges(const MHWRender::MRenderItem* item,
std::set<int> & vertexIdSet,
unsigned int totalVerts)
{
MHWRender::MIndexBuffer* indexBuffer =
if (indexBuffer)
{
unsigned int totalEdges = 2*totalVerts;
unsigned int totalEdgesP1 = 2*totalVerts+1;
unsigned int* buffer = (unsigned int*)indexBuffer->acquire(totalEdges, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
std::set<int>::iterator vertexIdSetIter;
for (unsigned int i=0; i<totalEdges; i++)
{
buffer[i] = totalEdgesP1;
}
unsigned int base = 0;
unsigned int lastFound = 0;
unsigned int idx = 0;
int first = 0;
for (int faceIdx=0; faceIdx<fMeshGeom->faceCount; faceIdx++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceIdx];
if (numVerts > 2)
{
first = base;
for (int v=0; v<numVerts; v++)
{
// Check either ends of an "edge" to see if the
// vertex is in the active vertex list
//
bool foundActiveVertex = false;
unsigned int vindex1 = first + (v % numVerts);
unsigned int vindex2 = first + ((v+1) % numVerts);
int vertexId = fMeshGeom->face_connects[vindex1];
vertexIdSetIter = vertexIdSet.find( vertexId );
if (vertexIdSetIter != vertexIdSet.end())
{
foundActiveVertex = true;
lastFound = vindex1;
}
if (!foundActiveVertex)
{
int vertexId2 = fMeshGeom->face_connects[vindex2];
vertexIdSetIter = vertexIdSet.find( vertexId2 );
if (vertexIdSetIter != vertexIdSet.end())
{
foundActiveVertex = true;
lastFound = vindex2;
}
}
// Add indices for affected "edge"
if (foundActiveVertex)
{
buffer[idx++] = vindex1;
buffer[idx++] = vindex2;
}
}
base += numVerts;
}
}
for (unsigned int i=0; i<totalEdges; i++)
{
if (buffer[i] == totalEdgesP1)
buffer[i] = lastFound;
}
}
if (buffer)
indexBuffer->commit(buffer);
item->associateWithIndexBuffer(indexBuffer);
}
}
/*
Create / update indexing for render items which draw affected faces
*/
void apiMeshGeometryOverride::updateIndexingForAffectedFaces(const MHWRender::MRenderItem* item,
std::set<int> & vertexIdSet,
unsigned int numTriangles)
{
MHWRender::MIndexBuffer* indexBuffer =
if (indexBuffer)
{
unsigned int numTriangleVertices = 3*numTriangles;
unsigned int* buffer = (unsigned int*)indexBuffer->acquire(numTriangleVertices, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
for (unsigned int i=0; i<numTriangleVertices; i++)
{
buffer[i] = numTriangleVertices+1;
}
unsigned int base = 0;
unsigned int lastFound = 0;
unsigned int idx = 0;
std::set<int>::iterator vertexIdSetIter;
for (int faceIdx=0; faceIdx<fMeshGeom->faceCount; faceIdx++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceIdx];
if (numVerts > 2)
{
// Scan for any vertex in the active list
//
bool foundActiveVertex = false;
for (int v=1; v<numVerts-1; v++)
{
int vertexId = fMeshGeom->face_connects[base];
vertexIdSetIter = vertexIdSet.find( vertexId );
if (vertexIdSetIter != vertexIdSet.end())
{
foundActiveVertex = true;
lastFound = base;
}
if (!foundActiveVertex)
{
int vertexId2 = fMeshGeom->face_connects[base+v];
vertexIdSetIter = vertexIdSet.find( vertexId2 );
if (vertexIdSetIter != vertexIdSet.end())
{
foundActiveVertex = true;
lastFound = base+v;
}
}
if (!foundActiveVertex)
{
int vertexId3 = fMeshGeom->face_connects[base+v+1];
vertexIdSetIter = vertexIdSet.find( vertexId3 );
if (vertexIdSetIter != vertexIdSet.end())
{
foundActiveVertex = true;
lastFound = base+v+1;
}
}
}
// Found one active vertex on the triangle so add indexing for the
// entire triangle.
//
if (foundActiveVertex)
{
for (int v=1; v<numVerts-1; v++)
{
buffer[idx++] = base;
buffer[idx++] = base+v;
buffer[idx++] = base+v+1;
}
}
base += numVerts;
}
}
for (unsigned int i=0; i<numTriangleVertices; i++)
{
if (buffer[i] == numTriangleVertices+1)
buffer[i] = lastFound;
}
}
if (buffer)
indexBuffer->commit(buffer);
item->associateWithIndexBuffer(indexBuffer);
}
}
/*
Create / update indexing for render items which draw filled / shaded
triangles.
*/
void apiMeshGeometryOverride::updateIndexingForShadedTriangles(const MHWRender::MRenderItem* item,
unsigned int numTriangles)
{
MHWRender::MIndexBuffer* indexBuffer =
if (indexBuffer)
{
unsigned int* buffer = (unsigned int*)indexBuffer->acquire(3*numTriangles, true /*writeOnly - we don't need the current buffer values*/);
if (buffer)
{
// compute index data for triangulated convex polygons sharing
// poly vertex data among triangles
unsigned int base = 0;
unsigned int idx = 0;
for (int faceIdx=0; faceIdx<fMeshGeom->faceCount; faceIdx++)
{
// ignore degenerate faces
int numVerts = fMeshGeom->face_counts[faceIdx];
if (numVerts > 2)
{
for (int v=1; v<numVerts-1; v++)
{
buffer[idx++] = base;
buffer[idx++] = base+v;
buffer[idx++] = base+v+1;
}
base += numVerts;
}
}
indexBuffer->commit(buffer);
item->associateWithIndexBuffer(indexBuffer);
}
}
}
/*
Fill in data and index streams based on the requirements passed in.
Associate indexing with the render items passed in.
Note that we leave both code paths to either draw shared or non-shared active vertices.
The choice of which to use is up to the circumstances per plug-in.
When drawing shared vertices, this requires an additional position buffer to be
created so will use more memory. If drawing unshared vertices redundent extra
vertices are drawn but will use less memory. The data member fDrawSharedActiveVertices
can be set to decide on which implementation to use.
*/
void apiMeshGeometryOverride::populateGeometry(
const MHWRender::MGeometryRequirements& requirements,
const MHWRender::MRenderItemList& renderItems,
{
static bool debugPopulateGeometry = false;
if (debugPopulateGeometry)
printf("> Begin populate geometry\n");
// Get the active vertex count
unsigned int activeVertexCount = fActiveVertices.length();
// Compute the number of triangles, assume polys are always convex
unsigned int numTriangles = 0;
unsigned int totalVerts = 0;
for (int i=0; i<fMeshGeom->faceCount; i++)
{
int numVerts = fMeshGeom->face_counts[i];
if (numVerts > 2)
{
numTriangles += numVerts - 2;
totalVerts += numVerts;
}
}
// Update data streams based on geometry requirements
updateGeometryRequirements(requirements, data, activeVertexCount, totalVerts,
debugPopulateGeometry);
// Update indexing data for all appropriate render items
MHWRender::MIndexBuffer* wireIndexBuffer = NULL; // reuse same index buffer for both wireframe and selected
// Cache a temporary list to do searching on the vertex component list
// since the native array class doesn't have any searching capabilities.
//
std::set<int> vertexIdSet;
for (unsigned int i=0; i<activeVertexCount; i++)
{
vertexIdSet.insert( fActiveVertices[i] );
}
int numItems = renderItems.length();
for (int i=0; i<numItems; i++)
{
const MHWRender::MRenderItem* item = renderItems.itemAt(i);
if (!item) continue;
// Enable to debug vertex buffers that are associated with each render item.
// Can also use to generate indexing better, but we don't need that here.
// Also debugs custom data on the render item.
static const bool debugStuff = false;
if (debugStuff)
{
int numBufs = itemBuffers.length();
for (int bufNum=0; bufNum<numBufs; bufNum++)
{
if (itemBuffers.getDescriptor(bufNum, desc))
{
printf("Buffer Required for Item #%d ('%s'):\n", i, item->name().asChar());
printf("\tBufferName: %s\n", desc.name().asChar());
printf("\tDataType: %s (dimension %d)\n", MHWRender::MGeometry::dataTypeString(desc.dataType()).asChar(), desc.dimension());
printf("\tSemantic: %s\n", MHWRender::MGeometry::semanticString(desc.semantic()).asChar());
printf("\n");
}
}
// Just print a message for illustration purposes. Note that the custom data is also
// accessible from the MRenderItem in MPxShaderOverride::draw().
apiMeshUserData* myCustomData = dynamic_cast<apiMeshUserData*>(item->customData());
if (myCustomData)
{
printf("Custom data on Item #%d: '%s', modified count='%d'\n\n", i, myCustomData->fMessage.asChar(), myCustomData->fNumModifications);
}
else
{
printf("No custom data on Item #%d\n\n", i);
}
}
// Update indexing for active vertex item
//
if (item->name() == fActiveVertexItemName)
{
updateIndexingForActiveVertices( item, data, numTriangles,
activeVertexCount,
vertexIdSet,
debugPopulateGeometry);
}
// Update indexing for face center item in wireframe mode and shaded mode
//
if ((item->name() == fShadedModeFaceCenterItemName || item->name() == fWireframeModeFaceCenterItemName) && fDrawFaceCenters)
{
updateIndexingForFaceCenters( item, data, debugPopulateGeometry);
}
// Create indexing for dormant and numeric vertex render items
//
else if (item->name() == fVertexItemName ||
item->name() == fVertexIdItemName ||
item->name() == fVertexPositionItemName)
{
updateIndexingForDormantVertices( item, data, numTriangles );
}
// Create indexing for wireframe render items
//
else if (item->name() == fWireframeItemName
|| item->name() == fShadedTemplateItemName
|| item->name() == fSelectedWireframeItemName
&& item->name() == fShadedProxyItemName))
{
updateIndexingForWireframeItems(wireIndexBuffer, item, data, totalVerts);
}
// Handle indexing for affected edge render items
// For each face we check the edges. If the edges are in the active vertex
// list we add indexing for the 2 vertices on the edge to the index buffer.
//
else if (item->name() == fAffectedEdgeItemName)
{
updateIndexingForAffectedEdges(item, data, vertexIdSet, totalVerts);
}
// Handle indexing for affected edge render items
// For each triangle we check the vertices. If any of the vertices are in the active vertex
// list we add indexing for the triangle to the index buffer.
//
else if (item->name() == fAffectedFaceItemName)
{
updateIndexingForAffectedFaces(item, data, vertexIdSet, numTriangles);
}
// Create indexing for filled (shaded) render items
//
{
updateIndexingForShadedTriangles(item, data, numTriangles);
}
}
if (debugPopulateGeometry)
printf("> End populate geometry\n");
}
void apiMeshGeometryOverride::cleanUp()
{
fMeshGeom = NULL;
fActiveVertices.clear();
}