Skip to content

Commit

Permalink
Updated nurbsCurve static exporter to export usd curve spec
Browse files Browse the repository at this point in the history
  • Loading branch information
Nick Wu committed Mar 16, 2023
1 parent b128c0d commit 9500a57
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 10 deletions.
5 changes: 5 additions & 0 deletions plugin/al/translators/NurbsCurve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ void NurbsCurve::writeEdits(
MProfilingScope profilerScope(
_nurbsCurveProfilerCategory, MProfiler::kColorE_L3, "Write edits");

const int curveOrder = fnCurve.degree() + 1;
if (!TF_VERIFY(curveOrder <= fnCurve.numCVs())) {
return;
}

uint32_t diff_curves = AL::usdmaya::utils::kAllNurbsCurveComponents;
bool performDiff = !writeAll;
if (performDiff) {
Expand Down
38 changes: 38 additions & 0 deletions plugin/al/translators/tests/testTranslators.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import maya.cmds as mc
import maya.mel as mel
from maya.api import OpenMaya

from pxr import Tf, Usd, UsdGeom, Gf
import translatortestutils
Expand Down Expand Up @@ -773,7 +774,44 @@ def testTranslatedPrimDeactiveCrashIssue(self):
# When a prim is within a translated hierarchy, deactivating it's parent should not crash Maya or clear the children of proxyShapeTransform.
self._performDisablePrimTest('./testTranslatedPrimDisableCrashIssue.usda')

def testUSDSpecNurbsCurveExportImport(self):
"""Testing exporting maya nurbsCurves in USD's curve spec, and importing USD curve spec in Maya"""
tempFile = tempfile.NamedTemporaryFile(suffix=".usda", prefix="test_USDNurbsCurveExportImport_", delete=False)
mc.CreateNURBSCircle()
spans = mc.getAttr('nurbsCircleShape1.spans')
degree = mc.getAttr('nurbsCircleShape1.degree')
mayaKnotCount = spans + 2 * degree - 1
usdnotCount = spans + 2 * degree + 1

# Export Maya curve to USD.
mc.select('nurbsCircle1', r=True)
mc.file(tempFile.name, exportSelected=True, force=True, type="AL usdmaya export",
options="Dynamic_Attributes=1;Duplicate_Instances=1;Merge_Transforms=1;Merge_Offset_Parent_Matrix=0;Animation=0;Use_Timeline_Range=0;Frame_Min=0;Frame_Max=1;Sub_Samples=1;Filter_Sample=0;Export_At_Which_Time=0;Export_In_World_Space=0;Activate_all_Plugin_Translators=1;Active_Translator_List=;Inactive_Translator_List=;Nurbs_Curves=1;Meshes=1;Mesh_Face_Connects=1;Mesh_Points=1;Mesh_Extents=1;Mesh_Normals=1;Mesh_Vertex_Creases=1;Mesh_Edge_Creases=1;Mesh_UVs=1;Mesh_UV_Only=0;Mesh_Points_as_PRef=0;Mesh_Colours=1;Default_RGB=0.18;Default_Alpha=1;Custom_Colour_Threshold=1;Colour_Threshold_Value=1e-05;Mesh_Holes=1;Write_Normals_as_Primvars=1;Reverse_Opposite_Normals=1;Subdivision_scheme=0;Compaction_Level=3;"
)

# Check whether exported curve's knot cout matches USD's curve spec.
# Reference: https://graphics.pixar.com/usd/release/api/class_usd_geom_nurbs_curves.html#details
stage = Usd.Stage.Open(tempFile.name)
prim = stage.GetPrimAtPath('/nurbsCircle1')
knotValues = prim.GetAttribute('knots').Get()
self.assertEqual(len(knotValues), usdnotCount)

# Import exported USD curve back to Maya.
mc.file(f=True, new=True)
mc.file(tempFile.name, i=True, force=True, type="AL usdmaya import", ignoreVersion=True, ra=True,
options="Parent_Path=;Prim_Path=;Import_Animations=1;Import_Dynamic_Attributes=1;Load_None=0;Read_Default_Values=1;Activate_all_Plugin_Translators=1;Active_Translator_List=;Inactive_Translator_List=;Import_Curves=1;Import_Meshes=1;",
pr=True, importFrameRate=True, importTimeRange='override'
)

# Check whether exported curve's knot cout matches USD's curve spec.
mc.select('nurbsCircle1Shape', r=True)
sel = OpenMaya.MGlobal.getActiveSelectionList()
curveFn = OpenMaya.MFnNurbsCurve(sel.getDependNode(0))
self.assertEqual(list(curveFn.knots()), list(knotValues[1:-1]))

os.remove(tempFile.name)


tests = unittest.TestLoader().loadTestsFromTestCase(TestTranslator)
result = unittest.TextTestRunner(verbosity=2).run(tests)

Expand Down
63 changes: 53 additions & 10 deletions plugin/al/usdmayautils/AL/usdmaya/utils/NurbsCurveUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,30 @@ void copyKnots(const MFnNurbsCurve& fnCurve, const UsdAttribute& knotsAttr, UsdT
{
MDoubleArray knots;
fnCurve.getKnots(knots);
const uint32_t count = knots.length();
VtArray<double> dataKnots(count);
memcpy((double*)dataKnots.cdata(), (const double*)&knots[0], sizeof(double) * count);
const uint32_t count = knots.length();

// USD's NurbsCurve representation requires 2 more knots
// Reference:
// https://graphics.pixar.com/usd/release/api/class_usd_geom_nurbs_curves.html#details
VtArray<double> dataKnots(count + 2);
for (uint32_t i = 0; i < count; ++i) {
dataKnots[i + 1] = knots[i];
}

auto curveForm = fnCurve.form();
if (curveForm == MFnNurbsCurve::kClosed || curveForm == MFnNurbsCurve::kPeriodic) {
// From USD spec: knot[0] = knot[1] - (knots[-2] - knots[-3]);
dataKnots[0]
= dataKnots[1] - (dataKnots[dataKnots.size() - 2] - dataKnots[dataKnots.size() - 3]);
// From USD spec: knot[-1] = knot[-2] + (knot[2] - knots[1]);
dataKnots[dataKnots.size() - 1]
= dataKnots[dataKnots.size() - 2] + (dataKnots[2] - dataKnots[1]);
} else {
// From USD spec: knot[0] = knot[1];
dataKnots[0] = dataKnots[1];
// From USD spec: knot[-1] = knot[-2];
dataKnots[dataKnots.size() - 1] = dataKnots[dataKnots.size() - 2];
}
knotsAttr.Set(dataKnots, time);
}

Expand Down Expand Up @@ -247,19 +268,41 @@ bool createMayaCurves(

size_t currentPointIndex = 0;
size_t currentKnotIndex = 0;

for (size_t i = 0, ncurves = dataCurveVertexCounts.size(); i < ncurves; ++i) {
const int32_t numPoints = dataCurveVertexCounts[i];
controlVertices.setLength(numPoints);

const int32_t numKnots = numPoints + dataOrder[i] - 2;
knotSequences.setLength(numKnots);

const float* pstart = (const float*)&dataPoints[currentPointIndex];
const double* kstart = &dataKnots[currentKnotIndex];
memcpy(&knotSequences[0], kstart, sizeof(double) * numKnots);
// Maya's curve spec requires (numberOfPoints + degree - 1) knots
uint32_t numKnots = numPoints + dataOrder[i] - 2;

// For backward compatibility, we check whether the knot count is in Maya's spec.
// If yes, we use the orignal knot values unchanged otherwise we need to convert to
// Maya's knot sepc from USD's knot spec
if (numKnots == dataKnots.size()) {
knotSequences.setLength(numKnots);
const double* kstart = &dataKnots[currentKnotIndex];
memcpy(&knotSequences[0], kstart, sizeof(double) * numKnots);
currentKnotIndex += numKnots;
} else {
// Converting knot values to Maya's spec. Maya requires 2 less knots than USD does.
// See
// https://graphics.pixar.com/usd/release/api/class_usd_geom_nurbs_curves.html#details
// and Maya MFnNurbsCurve API's Detailed Description session for more info.
numKnots = dataKnots.size() - 2;
knotSequences.setLength(numKnots);

// From Maya MFnNurbsCurve API's doc:
// To convert from one of these external representations into the Maya representation,
// simply omit the first and last knots from the external representation when creating
// the Maya representation
for (uint32_t j = 0; j < numKnots; ++j) {
knotSequences[j] = dataKnots[j + 1];
}
}

const float* pstart = (const float*)&dataPoints[currentPointIndex];
currentPointIndex += numPoints;
currentKnotIndex += numKnots;

AL::usdmaya::utils::convert3DFloatArrayTo4DDoubleArray(
pstart, (double*)&controlVertices[0], numPoints);
Expand Down

0 comments on commit 9500a57

Please sign in to comment.