Skip to content

Commit

Permalink
GeometryAlgo : Export subdivision tags
Browse files Browse the repository at this point in the history
And add support for loop subdivs.
  • Loading branch information
johnhaddon committed Nov 21, 2024
1 parent d2888a7 commit c470491
Show file tree
Hide file tree
Showing 2 changed files with 284 additions and 2 deletions.
180 changes: 180 additions & 0 deletions python/IECoreRenderManTest/RendererTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import IECore
import IECoreImage
import IECoreScene
import IECoreRenderManTest

import GafferTest
import GafferScene
Expand Down Expand Up @@ -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 )
Expand Down
106 changes: 104 additions & 2 deletions src/IECoreRenderMan/GeometryAlgo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,76 @@ GeometryAlgo::ConverterDescription<SpherePrimitive> 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(
Expand All @@ -432,17 +502,31 @@ 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<RtUString> tagNames;
vector<RtInt> tagArgCounts;
vector<RtInt> tagIntArgs;
vector<RtFloat> tagFloatArgs;
vector<RtToken> tagStringArgs;

// Creases

for( int creaseLength : mesh->creaseLengths()->readable() )
{
tagNames.push_back( Rix::k_crease );
Expand All @@ -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 );
Expand All @@ -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() );
Expand Down

0 comments on commit c470491

Please sign in to comment.