C++ API Reference
glslShader/crackFreePrimitiveGenerator.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.
// ==========================================================================
//+
// Example plugin: crackFreePrimitiveGenerator.cpp
//
// This plug-in is an example of a custom MPxIndexBufferMutator.
// It provides custom primitives based on shader requirements coming from
// an MPxShaderOverride. The name() in the MIndexBufferDescriptor is used
// to signify a unique identifier for a custom buffer.
#include "crackFreePrimitiveGenerator.h"
#include <maya/MStatus.h>
#include <maya/MFnMesh.h>
#include <maya/MIntArray.h>
#include <maya/MFloatArray.h>
#include <maya/MPxVertexBufferGenerator.h>
#include <maya/MHWGeometry.h>
#include <maya/MDrawRegistry.h>
#include <maya/MComponentDataIndexing.h>
#include <map>
namespace
{
struct Edge
{
Edge(unsigned int v0_ = 0, unsigned int v1_ = 0) : v0(v0_), v1(v1_) {}
Edge reversed() const
{
return Edge(v1, v0);
}
bool isEqual(const Edge &rhs) const
{
return (v0 == rhs.v0 && v1 == rhs.v1);
}
bool isReverse(const Edge &rhs) const
{
return (v0 == rhs.v1 && v1 == rhs.v0);
}
unsigned int v0;
unsigned int v1;
};
bool operator< (const Edge& lhs, const Edge& rhs)
{
return (lhs.v0 < rhs.v0) || (lhs.v0 == rhs.v0 && lhs.v1 < rhs.v1);
}
class EdgeMapping
{
public:
EdgeMapping();
void addTriangle(unsigned int faceVertexId0, unsigned int faceVertexId1, unsigned int faceVertexId2, unsigned int polyVertexId0, unsigned int polyVertexId1, unsigned int polyVertexId2);
void addEdge(const Edge& faceEdge, const Edge& polyEdge);
void addPositionUV(unsigned int faceVertexId, unsigned int polyVertexId, float u, float v);
bool adjacentEdge(const Edge& faceEdge, Edge& adjacentEdge) const;
bool dominantEdge(const Edge& faceEdge, Edge& dominantEdge) const;
bool dominantPosition(unsigned int faceVertexId, unsigned int& dominantVertexId) const;
private:
// Map each face edge to its polygon edge.
typedef std::map< Edge, Edge > FaceEdge2PolyEdgeMap;
FaceEdge2PolyEdgeMap fFaceEdge2PolyEdgeMap;
// Map each poly edge to both face edges that match.
typedef std::map< Edge, std::pair< Edge, Edge > > PolyEdge2FaceEdgeMap;
PolyEdge2FaceEdgeMap fPolyEdge2FaceEdgesMap;
// Map a single face vertex id to its polygon vertex id.
typedef std::map< unsigned int, unsigned int > FaceVertex2PolyVertexMap;
FaceVertex2PolyVertexMap fFaceVertex2PolyVertexMap;
// Map dominant vertex position via lowest uv coords.
typedef std::pair< unsigned int, std::pair< float, float > > VertexUVPair;
typedef std::map< unsigned int, VertexUVPair > PolyVertex2FaceVertexUVMap;
PolyVertex2FaceVertexUVMap fPolyVertex2FaceVertexUVMap;
};
EdgeMapping::EdgeMapping()
{
}
void EdgeMapping::addTriangle(unsigned int faceVertexId0, unsigned int faceVertexId1, unsigned int faceVertexId2, unsigned int polyVertexId0, unsigned int polyVertexId1, unsigned int polyVertexId2)
{
addEdge( Edge(faceVertexId0, faceVertexId1), Edge(polyVertexId0, polyVertexId1) );
addEdge( Edge(faceVertexId1, faceVertexId2), Edge(polyVertexId1, polyVertexId2) );
addEdge( Edge(faceVertexId2, faceVertexId0), Edge(polyVertexId2, polyVertexId0) );
}
// Add a new edge
// The edge is represented by two associated vertex ids pairs : one in face vertices array space and a second in polygon face vertices array space.
void EdgeMapping::addEdge(const Edge& faceEdge, const Edge& polyEdge)
{
if(polyEdge.v1 < polyEdge.v0)
{
// Revert edges
Edge faceEdgeR = faceEdge.reversed();
Edge polyEdgeR = polyEdge.reversed();
addEdge(faceEdgeR, polyEdgeR);
return;
}
fFaceEdge2PolyEdgeMap[faceEdge] = polyEdge;
PolyEdge2FaceEdgeMap::iterator itP2F = fPolyEdge2FaceEdgesMap.find( polyEdge );
if(itP2F == fPolyEdge2FaceEdgesMap.end())
{
fPolyEdge2FaceEdgesMap[polyEdge] = std::make_pair( faceEdge, faceEdge );
}
else
{
itP2F->second.second = faceEdge;
}
}
void EdgeMapping::addPositionUV(unsigned int faceVertexId, unsigned int polyVertexId, float u, float v)
{
fFaceVertex2PolyVertexMap[faceVertexId] = polyVertexId;
PolyVertex2FaceVertexUVMap::iterator it = fPolyVertex2FaceVertexUVMap.find(polyVertexId);
if(it == fPolyVertex2FaceVertexUVMap.end())
{
fPolyVertex2FaceVertexUVMap[polyVertexId] = std::make_pair( faceVertexId, std::make_pair(u,v) );
}
else
{
VertexUVPair& vertexUVPair = it->second;
float lastU = vertexUVPair.second.first;
float lastV = vertexUVPair.second.second;
if( u < lastU || ( u == lastU && v < lastV ) )
{
vertexUVPair.first = faceVertexId;
vertexUVPair.second = std::make_pair(u,v);
}
}
}
// Find the adjacent edge that is shared between two faces.
// The matching is done through the polygon vertex ids.
// The returning edge have vertices in face space.
bool EdgeMapping::adjacentEdge(const Edge& faceEdge, Edge& adjacentEdge) const
{
FaceEdge2PolyEdgeMap::const_iterator itF2P = fFaceEdge2PolyEdgeMap.find( faceEdge );
if(itF2P == fFaceEdge2PolyEdgeMap.end())
{
Edge faceEdgeR = faceEdge.reversed();
itF2P = fFaceEdge2PolyEdgeMap.find( faceEdgeR );
if(itF2P == fFaceEdge2PolyEdgeMap.end())
return false;
}
const Edge& polyEdge = itF2P->second;
PolyEdge2FaceEdgeMap::const_iterator itP2F = fPolyEdge2FaceEdgesMap.find( polyEdge );
if(itP2F == fPolyEdge2FaceEdgesMap.end())
return false;
const Edge& faceEdge0 = itP2F->second.first;
const Edge& faceEdge1 = itP2F->second.second;
bool foundMatch = false;
if(faceEdge.isEqual(faceEdge0))
{
adjacentEdge = faceEdge1;
foundMatch = true;
}
else if(faceEdge.isReverse(faceEdge0))
{
adjacentEdge = faceEdge1.reversed();
foundMatch = true;
}
else if(faceEdge.isEqual(faceEdge1))
{
adjacentEdge = faceEdge0;
foundMatch = true;
}
else if(faceEdge.isReverse(faceEdge1))
{
adjacentEdge = faceEdge0.reversed();
foundMatch = true;
}
return foundMatch;
}
bool EdgeMapping::dominantEdge(const Edge& faceEdge, Edge& dominantEdge) const
{
bool returnReversed = true;
FaceEdge2PolyEdgeMap::const_iterator itF2P = fFaceEdge2PolyEdgeMap.find( faceEdge );
if(itF2P == fFaceEdge2PolyEdgeMap.end())
{
Edge faceEdgeR = faceEdge.reversed();
itF2P = fFaceEdge2PolyEdgeMap.find( faceEdgeR );
if(itF2P == fFaceEdge2PolyEdgeMap.end())
return false;
returnReversed = false;
}
const Edge& polyEdge = itF2P->second;
PolyEdge2FaceEdgeMap::const_iterator itP2F = fPolyEdge2FaceEdgesMap.find( polyEdge );
if(itP2F == fPolyEdge2FaceEdgesMap.end())
return false;
const Edge& faceEdge0 = itP2F->second.first;
const Edge& faceEdge1 = itP2F->second.second;
if(faceEdge0 < faceEdge1)
{
dominantEdge = (returnReversed ? faceEdge0.reversed() : faceEdge0);
}
else
{
dominantEdge = (returnReversed ? faceEdge1.reversed() : faceEdge1);
}
return true;
}
bool EdgeMapping::dominantPosition(unsigned int faceVertexId, unsigned int& dominantVertexId) const
{
FaceVertex2PolyVertexMap::const_iterator itF2P = fFaceVertex2PolyVertexMap.find(faceVertexId);
if(itF2P == fFaceVertex2PolyVertexMap.end())
return false;
unsigned polyVertexId = itF2P->second;
PolyVertex2FaceVertexUVMap::const_iterator itP2FUV = fPolyVertex2FaceVertexUVMap.find(polyVertexId);
if(itP2FUV == fPolyVertex2FaceVertexUVMap.end())
return false;
dominantVertexId = itP2FUV->second.first;
return true;
}
struct VertexF
{
static const float kTolerance;
VertexF(const float* buffer, unsigned int index)
{
unsigned int bufferPos = index * 3;
x = buffer[bufferPos++];
y = buffer[bufferPos++];
z = buffer[bufferPos++];
}
bool isEqual(const VertexF &rhs) const
{
return (fabs(x - rhs.x) < kTolerance && fabs(y - rhs.y) < kTolerance && fabs(z - rhs.z) < kTolerance);
}
float x, y, z;
};
const float VertexF::kTolerance = 1e-5f;
bool operator< (const VertexF& lhs, const VertexF& rhs)
{
return ((lhs.x - rhs.x) < -VertexF::kTolerance) ||
(fabs(lhs.x - rhs.x) < VertexF::kTolerance && (lhs.y - rhs.y) < -VertexF::kTolerance) ||
(fabs(lhs.x - rhs.x) < VertexF::kTolerance && fabs(lhs.y - rhs.y) < VertexF::kTolerance && (lhs.z - rhs.z) < -VertexF::kTolerance);
}
struct VertexFMap
{
unsigned int getVertexId( const VertexF& v );
typedef std::map<VertexF, unsigned int> TVtxMap;
TVtxMap vertexMap;
};
unsigned int VertexFMap::getVertexId( const VertexF& v )
{
VertexFMap::TVtxMap::const_iterator itVtx = vertexMap.find(v);
if (itVtx != vertexMap.end())
return itVtx->second;
unsigned int nextId = (unsigned int)vertexMap.size();
vertexMap.insert(TVtxMap::value_type(v,nextId));
return nextId;
}
}
// Mode 1 : PN Triangles; no divergent normals and no displacement crack fix
// Mode 2 : PN AEN, divergent normals crack fix; no displacement UV seam crack fix
// Mode 3 : PN AEN, crack fix for divergent normals and UV seam displacement
CrackFreePrimitiveGenerator::CrackFreePrimitiveGenerator(bool addAdjacentEdges, bool addDominantEdges, bool addDominantPosition)
: fAddAdjacentEdges(addAdjacentEdges)
, fAddDominantEdges(addDominantEdges)
, fAddDominantPosition(addDominantPosition)
{
}
CrackFreePrimitiveGenerator::~CrackFreePrimitiveGenerator() {}
unsigned int CrackFreePrimitiveGenerator::computeTriangleSize(bool bAddAdjacentEdges,
bool bAddDominantEdges,
bool bAddDominantPosition)
{
return 3 /* triangles vertices */
+ (bAddAdjacentEdges ? 3 * 2 : 0) /* adjacent edges */
+ (bAddDominantEdges ? 3 * 2 : 0) /* dominant edges */
+ (bAddDominantPosition ? 3 : 0); /* dominant position */
}
void CrackFreePrimitiveGenerator::mutateIndexBuffer( const MUintArray& originalBufferIndices,
const float* positionBufferFloat,
const float* uvBufferFloat,
bool bAddAdjacentEdges,
bool bAddDominantEdges,
bool bAddDominantPosition,
MHWRender::MGeometry::DataType indexBufferDataType,
void* indexData )
{
unsigned int numTriVerts = originalBufferIndices.length();
EdgeMapping edges;
{
VertexFMap vertexMap;
// Iterate all triangles found in the old index buffer:
unsigned int vertexIndex = 0;
while (vertexIndex < numTriVerts)
{
unsigned int faceVertexId0 = originalBufferIndices[vertexIndex++];
unsigned int polyVertexId0 = vertexMap.getVertexId(VertexF(positionBufferFloat, faceVertexId0));
unsigned int faceVertexId1 = originalBufferIndices[vertexIndex++];
unsigned int polyVertexId1 = vertexMap.getVertexId(VertexF(positionBufferFloat, faceVertexId1));
unsigned int faceVertexId2 = originalBufferIndices[vertexIndex++];
unsigned int polyVertexId2 = vertexMap.getVertexId(VertexF(positionBufferFloat, faceVertexId2));
edges.addTriangle(faceVertexId0, faceVertexId1, faceVertexId2, polyVertexId0, polyVertexId1, polyVertexId2);
if(bAddDominantPosition && uvBufferFloat)
{
unsigned int uvIndex;
uvIndex = faceVertexId0 * 2;
edges.addPositionUV(faceVertexId0, polyVertexId0, uvBufferFloat[uvIndex], uvBufferFloat[uvIndex+1]);
uvIndex = faceVertexId1 * 2;
edges.addPositionUV(faceVertexId1, polyVertexId1, uvBufferFloat[uvIndex], uvBufferFloat[uvIndex+1]);
uvIndex = faceVertexId2 * 2;
edges.addPositionUV(faceVertexId2, polyVertexId2, uvBufferFloat[uvIndex], uvBufferFloat[uvIndex+1]);
}
}
}
unsigned int newTriId = 0;
for(unsigned int triId = 0; triId < numTriVerts; )
{
unsigned int vertexId0 = originalBufferIndices[triId++];
unsigned int vertexId1 = originalBufferIndices[triId++];
unsigned int vertexId2 = originalBufferIndices[triId++];
if (indexBufferDataType == MHWRender::MGeometry::kUnsignedInt32) {
// Triangle vertices
((unsigned int*)indexData)[newTriId++] = vertexId0;
((unsigned int*)indexData)[newTriId++] = vertexId1;
((unsigned int*)indexData)[newTriId++] = vertexId2;
// Adjacent edges
if(bAddAdjacentEdges)
{
Edge adjacentEdge;
// Edge0 : vertexId0 - vertexId1
edges.adjacentEdge(Edge(vertexId0, vertexId1), adjacentEdge);
((unsigned int*)indexData)[newTriId++] = adjacentEdge.v0;
((unsigned int*)indexData)[newTriId++] = adjacentEdge.v1;
// Edge1 : vertexId1 - vertexId2
edges.adjacentEdge(Edge(vertexId1, vertexId2), adjacentEdge);
((unsigned int*)indexData)[newTriId++] = adjacentEdge.v0;
((unsigned int*)indexData)[newTriId++] = adjacentEdge.v1;
// Edge2 : vertexId2 - vertexId0
edges.adjacentEdge(Edge(vertexId2, vertexId0), adjacentEdge);
((unsigned int*)indexData)[newTriId++] = adjacentEdge.v0;
((unsigned int*)indexData)[newTriId++] = adjacentEdge.v1;
}
// Dominant edges
if(bAddDominantEdges)
{
Edge dominantEdge;
// Edge0 : vertexId0 - vertexId1
edges.dominantEdge(Edge(vertexId0, vertexId1), dominantEdge);
((unsigned int*)indexData)[newTriId++] = dominantEdge.v0;
((unsigned int*)indexData)[newTriId++] = dominantEdge.v1;
// Edge1 : vertexId1 - vertexId2
edges.dominantEdge(Edge(vertexId1, vertexId2), dominantEdge);
((unsigned int*)indexData)[newTriId++] = dominantEdge.v0;
((unsigned int*)indexData)[newTriId++] = dominantEdge.v1;
// Edge2 : vertexId2 - vertexId0
edges.dominantEdge(Edge(vertexId2, vertexId0), dominantEdge);
((unsigned int*)indexData)[newTriId++] = dominantEdge.v0;
((unsigned int*)indexData)[newTriId++] = dominantEdge.v1;
}
// Dominant position
if(bAddDominantPosition)
{
unsigned int dominantVertexId = 0;
edges.dominantPosition(vertexId0, dominantVertexId);
((unsigned int*)indexData)[newTriId++] = dominantVertexId;
edges.dominantPosition(vertexId1, dominantVertexId);
((unsigned int*)indexData)[newTriId++] = dominantVertexId;
edges.dominantPosition(vertexId2, dominantVertexId);
((unsigned int*)indexData)[newTriId++] = dominantVertexId;
}
}
else if (indexBufferDataType == MHWRender::MGeometry::kUnsignedChar) {
((unsigned short*)indexData)[newTriId++] = (unsigned short)vertexId0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)vertexId1;
((unsigned short*)indexData)[newTriId++] = (unsigned short)vertexId2;
// Adjacent edges
if(bAddAdjacentEdges)
{
Edge adjacentEdge;
// Edge0 : vertexId0 - vertexId1
edges.adjacentEdge(Edge(vertexId0, vertexId1), adjacentEdge);
((unsigned short*)indexData)[newTriId++] = (unsigned short)adjacentEdge.v0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)adjacentEdge.v1;
// Edge1 : vertexId1 - vertexId2
edges.adjacentEdge(Edge(vertexId1, vertexId2), adjacentEdge);
((unsigned short*)indexData)[newTriId++] = (unsigned short)adjacentEdge.v0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)adjacentEdge.v1;
// Edge2 : vertexId2 - vertexId0
edges.adjacentEdge(Edge(vertexId2, vertexId0), adjacentEdge);
((unsigned short*)indexData)[newTriId++] = (unsigned short)adjacentEdge.v0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)adjacentEdge.v1;
}
// Dominant edges
if(bAddDominantEdges)
{
Edge dominantEdge;
// Edge0 : vertexId0 - vertexId1
edges.dominantEdge(Edge(vertexId0, vertexId1), dominantEdge);
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantEdge.v0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantEdge.v1;
// Edge1 : vertexId1 - vertexId2
edges.dominantEdge(Edge(vertexId1, vertexId2), dominantEdge);
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantEdge.v0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantEdge.v1;
// Edge2 : vertexId2 - vertexId0
edges.dominantEdge(Edge(vertexId2, vertexId0), dominantEdge);
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantEdge.v0;
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantEdge.v1;
}
// Dominant position
if(bAddDominantPosition)
{
unsigned int dominantVertexId = 0;
edges.dominantPosition(vertexId0, dominantVertexId);
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantVertexId;
edges.dominantPosition(vertexId1, dominantVertexId);
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantVertexId;
edges.dominantPosition(vertexId2, dominantVertexId);
((unsigned short*)indexData)[newTriId++] = (unsigned short)dominantVertexId;
}
}
}
}
MHWRender::MGeometry::Primitive CrackFreePrimitiveGenerator::mutateIndexing(const MHWRender::MComponentDataIndexingList& sourceIndexBuffers,
const MHWRender::MVertexBufferArray& vertexBuffers,
int& primitiveStride) const
{
MHWRender::MVertexBuffer *positionBuffer = NULL;
MHWRender::MVertexBuffer *uvBuffer = NULL;
for (unsigned int ivb = 0; ivb < vertexBuffers.count() && (positionBuffer == NULL || uvBuffer == NULL); ++ivb)
{
MHWRender::MVertexBuffer *currBuffer = vertexBuffers.getBuffer(ivb);
if (positionBuffer == NULL && currBuffer->descriptor().semantic() == MHWRender::MGeometry::kPosition)
positionBuffer = currBuffer;
if (uvBuffer == NULL && currBuffer->descriptor().semantic() == MHWRender::MGeometry::kTexture)
uvBuffer = currBuffer;
}
if (positionBuffer == NULL)
// We need at least the positions:
float* positionBufferFloat = (float*)positionBuffer->map();
float* uvBufferFloat = NULL;
if (uvBuffer)
uvBufferFloat = (float*)uvBuffer->map();
for (int x = 0; x < sourceIndexBuffers.length(); ++x)
{
if (sourceIndexBuffers[x]->componentType() != MHWRender::MComponentDataIndexing::kFaceVertex)
continue;
const MUintArray& originalBufferIndices = sourceIndexBuffers[x]->indices();
unsigned int numTriVerts = originalBufferIndices.length();
unsigned int numTri = numTriVerts / 3;
unsigned int triSize = computeTriangleSize(fAddAdjacentEdges, fAddDominantEdges, fAddDominantPosition);
unsigned int bufferSize = numTri * triSize;
void* indexData = indexBuffer.acquire(bufferSize, true /*writeOnly - we don't need the current buffer values*/);
if (indexData != NULL)
{
mutateIndexBuffer( originalBufferIndices, positionBufferFloat, uvBufferFloat,
fAddAdjacentEdges, fAddDominantEdges, fAddDominantPosition,
indexBuffer.dataType(), indexData );
}
if (positionBuffer) positionBuffer->unmap();
if (uvBuffer) uvBuffer->unmap();
indexBuffer.commit(indexData);
primitiveStride = triSize;
}
if (positionBuffer) positionBuffer->unmap();
if (uvBuffer) uvBuffer->unmap();
}
// This is the primitive generator creation function registered with the DrawRegistry.
// Used to initialize a custom primitive generator.
MHWRender::MPxIndexBufferMutator* CrackFreePrimitiveGenerator::createCrackFreePrimitiveGenerator18()
{
return new CrackFreePrimitiveGenerator(true /*addAdjacentEdges*/, true /*addDominantEdges*/, true /*addDominantPosition*/);
}
MHWRender::MPxIndexBufferMutator* CrackFreePrimitiveGenerator::createCrackFreePrimitiveGenerator9()
{
return new CrackFreePrimitiveGenerator(true /*addAdjacentEdges*/, false /*addDominantEdges*/, false /*addDominantPosition*/);
}