OpenSubdiv/examples/osdPolySmooth/osdPolySmooth.cpp
manuelk 99c34d2aec Adding functionality to store uniform face-varying data across multiple levels of subdivision
- switch FVarDataTable to a class
- add offsets to keep track of level
- modify dependent code
- rename 'totalFVarWidth' to 'fvarWidth'

fixes #271
2014-04-04 19:10:34 -07:00

1182 lines
50 KiB
C++

//
// Copyright 2013 Autodesk, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "Apache License")
// with the following modification; you may not use this file except in
// compliance with the Apache License and the following modification to it:
// Section 6. Trademarks. is deleted and replaced with:
//
// 6. Trademarks. This License does not grant permission to use the trade
// names, trademarks, service marks, or product names of the Licensor
// and its affiliates, except as required to comply with Section 4(c) of
// the License and to reproduce the content of the NOTICE file.
//
// You may obtain a copy of the Apache License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Apache License with the above modification is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the Apache License for the specific
// language governing permissions and limitations under the Apache License.
//
#include "osdPolySmooth.h"
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnTypedAttribute.h>
#include <maya/MFnEnumAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnMatrixAttribute.h>
#include <maya/MFnMessageAttribute.h>
#include <maya/MFnCompoundAttribute.h>
#include <maya/MFnGenericAttribute.h>
#include <maya/MFnLightDataAttribute.h>
#include <maya/MDataHandle.h>
#include <maya/MDataBlock.h>
#include <maya/MPlug.h>
#include <maya/MIOStream.h>
#include <maya/MGlobal.h>
#include <maya/MFnMesh.h>
#include <maya/MFnMeshData.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MFloatArray.h>
#include <maya/MFnSingleIndexedComponent.h>
#include <maya/MPointArray.h>
#include <maya/MFloatPointArray.h>
#include <maya/MDoubleArray.h>
#include <maya/MUintArray.h>
#include <maya/MFnPlugin.h>
#include <stdexcept>
#include <map>
#if defined(_MSV_VER) and (not defined(__INTEL_COMPILER))
#pragma warning( disable : 174 593 )
#endif
// OpenSubdiv includes
#include <osd/vertex.h>
#include <osd/vertex.h>
#include <osd/mesh.h>
#include <osd/cpuComputeContext.h>
#include <osd/cpuComputeController.h>
#include <osd/cpuVertexBuffer.h>
// ====================================
// Static Initialization
// ====================================
// MAYA_NODE_BUILDER:BEG [ATTRIBUTE INITIALIZATION] ==========
const MTypeId OsdPolySmooth::id( 0x0010a25b );
const MString OsdPolySmooth::typeNameStr("osdPolySmooth");
MObject OsdPolySmooth::a_inputPolymesh;
MObject OsdPolySmooth::a_output;
MObject OsdPolySmooth::a_subdivisionLevels;
MObject OsdPolySmooth::a_recommendedIsolation;
MObject OsdPolySmooth::a_vertBoundaryMethod;
MObject OsdPolySmooth::a_fvarBoundaryMethod;
MObject OsdPolySmooth::a_fvarPropagateCorners;
MObject OsdPolySmooth::a_smoothTriangles;
MObject OsdPolySmooth::a_creaseMethod;
// MAYA_NODE_BUILDER:END [ATTRIBUTE INITIALIZATION] ==========
// ATTR ENUMS
// Note: Do not change these values as these are serialized numerically in the Maya scenes)
//
enum BoundaryMethod {
k_BoundaryMethod_InterpolateBoundaryNone = 0,
k_BoundaryMethod_InterpolateBoundaryEdgeOnly = 1,
k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner = 2,
k_BoundaryMethod_InterpolateBoundaryAlwaysSharp = 3
};
enum CreaseMethod {
k_creaseMethod_normal = 0,
k_creaseMethod_chaikin = 1
};
typedef OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex> HMesh;
typedef OpenSubdiv::HbrFace<OpenSubdiv::OsdVertex> HFace;
typedef OpenSubdiv::HbrVertex<OpenSubdiv::OsdVertex> HVertex;
typedef OpenSubdiv::HbrHalfedge<OpenSubdiv::OsdVertex> HHalfedge;
typedef OpenSubdiv::HbrFVarData<OpenSubdiv::OsdVertex> HFvarData;
typedef OpenSubdiv::HbrCatmarkSubdivision<OpenSubdiv::OsdVertex> HCatmark;
typedef OpenSubdiv::FarMesh<OpenSubdiv::OsdVertex> FMesh;
typedef OpenSubdiv::FarMeshFactory<OpenSubdiv::OsdVertex> FMeshFactory;
HMesh::InterpolateBoundaryMethod
ConvertMayaBoundaryMethodShortToOsdInterpolateBoundaryMethod(short boundaryMethod) {
switch (boundaryMethod) {
case k_BoundaryMethod_InterpolateBoundaryNone: return HMesh::k_InterpolateBoundaryNone;
case k_BoundaryMethod_InterpolateBoundaryEdgeOnly: return HMesh::k_InterpolateBoundaryEdgeOnly;
case k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner: return HMesh::k_InterpolateBoundaryEdgeAndCorner;
case k_BoundaryMethod_InterpolateBoundaryAlwaysSharp: return HMesh::k_InterpolateBoundaryAlwaysSharp;
default: ;
}
MGlobal::displayError("InterpolateBoundaryMethod value out of range. Using \"none\"");
return OpenSubdiv::HbrMesh<OpenSubdiv::OsdVertex>::k_InterpolateBoundaryNone;
}
// ====================================
// Macros
// ====================================
#define MCHECKERR(status,message) \
if( MStatus::kSuccess != status ) { \
cerr << "ERROR: " << message << "[" << status << "]" << endl; \
return status; \
}
#define MWARNERR(status,message) \
if( MStatus::kSuccess != status ) { \
cerr << "ERROR: " << message << "[" << status << "]" << endl; \
}
// ====================================
// Constructors/Destructors
// ====================================
OsdPolySmooth::OsdPolySmooth() {}
OsdPolySmooth::~OsdPolySmooth() {}
// ====================================
// Helper Functions
// ====================================
// Create component groups
void
createComp(MFnMeshData &dataCreator, MFn::Type compType, unsigned compId, MIntArray &compList) {
MStatus returnStatus;
MFnSingleIndexedComponent comp;
MObject compObj = comp.create(compType,&returnStatus);
MWARNERR(returnStatus, "cannot create MFnSingleIndexedComponent");
returnStatus = comp.addElements(compList);
MWARNERR(returnStatus, "Error in addElements() for MFnSingleIndexedComponent");
returnStatus = dataCreator.addObjectGroup(compId);
MWARNERR(returnStatus, "Error in addObjectGroup()");
returnStatus = dataCreator.setObjectGroupComponent(compId, compObj);
MWARNERR(returnStatus, "Error in setObjectGroupComponent()");
}
// ====================================
// OpenSubdiv Functions
// ====================================
// Reference: OSD shape_utils.h:: applyTags() "crease"
float
applyCreaseEdges(MFnMesh const & inMeshFn, HMesh * hbrMesh) {
MStatus returnStatus;
MUintArray tEdgeIds;
MDoubleArray tCreaseData;
float maxCreaseValue = 0.0f;
if (inMeshFn.getCreaseEdges(tEdgeIds, tCreaseData)) {
assert( tEdgeIds.length() == tCreaseData.length() );
// Has crease edges
int2 edgeVerts;
for (unsigned int j=0; j < tEdgeIds.length(); j++) {
// Get vert ids from maya edge
int edgeId = tEdgeIds[j];
returnStatus = inMeshFn.getEdgeVertices(edgeId, edgeVerts);
// Assumption: The OSD vert ids are identical to those of the Maya mesh
HVertex const * v = hbrMesh->GetVertex( edgeVerts[0] ),
* w = hbrMesh->GetVertex( edgeVerts[1] );
HHalfedge * e = 0;
if( v and w ) {
if( (e = v->GetEdge(w)) == 0) {
e = w->GetEdge(v);
}
if(e) {
assert( tCreaseData[j] >= 0.0 );
e->SetSharpness( (float)tCreaseData[j] );
maxCreaseValue = std::max(float(tCreaseData[j]), maxCreaseValue);
} else {
fprintf(stderr,
"warning: cannot find edge for crease tag (%d,%d)\n",
edgeVerts[0], edgeVerts[1] );
}
}
}
}
return maxCreaseValue;
}
// Reference: OSD shape_utils.h:: applyTags() "corner"
float
applyCreaseVertices( MFnMesh const & inMeshFn, HMesh * hbrMesh ) {
MUintArray tVertexIds;
MDoubleArray tCreaseData;
float maxCreaseValue = 0.0f;
if ( inMeshFn.getCreaseVertices(tVertexIds, tCreaseData) ) {
assert( tVertexIds.length() == tCreaseData.length() );
// Has crease vertices
for (unsigned int j=0; j < tVertexIds.length(); j++) {
// Assumption: The OSD vert ids are identical to those of the Maya mesh
HVertex * v = hbrMesh->GetVertex( tVertexIds[j] );
if(v) {
assert( tCreaseData[j] >= 0.0 );
v->SetSharpness( (float)tCreaseData[j] );
maxCreaseValue = std::max(float(tCreaseData[j]), maxCreaseValue);
} else {
fprintf(stderr,
"warning: cannot find vertex for corner tag (%d)\n",
tVertexIds[j] );
}
}
}
return maxCreaseValue;
}
// XXX -- Future Data Optimization: Implement varying data instead of forcing face-varying for ColorSets.
// Collect UVs and ColorSet info to represent them as face-varying in OpenSubdiv
MStatus
getMayaFvarFieldParams(
MFnMesh const & inMeshFn,
MStringArray & uvSetNames,
MStringArray & colorSetNames,
std::vector<int> & colorSetChannels,
std::vector<MFnMesh::MColorRepresentation> &colorSetReps,
int & totalColorSetChannels) {
MStatus returnStatus;
returnStatus = inMeshFn.getUVSetNames(uvSetNames);
MCHECKERR(returnStatus, "Cannot get uvSet names");
returnStatus = inMeshFn.getColorSetNames(colorSetNames);
MCHECKERR(returnStatus, "Cannot get colorSet names");
colorSetChannels.resize(colorSetNames.length());
colorSetReps.resize(colorSetNames.length());
totalColorSetChannels = 0;
for (unsigned int i=0; i < colorSetNames.length(); i++) {
colorSetReps[i] = inMeshFn.getColorRepresentation(colorSetNames[i], &returnStatus);
MCHECKERR(returnStatus, "Cannot get colorSet representation");
if (colorSetReps[i] == MFnMesh::kAlpha) {
colorSetChannels[i] = 1;
} else if (colorSetReps[i] == MFnMesh::kRGB) {
colorSetChannels[i] = 3;
} else {
colorSetChannels[i] = 4; // kRGBA
}
totalColorSetChannels += colorSetChannels[i];
}
return MS::kSuccess;
}
//! Create OSD HBR mesh.
//! Caller is expected to delete the resulting hbrMesh returned
HMesh *
createOsdHbrFromPoly( MFnMesh const & inMeshFn,
MItMeshPolygon & inMeshItPolygon,
std::vector<int> & fvarIndices,
std::vector<int> & fvarWidths,
float * maxCreaseSharpness=0)
{
MStatus returnStatus;
// == Mesh Properties
// Gather FVarData
MStringArray uvSetNames;
MStringArray colorSetNames;
std::vector<int> colorSetChannels;
std::vector<MFnMesh::MColorRepresentation> colorSetReps;
int totalColorSetChannels = 0;
returnStatus = getMayaFvarFieldParams(inMeshFn, uvSetNames, colorSetNames, colorSetChannels, colorSetReps, totalColorSetChannels);
MWARNERR(returnStatus, "Failed to retrieve Maya face-varying parameters");
// Create face-varying data with independent float channels of dimension 1
// Note: This FVarData needs to be kept around for the duration of the HBR mesh
int totalFvarWidth = 2*uvSetNames.length() + totalColorSetChannels;
fvarIndices.resize(totalFvarWidth);
fvarWidths.resize(totalFvarWidth);
for (int i=0; i < totalFvarWidth; ++i) {
fvarIndices[i] = i;
fvarWidths[i] = 1;
}
// temp storage for UVs and ColorSets for a face
MIntArray fvArray; // face vertex array
std::vector<MFloatArray> uvSet_uCoords(uvSetNames.length());
std::vector<MFloatArray> uvSet_vCoords(uvSetNames.length());
std::vector<MColorArray> colorSet_colors(colorSetNames.length());
// =====================================
// Init HBR
// =====================================
// Determine HBR Subdiv Method
static HCatmark _catmark;
// Create HBR mesh
assert(fvarIndices.size() == fvarWidths.size());
HMesh * hbrMesh = new HMesh( &_catmark,
(int)fvarIndices.size(),
(fvarIndices.size() > 0) ? &fvarIndices[0] : NULL,
(fvarWidths.size() > 0) ? &fvarWidths[0] : NULL,
totalFvarWidth );
// Create Stub HBR Vertices
int numVerts = inMeshFn.numVertices();
OpenSubdiv::OsdVertex v;
for(int i=0; i<numVerts; i++ ) {
hbrMesh->NewVertex( i, v );
}
// ===================================================
// Create HBR Topology
// ===================================================
assert(totalFvarWidth == hbrMesh->GetTotalFVarWidth());
unsigned int ptxidx = 0;
for( inMeshItPolygon.reset(); !inMeshItPolygon.isDone(); inMeshItPolygon.next() ) {
// Verts for this face
inMeshItPolygon.getVertices(fvArray);
unsigned int nv = fvArray.length();
bool valid = true;
for(unsigned int j=0;j<fvArray.length(); j++) {
HVertex const * origin = hbrMesh->GetVertex( fvArray[j] ),
* destination = hbrMesh->GetVertex( fvArray[(j+1)%nv] );
HHalfedge const * opposite = destination->GetEdge(origin);
if(origin==NULL || destination==NULL) {
fprintf(stderr, "Skipping face: An edge was specified that connected a nonexistent vertex\n");
valid = false;
break;
}
if(origin == destination) {
fprintf(stderr, "Skipping face: An edge was specified that connected a vertex to itself\n");
valid = false;
break;
}
if(opposite && opposite->GetOpposite() ) {
fprintf(stderr, "Skipping face: A non-manifold edge incident to more than 2 faces was found\n");
valid = false;
break;
}
if(origin->GetEdge(destination)) {
fprintf(stderr, "Skipping face: An edge connecting two vertices was specified more than once."
" It's likely that an incident face was flipped\n");
valid = false;
break;
}
}
// Update faces
const int * fvArrayPtr = &(fvArray[0]); // get the raw int* array from std::vector<int>
OpenSubdiv::HbrFace<OpenSubdiv::OsdVertex> * face = hbrMesh->NewFace(nv, fvArrayPtr, 0);
// Increment ptex index
face->SetPtexIndex(ptxidx);
// Add FaceVaryingData (UVSets, ...)
if (totalFvarWidth > 0) {
// Retrieve all UV and ColorSet data
for (unsigned int i=0; i < uvSetNames.length(); ++i) {
inMeshItPolygon.getUVs(uvSet_uCoords[i], uvSet_vCoords[i], &uvSetNames[i] );
}
for (unsigned int i=0; i < colorSetNames.length(); ++i) {
inMeshItPolygon.getColors(colorSet_colors[i], &colorSetNames[i]);
}
std::vector<float> fvarItem(totalFvarWidth); // storage for all the face-varying channels for this face-vertex
// loop over each uvSet and the uvs within
for (unsigned int fvid=0; fvid < fvArray.length(); ++fvid) {
int fvarItemIndex = 0;
// Handle uvSets
for( unsigned int uvSetIt=0; uvSetIt < uvSetNames.length(); ++uvSetIt ) {
if (fvid < uvSet_uCoords[uvSetIt].length()) {
fvarItem[fvarItemIndex ] = uvSet_uCoords[uvSetIt][fvid];
fvarItem[fvarItemIndex+1] = uvSet_vCoords[uvSetIt][fvid];
} else {
// getUVs() can return incomplete or empty arrays
fvarItem[fvarItemIndex ] = 0.0f;
fvarItem[fvarItemIndex+1] = 0.0f;
}
fvarItemIndex += 2;
}
// Handle colorSets
for( unsigned int colorSetIt=0; colorSetIt < colorSetNames.length(); ++colorSetIt ) {
int nchannels = colorSetChannels[colorSetIt];
for (int channel=0; channel < nchannels; ++channel) {
if (fvid < colorSet_colors[colorSetIt].length()) {
fvarItem[fvarItemIndex+channel] = colorSet_colors[colorSetIt][fvid][channel];
} else {
// getColors() can return incomplete or empty arrays
fvarItem[fvarItemIndex+channel] = 0.0f;
}
}
fvarItemIndex += nchannels;
}
assert((fvarItemIndex) == totalFvarWidth); // For UVs, sanity check the resulting value
// Insert into the HBR structure for that face
HVertex * hbrVertex = hbrMesh->GetVertex( fvArray[fvid] );
HFvarData & fvarData = hbrVertex->GetFVarData(face);
if (not fvarData.IsInitialized()) {
fvarData.SetAllData(totalFvarWidth, &fvarItem[0]);
} else if (not fvarData.CompareAll(totalFvarWidth, &fvarItem[0])) {
// If data exists for this face vertex, but is different
// (e.g. we're on a UV seam) create another fvar datum
OpenSubdiv::HbrFVarData<OpenSubdiv::OsdVertex> &fvarData_new = hbrVertex->NewFVarData(face);
fvarData_new.SetAllData( totalFvarWidth, &fvarItem[0] );
}
}
}
// Increment ptxidx and store off ptex index values
// The number of ptexIds needed is 1 if a quad. Otherwise it is the number of
// vertices for the face.
int numPtexIdsForFace;
if (valid) {
numPtexIdsForFace = ( nv != 4 ) ? nv : 1 ;
}
else {
numPtexIdsForFace = 0;
}
ptxidx += numPtexIdsForFace;
}
// Apply Creases
float maxEdgeCrease = applyCreaseEdges( inMeshFn, hbrMesh );
float maxVertexCrease = applyCreaseVertices( inMeshFn, hbrMesh );
if (maxCreaseSharpness) {
*maxCreaseSharpness = std::max(maxEdgeCrease, maxVertexCrease);
}
// Return the resulting HBR Mesh
// Note that boundaryMethods and hbrMesh->Finish() still need to be called
return hbrMesh;
}
MStatus convertOsdFarToMayaMeshData(
FMesh const * farMesh,
OpenSubdiv::OsdCpuVertexBuffer * vertexBuffer,
int subdivisionLevel,
MFnMesh const & inMeshFn,
MObject newMeshDataObj ) {
MStatus returnStatus;
// Get sizing data from OSD
const OpenSubdiv::FarPatchTables *farPatchTables = farMesh->GetPatchTables();
int numPolygons = farPatchTables->GetNumFaces(); // use the highest level stored in the patch tables
const unsigned int *polygonConnects_orig = farPatchTables->GetFaceVertices(); // use the highest level stored in the patch tables
const OpenSubdiv::FarSubdivisionTables *farSubdivTables = farMesh->GetSubdivisionTables();
unsigned int numVertices = farSubdivTables->GetNumVertices(subdivisionLevel);
unsigned int vertexOffset = farSubdivTables->GetFirstVertexOffset(subdivisionLevel);
// Init Maya Data
MFloatPointArray points(numVertices);
MIntArray faceCounts(numPolygons); // number of edges for each polygon. Assume quads (4-edges per face)
MIntArray faceConnects(numPolygons*4); // array of vertex ids for all edges. assuming quads
// -- Face Counts
for (int i=0; i < numPolygons; ++i) {
faceCounts[i] = 4;
}
// -- Face Connects
for (unsigned int i=0; i < faceConnects.length(); i++) {
faceConnects[i] = polygonConnects_orig[i] - vertexOffset; // adjust vertex indices so that v0 is at index 0
}
// -- Points
// Number of floats in each vertex. (positions, normals, etc)
int numFloatsPerVertex = vertexBuffer->GetNumElements();
assert(numFloatsPerVertex == 3); // assuming only xyz stored for each vertex
const float *vertexData = vertexBuffer->BindCpuBuffer();
float *ptrVertexData;
for (unsigned int i=0; i < numVertices; i++) {
// make sure to offset to the first osd vertex for that subd level
unsigned int osdRawVertexIndex = i + vertexOffset;
// Lookup the data in the vertexData
ptrVertexData = (float *) vertexData + ((osdRawVertexIndex) * numFloatsPerVertex);
points.set(i, ptrVertexData[0], ptrVertexData[1], ptrVertexData[2]);
}
// Create New Mesh from MFnMesh
MFnMesh newMeshFn;
MObject newMeshObj = newMeshFn.create(points.length(), faceCounts.length(),
points, faceCounts, faceConnects, newMeshDataObj, &returnStatus);
MCHECKERR(returnStatus, "Cannot create new mesh");
// Attach UVs (if present)
// ASSUMPTION: Only tracking UVs as FVar data. Will need to change this
// ASSUMPTION: OSD has a unique UV for each face-vertex
int fvarTotalWidth = farMesh->GetPatchTables()->GetFVarData().GetFVarWidth();
if (fvarTotalWidth > 0) {
// Get face-varying set names and other info from the inMesh
MStringArray uvSetNames;
MStringArray colorSetNames;
std::vector<int> colorSetChannels;
std::vector<MFnMesh::MColorRepresentation> colorSetReps;
int totalColorSetChannels = 0;
returnStatus = getMayaFvarFieldParams(inMeshFn, uvSetNames, colorSetNames, colorSetChannels, colorSetReps, totalColorSetChannels);
int numUVSets = uvSetNames.length();
int expectedFvarTotalWidth = numUVSets*2 + totalColorSetChannels;
assert(fvarTotalWidth == expectedFvarTotalWidth);
std::vector<float> const &fvarData = farPatchTables->GetFVarData().GetAllData();
if (fvarData.size() != expectedFvarTotalWidth*faceConnects.length()) {
MCHECKERR(MS::kFailure, "Incorrect face-varying table length");
}
// Create an array of indices to map each face-vert to the UV and ColorSet Data
MIntArray fvarConnects(faceConnects.length());
for (unsigned int i=0; i < faceConnects.length(); i++) {
fvarConnects[i] = i;
}
MFloatArray uCoord(faceConnects.length());
MFloatArray vCoord(faceConnects.length());
for (int uvSetIndex=0; uvSetIndex < numUVSets; uvSetIndex++) {
for(unsigned int vertid=0; vertid < faceConnects.length(); vertid++) {
int fvarItem = vertid*fvarTotalWidth + uvSetIndex*2; // stride per vertex is the fvarTotalWidth
uCoord[vertid] = fvarData[fvarItem];
vCoord[vertid] = fvarData[fvarItem+1];
}
// Assign UV buffer and map the uvids for each face-vertex
if (uvSetIndex > 0) {
returnStatus = newMeshFn.createUVSetDataMesh( uvSetNames[uvSetIndex] );
MCHECKERR(returnStatus, "Cannot create UVSet");
}
static MString defaultUVName("map1");
MString const * uvname = uvSetIndex==0 ? &defaultUVName : &uvSetNames[uvSetIndex];
returnStatus = newMeshFn.setUVs(uCoord,vCoord, uvname);
MCHECKERR(returnStatus, "Cannot set UVs for set : "+*uvname);
newMeshFn.assignUVs(faceCounts, fvarConnects, uvname);
}
MColorArray colorArray(faceConnects.length());
int colorSetRelativeStartIndex = numUVSets*2;
for (unsigned int colorSetIndex=0; colorSetIndex < colorSetNames.length(); colorSetIndex++) {
for(unsigned int vertid=0; vertid < faceConnects.length(); vertid++) {
int fvarItem = vertid*fvarTotalWidth + colorSetRelativeStartIndex;
int nchannels = colorSetChannels[colorSetIndex];
for (int channel=0; channel<nchannels; ++channel) {
colorArray[vertid][channel] = fvarData[fvarItem+channel];
}
}
// Assign UV buffer and map the uvids for each face-vertex
// API Limitation: Cannot set MColorRepresentation here
returnStatus = newMeshFn.createColorSetDataMesh(colorSetNames[colorSetIndex]);
MCHECKERR(returnStatus, "Cannot create ColorSet");
bool isColorClamped = inMeshFn.isColorClamped(colorSetNames[colorSetIndex], &returnStatus);
newMeshFn.setIsColorClamped(colorSetNames[colorSetIndex], isColorClamped);
newMeshFn.setColors(colorArray, &colorSetNames[colorSetIndex], colorSetReps[colorSetIndex]);
newMeshFn.assignColors(fvarConnects, &colorSetNames[colorSetIndex]);
// Increment colorSet start location in fvar buffer
colorSetRelativeStartIndex += colorSetChannels[colorSetIndex];
}
}
return MS::kSuccess;
}
static inline int
computeNumSubfaces( int nverts, int level ) {
return nverts==4 ? 1<<(level<<1) : nverts*(1<<((level-1)<<1));
}
// Propagate objectGroups from inMesh to subdivided outMesh
// Note: Currently only supporting facet groups (for per-facet shading)
MStatus
createSmoothMesh_objectGroups( FMesh const * farMesh, MFnMesh const & inMeshFn,
MFnMeshData const & inMeshDat, MFnMeshData &newMeshDat, int level ) {
MStatus returnStatus;
MIntArray newCompElems;
int numSubfaces = farMesh->GetPatchTables()->GetNumFaces();
std::vector<unsigned int> offsets; // mapping of offsets for subdivided faces
for(unsigned int gi=0; gi<inMeshDat.objectGroupCount(); gi++) {
unsigned int compId = inMeshDat.objectGroup(gi, &returnStatus);
MCHECKERR(returnStatus, "cannot get objectGroup() comp ID.");
MFn::Type compType = inMeshDat.objectGroupType(compId, &returnStatus);
MCHECKERR(returnStatus, "cannot get objectGroupType().");
// Only supporting kMeshPolygonComponent ObjectGroups at this time
// Skip the other types
if (compType == MFn::kMeshPolygonComponent) {
// get elements from inMesh objectGroupComponent
MIntArray compElems;
MFnSingleIndexedComponent compFn(inMeshDat.objectGroupComponent(compId), &returnStatus );
MCHECKERR(returnStatus, "cannot get MFnSingleIndexedComponent for inMeshDat.objectGroupComponent().");
compFn.getElements(compElems);
if (compElems.length()==0) {
continue;
}
// over-allocation to maximum possible length
newCompElems.setLength( numSubfaces );
if (offsets.empty()) {
// lazy population of the subface offsets table
int nfaces = inMeshFn.numPolygons();
offsets.resize(nfaces);
for (int i=0, count=0; i<nfaces; ++i) {
int nverts = inMeshFn.polygonVertexCount(i),
nsubfaces = computeNumSubfaces(nverts, level);
offsets[i] = count;
count+=nsubfaces;
}
}
unsigned int idx = 0;
// convert/populate newCompElems from compElems of inMesh
// (with new face indices) to outMesh
for (unsigned int i=0; i < compElems.length(); i++) {
int nverts = inMeshFn.polygonVertexCount(compElems[i]),
nsubfaces = computeNumSubfaces(nverts, level);
unsigned int subFaceOffset = offsets[compElems[i]];
for (int j=0; j<nsubfaces; ++j) {
newCompElems[idx++] = subFaceOffset++;
}
}
// resize to actual length
newCompElems.setLength( idx );
// create comp
createComp(newMeshDat, compType, compId, newCompElems);
}
}
return MS::kSuccess;
}
// ====================================
// Compute
// ====================================
//
// Description:
// This method computes the value of the given output plug based
// on the values of the input attributes.
//
// Arguments:
// plug - the plug to compute
// data - object that provides access to the attributes for this node
//
MStatus OsdPolySmooth::compute( const MPlug& plug, MDataBlock& data ) {
MStatus returnStatus;
// Check which output attribute we have been asked to compute. If this
// node doesn't know how to compute it, we must return
// MS::kUnknownParameter.
//
if( plug == a_output ) {
bool createdSubdMesh = false;
int subdivisionLevel = data.inputValue(a_subdivisionLevels).asInt();
short stateH = data.inputValue(state).asShort();
if ((subdivisionLevel > 0) and (stateH !=1)) {
// == Retrieve input mesh ====================================
// Get attr values
MObject inMeshObj = data.inputValue(a_inputPolymesh).asMesh();
short vertBoundaryMethod = data.inputValue(a_vertBoundaryMethod).asShort();
short fvarBoundaryMethod = data.inputValue(a_fvarBoundaryMethod).asShort();
bool fvarPropCorners = data.inputValue(a_fvarPropagateCorners).asBool();
bool smoothTriangles = data.inputValue(a_smoothTriangles).asBool();
short creaseMethodVal = data.inputValue(a_creaseMethod).asShort();
// Convert attr values to OSD enums
HMesh::InterpolateBoundaryMethod vertInterpBoundaryMethod =
ConvertMayaBoundaryMethodShortToOsdInterpolateBoundaryMethod(vertBoundaryMethod);
HMesh::InterpolateBoundaryMethod fvarInterpBoundaryMethod =
ConvertMayaBoundaryMethodShortToOsdInterpolateBoundaryMethod(fvarBoundaryMethod);
HCatmark::CreaseSubdivision creaseMethod =
(creaseMethodVal == k_creaseMethod_chaikin) ?
HCatmark::k_CreaseChaikin : HCatmark::k_CreaseNormal;
HCatmark::TriangleSubdivision triangleSubdivision =
smoothTriangles ? HCatmark::k_New : HCatmark::k_Normal;
// == Get Mesh Functions and Iterators ==========================
MFnMeshData inMeshDat(inMeshObj);
MFnMesh inMeshFn(inMeshObj, &returnStatus);
MCHECKERR(returnStatus, "ERROR getting inMeshFn\n");
MItMeshPolygon inMeshItPolygon(inMeshObj, &returnStatus);
MCHECKERR(returnStatus, "ERROR getting inMeshItPolygon\n");
// == Convert MFnMesh to OpenSubdiv =============================
// Create the hbrMesh
// Note: These fvar values only need to be kept alive through the life of the farMesh
std::vector<int> fvarIndices;
std::vector<int> fvarWidths;
float maxCreaseSharpness=0.0;
HMesh *hbrMesh = createOsdHbrFromPoly(
inMeshFn, inMeshItPolygon, fvarIndices, fvarWidths, &maxCreaseSharpness);
assert(hbrMesh);
// Create the farMesh if successfully created the hbrMesh
if (hbrMesh) {
// Set Boundary methods and other hbr paramters
hbrMesh->SetInterpolateBoundaryMethod( vertInterpBoundaryMethod );
hbrMesh->SetFVarInterpolateBoundaryMethod( fvarInterpBoundaryMethod );
hbrMesh->SetFVarPropagateCorners(fvarPropCorners);
hbrMesh->GetSubdivision()->SetCreaseSubdivisionMethod(creaseMethod);
// Set HBR Catmark Subdivision parameters
HCatmark *catmarkSubdivision = dynamic_cast<HCatmark *>(hbrMesh->GetSubdivision());
if (catmarkSubdivision) {
catmarkSubdivision->SetTriangleSubdivisionMethod(triangleSubdivision);
}
// Finalize subd calculations -- apply boundary interpolation rules and resolves singular verts, etc.
// NOTE: This HAS to be called after all HBR parameters are set
hbrMesh->Finish();
// Create a FarMesh from the HBR mesh and pass into
// It will be owned by the OsdMesh and deleted in the ~OsdMesh()
FMeshFactory meshFactory(hbrMesh, subdivisionLevel, false);
FMesh *farMesh = meshFactory.Create((hbrMesh->GetTotalFVarWidth() > 0));
// == Setup OSD Data Structures =========================
int numVertexElements = 3; // only track vertex positions
int numVaryingElements = 0; // XXX Future: Revise to include varying ColorSets
int numVertices = inMeshFn.numVertices();
int numFarVerts = farMesh->GetNumVertices();
static OpenSubdiv::OsdCpuComputeController computeController = OpenSubdiv::OsdCpuComputeController();
OpenSubdiv::OsdCpuComputeController::ComputeContext *computeContext =
OpenSubdiv::OsdCpuComputeController::ComputeContext::Create(farMesh->GetSubdivisionTables(), farMesh->GetVertexEditTables());
OpenSubdiv::OsdCpuVertexBuffer *vertexBuffer =
OpenSubdiv::OsdCpuVertexBuffer::Create(numVertexElements, numFarVerts );
OpenSubdiv::OsdCpuVertexBuffer *varyingBuffer =
(numVaryingElements) ? OpenSubdiv::OsdCpuVertexBuffer::Create(numVaryingElements, numFarVerts) : NULL;
// == UPDATE VERTICES (can be done after farMesh generated from topology) ==
float const * vertex3fArray = inMeshFn.getRawPoints(&returnStatus);
vertexBuffer->UpdateData(vertex3fArray, 0, numVertices );
// == Delete HBR
// Can now delete the hbrMesh as we will only be referencing the farMesh from this point on
delete hbrMesh;
hbrMesh = NULL;
// == Subdivide OpenSubdiv mesh ==========================
computeController.Refine(computeContext, farMesh->GetKernelBatches(), vertexBuffer, varyingBuffer);
computeController.Synchronize();
// == Convert subdivided OpenSubdiv mesh to MFnMesh Data outputMesh =============
// Create New Mesh Data Object
MFnMeshData newMeshData;
MObject newMeshDataObj = newMeshData.create(&returnStatus);
MCHECKERR(returnStatus, "ERROR creating outputData");
// Create out mesh
returnStatus = convertOsdFarToMayaMeshData(farMesh, vertexBuffer, subdivisionLevel, inMeshFn, newMeshDataObj);
MCHECKERR(returnStatus, "ERROR convertOsdFarToMayaMesh");
// Propagate objectGroups from inMesh to outMesh (for per-facet shading, etc)
returnStatus = createSmoothMesh_objectGroups(farMesh, inMeshFn, inMeshDat, newMeshData, subdivisionLevel );
// Write to output plug
MDataHandle outMeshH = data.outputValue(a_output, &returnStatus);
MCHECKERR(returnStatus, "ERROR getting polygon data handle\n");
outMeshH.set(newMeshDataObj);
int isolation = std::min(10,(int)ceil(maxCreaseSharpness)+1);
data.outputValue(a_recommendedIsolation).set(isolation);
// == Cleanup OSD ============================================
// REVISIT: Re-add these deletes
delete(vertexBuffer);
delete(varyingBuffer);
delete(computeContext);
delete(farMesh);
// note that the subd mesh was created (see the section below if !createdSubdMesh)
createdSubdMesh = true;
}
}
// Pass-through inMesh to outMesh if not created the subd mesh
if (!createdSubdMesh) {
MDataHandle outMeshH = data.outputValue(a_output, &returnStatus);
returnStatus = outMeshH.copy(data.outputValue(a_inputPolymesh, &returnStatus));
MCHECKERR(returnStatus, "ERROR getting polygon data handle\n");
}
// Clean up Maya Plugs
data.setClean(plug);
}
else {
// Unhandled parameter in this compute function, so return MS::kUnknownParameter
// so it is handled in a parent compute() function.
return MS::kUnknownParameter;
}
return MS::kSuccess;
}
// Creator
//
// Description:
// this method exists to give Maya a way to create new objects
// of this type.
//
// Return Value:
// a new object of this type
//
void* OsdPolySmooth::creator() {
return new OsdPolySmooth;
}
// Create and Add Attributes
//
// Description:
// This method is called to create and initialize all of the attributes
// and attribute dependencies for this node type. This is only called
// once when the node type is registered with Maya.
//
// Return Values:
// MS::kSuccess
// MS::kFailure
//
MStatus OsdPolySmooth::initialize() {
MStatus stat;
MFnCompoundAttribute cAttr;
MFnEnumAttribute eAttr;
MFnGenericAttribute gAttr;
MFnLightDataAttribute lAttr;
MFnMatrixAttribute mAttr;
MFnMessageAttribute msgAttr;
MFnNumericAttribute nAttr;
MFnTypedAttribute tAttr;
MFnUnitAttribute uAttr;
// MAYA_NODE_BUILDER:BEG [ATTRIBUTE CREATION] ==========
// a_inputPolymesh : This is a description for this attribute
a_inputPolymesh = tAttr.create("inputPolymesh", "ip", MFnData::kMesh, MObject::kNullObj, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::inputPolymesh" );
stat = tAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::inputPolymesh.setReadable()" );
stat = tAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::inputPolymesh.setWritable()" );
stat = tAttr.setHidden(true);
MCHECKERR( stat, "cannot OsdPolySmooth::inputPolymesh.setHidden()" );
stat = addAttribute( a_inputPolymesh );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_inputPolymesh)" );
// a_output : This is a description for this attribute
a_output = tAttr.create("output", "out", MFnData::kMesh, MObject::kNullObj, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::output" );
stat = tAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::output.setReadable()" );
stat = tAttr.setWritable(false);
MCHECKERR( stat, "cannot OsdPolySmooth::output.setWritable()" );
stat = tAttr.setHidden(true);
MCHECKERR( stat, "cannot OsdPolySmooth::output.setHidden()" );
stat = addAttribute( a_output );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_output)" );
// a_subdivisionLevels : The number of recursive quad subdivisions to perform on each face.
a_subdivisionLevels = nAttr.create("subdivisionLevels", "sl", MFnNumericData::kInt, 0.0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::subdivisionLevels" );
stat = nAttr.setDefault(2);
MCHECKERR( stat, "cannot OsdPolySmooth::subdivisionLevels.setDefault(2)" );
stat = nAttr.setMin(0);
MCHECKERR( stat, "cannot OsdPolySmooth::subdivisionLevels.setMin(0)" );
stat = nAttr.setMax(10);
MCHECKERR( stat, "cannot OsdPolySmooth::subdivisionLevels.setMax(10)" );
stat = nAttr.setSoftMax(4);
MCHECKERR( stat, "cannot OsdPolySmooth::subdivisionLevels.setSoftMax(4)" );
stat = nAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::subdivisionLevels.setReadable()" );
stat = nAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::subdivisionLevels.setWritable()" );
stat = addAttribute( a_subdivisionLevels );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_subdivisionLevels)" );
// a_recommendedIsolation : The number of recursive quad subdivisions to perform on each face.
a_recommendedIsolation = nAttr.create("recommendedIsolation", "ri", MFnNumericData::kInt, 0.0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::recommendedIsolation" );
stat = nAttr.setDefault(2);
MCHECKERR( stat, "cannot OsdPolySmooth::recommendedIsolation.setDefault(0)" );
stat = nAttr.setMin(0);
MCHECKERR( stat, "cannot OsdPolySmooth::recommendedIsolation.setMin(0)" );
stat = nAttr.setMax(10);
MCHECKERR( stat, "cannot OsdPolySmooth::recommendedIsolation.setSoftMax(10)" );
stat = nAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::recommendedIsolation.setReadable()" );
stat = nAttr.setWritable(false);
MCHECKERR( stat, "cannot OsdPolySmooth::recommendedIsolation.setWritable()" );
stat = nAttr.setHidden(false);
MCHECKERR( stat, "cannot OsdPolySmooth::recommendedIsolation.setHidden()" );
stat = addAttribute( a_recommendedIsolation );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_recommendedIsolation)" );
// a_vertBoundaryMethod : Controls how boundary edges and vertices are interpolated. <ul> <li>Smooth, Edges: Renderman: InterpolateBoundaryEdgeOnly</li> <li>Smooth, Edges and Corners: Renderman: InterpolateBoundaryEdgeAndCorner</li> </ul>
a_vertBoundaryMethod = eAttr.create("vertBoundaryMethod", "vbm", 0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::vertBoundaryMethod" );
stat = eAttr.addField("Interpolate Edges", k_BoundaryMethod_InterpolateBoundaryEdgeOnly);
MCHECKERR( stat, "cannot OsdPolySmooth::vertBoundaryMethod.addField(Interpolate Edges, k_BoundaryMethod_InterpolateBoundaryEdgeOnly)" );
stat = eAttr.addField("Interpolate Edges And Corners", k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner);
MCHECKERR( stat, "cannot OsdPolySmooth::vertBoundaryMethod.addField(Interpolate Edges And Corners, k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner)" );
stat = eAttr.setDefault(k_BoundaryMethod_InterpolateBoundaryEdgeOnly);
MCHECKERR( stat, "cannot OsdPolySmooth::vertBoundaryMethod.setDefault(k_BoundaryMethod_InterpolateBoundaryEdgeOnly)" );
stat = eAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::vertBoundaryMethod.setReadable()" );
stat = eAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::vertBoundaryMethod.setWritable()" );
stat = addAttribute( a_vertBoundaryMethod );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_vertBoundaryMethod)" );
// a_fvarBoundaryMethod : Controls how boundaries are treated for face-varying data (UVs and Vertex Colors). <ul> <li>Bi-linear (None): Renderman: InterpolateBoundaryNone</li> <li>Smooth, (Edge Only): Renderman: InterpolateBoundaryEdgeOnly</li> <li>Smooth, (Edges and Corners: Renderman: InterpolateBoundaryEdgeAndCorner</li> <li>Smooth, (ZBrush and Maya "Smooth Internal Only"): Renderman: InterpolateBoundaryAlwaysSharp</li> </ul>
a_fvarBoundaryMethod = eAttr.create("fvarBoundaryMethod", "fvbm", 0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::fvarBoundaryMethod" );
stat = eAttr.addField("Bi-linear (None)", k_BoundaryMethod_InterpolateBoundaryNone);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.addField(Bi-linear (None), k_BoundaryMethod_InterpolateBoundaryNone)" );
stat = eAttr.addField("Smooth (Edge Only)", k_BoundaryMethod_InterpolateBoundaryEdgeOnly);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.addField(Smooth (Edge Only), k_BoundaryMethod_InterpolateBoundaryEdgeOnly)" );
stat = eAttr.addField("Smooth (Edge and Corner)", k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.addField(Smooth (Edge and Corner), k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner)" );
stat = eAttr.addField("Smooth (Always Sharp)", k_BoundaryMethod_InterpolateBoundaryAlwaysSharp);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.addField(Smooth (Always Sharp), k_BoundaryMethod_InterpolateBoundaryAlwaysSharp)" );
stat = eAttr.setDefault(k_BoundaryMethod_InterpolateBoundaryNone);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.setDefault(k_BoundaryMethod_InterpolateBoundaryNone)" );
stat = eAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.setReadable()" );
stat = eAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarBoundaryMethod.setWritable()" );
stat = addAttribute( a_fvarBoundaryMethod );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_fvarBoundaryMethod)" );
// a_fvarPropagateCorners :
a_fvarPropagateCorners = nAttr.create("fvarPropagateCorners", "fvpc", MFnNumericData::kBoolean, 0.0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::fvarPropagateCorners" );
stat = nAttr.setDefault(false);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarPropagateCorners.setDefault(false)" );
stat = nAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarPropagateCorners.setReadable()" );
stat = nAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::fvarPropagateCorners.setWritable()" );
stat = addAttribute( a_fvarPropagateCorners );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_fvarPropagateCorners)" );
// a_smoothTriangles : Apply a special subdivision rule be applied to all triangular faces that was empirically determined to make triangles subdivide more smoothly.
a_smoothTriangles = nAttr.create("smoothTriangles", "stri", MFnNumericData::kBoolean, 0.0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::smoothTriangles" );
stat = nAttr.setDefault(true);
MCHECKERR( stat, "cannot OsdPolySmooth::smoothTriangles.setDefault(true)" );
stat = nAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::smoothTriangles.setReadable()" );
stat = nAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::smoothTriangles.setWritable()" );
stat = addAttribute( a_smoothTriangles );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_smoothTriangles)" );
// a_creaseMethod : Controls how boundary edges and vertices are interpolated. <ul> <li>Normal</li> <li>Chaikin: Improves the appearance of multiedge creases with varying weight</li> </ul>
a_creaseMethod = eAttr.create("creaseMethod", "crm", 0, &stat);
MCHECKERR( stat, "cannot create OsdPolySmooth::creaseMethod" );
stat = eAttr.addField("Normal", k_creaseMethod_normal);
MCHECKERR( stat, "cannot OsdPolySmooth::creaseMethod.addField(Normal, k_creaseMethod_normal)" );
stat = eAttr.addField("Chaikin", k_creaseMethod_chaikin);
MCHECKERR( stat, "cannot OsdPolySmooth::creaseMethod.addField(Chaikin, k_creaseMethod_chaikin)" );
stat = eAttr.setDefault(0);
MCHECKERR( stat, "cannot OsdPolySmooth::creaseMethod.setDefault(0)" );
stat = eAttr.setReadable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::creaseMethod.setReadable()" );
stat = eAttr.setWritable(true);
MCHECKERR( stat, "cannot OsdPolySmooth::creaseMethod.setWritable()" );
stat = addAttribute( a_creaseMethod );
MCHECKERR( stat, "cannot OsdPolySmooth::addAttribute(a_creaseMethod)" );
// MAYA_NODE_BUILDER:END [ATTRIBUTE CREATION] ==========
// Set up a dependency between the input and the output. This will cause
// the output to be marked dirty when the input changes. The output will
// then be recomputed the next time the value of the output is requested.
//
// MAYA_NODE_BUILDER:BEG [ATTRIBUTE DEPENDS] ==========
stat = attributeAffects( a_creaseMethod, a_output );
MCHECKERR( stat, "cannot have attribute creaseMethod affect output" );
stat = attributeAffects( a_inputPolymesh, a_output );
MCHECKERR( stat, "cannot have attribute inputPolymesh affect output" );
stat = attributeAffects( a_subdivisionLevels, a_output );
MCHECKERR( stat, "cannot have attribute subdivisionLevels affect output" );
stat = attributeAffects( a_smoothTriangles, a_output );
MCHECKERR( stat, "cannot have attribute smoothTriangles affect output" );
stat = attributeAffects( a_fvarPropagateCorners, a_output );
MCHECKERR( stat, "cannot have attribute fvarPropagateCorners affect output" );
stat = attributeAffects( a_vertBoundaryMethod, a_output );
MCHECKERR( stat, "cannot have attribute vertBoundaryMethod affect output" );
stat = attributeAffects( a_fvarBoundaryMethod, a_output );
MCHECKERR( stat, "cannot have attribute fvarBoundaryMethod affect output" );
stat = attributeAffects( a_creaseMethod, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute creaseMethod affect .si output" );
stat = attributeAffects( a_inputPolymesh, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute inputPolymesh affect .si output" );
stat = attributeAffects( a_subdivisionLevels, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute subdivisionLevels affect .si output" );
stat = attributeAffects( a_smoothTriangles, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute smoothTriangles affect .si output" );
stat = attributeAffects( a_fvarPropagateCorners, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute fvarPropagateCorners affect .si output" );
stat = attributeAffects( a_vertBoundaryMethod, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute vertBoundaryMethod affect .si output" );
stat = attributeAffects( a_fvarBoundaryMethod, a_recommendedIsolation );
MCHECKERR( stat, "cannot have attribute fvarBoundaryMethod affect .si output" );
// MAYA_NODE_BUILDER:END [ATTRIBUTE DEPENDS] ==========
return MS::kSuccess;
}
// ==========================================
// Plugin
// ==========================================
MStatus initializePlugin( MObject obj )
{
MStatus status = MS::kSuccess;
MFnPlugin plugin( obj, "OsdPolySmooth", "1.0", "Any");
status = plugin.registerNode(
OsdPolySmooth::typeNameStr,
OsdPolySmooth::id,
OsdPolySmooth::creator,
OsdPolySmooth::initialize);
MCHECKERR(status, "registerNode");
// Source UI scripts
MString path = plugin.loadPath()+"/osdPolySmooth.mel";
if (not MGlobal::sourceFile(path)) {
path = "osdPolySmooth.mel";
if (not MGlobal::sourceFile(path)) {
MGlobal::displayWarning("Failed to source osdPolySmooth.mel.");
}
}
// RegisterUI
status = plugin.registerUI("osdPolySmooth_addUI()", "osdPolySmooth_removeUI()");
MCHECKERR(status, "registerUI");
return status;
}
MStatus uninitializePlugin( MObject obj)
{
MStatus returnStatus = MS::kSuccess;
MFnPlugin plugin( obj );
returnStatus = plugin.deregisterNode( OsdPolySmooth::id );
MCHECKERR(returnStatus, "deregisterNode");
return returnStatus;
}