From 8eda638a2e75f05d79cca38221cd257855e10a8d Mon Sep 17 00:00:00 2001 From: Daniel Dresser Date: Tue, 16 Apr 2024 16:29:44 -0700 Subject: [PATCH] IECOreUSD : support subdiv options --- contrib/IECoreUSD/src/IECoreUSD/MeshAlgo.cpp | 34 ++++++- .../IECoreUSD/test/IECoreUSD/USDSceneTest.py | 90 +++++++++++++++++++ 2 files changed, 122 insertions(+), 2 deletions(-) diff --git a/contrib/IECoreUSD/src/IECoreUSD/MeshAlgo.cpp b/contrib/IECoreUSD/src/IECoreUSD/MeshAlgo.cpp index 5703c51e5d..65c59d0350 100644 --- a/contrib/IECoreUSD/src/IECoreUSD/MeshAlgo.cpp +++ b/contrib/IECoreUSD/src/IECoreUSD/MeshAlgo.cpp @@ -76,8 +76,30 @@ IECore::ObjectPtr readMesh( pxr::UsdGeomMesh &mesh, pxr::UsdTimeCode time, const if( subdivScheme == pxr::UsdGeomTokens->catmullClark ) { - newMesh->setInterpolation( "catmullClark" ); + newMesh->setInterpolation( IECoreScene::MeshPrimitive::interpolationCatmullClark.string() ); } + else if( subdivScheme == pxr::UsdGeomTokens->loop ) + { + newMesh->setInterpolation( IECoreScene::MeshPrimitive::interpolationLoop.string() ); + } + else + { + // For "none", we currently use the default value of "linear". It would probably be preferrable if + // we used the name "none", since this is different from "bilinear", which would indicate that + // subdivision is being requested, but without altering the shape of the limit surface. + } + + pxr::TfToken interpolateBoundary; + mesh.GetInterpolateBoundaryAttr().Get( &interpolateBoundary ); + newMesh->setInterpolateBoundary( interpolateBoundary.GetString() ); + + pxr::TfToken faceVaryingLinearInterpolation; + mesh.GetFaceVaryingLinearInterpolationAttr().Get( &faceVaryingLinearInterpolation ); + newMesh->setFaceVaryingLinearInterpolation( faceVaryingLinearInterpolation.GetString() ); + + pxr::TfToken triangleSubdivisionRule; + mesh.GetTriangleSubdivisionRuleAttr().Get( &triangleSubdivisionRule ); + newMesh->setTriangleSubdivisionRule( triangleSubdivisionRule.GetString() ); // Corners @@ -170,15 +192,23 @@ bool writeMesh( const IECoreScene::MeshPrimitive *mesh, const pxr::UsdStagePtr & // Interpolation - if( mesh->interpolation() == std::string( "catmullClark" ) ) + if( mesh->interpolation() == IECoreScene::MeshPrimitive::interpolationCatmullClark.string() ) { usdMesh.CreateSubdivisionSchemeAttr().Set( pxr::UsdGeomTokens->catmullClark ); } + else if( mesh->interpolation() == IECoreScene::MeshPrimitive::interpolationLoop.string() ) + { + usdMesh.CreateSubdivisionSchemeAttr().Set( pxr::UsdGeomTokens->loop ); + } else { usdMesh.CreateSubdivisionSchemeAttr().Set( pxr::UsdGeomTokens->none ); } + usdMesh.CreateInterpolateBoundaryAttr().Set( pxr::TfToken( mesh->getInterpolateBoundary().string() ) ); + usdMesh.CreateFaceVaryingLinearInterpolationAttr().Set( pxr::TfToken( mesh->getFaceVaryingLinearInterpolation().string() ) ); + usdMesh.CreateTriangleSubdivisionRuleAttr().Set( pxr::TfToken( mesh->getTriangleSubdivisionRule().string() ) ); + // Corners if( mesh->cornerIds()->readable().size() ) diff --git a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py index 5bd4576222..12026b3dcd 100644 --- a/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py +++ b/contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py @@ -843,6 +843,96 @@ def testCanWriteSubD( self ): self.assertEqual(readChild.readObject( 0.0 ).interpolation, "catmullClark") + def testSubdOptions( self ) : + + fileName = os.path.join( self.temporaryDirectory(), "test.usda" ) + resaveFileName = os.path.join( self.temporaryDirectory(), "resave.usda" ) + + # We need a list of all the values from USD we should support. There probably should be a + # more direct way to get this, but I have already wasted far, far too much time trying to + # understand which USD API to use. + dummyStage = pxr.Usd.Stage.CreateInMemory() + dummyMesh = pxr.UsdGeom.Mesh.Define( dummyStage, "/mesh" ) + allowedSubScheme = dummyMesh.GetSubdivisionSchemeAttr().GetMetadata( "allowedTokens" ) + allowedIB = dummyMesh.GetInterpolateBoundaryAttr().GetMetadata( "allowedTokens" ) + allowedFVLI = dummyMesh.GetFaceVaryingLinearInterpolationAttr().GetMetadata( "allowedTokens" ) + allowedTS = dummyMesh.GetTriangleSubdivisionRuleAttr().GetMetadata( "allowedTokens" ) + + del dummyMesh + del dummyStage + + for property, allowed in [ + ( "subdivisionScheme", allowedSubScheme ), + ( "interpolateBoundary", allowedIB ), + ( "faceVaryingLinearInterpolation", allowedFVLI ), + ( "triangleSubdivisionRule", allowedTS ), + + ]: + for value in allowed: + + if property == "subdivisionScheme" and value == "bilinear": + # We know we don't support this + continue + + stage = pxr.Usd.Stage.CreateNew( fileName ) + mesh = pxr.UsdGeom.Mesh.Define( stage, "/mesh" ) + if property == "subdivisionScheme": + mesh.CreateSubdivisionSchemeAttr().Set( value ) + else: + mesh.CreateSubdivisionSchemeAttr().Set( "catmullClark" ) + + if property == "interpolateBoundary": + mesh.CreateInterpolateBoundaryAttr().Set( value ) + + if property == "faceVaryingLinearInterpolation": + mesh.CreateFaceVaryingLinearInterpolationAttr().Set( value ) + + if property == "triangleSubdivisionRule": + mesh.CreateTriangleSubdivisionRuleAttr().Set( value ) + + stage.GetRootLayer().Save() + del stage + + root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read ) + + cortexMesh = root.child( "mesh" ).readObject( 0.0 ) + if property == "subdivisionScheme": + if value == "none": + self.assertEqual( cortexMesh.interpolation, "linear" ) + else: + self.assertEqual( cortexMesh.interpolation, value ) + elif property == "interpolateBoundary": + self.assertEqual( cortexMesh.getInterpolateBoundary(), value ) + elif property == "faceVaryingLinearInterpolation": + self.assertEqual( cortexMesh.getFaceVaryingLinearInterpolation(), value ) + elif property == "triangleSubdivisionRule": + self.assertEqual( cortexMesh.getTriangleSubdivisionRule(), value ) + + sceneWrite = IECoreScene.SceneInterface.create( resaveFileName, IECore.IndexedIO.OpenMode.Write ) + root = sceneWrite.createChild( "root" ) + child = root.createChild( "mesh" ) + + child.writeObject ( cortexMesh, 0.0 ) + + del child + del root + del sceneWrite + + rereadFile = pxr.Usd.Stage.Open( resaveFileName ) + rereadMesh = pxr.UsdGeom.Mesh.Get( rereadFile, "/root/mesh" ) + + if property == "subdivisionScheme": + self.assertEqual( rereadMesh.GetSubdivisionSchemeAttr().Get(), value ) + elif property == "interpolateBoundary": + self.assertEqual( rereadMesh.GetInterpolateBoundaryAttr().Get(), value ) + elif property == "faceVaryingLinearInterpolation": + self.assertEqual( rereadMesh.GetFaceVaryingLinearInterpolationAttr().Get(), value ) + elif property == "triangleSubdivisionRule": + self.assertEqual( rereadMesh.GetTriangleSubdivisionRuleAttr().Get(), value ) + + del rereadMesh + del rereadFile + def testCanWriteAnimatedPrimitiveVariable ( self ): fileName = os.path.join( self.temporaryDirectory(), "usd_animated_primvar.usda" )