From 285ab044dbf4c66690851188d0a31b369f68ba2c Mon Sep 17 00:00:00 2001 From: John Haddon Date: Tue, 19 Dec 2023 11:18:23 +0000 Subject: [PATCH] USD CurvesAlgo : Add basic support for reading UsdGeomNurbsCurves We don't have a NURBS curve in Cortex, and one isn't supported by any of the renderers we connect to. So we just do a very basic conversion to CurvesPrimitive instead. --- Changes | 3 + .../IECoreUSD/src/IECoreUSD/CurvesAlgo.cpp | 69 ++++++++++++++----- .../IECoreUSD/test/IECoreUSD/USDSceneTest.py | 52 ++++++++++++++ 3 files changed, 108 insertions(+), 16 deletions(-) diff --git a/Changes b/Changes index 62084aefb5..6c4553738d 100644 --- a/Changes +++ b/Changes @@ -1,7 +1,10 @@ 10.5.x.x (relative to 10.5.4.2) ======== +Improvements +------------ +- USDScene : Added basic loading of UsdGeomNurbsCurves, converting them to CurvesPrimitives. 10.5.4.2 (relative to 10.5.4.1) ======== diff --git a/contrib/IECoreUSD/src/IECoreUSD/CurvesAlgo.cpp b/contrib/IECoreUSD/src/IECoreUSD/CurvesAlgo.cpp index 866695dcdf..a92ca29dd5 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/CurvesAlgo.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/CurvesAlgo.cpp @@ -42,6 +42,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY #include "pxr/usd/usdGeom/basisCurves.h" +#include "pxr/usd/usdGeom/nurbsCurves.h" IECORE_POP_DEFAULT_VISIBILITY using namespace IECore; @@ -55,13 +56,37 @@ using namespace IECoreUSD; namespace { -IECore::ObjectPtr readCurves( pxr::UsdGeomBasisCurves &curves, pxr::UsdTimeCode time, const Canceller *canceller ) +IECore::ObjectPtr readCurves( pxr::UsdGeomCurves &curves, pxr::UsdTimeCode time, const IECore::CubicBasisf &basis, bool periodic, const Canceller *canceller ) { Canceller::check( canceller ); pxr::VtIntArray vertexCountsArray; curves.GetCurveVertexCountsAttr().Get( &vertexCountsArray, time ); IECore::IntVectorDataPtr countData = DataAlgo::fromUSD( vertexCountsArray ); + Canceller::check( canceller ); + IECoreScene::CurvesPrimitivePtr newCurves = new IECoreScene::CurvesPrimitive( countData, basis, periodic ); + PrimitiveAlgo::readPrimitiveVariables( curves, time, newCurves.get(), canceller ); + + Canceller::check( canceller ); + PrimitiveAlgo::readPrimitiveVariable( + curves.GetWidthsAttr(), time, newCurves.get(), "width", PrimitiveAlgo::fromUSD( curves.GetWidthsInterpolation() ) + ); + + return newCurves; +} + +bool curvesMightBeTimeVarying( pxr::UsdGeomCurves &curves ) +{ + return + curves.GetCurveVertexCountsAttr().ValueMightBeTimeVarying() || + curves.GetWidthsAttr().ValueMightBeTimeVarying() || + PrimitiveAlgo::primitiveVariablesMightBeTimeVarying( curves ) + ; +} + +IECore::ObjectPtr readBasisCurves( pxr::UsdGeomBasisCurves &curves, pxr::UsdTimeCode time, const Canceller *canceller ) +{ + // Basis Canceller::check( canceller ); IECore::CubicBasisf basis = CubicBasisf::linear(); @@ -103,32 +128,44 @@ IECore::ObjectPtr readCurves( pxr::UsdGeomBasisCurves &curves, pxr::UsdTimeCode IECore::msg( IECore::Msg::Warning, "USDScene", boost::format( "Unsupported wrap \"%1%\"" ) % wrap ); } - // Curves and primvars + return readCurves( curves, time, basis, periodic, canceller ); +} - IECoreScene::CurvesPrimitivePtr newCurves = new IECoreScene::CurvesPrimitive( countData, basis, periodic ); - PrimitiveAlgo::readPrimitiveVariables( curves, time, newCurves.get(), canceller ); +bool basisCurvesMightBeTimeVarying( pxr::UsdGeomBasisCurves &curves ) +{ + return + curvesMightBeTimeVarying( curves ) || + curves.GetTypeAttr().ValueMightBeTimeVarying() || + curves.GetBasisAttr().ValueMightBeTimeVarying() || + curves.GetWrapAttr().ValueMightBeTimeVarying() + ; +} + +IECore::ObjectPtr readNurbsCurves( pxr::UsdGeomNurbsCurves &curves, pxr::UsdTimeCode time, const Canceller *canceller ) +{ + IECore::CubicBasisf basis = IECore::CubicBasisf::linear(); Canceller::check( canceller ); - PrimitiveAlgo::readPrimitiveVariable( - curves.GetWidthsAttr(), time, newCurves.get(), "width", PrimitiveAlgo::fromUSD( curves.GetWidthsInterpolation() ) - ); + pxr::VtIntArray order; + curves.GetOrderAttr().Get( &order, time ); + if( std::all_of( order.begin(), order.end(), [] ( int o ) { return o == 4; } ) ) + { + basis = CubicBasisf::bSpline(); + } - return newCurves; + return readCurves( curves, time, basis, false, canceller ); } -bool curvesMightBeTimeVarying( pxr::UsdGeomBasisCurves &curves ) +bool nurbsCurvesMightBeTimeVarying( pxr::UsdGeomNurbsCurves &curves ) { return - curves.GetCurveVertexCountsAttr().ValueMightBeTimeVarying() || - curves.GetTypeAttr().ValueMightBeTimeVarying() || - curves.GetBasisAttr().ValueMightBeTimeVarying() || - curves.GetWrapAttr().ValueMightBeTimeVarying() || - curves.GetWidthsAttr().ValueMightBeTimeVarying() || - PrimitiveAlgo::primitiveVariablesMightBeTimeVarying( curves ) + curvesMightBeTimeVarying( curves ) || + curves.GetOrderAttr().ValueMightBeTimeVarying() ; } -ObjectAlgo::ReaderDescription g_curvesReaderDescription( pxr::TfToken( "BasisCurves" ), readCurves, curvesMightBeTimeVarying ); +ObjectAlgo::ReaderDescription g_curvesReaderDescription( pxr::TfToken( "BasisCurves" ), readBasisCurves, basisCurvesMightBeTimeVarying ); +ObjectAlgo::ReaderDescription g_nurbsCurvesReaderDescription( pxr::TfToken( "NurbsCurves" ), readNurbsCurves, nurbsCurvesMightBeTimeVarying ); } // namespace diff --git a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py index e52717d613..3f15b205cc 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py @@ -1655,6 +1655,58 @@ def testCurveBasisAndWrap( self ) : self.assertEqual( curves2, curves ) del root + def testReadNurbsCurves( self ) : + + # Write USD file + + fileName = os.path.join( self.temporaryDirectory(), "nurbs.usda" ) + stage = pxr.Usd.Stage.CreateNew( fileName ) + + curves = pxr.UsdGeom.NurbsCurves.Define( stage, "/cubic" ) + curves.CreateCurveVertexCountsAttr().Set( [ 4 ] ) + curves.CreatePointsAttr().Set( [ ( x, x, x ) for x in range( 0, 4 ) ] ) + curves.CreateOrderAttr().Set( [ 4 ] ) + curves.CreateKnotsAttr().Set( [ 0, 0, 0, 0.333, 0.666, 1, 1, 1 ] ) + + curves = pxr.UsdGeom.NurbsCurves.Define( stage, "/nonCubic" ) + curves.CreateCurveVertexCountsAttr().Set( [ 4 ] ) + curves.CreatePointsAttr().Set( [ ( x, x, x ) for x in range( 0, 4 ) ] ) + curves.CreateOrderAttr().Set( [ 3 ] ) + curves.CreateKnotsAttr().Set( [ 0, 0, 0, 0.5, 1, 1, 1 ] ) + + stage.GetRootLayer().Save() + del stage + + # Load and check + + root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read ) + + cubic = root.child( "cubic" ).readObject( 0.0 ) + self.assertIsInstance( cubic, IECoreScene.CurvesPrimitive ) + self.assertEqual( cubic.verticesPerCurve(), IECore.IntVectorData( [ 4 ] ) ) + self.assertEqual( cubic.basis(), IECore.CubicBasisf.bSpline() ) + self.assertEqual( cubic.periodic(), False ) + self.assertEqual( + cubic["P"].data, + IECore.V3fVectorData( + [ imath.V3f( x ) for x in range( 0, 4 ) ], + IECore.GeometricData.Interpretation.Point + ) + ) + + nonCubic = root.child( "nonCubic" ).readObject( 0.0 ) + self.assertIsInstance( nonCubic, IECoreScene.CurvesPrimitive ) + self.assertEqual( nonCubic.verticesPerCurve(), IECore.IntVectorData( [ 4 ] ) ) + self.assertEqual( nonCubic.basis(), IECore.CubicBasisf.linear() ) + self.assertEqual( nonCubic.periodic(), False ) + self.assertEqual( + cubic["P"].data, + IECore.V3fVectorData( + [ imath.V3f( x ) for x in range( 0, 4 ) ], + IECore.GeometricData.Interpretation.Point + ) + ) + def testIndexedWidths( self ) : # Write USD file from points with indexed widths