diff --git a/python/IECoreRenderManTest/RendererTest.py b/python/IECoreRenderManTest/RendererTest.py index 08b28f1443e..786e42515c5 100644 --- a/python/IECoreRenderManTest/RendererTest.py +++ b/python/IECoreRenderManTest/RendererTest.py @@ -45,6 +45,7 @@ import IECore import IECoreImage import IECoreScene +import IECoreRenderManTest import GafferTest import GafferScene @@ -825,6 +826,185 @@ def testConnectionToOSLShader( self ) : image = OpenImageIO.ImageBuf( fileName ) self.assertEqual( image.getpixel( 320, 240, 0 ), ( 1.0, 1.0, 0.0, 1.0 ) ) + def testSubdivInterpolatedBoundary( self ) : + + for interpolateBoundary, expected in [ + ( IECoreScene.MeshPrimitive.interpolateBoundaryNone, 0 ), + ( IECoreScene.MeshPrimitive.interpolateBoundaryEdgeAndCorner, 1 ), + ( IECoreScene.MeshPrimitive.interpolateBoundaryEdgeOnly, 2 ), + ] : + + with self.subTest( interpolateBoundary = interpolateBoundary ) : + + with IECoreRenderManTest.RileyCapture() as capture : + + renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( + "RenderMan", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Batch + ) + + mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ) + mesh.setInterpolation( "catmullClark" ) + mesh.setInterpolateBoundary( interpolateBoundary ) + + renderer.object( + "mesh", mesh, renderer.attributes( IECore.CompoundObject() ) + ) + + del mesh, renderer + + proto = next( + x for x in capture.json if x["method"] == "CreateGeometryPrototype" + ) + self.__assertInTags( + proto, "interpolateboundary", intArgs = [ expected ] + ) + + def testSubdivFaceVaryingLinearInterpolation( self ) : + + for faceVaryingLinearInterpolation, expected in [ + ( IECoreScene.MeshPrimitive.faceVaryingLinearInterpolationNone, 2 ), + ( IECoreScene.MeshPrimitive.faceVaryingLinearInterpolationCornersOnly, 1 ), + ( IECoreScene.MeshPrimitive.faceVaryingLinearInterpolationCornersPlus1, 1 ), + ( IECoreScene.MeshPrimitive.faceVaryingLinearInterpolationCornersPlus2, 1 ), + ( IECoreScene.MeshPrimitive.faceVaryingLinearInterpolationBoundaries, 3 ), + ( IECoreScene.MeshPrimitive.faceVaryingLinearInterpolationAll, 0 ), + ] : + + with self.subTest( faceVaryingLinearInterpolation = faceVaryingLinearInterpolation ) : + + with IECoreRenderManTest.RileyCapture() as capture : + + renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( + "RenderMan", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Batch + ) + + mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ) + mesh.setInterpolation( "catmullClark" ) + mesh.setFaceVaryingLinearInterpolation( faceVaryingLinearInterpolation ) + + renderer.object( + "mesh", mesh, renderer.attributes( IECore.CompoundObject() ) + ) + + del mesh, renderer + + proto = next( + x for x in capture.json if x["method"] == "CreateGeometryPrototype" + ) + self.__assertInTags( + proto, "facevaryinginterpolateboundary", intArgs = [ expected ] + ) + + def testSubdivTriangleSubdivisionRule( self ) : + + for rule, expected in [ + ( IECoreScene.MeshPrimitive.triangleSubdivisionRuleCatmullClark, 0 ), + ( IECoreScene.MeshPrimitive.triangleSubdivisionRuleSmooth, 2 ), + ] : + + with self.subTest( rule = rule ) : + + with IECoreRenderManTest.RileyCapture() as capture : + + renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( + "RenderMan", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Batch + ) + + mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ) + mesh.setInterpolation( "catmullClark" ) + mesh.setTriangleSubdivisionRule( rule ) + + renderer.object( + "mesh", mesh, renderer.attributes( IECore.CompoundObject() ) + ) + + del mesh, renderer + + proto = next( + x for x in capture.json if x["method"] == "CreateGeometryPrototype" + ) + self.__assertInTags( + proto, "smoothtriangles", intArgs = [ expected ] + ) + + def testSubdivisionScheme( self ) : + + for interpolation, scheme in [ + ( IECoreScene.MeshPrimitive.interpolationLinear, None ), + ( IECoreScene.MeshPrimitive.interpolationCatmullClark, "catmull-clark" ), + ( IECoreScene.MeshPrimitive.interpolationLoop, "loop" ), + ] : + + with self.subTest( interpolation = interpolation ) : + + with IECoreRenderManTest.RileyCapture() as capture : + + renderer = GafferScene.Private.IECoreScenePreview.Renderer.create( + "RenderMan", + GafferScene.Private.IECoreScenePreview.Renderer.RenderType.Batch + ) + + mesh = IECoreScene.MeshPrimitive.createPlane( imath.Box2f( imath.V2f( -1 ), imath.V2f( 1 ) ) ) + mesh.setInterpolation( interpolation ) + + renderer.object( + "mesh", mesh, renderer.attributes( IECore.CompoundObject() ) + ) + + del mesh, renderer + + proto = next( + x for x in capture.json if x["method"] == "CreateGeometryPrototype" + ) + + if scheme is not None : + self.assertEqual( proto["type"], "Ri:SubdivisionMesh" ) + self.__assertPrimitiveVariableEqual( proto, "Ri:scheme", [ scheme ] ) + else : + self.__assertNotInPrimitiveVariables( proto, "Ri:scheme" ) + self.assertEqual( proto["type"], "Ri:PolygonMesh" ) + + def __assertPrimitiveVariableEqual( self, geometryPrototype, name, data ) : + + p = next( x for x in geometryPrototype["primvars"]["params"] if x["info"]["name"] == name ) + self.assertEqual( p["data"], data ) + + def __assertNotInPrimitiveVariables( self, geometryPrototype, name ) : + + self.assertNotIn( + name, { x["info"]["name"] for x in geometryPrototype["primvars"]["params"] } + ) + + def __assertInTags( self, geometryPrototype, tag, intArgs = [], floatArgs = [] ) : + + tags = next( x for x in geometryPrototype["primvars"]["params"] if x["info"]["name"] == "Ri:subdivtags" )["data"] + numArgs = next( x for x in geometryPrototype["primvars"]["params"] if x["info"]["name"] == "Ri:subdivtagnargs" )["data"] + ints = next( x for x in geometryPrototype["primvars"]["params"] if x["info"]["name"] == "Ri:subdivtagintargs" )["data"] + floats = next( x for x in geometryPrototype["primvars"]["params"] if x["info"]["name"] == "Ri:subdivtagfloatargs" )["data"] + + foundTag = False + for t in tags : + + if t == tag : + self.assertEqual( numArgs[0:3], [ len( intArgs ), len( floatArgs ), 0 ] ) + self.assertEqual( ints[0:len(intArgs)], intArgs ) + self.assertEqual( floats[0:len(floatArgs)], floatArgs ) + foundTag = True + + # Move to next tag + del ints[0:numArgs[0]] + del floats[0:numArgs[1]] + del numArgs[0:3] + + self.assertEqual( len( numArgs ), 0 ) + self.assertEqual( len( ints ), 0 ) + self.assertEqual( len( floats ), 0 ) + + self.assertTrue( foundTag ) + def __colorAtUV( self, image, uv ) : dimensions = image.dataWindow.size() + imath.V2i( 1 ) diff --git a/src/IECoreRenderMan/GeometryAlgo.cpp b/src/IECoreRenderMan/GeometryAlgo.cpp index e4702ee2095..f9974e8e4c4 100644 --- a/src/IECoreRenderMan/GeometryAlgo.cpp +++ b/src/IECoreRenderMan/GeometryAlgo.cpp @@ -413,6 +413,76 @@ GeometryAlgo::ConverterDescription g_sphereConverterDescription namespace { +int interpolateBoundary( const IECoreScene::MeshPrimitive *mesh ) +{ + const InternedString s = mesh->getInterpolateBoundary(); + if( s == IECoreScene::MeshPrimitive::interpolateBoundaryNone ) + { + return 0; + } + else if( s == IECoreScene::MeshPrimitive::interpolateBoundaryEdgeAndCorner ) + { + return 1; + } + else if( s == IECoreScene::MeshPrimitive::interpolateBoundaryEdgeOnly ) + { + return 2; + } + else + { + msg( Msg::Error, "GeometryAlgo", fmt::format( "Unknown boundary interpolation \"{}\"", s.string() ) ); + return 0; + } +} + +int faceVaryingInterpolateBoundary( const IECoreScene::MeshPrimitive *mesh ) +{ + const InternedString s = mesh->getFaceVaryingLinearInterpolation(); + if( s == IECoreScene::MeshPrimitive::faceVaryingLinearInterpolationNone ) + { + return 2; + } + else if( + s == IECoreScene::MeshPrimitive::faceVaryingLinearInterpolationCornersOnly || + s == IECoreScene::MeshPrimitive::faceVaryingLinearInterpolationCornersPlus1 || + s == IECoreScene::MeshPrimitive::faceVaryingLinearInterpolationCornersPlus2 + ) + { + return 1; + } + else if( s == IECoreScene::MeshPrimitive::faceVaryingLinearInterpolationBoundaries ) + { + return 3; + } + else if( s == IECoreScene::MeshPrimitive::faceVaryingLinearInterpolationAll ) + { + return 0; + } + else + { + msg( Msg::Error, "GeometryAlgo", fmt::format( "Unknown facevarying linear interpolation \"{}\"", s.string() ) ); + return 0; + } +} + +int smoothTriangles( const IECoreScene::MeshPrimitive *mesh ) +{ + const InternedString s = mesh->getTriangleSubdivisionRule(); + if( s == IECoreScene::MeshPrimitive::triangleSubdivisionRuleCatmullClark ) + { + return 0; + } + else if( s == IECoreScene::MeshPrimitive::triangleSubdivisionRuleSmooth ) + { + return 2; + } + else + { + msg( Msg::Error, "GeometryAlgo", fmt::format( "Unknown triangle subdivision rule \"{}\"", s.string() ) ); + return 0; + } +} + riley::GeometryPrototypeId convertStaticMesh( const IECoreScene::MeshPrimitive *mesh, riley::Riley *riley ) { RtPrimVarList primVars( @@ -432,10 +502,22 @@ riley::GeometryPrototypeId convertStaticMesh( const IECoreScene::MeshPrimitive * primVars.SetIntegerDetail( Rix::k_Ri_vertices, mesh->vertexIds()->readable().data(), RtDetailType::k_facevarying ); RtUString geometryType = Rix::k_Ri_PolygonMesh; - if( mesh->interpolation() == "catmullClark" ) + if( mesh->interpolation() != MeshPrimitive::interpolationLinear.string() ) { geometryType = Rix::k_Ri_SubdivisionMesh; - primVars.SetString( Rix::k_Ri_scheme, Rix::k_catmullclark ); + if( mesh->interpolation() == MeshPrimitive::interpolationCatmullClark.string() ) + { + primVars.SetString( Rix::k_Ri_scheme, Rix::k_catmullclark ); + } + else if( mesh->interpolation() == MeshPrimitive::interpolationLoop.string() ) + { + primVars.SetString( Rix::k_Ri_scheme, Rix::k_loop ); + } + else + { + msg( Msg::Error, "GeometryAlgo", fmt::format( "Unknown mesh interpolation \"{}\"", mesh->interpolation() ) ); + primVars.SetString( Rix::k_Ri_scheme, Rix::k_catmullclark ); + } vector tagNames; vector tagArgCounts; @@ -443,6 +525,8 @@ riley::GeometryPrototypeId convertStaticMesh( const IECoreScene::MeshPrimitive * vector tagFloatArgs; vector tagStringArgs; + // Creases + for( int creaseLength : mesh->creaseLengths()->readable() ) { tagNames.push_back( Rix::k_crease ); @@ -454,6 +538,8 @@ riley::GeometryPrototypeId convertStaticMesh( const IECoreScene::MeshPrimitive * tagIntArgs = mesh->creaseIds()->readable(); tagFloatArgs = mesh->creaseSharpnesses()->readable(); + // Corners + if( mesh->cornerIds()->readable().size() ) { tagNames.push_back( Rix::k_corner ); @@ -464,6 +550,22 @@ riley::GeometryPrototypeId convertStaticMesh( const IECoreScene::MeshPrimitive * tagFloatArgs.insert( tagFloatArgs.end(), mesh->cornerSharpnesses()->readable().begin(), mesh->cornerSharpnesses()->readable().end() ); } + // Interpolation rules + + tagNames.push_back( Rix::k_interpolateboundary ); + tagArgCounts.insert( tagArgCounts.end(), { 1, 0, 0 } ); + tagIntArgs.push_back( interpolateBoundary( mesh ) ); + + tagNames.push_back( Rix::k_facevaryinginterpolateboundary ); + tagArgCounts.insert( tagArgCounts.end(), { 1, 0, 0 } ); + tagIntArgs.push_back( faceVaryingInterpolateBoundary( mesh ) ); + + tagNames.push_back( Rix::k_smoothtriangles ); + tagArgCounts.insert( tagArgCounts.end(), { 1, 0, 0 } ); + tagIntArgs.push_back( smoothTriangles( mesh ) ); + + // Pseudo-primvars to hold the tags + primVars.SetStringArray( Rix::k_Ri_subdivtags, tagNames.data(), tagNames.size() ); primVars.SetIntegerArray( Rix::k_Ri_subdivtagnargs, tagArgCounts.data(), tagArgCounts.size() ); primVars.SetFloatArray( Rix::k_Ri_subdivtagfloatargs, tagFloatArgs.data(), tagFloatArgs.size() );