mirror of
https://github.com/PixarAnimationStudios/OpenSubdiv
synced 2024-12-13 20:40:09 +00:00
99c34d2aec
- switch FVarDataTable to a class - add offsets to keep track of level - modify dependent code - rename 'totalFVarWidth' to 'fvarWidth' fixes #271
1182 lines
50 KiB
C++
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;
|
|
}
|