Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

USDScene : Load bounds from UsdGeomModelAPI extents hints #1421

Merged
merged 1 commit into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Changes
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
10.5.x.x (relative to 10.5.8.0)
========

Improvements
------------

- USDScene : `hasBound()` and `readBound()` now use `UsdGeomModelAPI` extents hints if they are available. This behaviour can be disabled by setting the `IECOREUSD_USE_MODELAPI_BOUNDS` environment variable to a value of `0`.

10.5.8.0 (relative to 10.5.7.1)
========
Expand Down
65 changes: 44 additions & 21 deletions contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY
#include "pxr/usd/usdGeom/camera.h"
#include "pxr/usd/usdGeom/gprim.h"
#include "pxr/usd/usdGeom/metrics.h"
#include "pxr/usd/usdGeom/modelAPI.h"
#include "pxr/usd/usdGeom/pointInstancer.h"
#include "pxr/usd/usdGeom/primvar.h"
#include "pxr/usd/usdGeom/primvarsAPI.h"
Expand Down Expand Up @@ -528,6 +529,33 @@ Imath::M44d localTransform( const pxr::UsdPrim &prim, pxr::UsdTimeCode time )
return result;
}

static const bool g_useModelAPIBounds = []() -> bool {
const char *c = getenv( "IECOREUSD_USE_MODELAPI_BOUNDS" );
if( !c )
{
return true;
}
return strcmp( c, "0" );
}();

pxr::UsdAttribute boundAttribute( const pxr::UsdPrim &prim )
{
if( auto boundable = pxr::UsdGeomBoundable( prim ) )
{
return boundable.GetExtentAttr();
}

if( g_useModelAPIBounds )
{
if( auto modelAPI = pxr::UsdGeomModelAPI( prim ) )
{
return modelAPI.GetExtentsHintAttr();
}
}

return pxr::UsdAttribute();
}

// Used to assign a unique hash to each USD file. Using a global counter rather than the file name
// means that we treat the same file as separate if it is closed and reopened. This means it's not
// a problem if USD changes things when a file is reopened. USD appears to not in general guarantee
Expand Down Expand Up @@ -820,30 +848,32 @@ std::string USDScene::fileName() const

Imath::Box3d USDScene::readBound( double time ) const
{
pxr::UsdGeomBoundable boundable = pxr::UsdGeomBoundable( m_location->prim );
if( !boundable )
{
return Imath::Box3d();
}

pxr::UsdAttribute attr = boundable.GetExtentAttr();
pxr::UsdAttribute attr = boundAttribute( m_location->prim );
if( !attr.IsValid() )
{
return Imath::Box3d();
}

pxr::VtArray<pxr::GfVec3f> extents;
attr.Get<pxr::VtArray<pxr::GfVec3f> >( &extents, m_root->getTime( time ) );
attr.Get( &extents, m_root->getTime( time ) );

if( extents.size() == 2 )
// When coming from UsdGeomModelAPI, `extents` may contain several bounds,
// on a per-purpose basis. Take the union, since the SceneInterface API only
// has a single bound per location.
Imath::Box3d result;
for( size_t i = 0; i + 1 < extents.size(); i += 2 )
{
return Imath::Box3d(
DataAlgo::fromUSD( extents[0] ),
DataAlgo::fromUSD( extents[1] )
const Imath::Box3d b(
DataAlgo::fromUSD( extents[i] ),
DataAlgo::fromUSD( extents[i+1] )
);
if( !b.isEmpty() )
{
result.extendBy( b );
}
}

return Imath::Box3d();
return result;
}

ConstDataPtr USDScene::readTransform( double time ) const
Expand Down Expand Up @@ -873,14 +903,7 @@ void USDScene::path( SceneInterface::Path &p ) const

bool USDScene::hasBound() const
{
pxr::UsdGeomBoundable boundable = pxr::UsdGeomBoundable( m_location->prim );
pxr::UsdAttribute attr;

if( boundable )
{
attr = boundable.GetExtentAttr();
}

pxr::UsdAttribute attr = boundAttribute( m_location->prim );
return attr.IsValid();
}

Expand Down
46 changes: 46 additions & 0 deletions contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4211,5 +4211,51 @@ def testTreatLightAsPointOrLine( self ) :
self.assertEqual( root.child( "sphereLight" ).readAttribute( "light", 0 ), sphereLightShader )
self.assertEqual( root.child( "cylinderLight" ).readAttribute( "light", 0 ), cylinderLightShader )

def testModelBound( self ) :

fileName = os.path.join( self.temporaryDirectory(), "modelBound.usda" )

stage = pxr.Usd.Stage.CreateNew( fileName )
pxr.UsdGeom.Xform.Define( stage, "/withoutModelAPI" )
pxr.UsdGeom.Xform.Define( stage, "/withModelAPI" )
pxr.UsdGeom.Xform.Define( stage, "/withModelAPIAndExtent" )

pxr.UsdGeom.ModelAPI.Apply( stage.GetPrimAtPath( "/withModelAPI" ) )
modelAPI = pxr.UsdGeom.ModelAPI.Apply( stage.GetPrimAtPath( "/withModelAPIAndExtent" ) )
modelAPI.SetExtentsHint( [ ( 1, 2, 3 ), ( 4, 5, 6 ) ] )

stage.GetRootLayer().Save()
del stage

root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
self.assertFalse( root.hasBound() )

self.assertFalse( root.child( "withoutModelAPI" ).hasBound() )
self.assertFalse( root.child( "withModelAPI" ).hasBound() )
self.assertTrue( root.child( "withModelAPIAndExtent" ).hasBound() )
self.assertEqual( root.child( "withModelAPIAndExtent" ).readBound( 0 ), imath.Box3d( imath.V3d( 1, 2, 3 ), imath.V3d( 4, 5, 6 ) ) )

def testPerPurposeModelBound( self ) :

fileName = os.path.join( self.temporaryDirectory(), "testPerPurposeModelBound.usda" )

stage = pxr.Usd.Stage.CreateNew( fileName )
pxr.UsdGeom.Xform.Define( stage, "/group" )
cube = pxr.UsdGeom.Cube.Define( stage, "/group/proxy" )
cube.CreatePurposeAttr().Set( "proxy" )

bboxCache = pxr.UsdGeom.BBoxCache( pxr.Usd.TimeCode( 0 ), [ "default", "render", "proxy", "guide" ] )
modelAPI = pxr.UsdGeom.ModelAPI.Apply( stage.GetPrimAtPath( "/group" ) )
modelAPI.SetExtentsHint( modelAPI.ComputeExtentsHint( bboxCache ) )

stage.GetRootLayer().Save()
del stage

root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
self.assertFalse( root.hasBound() )

self.assertTrue( root.child( "group" ).hasBound() )
self.assertEqual( root.child( "group" ).readBound( 0 ), imath.Box3d( imath.V3d( -1 ), imath.V3d( 1 ) ) )

if __name__ == "__main__":
unittest.main()
Loading