mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-03 16:31:04 +00:00
33bfbf699b
In OpenSubdiv 2.x, we encapsulated subdivision tables into compute context in osd layer since those tables are order-dependent and have to be applied in a certain manner. In 3.0, we adopted stencil table based refinement. It's more simple and such an encapsulation is no longer needed. Also 2.0 API has several ownership issues of GPU kernel caching, and forces unnecessary instantiation of controllers even though the cpu kernels typically don't need instances unlike GPU ones. This change completely revisit osd client facing APIs. All contexts and controllers were replaced with device-specific tables and evaluators. While we can still use consistent API across various device backends, unnecessary complexities have been removed. For example, cpu evaluator is just a set of static functions and also there's no need to replicate FarStencilTables to ComputeContext. Also the new API delegates the ownership of compiled GPU kernels to clients, for the better management of resources especially in multiple GPU environment. In addition to integrating ComputeController and EvalStencilController into a single function Evaluator::EvalStencils(), EvalLimit API is also added into Evaluator. This is working but still in progress, and we'll make a followup change for the complete implementation. -some naming convention changes: GLSLTransformFeedback to GLXFBEvaluator GLSLCompute to GLComputeEvaluator -move LimitLocation struct into examples/glEvalLimit. We're still discussing patch evaluation interface. Basically we'd like to tease all ptex-specific parametrization out of far/osd layer. TODO: -implments EvalPatches() in the right way -derivative evaluation API is still interim. -VertexBufferDescriptor needs a better API to advance its location -synchronization mechanism is not ideal (too global). -OsdMesh class is hacky. need to fix it.
970 lines
38 KiB
C++
970 lines
38 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 "mayaPolySmooth.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 <cassert>
|
|
#include <map>
|
|
|
|
#if defined(_MSV_VER) and (not defined(__INTEL_COMPILER))
|
|
#pragma warning( disable : 174 593 )
|
|
#endif
|
|
|
|
// OpenSubdiv includes
|
|
#include <far/topologyRefinerFactory.h>
|
|
#include <far/stencilTablesFactory.h>
|
|
|
|
#include <osd/mesh.h>
|
|
#include <osd/cpuVertexBuffer.h>
|
|
|
|
|
|
// ====================================
|
|
// Static Initialization
|
|
// ====================================
|
|
|
|
// MAYA_NODE_BUILDER:BEG [ATTRIBUTE INITIALIZATION] ==========
|
|
const MTypeId MayaPolySmooth::id( 0x0010a25b );
|
|
const MString MayaPolySmooth::typeNameStr("mayaPolySmooth");
|
|
MObject MayaPolySmooth::a_inputPolymesh;
|
|
MObject MayaPolySmooth::a_output;
|
|
MObject MayaPolySmooth::a_subdivisionLevels;
|
|
MObject MayaPolySmooth::a_recommendedIsolation;
|
|
MObject MayaPolySmooth::a_vertBoundaryMethod;
|
|
MObject MayaPolySmooth::a_fvarBoundaryMethod;
|
|
MObject MayaPolySmooth::a_fvarPropagateCorners;
|
|
MObject MayaPolySmooth::a_smoothTriangles;
|
|
MObject MayaPolySmooth::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
|
|
};
|
|
|
|
static OpenSubdiv::Sdc::Options::VtxBoundaryInterpolation
|
|
ConvertMayaVtxBoundary(short boundaryMethod) {
|
|
|
|
typedef OpenSubdiv::Sdc::Options Sdc;
|
|
|
|
switch (boundaryMethod) {
|
|
case k_BoundaryMethod_InterpolateBoundaryNone : return Sdc::VTX_BOUNDARY_NONE;
|
|
case k_BoundaryMethod_InterpolateBoundaryEdgeOnly : return Sdc::VTX_BOUNDARY_EDGE_ONLY;
|
|
case k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner : return Sdc::VTX_BOUNDARY_EDGE_AND_CORNER;
|
|
default: ;
|
|
}
|
|
MGlobal::displayError("VTX InterpolateBoundaryMethod value out of range. Using \"none\"");
|
|
return Sdc::VTX_BOUNDARY_NONE;
|
|
}
|
|
|
|
// XXXX note: This function converts the options exposed in Maya's GUI which are
|
|
// based on prman legacy face-varying boundary interpolation rules.
|
|
// As a result, some OpenSubdiv 3.0 FVar interpolation rules are not
|
|
// exposed, and the some of the ones exposed fix incorrect behavior
|
|
// from legacy prman code, so the results are not 100% backward compatible.
|
|
static OpenSubdiv::Sdc::Options::FVarLinearInterpolation
|
|
ConvertMayaFVarBoundary(short boundaryMethod, bool propagateCorner) {
|
|
|
|
typedef OpenSubdiv::Sdc::Options Sdc;
|
|
|
|
switch (boundaryMethod) {
|
|
case k_BoundaryMethod_InterpolateBoundaryNone : return Sdc::FVAR_LINEAR_ALL;
|
|
case k_BoundaryMethod_InterpolateBoundaryEdgeOnly : return Sdc::FVAR_LINEAR_NONE;
|
|
case k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner :
|
|
return propagateCorner ? Sdc::FVAR_LINEAR_CORNERS_PLUS2 : Sdc::FVAR_LINEAR_CORNERS_PLUS1;
|
|
case k_BoundaryMethod_InterpolateBoundaryAlwaysSharp : return Sdc::FVAR_LINEAR_BOUNDARIES;
|
|
default: ;
|
|
}
|
|
MGlobal::displayError("FVar InterpolateMethod value out of range. Using \"none\"");
|
|
return Sdc::FVAR_LINEAR_ALL;
|
|
}
|
|
|
|
// ====================================
|
|
// 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
|
|
// ====================================
|
|
MayaPolySmooth::MayaPolySmooth() {}
|
|
|
|
MayaPolySmooth::~MayaPolySmooth() {}
|
|
|
|
|
|
// ====================================
|
|
// 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
|
|
// ====================================
|
|
|
|
typedef OpenSubdiv::Far::TopologyRefinerFactoryBase::TopologyDescriptor Descriptor;
|
|
|
|
// Reference: OSD shape_utils.h:: applyTags() "crease"
|
|
static float
|
|
getCreaseEdges(MFnMesh const & inMeshFn, Descriptor & outDesc) {
|
|
|
|
MUintArray tEdgeIds;
|
|
MDoubleArray tCreaseData;
|
|
float maxCreaseValue = 0.0f;
|
|
|
|
if (inMeshFn.getCreaseEdges(tEdgeIds, tCreaseData)) {
|
|
|
|
assert( tEdgeIds.length() == tCreaseData.length() );
|
|
|
|
int ncreases = tEdgeIds.length();
|
|
int * vertPairs = new int[ncreases*2];
|
|
float * weights = new float[ncreases];
|
|
|
|
int2 edgeVerts;
|
|
for (unsigned int j=0; j < tEdgeIds.length(); j++) {
|
|
|
|
assert( tCreaseData[j] >= 0.0 );
|
|
inMeshFn.getEdgeVertices(tEdgeIds[j], edgeVerts);
|
|
|
|
vertPairs[j*2 ] = edgeVerts[0];
|
|
vertPairs[j*2+1] = edgeVerts[1];
|
|
weights[j] = float(tCreaseData[j]);
|
|
maxCreaseValue = std::max(float(tCreaseData[j]), maxCreaseValue);
|
|
}
|
|
|
|
outDesc.numCreases = ncreases;
|
|
outDesc.creaseVertexIndexPairs = vertPairs;
|
|
outDesc.creaseWeights = weights;
|
|
}
|
|
return maxCreaseValue;
|
|
}
|
|
|
|
|
|
// Reference: OSD shape_utils.h:: applyTags() "corner"
|
|
static float
|
|
getCreaseVertices( MFnMesh const & inMeshFn, Descriptor & outDesc) {
|
|
|
|
MUintArray tVertexIds;
|
|
MDoubleArray tCreaseData;
|
|
float maxCreaseValue = 0.0f;
|
|
|
|
if ( inMeshFn.getCreaseVertices(tVertexIds, tCreaseData) ) {
|
|
|
|
assert( tVertexIds.length() == tCreaseData.length() );
|
|
|
|
int ncorners = tVertexIds.length();
|
|
int * verts = new int[ncorners*2];
|
|
float * weights = new float[ncorners];
|
|
|
|
// Has crease vertices
|
|
for (unsigned int j=0; j < tVertexIds.length(); j++) {
|
|
|
|
assert( tCreaseData[j] >= 0.0 );
|
|
|
|
verts[j] = tVertexIds[j];
|
|
weights[j] = float(tCreaseData[j]);
|
|
|
|
maxCreaseValue = std::max(float(tCreaseData[j]), maxCreaseValue);
|
|
}
|
|
|
|
outDesc.numCorners = ncorners;
|
|
outDesc.cornerVertexIndices = verts;
|
|
outDesc.cornerWeights = weights;
|
|
}
|
|
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
|
|
static 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;
|
|
}
|
|
|
|
|
|
//! Caller is expected to delete the returned value
|
|
static OpenSubdiv::Far::TopologyRefiner *
|
|
gatherTopology( MFnMesh const & inMeshFn,
|
|
MItMeshPolygon & inMeshItPolygon,
|
|
OpenSubdiv::Sdc::SchemeType type,
|
|
OpenSubdiv::Sdc::Options options,
|
|
float * maxCreaseSharpness=0 ) {
|
|
|
|
MStatus returnStatus;
|
|
|
|
// 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;
|
|
|
|
// 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());
|
|
|
|
Descriptor desc;
|
|
|
|
desc.numVertices = inMeshFn.numVertices();
|
|
desc.numFaces = inMeshItPolygon.count();
|
|
|
|
int * vertsPerFace = new int[desc.numFaces],
|
|
* vertIndices = new int[inMeshFn.numFaceVertices()];
|
|
|
|
desc.numVertsPerFace = vertsPerFace;
|
|
desc.vertIndicesPerFace = vertIndices;
|
|
|
|
// Create Topology
|
|
|
|
for( inMeshItPolygon.reset(); !inMeshItPolygon.isDone(); inMeshItPolygon.next() ) {
|
|
|
|
inMeshItPolygon.getVertices(fvArray);
|
|
int nverts = fvArray.length();
|
|
|
|
vertsPerFace[inMeshItPolygon.index()] = nverts;
|
|
|
|
for (int i=0; i<nverts; ++i) {
|
|
*vertIndices++ = fvArray[i];
|
|
}
|
|
|
|
// Add FaceVaryingData (UVSets, ...)
|
|
if (totalFvarWidth > 0) {
|
|
// XXXX
|
|
// Retrieve all UV and ColorSet topology
|
|
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]);
|
|
}
|
|
|
|
// Handle uvSets
|
|
for (unsigned int fvid=0; fvid < fvArray.length(); ++fvid) {
|
|
}
|
|
// Handle colorSets
|
|
for( unsigned int colorSetIt=0; colorSetIt < colorSetNames.length(); ++colorSetIt ) {
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply Creases
|
|
float maxEdgeCrease = getCreaseEdges( inMeshFn, desc );
|
|
|
|
float maxVertexCrease = getCreaseVertices( inMeshFn, desc );
|
|
|
|
OpenSubdiv::Far::TopologyRefiner * refiner =
|
|
OpenSubdiv::Far::TopologyRefinerFactory<Descriptor>::Create(desc,
|
|
OpenSubdiv::Far::TopologyRefinerFactory<Descriptor>::Options(type, options));
|
|
|
|
delete [] desc.numVertsPerFace;
|
|
delete [] desc.vertIndicesPerFace;
|
|
|
|
delete [] desc.creaseVertexIndexPairs;
|
|
delete [] desc.creaseWeights;
|
|
delete [] desc.cornerVertexIndices;
|
|
delete [] desc.cornerWeights;
|
|
|
|
if (maxCreaseSharpness) {
|
|
*maxCreaseSharpness = std::max(maxEdgeCrease, maxVertexCrease);
|
|
}
|
|
|
|
return refiner;
|
|
}
|
|
|
|
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( MFnMesh const & inMeshFn,
|
|
MFnMeshData const & inMeshDat, MFnMeshData &newMeshDat, int level, int numSubfaces ) {
|
|
|
|
MStatus status;
|
|
|
|
MIntArray newCompElems;
|
|
|
|
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, &status);
|
|
MCHECKERR(status, "cannot get objectGroup() comp ID.");
|
|
|
|
MFn::Type compType = inMeshDat.objectGroupType(compId, &status);
|
|
MCHECKERR(status, "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), &status );
|
|
|
|
MCHECKERR(status, "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;
|
|
}
|
|
|
|
//
|
|
// Vertex container implementation.
|
|
//
|
|
struct Vertex {
|
|
|
|
Vertex() { }
|
|
|
|
Vertex(Vertex const & src) {
|
|
position[0] = src.position[0];
|
|
position[1] = src.position[1];
|
|
position[2] = src.position[2];
|
|
}
|
|
|
|
void Clear( void * =0 ) {
|
|
position[0]=position[1]=position[2]=0.0f;
|
|
}
|
|
|
|
void AddWithWeight(Vertex const & src, float weight) {
|
|
position[0]+=weight*src.position[0];
|
|
position[1]+=weight*src.position[1];
|
|
position[2]+=weight*src.position[2];
|
|
}
|
|
|
|
void AddVaryingWithWeight(Vertex const &, float) { }
|
|
|
|
float position[3];
|
|
};
|
|
|
|
static MStatus
|
|
convertToMayaMeshData(OpenSubdiv::Far::TopologyRefiner const & refiner,
|
|
std::vector<Vertex> const & vertexBuffer, MFnMesh const & inMeshFn,
|
|
MObject newMeshDataObj) {
|
|
|
|
MStatus status;
|
|
|
|
typedef OpenSubdiv::Far::ConstIndexArray IndexArray;
|
|
|
|
int maxlevel = refiner.GetMaxLevel(),
|
|
nfaces = refiner.GetNumFaces(maxlevel);
|
|
|
|
// Init Maya Data
|
|
|
|
// -- Face Counts
|
|
MIntArray faceCounts(nfaces);
|
|
for (int face=0; face < nfaces; ++face) {
|
|
faceCounts[face] = 4;
|
|
}
|
|
|
|
// -- Face Connects
|
|
MIntArray faceConnects(nfaces*4);
|
|
for (int face=0, idx=0; face < nfaces; ++face) {
|
|
IndexArray fverts = refiner.GetFaceVertices(maxlevel, face);
|
|
for (int vert=0; vert < fverts.size(); ++vert) {
|
|
faceConnects[idx++] = fverts[vert];
|
|
}
|
|
}
|
|
|
|
// -- Points
|
|
MFloatPointArray points(refiner.GetNumVertices(maxlevel));
|
|
Vertex const * v = &vertexBuffer.at(0);
|
|
|
|
for (int level=1; level<=maxlevel; ++level) {
|
|
int nverts = refiner.GetNumVertices(level);
|
|
if (level==maxlevel) {
|
|
for (int vert=0; vert < nverts; ++vert, ++v) {
|
|
points.set(vert, v->position[0], v->position[1], v->position[2]);
|
|
}
|
|
} else {
|
|
v += nverts;
|
|
}
|
|
}
|
|
|
|
// Create New Mesh from MFnMesh
|
|
MFnMesh newMeshFn;
|
|
MObject newMeshObj = newMeshFn.create(points.length(), faceCounts.length(),
|
|
points, faceCounts, faceConnects, newMeshDataObj, &status);
|
|
MCHECKERR(status, "Cannot create new mesh");
|
|
|
|
int fvarTotalWidth = 0;
|
|
|
|
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;
|
|
status = getMayaFvarFieldParams(inMeshFn, uvSetNames, colorSetNames,
|
|
colorSetChannels, colorSetReps, totalColorSetChannels);
|
|
|
|
#if defined(DEBUG) or defined(_DEBUG)
|
|
int numUVSets = uvSetNames.length();
|
|
int expectedFvarTotalWidth = numUVSets*2 + totalColorSetChannels;
|
|
assert(fvarTotalWidth == expectedFvarTotalWidth);
|
|
#endif
|
|
|
|
// XXXX fvar stuff here
|
|
|
|
}
|
|
|
|
return MS::kSuccess;
|
|
}
|
|
|
|
MStatus
|
|
MayaPolySmooth::compute( const MPlug& plug, MDataBlock& data ) {
|
|
|
|
MStatus status;
|
|
|
|
// 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();
|
|
|
|
// == Get Mesh Functions and Iterators ==========================
|
|
MFnMeshData inMeshDat(inMeshObj);
|
|
MFnMesh inMeshFn(inMeshObj, &status);
|
|
MCHECKERR(status, "ERROR getting inMeshFn\n");
|
|
MItMeshPolygon inMeshItPolygon(inMeshObj, &status);
|
|
MCHECKERR(status, "ERROR getting inMeshItPolygon\n");
|
|
|
|
// Convert attr values to OSD enums
|
|
OpenSubdiv::Sdc::SchemeType type = OpenSubdiv::Sdc::SCHEME_CATMARK;
|
|
|
|
//
|
|
// Create Far topology
|
|
//
|
|
OpenSubdiv::Sdc::Options options;
|
|
options.SetVtxBoundaryInterpolation(ConvertMayaVtxBoundary(vertBoundaryMethod));
|
|
options.SetFVarLinearInterpolation(ConvertMayaFVarBoundary(fvarBoundaryMethod, fvarPropCorners));
|
|
options.SetCreasingMethod(creaseMethodVal ?
|
|
OpenSubdiv::Sdc::Options::CREASE_CHAIKIN : OpenSubdiv::Sdc::Options::CREASE_UNIFORM);
|
|
options.SetTriangleSubdivision(smoothTriangles ?
|
|
OpenSubdiv::Sdc::Options::TRI_SUB_SMOOTH : OpenSubdiv::Sdc::Options::TRI_SUB_CATMARK);
|
|
|
|
float maxCreaseSharpness=0.0f;
|
|
OpenSubdiv::Far::TopologyRefiner * refiner =
|
|
gatherTopology(inMeshFn, inMeshItPolygon, type, options, &maxCreaseSharpness);
|
|
|
|
assert(refiner);
|
|
|
|
// Refine & Interpolate
|
|
refiner->RefineUniform(OpenSubdiv::Far::TopologyRefiner::UniformOptions(subdivisionLevel));
|
|
|
|
Vertex const * controlVerts =
|
|
reinterpret_cast<Vertex const *>(inMeshFn.getRawPoints(&status));
|
|
|
|
std::vector<Vertex> refinedVerts(
|
|
refiner->GetNumVerticesTotal() - refiner->GetNumVertices(0));
|
|
|
|
refiner->Interpolate(controlVerts, &refinedVerts.at(0));
|
|
|
|
// == Convert subdivided OpenSubdiv mesh to MFnMesh Data outputMesh =============
|
|
|
|
// Create New Mesh Data Object
|
|
MFnMeshData newMeshData;
|
|
MObject newMeshDataObj = newMeshData.create(&status);
|
|
MCHECKERR(status, "ERROR creating outputData");
|
|
|
|
// Create out mesh
|
|
status = convertToMayaMeshData(*refiner, refinedVerts, inMeshFn, newMeshDataObj);
|
|
MCHECKERR(status, "ERROR convertOsdFarToMayaMesh");
|
|
|
|
// Propagate objectGroups from inMesh to outMesh (for per-facet shading, etc)
|
|
status = createSmoothMesh_objectGroups(inMeshFn, inMeshDat,
|
|
newMeshData, subdivisionLevel, refiner->GetNumFaces(subdivisionLevel));
|
|
|
|
// Write to output plug
|
|
MDataHandle outMeshH = data.outputValue(a_output, &status);
|
|
MCHECKERR(status, "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 refiner;
|
|
|
|
// 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, &status);
|
|
status = outMeshH.copy(data.outputValue(a_inputPolymesh, &status));
|
|
MCHECKERR(status, "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* MayaPolySmooth::creator() {
|
|
|
|
return new MayaPolySmooth;
|
|
}
|
|
|
|
// 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
|
|
MayaPolySmooth::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 MayaPolySmooth::inputPolymesh" );
|
|
stat = tAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::inputPolymesh.setReadable()" );
|
|
stat = tAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::inputPolymesh.setWritable()" );
|
|
stat = tAttr.setHidden(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::inputPolymesh.setHidden()" );
|
|
stat = addAttribute( a_inputPolymesh );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::output" );
|
|
stat = tAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::output.setReadable()" );
|
|
stat = tAttr.setWritable(false);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::output.setWritable()" );
|
|
stat = tAttr.setHidden(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::output.setHidden()" );
|
|
stat = addAttribute( a_output );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::subdivisionLevels" );
|
|
stat = nAttr.setDefault(2);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::subdivisionLevels.setDefault(2)" );
|
|
stat = nAttr.setMin(0);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::subdivisionLevels.setMin(0)" );
|
|
stat = nAttr.setMax(10);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::subdivisionLevels.setMax(10)" );
|
|
stat = nAttr.setSoftMax(4);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::subdivisionLevels.setSoftMax(4)" );
|
|
stat = nAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::subdivisionLevels.setReadable()" );
|
|
stat = nAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::subdivisionLevels.setWritable()" );
|
|
stat = addAttribute( a_subdivisionLevels );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::recommendedIsolation" );
|
|
stat = nAttr.setDefault(2);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::recommendedIsolation.setDefault(0)" );
|
|
stat = nAttr.setMin(0);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::recommendedIsolation.setMin(0)" );
|
|
stat = nAttr.setMax(10);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::recommendedIsolation.setSoftMax(10)" );
|
|
stat = nAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::recommendedIsolation.setReadable()" );
|
|
stat = nAttr.setWritable(false);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::recommendedIsolation.setWritable()" );
|
|
stat = nAttr.setHidden(false);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::recommendedIsolation.setHidden()" );
|
|
stat = addAttribute( a_recommendedIsolation );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::vertBoundaryMethod" );
|
|
stat = eAttr.addField("Interpolate Edges", k_BoundaryMethod_InterpolateBoundaryEdgeOnly);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::vertBoundaryMethod.addField(Interpolate Edges, k_BoundaryMethod_InterpolateBoundaryEdgeOnly)" );
|
|
stat = eAttr.addField("Interpolate Edges And Corners", k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::vertBoundaryMethod.addField(Interpolate Edges And Corners, k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner)" );
|
|
stat = eAttr.setDefault(k_BoundaryMethod_InterpolateBoundaryEdgeOnly);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::vertBoundaryMethod.setDefault(k_BoundaryMethod_InterpolateBoundaryEdgeOnly)" );
|
|
stat = eAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::vertBoundaryMethod.setReadable()" );
|
|
stat = eAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::vertBoundaryMethod.setWritable()" );
|
|
stat = addAttribute( a_vertBoundaryMethod );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::fvarBoundaryMethod" );
|
|
stat = eAttr.addField("Bi-linear (None)", k_BoundaryMethod_InterpolateBoundaryNone);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.addField(Bi-linear (None), k_BoundaryMethod_InterpolateBoundaryNone)" );
|
|
stat = eAttr.addField("Smooth (Edge Only)", k_BoundaryMethod_InterpolateBoundaryEdgeOnly);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.addField(Smooth (Edge Only), k_BoundaryMethod_InterpolateBoundaryEdgeOnly)" );
|
|
stat = eAttr.addField("Smooth (Edge and Corner)", k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.addField(Smooth (Edge and Corner), k_BoundaryMethod_InterpolateBoundaryEdgeAndCorner)" );
|
|
stat = eAttr.addField("Smooth (Always Sharp)", k_BoundaryMethod_InterpolateBoundaryAlwaysSharp);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.addField(Smooth (Always Sharp), k_BoundaryMethod_InterpolateBoundaryAlwaysSharp)" );
|
|
stat = eAttr.setDefault(k_BoundaryMethod_InterpolateBoundaryNone);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.setDefault(k_BoundaryMethod_InterpolateBoundaryNone)" );
|
|
stat = eAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.setReadable()" );
|
|
stat = eAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarBoundaryMethod.setWritable()" );
|
|
stat = addAttribute( a_fvarBoundaryMethod );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::addAttribute(a_fvarBoundaryMethod)" );
|
|
|
|
// a_fvarPropagateCorners :
|
|
a_fvarPropagateCorners = nAttr.create("fvarPropagateCorners", "fvpc", MFnNumericData::kBoolean, 0.0, &stat);
|
|
MCHECKERR( stat, "cannot create MayaPolySmooth::fvarPropagateCorners" );
|
|
stat = nAttr.setDefault(false);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarPropagateCorners.setDefault(false)" );
|
|
stat = nAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarPropagateCorners.setReadable()" );
|
|
stat = nAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::fvarPropagateCorners.setWritable()" );
|
|
stat = addAttribute( a_fvarPropagateCorners );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::smoothTriangles" );
|
|
stat = nAttr.setDefault(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::smoothTriangles.setDefault(true)" );
|
|
stat = nAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::smoothTriangles.setReadable()" );
|
|
stat = nAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::smoothTriangles.setWritable()" );
|
|
stat = addAttribute( a_smoothTriangles );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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 MayaPolySmooth::creaseMethod" );
|
|
stat = eAttr.addField("Normal", k_creaseMethod_normal);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::creaseMethod.addField(Normal, k_creaseMethod_normal)" );
|
|
stat = eAttr.addField("Chaikin", k_creaseMethod_chaikin);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::creaseMethod.addField(Chaikin, k_creaseMethod_chaikin)" );
|
|
stat = eAttr.setDefault(0);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::creaseMethod.setDefault(0)" );
|
|
stat = eAttr.setReadable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::creaseMethod.setReadable()" );
|
|
stat = eAttr.setWritable(true);
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::creaseMethod.setWritable()" );
|
|
stat = addAttribute( a_creaseMethod );
|
|
MCHECKERR( stat, "cannot MayaPolySmooth::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, "MayaPolySmooth", "1.0", "Any");
|
|
|
|
status = plugin.registerNode(
|
|
MayaPolySmooth::typeNameStr,
|
|
MayaPolySmooth::id,
|
|
MayaPolySmooth::creator,
|
|
MayaPolySmooth::initialize);
|
|
MCHECKERR(status, "registerNode");
|
|
|
|
// Source UI scripts
|
|
MString path = plugin.loadPath()+"/mayaPolySmooth.mel";
|
|
if (not MGlobal::sourceFile(path)) {
|
|
path = "mayaPolySmooth.mel";
|
|
if (not MGlobal::sourceFile(path)) {
|
|
MGlobal::displayWarning("Failed to source mayaPolySmooth.mel.");
|
|
}
|
|
}
|
|
|
|
// RegisterUI
|
|
status = plugin.registerUI("mayaPolySmooth_addUI()", "mayaPolySmooth_removeUI()");
|
|
MCHECKERR(status, "registerUI");
|
|
|
|
return status;
|
|
}
|
|
|
|
MStatus uninitializePlugin( MObject obj)
|
|
{
|
|
MStatus returnStatus = MS::kSuccess;
|
|
MFnPlugin plugin( obj );
|
|
|
|
returnStatus = plugin.deregisterNode( MayaPolySmooth::id );
|
|
MCHECKERR(returnStatus, "deregisterNode");
|
|
|
|
return returnStatus;
|
|
}
|