Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
alembic/maya/AbcExport/MayaMeshWriter.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1102 lines (950 sloc)
33.3 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //-***************************************************************************** | |
| // | |
| // Copyright (c) 2009-2014, | |
| // Sony Pictures Imageworks Inc. and | |
| // Industrial Light & Magic, a division of Lucasfilm Entertainment Company Ltd. | |
| // | |
| // All rights reserved. | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are | |
| // met: | |
| // * Redistributions of source code must retain the above copyright | |
| // notice, this list of conditions and the following disclaimer. | |
| // * Redistributions in binary form must reproduce the above | |
| // copyright notice, this list of conditions and the following disclaimer | |
| // in the documentation and/or other materials provided with the | |
| // distribution. | |
| // * Neither the name of Sony Pictures Imageworks, nor | |
| // Industrial Light & Magic, nor the names of their contributors may be used | |
| // to endorse or promote products derived from this software without specific | |
| // prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| // | |
| //-***************************************************************************** | |
| #include "MayaMeshWriter.h" | |
| #include "MayaUtility.h" | |
| #include <maya/MItSelectionList.h> | |
| #include <maya/MFnSingleIndexedComponent.h> | |
| namespace { | |
| void getColorSet(MFnMesh & iMesh, const MString * iColorSet, bool isRGBA, | |
| std::vector<float> & oColors, | |
| std::vector< Alembic::Util::uint32_t > & oColorIndices) | |
| { | |
| MColorArray colorArray; | |
| iMesh.getColors(colorArray, iColorSet); | |
| bool addDefaultColor = true; | |
| int numFaces = iMesh.numPolygons(); | |
| for (int faceIndex = 0; faceIndex < numFaces; faceIndex++) | |
| { | |
| MIntArray vertexList; | |
| iMesh.getPolygonVertices(faceIndex, vertexList); | |
| int numVertices = iMesh.polygonVertexCount(faceIndex); | |
| for ( int v = numVertices-1; v >=0; v-- ) | |
| { | |
| int colorIndex = 0; | |
| iMesh.getColorIndex(faceIndex, v, colorIndex, iColorSet); | |
| if (colorIndex == -1) | |
| { | |
| if (addDefaultColor) | |
| { | |
| addDefaultColor = false; | |
| colorArray.append(MColor(1.0, 1.0, 1.0, 1.0)); | |
| } | |
| colorIndex = colorArray.length() - 1; | |
| } | |
| oColorIndices.push_back(colorIndex); | |
| } | |
| } | |
| int colorLen = colorArray.length(); | |
| for (int i = 0; i < colorLen; ++i) | |
| { | |
| MColor color = colorArray[i]; | |
| oColors.push_back(color.r); | |
| oColors.push_back(color.g); | |
| oColors.push_back(color.b); | |
| if (isRGBA) | |
| { | |
| oColors.push_back(color.a); | |
| } | |
| } | |
| }; | |
| void getUVSet(const MFnMesh & iMesh, const MString & iUVSetName, | |
| std::vector<float> & oUVs, std::vector<Alembic::Util::uint32_t> & oIndices) | |
| { | |
| MFloatArray uArray, vArray; | |
| iMesh.getUVs(uArray, vArray, &iUVSetName); | |
| if ( uArray.length() != vArray.length() ) | |
| { | |
| MString msg = "UV Set " + iUVSetName + | |
| " uArray and vArray not the same length"; | |
| MGlobal::displayError(msg); | |
| return; | |
| } | |
| unsigned int arLen = uArray.length(); | |
| oUVs.clear(); | |
| oUVs.reserve(arLen * 2); | |
| for (unsigned int i = 0; i < arLen; ++i) | |
| { | |
| oUVs.push_back(uArray[i]); | |
| oUVs.push_back(vArray[i]); | |
| } | |
| oIndices.clear(); | |
| oIndices.reserve(iMesh.numFaceVertices()); | |
| int faceCount = iMesh.numPolygons(); | |
| int uvId = 0; | |
| for (int j = 0; j < faceCount; ++j) | |
| { | |
| int vc = iMesh.polygonVertexCount(j); | |
| for (int i = vc - 1; i >= 0; i--) | |
| { | |
| iMesh.getPolygonUVid(j, i, uvId, &iUVSetName); | |
| oIndices.push_back(uvId); | |
| } | |
| } | |
| } | |
| } | |
| void MayaMeshWriter::getUVs(std::vector<float> & uvs, | |
| std::vector<Alembic::Util::uint32_t> & indices, | |
| std::string & name) | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| MString uvSetName = lMesh.currentUVSetName(&status); | |
| if (uvSetName.length() != 0) | |
| { | |
| MFloatArray uArray, vArray; | |
| status = lMesh.getUVs(uArray, vArray, &uvSetName); | |
| // convert the raw uv list into vector uvsvec.clear(); | |
| if ( uArray.length() != vArray.length() ) | |
| { | |
| MString msg = "uv Set" + uvSetName + | |
| "uArray and vArray not the same length"; | |
| MGlobal::displayError(msg); | |
| return; | |
| } | |
| if (uvSetName != "map1") | |
| { | |
| name = uvSetName.asChar(); | |
| } | |
| unsigned int len = uArray.length(); | |
| uvs.clear(); | |
| uvs.reserve(len * 2); | |
| for (unsigned int i = 0; i < len; i++) | |
| { | |
| uvs.push_back(uArray[i]); | |
| uvs.push_back(vArray[i]); | |
| } | |
| indices.clear(); | |
| indices.reserve(lMesh.numFaceVertices()); | |
| int faceCount = lMesh.numPolygons(); | |
| int uvId = 0; | |
| for (int f = 0; f < faceCount; f++) | |
| { | |
| int len = lMesh.polygonVertexCount(f); | |
| for (int i = len-1; i >= 0; i--) | |
| { | |
| lMesh.getPolygonUVid(f, i, uvId, &uvSetName); | |
| indices.push_back(uvId); | |
| } | |
| } | |
| } | |
| } | |
| MayaMeshWriter::MayaMeshWriter(MDagPath & iDag, | |
| Alembic::Abc::OObject & iParent, Alembic::Util::uint32_t iTimeIndex, | |
| const JobArgs & iArgs) | |
| : mNoNormals(iArgs.noNormals), | |
| mWriteGeometry(iArgs.writeGeometry), | |
| mWriteUVs(iArgs.writeUVs), | |
| mWriteColorSets(iArgs.writeColorSets), | |
| mWriteUVSets(iArgs.writeUVSets), | |
| mIsGeometryAnimated(false), | |
| mDagPath(iDag) | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| // intermediate objects aren't translated | |
| MObject surface = iDag.node(); | |
| if (iTimeIndex != 0 && util::isAnimated(surface)) | |
| { | |
| mIsGeometryAnimated = true; | |
| } | |
| else | |
| { | |
| iTimeIndex = 0; | |
| } | |
| std::vector<float> uvs; | |
| std::vector<Alembic::Util::uint32_t> indices; | |
| std::string uvSetName; | |
| MString name = lMesh.name(); | |
| name = util::stripNamespaces(name, iArgs.stripNamespace); | |
| // check to see if this poly has been tagged as a SubD | |
| MPlug plug = lMesh.findPlug("SubDivisionMesh", true); | |
| // if there is flag "autoSubd", and NO "SubDivisionMesh" was defined, | |
| // let's check whether the mesh has crease edge, crease vertex or holes | |
| // then the mesh will be treated as SubD | |
| bool hasToWriteSubd = false; | |
| if( plug.isNull() && iArgs.autoSubd ) | |
| { | |
| MUintArray edgeIds, vertexIds; | |
| MDoubleArray edgeCreaseData, vertexCreaseData; | |
| lMesh.getCreaseEdges(edgeIds, edgeCreaseData); | |
| lMesh.getCreaseVertices(vertexIds, vertexCreaseData); | |
| hasToWriteSubd = ( (edgeIds.length() > 0) || (vertexIds.length() > 0) ); | |
| #if MAYA_API_VERSION >= 201100 | |
| if (!hasToWriteSubd) | |
| { | |
| MUintArray invisibleFaceIds = lMesh.getInvisibleFaces(); | |
| hasToWriteSubd = ( hasToWriteSubd || (invisibleFaceIds.length() > 0) ); | |
| } | |
| #endif | |
| } | |
| Alembic::Abc::SparseFlag sf = Alembic::Abc::kFull; | |
| if ( !mWriteGeometry ) | |
| { | |
| sf = Alembic::Abc::kSparse; | |
| } | |
| if ( (!plug.isNull() && plug.asBool()) || hasToWriteSubd ) | |
| { | |
| Alembic::AbcGeom::OSubD obj(iParent, name.asChar(), sf, iTimeIndex); | |
| mSubDSchema = obj.getSchema(); | |
| Alembic::AbcGeom::OV2fGeomParam::Sample uvSamp; | |
| if (mWriteUVs || mWriteUVSets) | |
| { | |
| getUVs(uvs, indices, uvSetName); | |
| if (!uvs.empty()) | |
| { | |
| if (!uvSetName.empty()) | |
| { | |
| mSubDSchema.setUVSourceName(uvSetName); | |
| } | |
| uvSamp.setScope( Alembic::AbcGeom::kFacevaryingScope ); | |
| uvSamp.setVals(Alembic::AbcGeom::V2fArraySample( | |
| (const Imath::V2f *) &uvs.front(), uvs.size() / 2)); | |
| if (!indices.empty()) | |
| { | |
| uvSamp.setIndices(Alembic::Abc::UInt32ArraySample( | |
| &indices.front(), indices.size())); | |
| } | |
| } | |
| } | |
| Alembic::Abc::OCompoundProperty cp; | |
| Alembic::Abc::OCompoundProperty up; | |
| if (AttributesWriter::hasAnyAttr(lMesh, iArgs)) | |
| { | |
| cp = mSubDSchema.getArbGeomParams(); | |
| up = mSubDSchema.getUserProperties(); | |
| } | |
| mAttrs = AttributesWriterPtr(new AttributesWriter(cp, up, obj, lMesh, | |
| iTimeIndex, iArgs, true)); | |
| if (!mIsGeometryAnimated || iArgs.setFirstAnimShape) | |
| { | |
| writeSubD(uvSamp); | |
| } | |
| } | |
| else | |
| { | |
| Alembic::AbcGeom::OPolyMesh obj(iParent, name.asChar(), sf, iTimeIndex); | |
| mPolySchema = obj.getSchema(); | |
| Alembic::AbcGeom::OV2fGeomParam::Sample uvSamp; | |
| if (mWriteUVs || mWriteUVSets) | |
| { | |
| getUVs(uvs, indices, uvSetName); | |
| if (!uvs.empty()) | |
| { | |
| if (!uvSetName.empty()) | |
| { | |
| mPolySchema.setUVSourceName(uvSetName); | |
| } | |
| uvSamp.setScope( Alembic::AbcGeom::kFacevaryingScope ); | |
| uvSamp.setVals(Alembic::AbcGeom::V2fArraySample( | |
| (const Imath::V2f *) &uvs.front(), uvs.size() / 2)); | |
| if (!indices.empty()) | |
| { | |
| uvSamp.setIndices(Alembic::Abc::UInt32ArraySample( | |
| &indices.front(), indices.size())); | |
| } | |
| } | |
| } | |
| Alembic::Abc::OCompoundProperty cp; | |
| Alembic::Abc::OCompoundProperty up; | |
| if (AttributesWriter::hasAnyAttr(lMesh, iArgs)) | |
| { | |
| cp = mPolySchema.getArbGeomParams(); | |
| up = mPolySchema.getUserProperties(); | |
| } | |
| // set the rest of the props and write to the writer node | |
| mAttrs = AttributesWriterPtr(new AttributesWriter(cp, up, obj, lMesh, | |
| iTimeIndex, iArgs, true)); | |
| if (!mIsGeometryAnimated || iArgs.setFirstAnimShape) | |
| { | |
| writePoly(uvSamp); | |
| } | |
| } | |
| if (mWriteColorSets) | |
| { | |
| MStringArray colorSetNames; | |
| lMesh.getColorSetNames(colorSetNames); | |
| if (colorSetNames.length() > 0) | |
| { | |
| // Create the color sets compound prop | |
| Alembic::Abc::OCompoundProperty arbParams; | |
| if (mPolySchema.valid()) | |
| { | |
| arbParams = mPolySchema.getArbGeomParams(); | |
| } | |
| else | |
| { | |
| arbParams = mSubDSchema.getArbGeomParams(); | |
| } | |
| std::string currentColorSet = lMesh.currentColorSetName().asChar(); | |
| for (unsigned int i=0; i < colorSetNames.length(); ++i) | |
| { | |
| // Create an array property for each color set | |
| std::string colorSetPropName = colorSetNames[i].asChar(); | |
| Alembic::AbcCoreAbstract::MetaData md; | |
| if (currentColorSet == colorSetPropName) | |
| { | |
| md.set("mayaColorSet", "1"); | |
| } | |
| else | |
| { | |
| md.set("mayaColorSet", "0"); | |
| } | |
| if (lMesh.getColorRepresentation(colorSetNames[i]) == | |
| MFnMesh::kRGB) | |
| { | |
| Alembic::AbcGeom::OC3fGeomParam colorProp(arbParams, | |
| colorSetPropName, true, | |
| Alembic::AbcGeom::kFacevaryingScope, 1, iTimeIndex, md); | |
| mRGBParams.push_back(colorProp); | |
| } | |
| else | |
| { | |
| Alembic::AbcGeom::OC4fGeomParam colorProp(arbParams, | |
| colorSetPropName, true, | |
| Alembic::AbcGeom::kFacevaryingScope, 1, iTimeIndex, md); | |
| mRGBAParams.push_back(colorProp); | |
| } | |
| } | |
| if (!mIsGeometryAnimated || iArgs.setFirstAnimShape) | |
| { | |
| writeColor(); | |
| } | |
| } | |
| } | |
| if (mWriteUVSets) | |
| { | |
| MStringArray uvSetNames; | |
| lMesh.getUVSetNames(uvSetNames); | |
| unsigned int uvSetNamesLen = uvSetNames.length(); | |
| if (uvSetNamesLen > 1) | |
| { | |
| // Create the uv sets compound prop | |
| Alembic::Abc::OCompoundProperty arbParams; | |
| if (mPolySchema.valid()) | |
| { | |
| arbParams = mPolySchema.getArbGeomParams(); | |
| } | |
| else | |
| { | |
| arbParams = mSubDSchema.getArbGeomParams(); | |
| } | |
| MString currentUV = lMesh.currentUVSetName(); | |
| for (unsigned int i = 0; i < uvSetNamesLen; ++i) | |
| { | |
| // Create an array property for each uv set | |
| MString uvSetPropName = uvSetNames[i]; | |
| // the current UV set gets mapped to the primary UVs | |
| if (currentUV == uvSetPropName) | |
| { | |
| continue; | |
| } | |
| if (uvSetPropName.length() > 0 && | |
| lMesh.numUVs(uvSetPropName) > 0) | |
| { | |
| mUVparams.push_back(Alembic::AbcGeom::OV2fGeomParam( | |
| arbParams, uvSetPropName.asChar(), true, | |
| Alembic::AbcGeom::kFacevaryingScope, 1, iTimeIndex)); | |
| } | |
| } | |
| if (!mIsGeometryAnimated || iArgs.setFirstAnimShape) | |
| { | |
| writeUVSets(); | |
| } | |
| } | |
| } | |
| // write out facesets | |
| if(!iArgs.writeFaceSets) | |
| return; | |
| MFnDependencyNode meshDep(mDagPath.node()); | |
| MPlug iogPlug( meshDep.findPlug("instObjGroups", false, &status) ); | |
| if( status == MS::kFailure ) | |
| { | |
| return; | |
| } | |
| // we can have multiple connects to the same shading engine | |
| std::map< std::string, | |
| std::pair< MObject, std::vector< MPlug > > > facesetMap; | |
| // mesh.instObjGroups is an array of compounds | |
| for (unsigned int i = 0; i < iogPlug.numElements(); ++i) | |
| { | |
| MPlug compPlug = iogPlug.elementByPhysicalIndex(i); | |
| for (unsigned int j = 0; j < compPlug.numChildren(); ++j) | |
| { | |
| // no elements in the child could mean the whole mesh is shaded | |
| MPlug objGroupPlug = compPlug.child(j); | |
| for (unsigned int k = 0; k < objGroupPlug.numElements(); ++k) | |
| { | |
| // mesh.instObjGroups[i].objectGroups[k] | |
| MPlug objGrpCompPlug = objGroupPlug.elementByPhysicalIndex(k); | |
| if (objGrpCompPlug.isNull()) | |
| { | |
| continue; | |
| } | |
| MPlugArray dests; | |
| if (!objGrpCompPlug.destinations(dests)) | |
| { | |
| continue; | |
| } | |
| MObject shadingObj; | |
| // look for a shading engine, can we have more than 1? | |
| for (unsigned int p = 0; p < dests.length(); ++p) | |
| { | |
| if (dests[p].isNull()) | |
| { | |
| continue; | |
| } | |
| MObject testObj = dests[p].node(); | |
| if (!testObj.hasFn(MFn::kShadingEngine)) | |
| { | |
| continue; | |
| } | |
| else | |
| { | |
| shadingObj = testObj; | |
| } | |
| } | |
| if (shadingObj.isNull()) | |
| { | |
| continue; | |
| } | |
| // mesh.instObjGroups[i].objectGroups[k].objectGrpCompList | |
| for (unsigned int l = 0; l < objGrpCompPlug.numChildren(); ++l) | |
| { | |
| MPlug childPlug = objGrpCompPlug.child(l); | |
| MString plugName = childPlug.name(); | |
| plugName = plugName.substringW(plugName.length() - 17, plugName.length() - 1); | |
| if (plugName == "objectGrpCompList") | |
| { | |
| MFnDependencyNode fnDepNode(shadingObj); | |
| std::string faceSetName = fnDepNode.name().asChar(); | |
| std::map< std::string, std::pair< MObject, | |
| std::vector< MPlug > > >::iterator it = | |
| facesetMap.find(faceSetName); | |
| if (it == facesetMap.end()) | |
| { | |
| std::pair< MObject, std::vector< MPlug > > opPair; | |
| opPair.first = shadingObj; | |
| opPair.second.push_back(childPlug); | |
| facesetMap[faceSetName] = opPair; | |
| } | |
| else | |
| { | |
| it->second.second.push_back(childPlug); | |
| } | |
| // we can move on to the next | |
| // mesh.instObjGroups[i].objectGroups[k] | |
| break; | |
| } | |
| } // for l | |
| } // for k | |
| } // for j | |
| } // for i | |
| Alembic::Abc::OObject parentObj; | |
| if (mPolySchema.valid()) | |
| { | |
| parentObj = mPolySchema.getObject(); | |
| } | |
| else | |
| { | |
| parentObj = mSubDSchema.getObject(); | |
| } | |
| std::map< std::string, | |
| std::pair< MObject, std::vector< MPlug > > >::iterator it; | |
| for (it = facesetMap.begin(); it != facesetMap.end(); ++it) | |
| { | |
| MayaFaceSetWriterPtr facePtr(new MayaFaceSetWriter( | |
| it->second.first, it->second.second, parentObj, | |
| iTimeIndex, iArgs)); | |
| mFaceSets.push_back(facePtr); | |
| } | |
| } | |
| bool MayaMeshWriter::isSubD() | |
| { | |
| return mSubDSchema.valid(); | |
| } | |
| unsigned int MayaMeshWriter::getNumCVs() | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| return lMesh.numVertices(); | |
| } | |
| unsigned int MayaMeshWriter::getNumFaces() | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| return lMesh.numPolygons(); | |
| } | |
| void MayaMeshWriter::getPolyNormals(std::vector<float> & oNormals) | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| // no normals bail early | |
| if (mNoNormals) | |
| { | |
| return; | |
| } | |
| MPlug plug = lMesh.findPlug("noNormals", true, &status); | |
| if (status == MS::kSuccess && plug.asBool() == true) | |
| { | |
| return; | |
| } | |
| // we need to check the locked state of the normals | |
| else if ( status != MS::kSuccess ) | |
| { | |
| bool userSetNormals = false; | |
| // go through all per face-vertex normals and verify if any of them | |
| // has been tweaked by users | |
| unsigned int numFaces = lMesh.numPolygons(); | |
| for (unsigned int faceIndex = 0; faceIndex < numFaces; faceIndex++) | |
| { | |
| MIntArray normals; | |
| lMesh.getFaceNormalIds(faceIndex, normals); | |
| unsigned int numNormals = normals.length(); | |
| for (unsigned int n = 0; n < numNormals; n++) | |
| { | |
| if (lMesh.isNormalLocked(normals[n])) | |
| { | |
| userSetNormals = true; | |
| break; | |
| } | |
| } | |
| } | |
| // we looped over all the normals and they were all calculated by Maya | |
| // so we just need to check to see if any of the edges are hard | |
| // before we decide not to write the normals. | |
| if (!userSetNormals) | |
| { | |
| bool hasHardEdges = false; | |
| // go through all edges and verify if any of them is hard edge | |
| unsigned int numEdges = lMesh.numEdges(); | |
| for (unsigned int edgeIndex = 0; edgeIndex < numEdges; edgeIndex++) | |
| { | |
| if (!lMesh.isEdgeSmooth(edgeIndex)) | |
| { | |
| hasHardEdges = true; | |
| break; | |
| } | |
| } | |
| // all the edges were smooth, we don't need to write the normals | |
| if (!hasHardEdges) | |
| { | |
| return; | |
| } | |
| } | |
| } | |
| bool flipNormals = false; | |
| plug = lMesh.findPlug("flipNormals", true, &status); | |
| if ( status == MS::kSuccess ) | |
| flipNormals = plug.asBool(); | |
| // get the per vertex per face normals (aka vertex) | |
| unsigned int numFaces = lMesh.numPolygons(); | |
| for (unsigned int faceIndex = 0; faceIndex < numFaces; faceIndex++ ) | |
| { | |
| MIntArray vertexList; | |
| lMesh.getPolygonVertices(faceIndex, vertexList); | |
| // re-pack the order of normals in this vector before writing into prop | |
| // so that Renderman can also use it | |
| unsigned int numVertices = vertexList.length(); | |
| for ( int v = numVertices-1; v >=0; v-- ) | |
| { | |
| unsigned int vertexIndex = vertexList[v]; | |
| MVector normal; | |
| lMesh.getFaceVertexNormal(faceIndex, vertexIndex, normal); | |
| if (flipNormals) | |
| normal = -normal; | |
| oNormals.push_back(static_cast<float>(normal[0])); | |
| oNormals.push_back(static_cast<float>(normal[1])); | |
| oNormals.push_back(static_cast<float>(normal[2])); | |
| } | |
| } | |
| } | |
| void MayaMeshWriter::writeUVSets() | |
| { | |
| MStatus status = MS::kSuccess; | |
| const MFnMesh lMesh(mDagPath, &status); | |
| if (!status) | |
| { | |
| MGlobal::displayError( | |
| "MFnMesh() failed for MayaMeshWriter::writeUV" ); | |
| return; | |
| } | |
| //Write uvs | |
| const UVParamsVec::const_iterator uvItEnd = mUVparams.end(); | |
| for (UVParamsVec::iterator uvIt = mUVparams.begin(); | |
| uvIt != uvItEnd; ++uvIt) | |
| { | |
| std::vector<float> uvs; | |
| std::vector<Alembic::Util::uint32_t> indices; | |
| MString uvSetName(uvIt->getName().c_str()); | |
| getUVSet(lMesh, uvSetName, uvs, indices); | |
| //cast the vector to the sample type | |
| Alembic::AbcGeom::OV2fGeomParam::Sample sample( | |
| Alembic::Abc::V2fArraySample( | |
| (const Imath::V2f *) &uvs.front(), uvs.size() / 2), | |
| Alembic::Abc::UInt32ArraySample(indices), | |
| Alembic::AbcGeom::kFacevaryingScope); | |
| uvIt->set(sample); | |
| } | |
| } | |
| void MayaMeshWriter::writeColor() | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( | |
| "MFnMesh() failed for MayaMeshWriter::writeColor" ); | |
| return; | |
| } | |
| //Write colors | |
| std::vector<Alembic::AbcGeom::OC4fGeomParam>::iterator rgbaIt; | |
| std::vector<Alembic::AbcGeom::OC4fGeomParam>::iterator rgbaItEnd; | |
| rgbaIt = mRGBAParams.begin(); | |
| rgbaItEnd = mRGBAParams.end(); | |
| for (; rgbaIt != rgbaItEnd; ++rgbaIt) | |
| { | |
| std::vector<float> colors; | |
| std::vector< Alembic::Util::uint32_t > colorIndices; | |
| MString colorSetName(rgbaIt->getName().c_str()); | |
| getColorSet(lMesh, &colorSetName, true, colors, colorIndices); | |
| //cast the vector to the sample type | |
| Alembic::AbcGeom::OC4fGeomParam::Sample samp( | |
| Alembic::Abc::C4fArraySample( | |
| (const Imath::C4f *) &colors.front(), colors.size()/4), | |
| Alembic::Abc::UInt32ArraySample(colorIndices), | |
| Alembic::AbcGeom::kFacevaryingScope ); | |
| rgbaIt->set(samp); | |
| } | |
| std::vector<Alembic::AbcGeom::OC3fGeomParam>::iterator rgbIt; | |
| std::vector<Alembic::AbcGeom::OC3fGeomParam>::iterator rgbItEnd; | |
| rgbIt = mRGBParams.begin(); | |
| rgbItEnd = mRGBParams.end(); | |
| for (; rgbIt != rgbItEnd; ++rgbIt) | |
| { | |
| std::vector<float> colors; | |
| std::vector< Alembic::Util::uint32_t > colorIndices; | |
| MString colorSetName(rgbIt->getName().c_str()); | |
| getColorSet(lMesh, &colorSetName, false, colors, colorIndices); | |
| //cast the vector to the sample type | |
| Alembic::AbcGeom::OC3fGeomParam::Sample samp( | |
| Alembic::Abc::C3fArraySample( | |
| (const Imath::C3f *) &colors.front(), colors.size()/3), | |
| Alembic::Abc::UInt32ArraySample(colorIndices), | |
| Alembic::AbcGeom::kFacevaryingScope); | |
| rgbIt->set(samp); | |
| } | |
| } | |
| void MayaMeshWriter::write() | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| Alembic::AbcGeom::OV2fGeomParam::Sample uvSamp; | |
| std::vector<float> uvs; | |
| std::vector<Alembic::Util::uint32_t> indices; | |
| std::string uvSetName; | |
| if (mWriteUVs || mWriteUVSets) | |
| { | |
| getUVs(uvs, indices, uvSetName); | |
| if (!uvs.empty()) | |
| { | |
| if (!uvSetName.empty()) | |
| { | |
| if (mPolySchema.valid()) | |
| { | |
| mPolySchema.setUVSourceName(uvSetName); | |
| } | |
| else if (mSubDSchema.valid()) | |
| { | |
| mSubDSchema.setUVSourceName(uvSetName); | |
| } | |
| } | |
| uvSamp.setScope( Alembic::AbcGeom::kFacevaryingScope ); | |
| uvSamp.setVals(Alembic::AbcGeom::V2fArraySample( | |
| (const Imath::V2f *) &uvs.front(), uvs.size() / 2)); | |
| if (!indices.empty()) | |
| { | |
| uvSamp.setIndices(Alembic::Abc::UInt32ArraySample( | |
| &indices.front(), indices.size())); | |
| } | |
| } | |
| } | |
| std::vector<float> points; | |
| std::vector<Alembic::Util::int32_t> facePoints; | |
| std::vector<Alembic::Util::int32_t> faceList; | |
| if (mPolySchema.valid()) | |
| { | |
| writePoly(uvSamp); | |
| } | |
| else if (mSubDSchema.valid()) | |
| { | |
| writeSubD(uvSamp); | |
| } | |
| std::vector< MayaFaceSetWriterPtr >::iterator it; | |
| for (it = mFaceSets.begin(); it != mFaceSets.end(); ++it) | |
| { | |
| (*it)->write(); | |
| } | |
| } | |
| bool MayaMeshWriter::isAnimated() const | |
| { | |
| return mIsGeometryAnimated; | |
| } | |
| void MayaMeshWriter::writePoly( | |
| const Alembic::AbcGeom::OV2fGeomParam::Sample & iUVs) | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| std::vector<float> points; | |
| std::vector<Alembic::Util::int32_t> facePoints; | |
| std::vector<Alembic::Util::int32_t> pointCounts; | |
| if( mWriteGeometry ) | |
| { | |
| fillTopology(points, facePoints, pointCounts); | |
| } | |
| Alembic::AbcGeom::ON3fGeomParam::Sample normalsSamp; | |
| std::vector<float> normals; | |
| getPolyNormals(normals); | |
| if (!normals.empty()) | |
| { | |
| normalsSamp.setScope( Alembic::AbcGeom::kFacevaryingScope ); | |
| normalsSamp.setVals(Alembic::AbcGeom::N3fArraySample( | |
| (const Imath::V3f *) &normals.front(), normals.size() / 3)); | |
| } | |
| Alembic::AbcGeom::OPolyMeshSchema::Sample samp; | |
| if ( mWriteGeometry ) | |
| { | |
| samp.setPositions(Alembic::Abc::V3fArraySample( | |
| (const Imath::V3f *)&points.front(), points.size() / 3) ); | |
| samp.setFaceIndices(Alembic::Abc::Int32ArraySample(facePoints)); | |
| samp.setFaceCounts(Alembic::Abc::Int32ArraySample(pointCounts)); | |
| } | |
| samp.setUVs( iUVs ); | |
| samp.setNormals( normalsSamp ); | |
| mPolySchema.set(samp); | |
| writeColor(); | |
| writeUVSets(); | |
| } | |
| void MayaMeshWriter::writeSubD( | |
| const Alembic::AbcGeom::OV2fGeomParam::Sample & iUVs) | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| std::vector<float> points; | |
| std::vector<Alembic::Util::int32_t> facePoints; | |
| std::vector<Alembic::Util::int32_t> pointCounts; | |
| if( mWriteGeometry ) | |
| { | |
| fillTopology(points, facePoints, pointCounts); | |
| } | |
| Alembic::AbcGeom::OSubDSchema::Sample samp; | |
| if ( !mWriteGeometry ) | |
| { | |
| samp.setUVs( iUVs ); | |
| mSubDSchema.set(samp); | |
| writeColor(); | |
| writeUVSets(); | |
| return; | |
| } | |
| samp.setPositions(Alembic::AbcGeom::V3fArraySample( | |
| (const Imath::V3f *)&points.front(), points.size() / 3)); | |
| samp.setFaceIndices(Alembic::Abc::Int32ArraySample(facePoints)); | |
| samp.setFaceCounts(Alembic::Abc::Int32ArraySample(pointCounts)); | |
| MPlug plug = lMesh.findPlug("faceVaryingInterpolateBoundary", true); | |
| if (!plug.isNull()) | |
| samp.setFaceVaryingInterpolateBoundary(plug.asInt()); | |
| plug = lMesh.findPlug("interpolateBoundary", true); | |
| if (!plug.isNull()) | |
| samp.setInterpolateBoundary(plug.asInt()); | |
| plug = lMesh.findPlug("faceVaryingPropagateCorners", true); | |
| if (!plug.isNull()) | |
| samp.setFaceVaryingPropagateCorners(plug.asInt()); | |
| std::vector <Alembic::Util::int32_t> creaseIndices; | |
| std::vector <Alembic::Util::int32_t> creaseLengths; | |
| std::vector <float> creaseSharpness; | |
| std::vector <Alembic::Util::int32_t> cornerIndices; | |
| std::vector <float> cornerSharpness; | |
| MUintArray edgeIds; | |
| MDoubleArray creaseData; | |
| if (lMesh.getCreaseEdges(edgeIds, creaseData) == MS::kSuccess) | |
| { | |
| unsigned int numCreases = creaseData.length(); | |
| creaseIndices.resize(numCreases * 2); | |
| creaseLengths.resize(numCreases, 2); | |
| creaseSharpness.resize(numCreases); | |
| for (unsigned int i = 0; i < numCreases; ++i) | |
| { | |
| int verts[2]; | |
| lMesh.getEdgeVertices(edgeIds[i], verts); | |
| creaseIndices[2 * i] = verts[0]; | |
| creaseIndices[2 * i + 1] = verts[1]; | |
| creaseSharpness[i] = static_cast<float>(creaseData[i]); | |
| } | |
| samp.setCreaseIndices(Alembic::Abc::Int32ArraySample(creaseIndices)); | |
| samp.setCreaseLengths(Alembic::Abc::Int32ArraySample(creaseLengths)); | |
| samp.setCreaseSharpnesses( | |
| Alembic::Abc::FloatArraySample(creaseSharpness)); | |
| } | |
| MUintArray cornerIds; | |
| MDoubleArray cornerData; | |
| if (lMesh.getCreaseVertices(cornerIds, cornerData) == MS::kSuccess) | |
| { | |
| unsigned int numCorners = cornerIds.length(); | |
| cornerIndices.resize(numCorners); | |
| cornerSharpness.resize(numCorners); | |
| for (unsigned int i = 0; i < numCorners; ++i) | |
| { | |
| cornerIndices[i] = cornerIds[i]; | |
| cornerSharpness[i] = static_cast<float>(cornerData[i]); | |
| } | |
| samp.setCornerSharpnesses( | |
| Alembic::Abc::FloatArraySample(cornerSharpness)); | |
| samp.setCornerIndices( | |
| Alembic::Abc::Int32ArraySample(cornerIndices)); | |
| } | |
| #if MAYA_API_VERSION >= 201100 | |
| MUintArray holes = lMesh.getInvisibleFaces(); | |
| unsigned int numHoles = holes.length(); | |
| std::vector <Alembic::Util::int32_t> holeIndices(numHoles); | |
| for (unsigned int i = 0; i < numHoles; ++i) | |
| { | |
| holeIndices[i] = holes[i]; | |
| } | |
| if (!holeIndices.empty()) | |
| { | |
| samp.setHoles(holeIndices); | |
| } | |
| #endif | |
| samp.setUVs( iUVs ); | |
| mSubDSchema.set(samp); | |
| writeColor(); | |
| writeUVSets(); | |
| } | |
| // the arrays being passed in are assumed to be empty | |
| void MayaMeshWriter::fillTopology( | |
| std::vector<float> & oPoints, | |
| std::vector<Alembic::Util::int32_t> & oFacePoints, | |
| std::vector<Alembic::Util::int32_t> & oPointCounts) | |
| { | |
| MStatus status = MS::kSuccess; | |
| MFnMesh lMesh( mDagPath, &status ); | |
| if ( !status ) | |
| { | |
| MGlobal::displayError( "MFnMesh() failed for MayaMeshWriter" ); | |
| } | |
| MFloatPointArray pts; | |
| lMesh.getPoints(pts); | |
| if (pts.length() < 3 && pts.length() > 0) | |
| { | |
| MString err = lMesh.fullPathName() + | |
| " is not a valid mesh, because it only has "; | |
| err += pts.length(); | |
| err += " points."; | |
| MGlobal::displayError(err); | |
| return; | |
| } | |
| unsigned int numPolys = lMesh.numPolygons(); | |
| if (numPolys == 0) | |
| { | |
| MGlobal::displayWarning(lMesh.fullPathName() + " has no polygons."); | |
| return; | |
| } | |
| unsigned int i; | |
| int j; | |
| oPoints.resize(pts.length() * 3); | |
| // repack the float | |
| for (i = 0; i < pts.length(); i++) | |
| { | |
| size_t local = i * 3; | |
| oPoints[local] = pts[i].x; | |
| oPoints[local+1] = pts[i].y; | |
| oPoints[local+2] = pts[i].z; | |
| } | |
| /* | |
| oPoints - | |
| oFacePoints - vertex list | |
| oPointCounts - number of points per polygon | |
| */ | |
| MIntArray faceArray; | |
| for (i = 0; i < numPolys; i++) | |
| { | |
| lMesh.getPolygonVertices(i, faceArray); | |
| if (faceArray.length() < 3) | |
| { | |
| MGlobal::displayWarning("Skipping degenerate polygon"); | |
| continue; | |
| } | |
| // write backwards cause polygons in Maya are in a different order | |
| // from Renderman (clockwise vs counter-clockwise?) | |
| int faceArrayLength = faceArray.length() - 1; | |
| for (j = faceArrayLength; j > -1; j--) | |
| { | |
| oFacePoints.push_back(faceArray[j]); | |
| } | |
| oPointCounts.push_back(faceArray.length()); | |
| } | |
| } |