//-***************************************************************************** // // 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 "MayaNurbsCurveWriter.h" #include "MayaUtility.h" #include "MayaTransformWriter.h" namespace { // get all the nurbs curves from below the given dagpath. // the curve group is considered animated if at least one curve is animated void collectNurbsCurves(const MDagPath &dagPath, bool iExcludeInvisible, MDagPathArray &dagPaths, bool & oIsAnimated) { MStatus stat; MItDag itDag(MItDag::kDepthFirst, MFn::kNurbsCurve, &stat); stat = itDag.reset(dagPath, MItDag::kDepthFirst, MFn::kNurbsCurve); if (stat == MS::kSuccess) { for (;!itDag.isDone();itDag.next()) { MDagPath curvePath; stat = itDag.getPath(curvePath); if (stat == MS::kSuccess) { MObject curve = curvePath.node(); if ( !util::isIntermediate(curve) && curve.hasFn(MFn::kNurbsCurve) && (!iExcludeInvisible || util::isRenderable(curve)) ) { dagPaths.append(curvePath); } // don't bother checking the animated state if the curve // wasn't appended to the list else { continue; } // with the flag set to true, check the DagPath and its // parent. // Note since we're collecting a group of curves, and // if even one is animated, the whole group will be, // so don't bother checking additional curves. if (!oIsAnimated) { if (util::isAnimated(curve, true)) { oIsAnimated = true; } MObject curveXform(curvePath.transform()); if (util::isAnimated(curveXform, true)) { oIsAnimated = true; } } } } } } // end of function collectNurbsCurves } MayaNurbsCurveWriter::MayaNurbsCurveWriter(MDagPath & iDag, Alembic::Abc::OObject & iParent, Alembic::Util::uint32_t iTimeIndex, bool iIsCurveGrp, const JobArgs & iArgs) : mIsAnimated(false), mRootDagPath(iDag), mIsCurveGrp(iIsCurveGrp) { MStatus stat; MFnDependencyNode fnDepNode(iDag.node(), &stat); MString name = fnDepNode.name(); if (mIsCurveGrp) { collectNurbsCurves(mRootDagPath, iArgs.excludeInvisible, mNurbsCurves, mIsAnimated); // if no curves were found bail early if (mNurbsCurves.length() == 0) return; } else { MObject curve = iDag.node(); if (iTimeIndex != 0 && util::isAnimated(curve)) { mIsAnimated = true; } else { iTimeIndex = 0; } } name = util::stripNamespaces(name, iArgs.stripNamespace); Alembic::AbcGeom::OCurves obj(iParent, name.asChar(), iTimeIndex); mSchema = obj.getSchema(); Alembic::Abc::OCompoundProperty cp; Alembic::Abc::OCompoundProperty up; if (AttributesWriter::hasAnyAttr(fnDepNode, iArgs)) { cp = mSchema.getArbGeomParams(); up = mSchema.getUserProperties(); } mAttrs = AttributesWriterPtr(new AttributesWriter(cp, up, obj, fnDepNode, iTimeIndex, iArgs, true)); if (!mIsAnimated || iArgs.setFirstAnimShape) { write(); } } unsigned int MayaNurbsCurveWriter::getNumCVs() { return mCVCount; } unsigned int MayaNurbsCurveWriter::getNumCurves() { if (mIsCurveGrp) return mNurbsCurves.length(); else return 1; } bool MayaNurbsCurveWriter::isAnimated() const { return mIsAnimated; } void MayaNurbsCurveWriter::write() { Alembic::AbcGeom::OCurvesSchema::Sample samp; samp.setBasis(Alembic::AbcGeom::kBsplineBasis); MStatus stat; mCVCount = 0; // if inheritTransform is on and the curve group is animated, // bake the cv positions in the world space MMatrix exclusiveMatrixInv = mRootDagPath.exclusiveMatrixInverse(&stat); std::size_t numCurves = 1; if (mIsCurveGrp) numCurves = mNurbsCurves.length(); std::vector nVertices(numCurves); std::vector points; std::vector width; std::vector knots; std::vector orders(numCurves); MMatrix transformMatrix; bool useConstWidth = false; MFnDependencyNode dep(mRootDagPath.transform()); MPlug constWidthPlug = dep.findPlug("width", true); if (!constWidthPlug.isNull()) { useConstWidth = true; width.push_back(constWidthPlug.asFloat()); } for (unsigned int i = 0; i < numCurves; i++) { MFnNurbsCurve curve; if (mIsCurveGrp) { curve.setObject(mNurbsCurves[i]); MMatrix inclusiveMatrix = mNurbsCurves[i].inclusiveMatrix(&stat); transformMatrix = inclusiveMatrix*exclusiveMatrixInv; } else { curve.setObject(mRootDagPath.node()); } if (i == 0) { if (curve.form() == MFnNurbsCurve::kOpen) { samp.setWrap(Alembic::AbcGeom::kNonPeriodic); } else { samp.setWrap(Alembic::AbcGeom::kPeriodic); } if (curve.degree() == 3) { samp.setType(Alembic::AbcGeom::kCubic); } else if (curve.degree() == 1) { samp.setType(Alembic::AbcGeom::kLinear); } else { samp.setType(Alembic::AbcGeom::kVariableOrder); } } else { if (curve.form() == MFnNurbsCurve::kOpen) { samp.setWrap(Alembic::AbcGeom::kNonPeriodic); } if ((samp.getType() == Alembic::AbcGeom::kCubic && curve.degree() != 3) || (samp.getType() == Alembic::AbcGeom::kLinear && curve.degree() != 1)) { samp.setType(Alembic::AbcGeom::kVariableOrder); } } orders[i] = static_cast(curve.degree() + 1); Alembic::Util::int32_t numCVs = curve.numCVs(&stat); MPointArray cvArray; stat = curve.getCVs(cvArray, MSpace::kObject); mCVCount += numCVs; nVertices[i] = numCVs; for (Alembic::Util::int32_t j = 0; j < numCVs; j++) { MPoint transformdPt; if (mIsCurveGrp) { transformdPt = cvArray[j]*transformMatrix; } else { transformdPt = cvArray[j]; } points.push_back(static_cast(transformdPt.x)); points.push_back(static_cast(transformdPt.y)); points.push_back(static_cast(transformdPt.z)); } MDoubleArray knotsArray; curve.getKnots(knotsArray); knots.reserve(knotsArray.length() + 2); // need to add a knot to the start and end (M + 2N + 1) if (knotsArray.length() > 1) { unsigned int knotsLength = knotsArray.length(); if (knotsArray[0] == knotsArray[knotsLength - 1] || knotsArray[0] == knotsArray[1]) { knots.push_back(static_cast(knotsArray[0])); } else { knots.push_back(static_cast(2 * knotsArray[0] - knotsArray[1])); } for (unsigned int j = 0; j < knotsLength; ++j) { knots.push_back(static_cast(knotsArray[j])); } if (knotsArray[0] == knotsArray[knotsLength - 1] || knotsArray[knotsLength - 1] == knotsArray[knotsLength - 2]) { knots.push_back(static_cast((knotsArray[knotsLength - 1]))); } else { knots.push_back(static_cast(2 * knotsArray[knotsLength - 1] - knotsArray[knotsLength - 2])); } } // width MPlug widthPlug = curve.findPlug("width", true); if (!useConstWidth && !widthPlug.isNull()) { MObject widthObj; MStatus status = widthPlug.getValue(widthObj); MFnDoubleArrayData fnDoubleArrayData(widthObj, &status); MDoubleArray doubleArrayData = fnDoubleArrayData.array(); Alembic::Util::int32_t arraySum = doubleArrayData.length(); if (arraySum == numCVs) { for (Alembic::Util::int32_t i = 0; i < arraySum; i++) { width.push_back(static_cast(doubleArrayData[i])); } } // successfully got as a double array but not enough values! else if (status == MS::kSuccess) { MString msg = "Curve "; msg += curve.partialPathName(); msg += " has incorrect size for the width vector."; msg += "\nUsing default constant width of 0.1."; MGlobal::displayWarning(msg); width.clear(); width.push_back(0.1f); useConstWidth = true; } else { width.push_back(widthPlug.asFloat()); } } // no width plug on the curve, but not yet noticed as constant width // force constant else if (!useConstWidth) { // pick a default value width.clear(); width.push_back(0.1f); useConstWidth = true; } } Alembic::AbcGeom::GeometryScope scope = Alembic::AbcGeom::kVertexScope; if (!useConstWidth && width.size() > 1 && width.size() == numCurves) { scope = Alembic::AbcGeom::kUniformScope; } else if (width.size() == 1) { scope = Alembic::AbcGeom::kConstantScope; } samp.setCurvesNumVertices(Alembic::Abc::Int32ArraySample(nVertices)); samp.setPositions(Alembic::Abc::V3fArraySample( (const Imath::V3f *)&points.front(), points.size() / 3 )); samp.setWidths(Alembic::AbcGeom::OFloatGeomParam::Sample( Alembic::Abc::FloatArraySample(width), scope) ); if (samp.getType() == Alembic::AbcGeom::kVariableOrder) { samp.setOrders(Alembic::Abc::UcharArraySample(orders)); } if (!knots.empty()) { samp.setKnots(Alembic::Abc::FloatArraySample(knots)); } mSchema.set(samp); }