diff --git a/Changes.md b/Changes.md index 3ddfd6b7ed1..93f66ecd0aa 100644 --- a/Changes.md +++ b/Changes.md @@ -1,6 +1,11 @@ 1.3.x.x (relative to 1.3.8.0) ======= +Improvements +------------ + +- 3Delight : Added support for subdivision corners and creases. + Fixes ----- diff --git a/python/IECoreDelightTest/RendererTest.py b/python/IECoreDelightTest/RendererTest.py index 326ccf0efa0..fc664347241 100644 --- a/python/IECoreDelightTest/RendererTest.py +++ b/python/IECoreDelightTest/RendererTest.py @@ -1048,6 +1048,32 @@ def testUVCoordShaderNotInserted( self ) : self.assertEqual( len( nsi[testShader]["uvCoord"] ), 1 ) self.assertEqual( self.__connectionSource( nsi[testShader]["uvCoord"][0], nsi ), nsi[uvShader] ) + def testCornersAndCreases( self ) : + + mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ) + mesh.setInterpolation( "catmullClark" ) + mesh.setCorners( IECore.IntVectorData( [ 3 ] ), IECore.FloatVectorData( [ 5 ] ) ) + mesh.setCreases( IECore.IntVectorData( [ 3 ] ), IECore.IntVectorData( [ 0, 1, 2 ] ), IECore.FloatVectorData( [ 6 ] ) ) + + renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( + "3Delight", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.SceneDescription, + str( self.temporaryDirectory() / "test.nsi" ), + ) + + renderer.object( "testPlane", mesh, renderer.attributes( IECore.CompoundObject() ) ) + + renderer.render() + del renderer + + nsi = self.__parseDict( self.temporaryDirectory() / "test.nsi" ) + + mesh = next( node for node in nsi.values() if node["nodeType"] == "mesh" ) + self.assertEqual( mesh["subdivision.creasevertices"], [ 0, 1, 1, 2 ] ) + self.assertEqual( mesh["subdivision.creasesharpness"], [ 6, 6 ] ) + self.assertEqual( mesh["subdivision.cornervertices"], 3 ) + self.assertEqual( mesh["subdivision.cornersharpness"], 5 ) + # Helper methods used to check that NSI files we write contain what we # expect. The 3delight API only allows values to be set, not queried, # so we build a simple dictionary-based node graph for now. diff --git a/src/IECoreDelight/MeshAlgo.cpp b/src/IECoreDelight/MeshAlgo.cpp index 548c798ecf8..2a001cdcc7f 100644 --- a/src/IECoreDelight/MeshAlgo.cpp +++ b/src/IECoreDelight/MeshAlgo.cpp @@ -39,6 +39,8 @@ #include +#include + using namespace std; using namespace IECore; using namespace IECoreScene; @@ -66,6 +68,58 @@ void staticParameters( const IECoreScene::MeshPrimitive *mesh, ParameterList &pa } } +void convertCornersAndCreases( const IECoreScene::MeshPrimitive *mesh, NSIContext_t context, const char *handle ) +{ + ParameterList parameters; + + if( mesh->cornerIds()->readable().size() ) + { + parameters.add( "subdivision.cornervertices", mesh->cornerIds() ); + parameters.add( "subdivision.cornersharpness", mesh->cornerSharpnesses() ); + } + + IntVectorDataPtr delightIndicesData; // Must remain alive until we call + FloatVectorDataPtr delightSharpnessesData; // NSISetAttribute. + if( mesh->creaseLengths()->readable().size() ) + { + // Convert from our arbitrary-length creases + // to 3Delight's representation, which specifies + // an edge at a time using pairs of ids. + + const auto &lengths = mesh->creaseLengths()->readable(); + const size_t numEdges = std::accumulate( lengths.begin(), lengths.end(), 0 ) - lengths.size(); + + delightIndicesData = new IntVectorData; + delightSharpnessesData = new FloatVectorData; + auto &delightIndices = delightIndicesData->writable(); + auto &delightSharpnesses = delightSharpnessesData->writable(); + + delightIndices.reserve( numEdges * 2 ); + delightSharpnesses.reserve( numEdges ); + + auto idIt = mesh->creaseIds()->readable().begin(); + auto sharpnessIt = mesh->creaseSharpnesses()->readable().begin(); + for( int length : lengths ) + { + for( int j = 0; j < length - 1; ++j ) + { + delightSharpnesses.push_back( *sharpnessIt ); + delightIndices.push_back( *idIt++ ); + delightIndices.push_back( *idIt ); + } + sharpnessIt++; + } + + parameters.add( "subdivision.creasevertices", delightIndicesData.get() ); + parameters.add( "subdivision.creasesharpness", delightSharpnessesData.get() ); + } + + if( parameters.size() ) + { + NSISetAttribute( context, handle, parameters.size(), parameters.data() ); + } +} + bool convertStatic( const IECoreScene::MeshPrimitive *mesh, NSIContext_t context, const char *handle ) { NSICreate( context, handle, "mesh", 0, nullptr ); @@ -76,6 +130,8 @@ bool convertStatic( const IECoreScene::MeshPrimitive *mesh, NSIContext_t context NSISetAttribute( context, handle, parameters.size(), parameters.data() ); + convertCornersAndCreases( mesh, context, handle ); + return true; } @@ -103,6 +159,8 @@ bool convertAnimated( const vector &meshes, } } + convertCornersAndCreases( meshes.front(), context, handle ); + return true; }