Skip to content

Commit 94bddf2

Browse files
Merge pull request #1421 from johnhaddon/modelAPIBounds
USDScene : Load bounds from UsdGeomModelAPI extents hints
2 parents fdfd188 + 8a3a9ea commit 94bddf2

File tree

3 files changed

+93
-21
lines changed

3 files changed

+93
-21
lines changed

Changes

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
10.5.x.x (relative to 10.5.8.0)
22
========
33

4+
Improvements
5+
------------
46

7+
- 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`.
58

69
10.5.8.0 (relative to 10.5.7.1)
710
========

contrib/IECoreUSD/src/IECoreUSD/USDScene.cpp

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ IECORE_PUSH_DEFAULT_VISIBILITY
5858
#include "pxr/usd/usdGeom/camera.h"
5959
#include "pxr/usd/usdGeom/gprim.h"
6060
#include "pxr/usd/usdGeom/metrics.h"
61+
#include "pxr/usd/usdGeom/modelAPI.h"
6162
#include "pxr/usd/usdGeom/pointInstancer.h"
6263
#include "pxr/usd/usdGeom/primvar.h"
6364
#include "pxr/usd/usdGeom/primvarsAPI.h"
@@ -528,6 +529,33 @@ Imath::M44d localTransform( const pxr::UsdPrim &prim, pxr::UsdTimeCode time )
528529
return result;
529530
}
530531

532+
static const bool g_useModelAPIBounds = []() -> bool {
533+
const char *c = getenv( "IECOREUSD_USE_MODELAPI_BOUNDS" );
534+
if( !c )
535+
{
536+
return true;
537+
}
538+
return strcmp( c, "0" );
539+
}();
540+
541+
pxr::UsdAttribute boundAttribute( const pxr::UsdPrim &prim )
542+
{
543+
if( auto boundable = pxr::UsdGeomBoundable( prim ) )
544+
{
545+
return boundable.GetExtentAttr();
546+
}
547+
548+
if( g_useModelAPIBounds )
549+
{
550+
if( auto modelAPI = pxr::UsdGeomModelAPI( prim ) )
551+
{
552+
return modelAPI.GetExtentsHintAttr();
553+
}
554+
}
555+
556+
return pxr::UsdAttribute();
557+
}
558+
531559
// Used to assign a unique hash to each USD file. Using a global counter rather than the file name
532560
// means that we treat the same file as separate if it is closed and reopened. This means it's not
533561
// a problem if USD changes things when a file is reopened. USD appears to not in general guarantee
@@ -820,30 +848,32 @@ std::string USDScene::fileName() const
820848

821849
Imath::Box3d USDScene::readBound( double time ) const
822850
{
823-
pxr::UsdGeomBoundable boundable = pxr::UsdGeomBoundable( m_location->prim );
824-
if( !boundable )
825-
{
826-
return Imath::Box3d();
827-
}
828-
829-
pxr::UsdAttribute attr = boundable.GetExtentAttr();
851+
pxr::UsdAttribute attr = boundAttribute( m_location->prim );
830852
if( !attr.IsValid() )
831853
{
832854
return Imath::Box3d();
833855
}
834856

835857
pxr::VtArray<pxr::GfVec3f> extents;
836-
attr.Get<pxr::VtArray<pxr::GfVec3f> >( &extents, m_root->getTime( time ) );
858+
attr.Get( &extents, m_root->getTime( time ) );
837859

838-
if( extents.size() == 2 )
860+
// When coming from UsdGeomModelAPI, `extents` may contain several bounds,
861+
// on a per-purpose basis. Take the union, since the SceneInterface API only
862+
// has a single bound per location.
863+
Imath::Box3d result;
864+
for( size_t i = 0; i + 1 < extents.size(); i += 2 )
839865
{
840-
return Imath::Box3d(
841-
DataAlgo::fromUSD( extents[0] ),
842-
DataAlgo::fromUSD( extents[1] )
866+
const Imath::Box3d b(
867+
DataAlgo::fromUSD( extents[i] ),
868+
DataAlgo::fromUSD( extents[i+1] )
843869
);
870+
if( !b.isEmpty() )
871+
{
872+
result.extendBy( b );
873+
}
844874
}
845875

846-
return Imath::Box3d();
876+
return result;
847877
}
848878

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

874904
bool USDScene::hasBound() const
875905
{
876-
pxr::UsdGeomBoundable boundable = pxr::UsdGeomBoundable( m_location->prim );
877-
pxr::UsdAttribute attr;
878-
879-
if( boundable )
880-
{
881-
attr = boundable.GetExtentAttr();
882-
}
883-
906+
pxr::UsdAttribute attr = boundAttribute( m_location->prim );
884907
return attr.IsValid();
885908
}
886909

contrib/IECoreUSD/test/IECoreUSD/USDSceneTest.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4211,5 +4211,51 @@ def testTreatLightAsPointOrLine( self ) :
42114211
self.assertEqual( root.child( "sphereLight" ).readAttribute( "light", 0 ), sphereLightShader )
42124212
self.assertEqual( root.child( "cylinderLight" ).readAttribute( "light", 0 ), cylinderLightShader )
42134213

4214+
def testModelBound( self ) :
4215+
4216+
fileName = os.path.join( self.temporaryDirectory(), "modelBound.usda" )
4217+
4218+
stage = pxr.Usd.Stage.CreateNew( fileName )
4219+
pxr.UsdGeom.Xform.Define( stage, "/withoutModelAPI" )
4220+
pxr.UsdGeom.Xform.Define( stage, "/withModelAPI" )
4221+
pxr.UsdGeom.Xform.Define( stage, "/withModelAPIAndExtent" )
4222+
4223+
pxr.UsdGeom.ModelAPI.Apply( stage.GetPrimAtPath( "/withModelAPI" ) )
4224+
modelAPI = pxr.UsdGeom.ModelAPI.Apply( stage.GetPrimAtPath( "/withModelAPIAndExtent" ) )
4225+
modelAPI.SetExtentsHint( [ ( 1, 2, 3 ), ( 4, 5, 6 ) ] )
4226+
4227+
stage.GetRootLayer().Save()
4228+
del stage
4229+
4230+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
4231+
self.assertFalse( root.hasBound() )
4232+
4233+
self.assertFalse( root.child( "withoutModelAPI" ).hasBound() )
4234+
self.assertFalse( root.child( "withModelAPI" ).hasBound() )
4235+
self.assertTrue( root.child( "withModelAPIAndExtent" ).hasBound() )
4236+
self.assertEqual( root.child( "withModelAPIAndExtent" ).readBound( 0 ), imath.Box3d( imath.V3d( 1, 2, 3 ), imath.V3d( 4, 5, 6 ) ) )
4237+
4238+
def testPerPurposeModelBound( self ) :
4239+
4240+
fileName = os.path.join( self.temporaryDirectory(), "testPerPurposeModelBound.usda" )
4241+
4242+
stage = pxr.Usd.Stage.CreateNew( fileName )
4243+
pxr.UsdGeom.Xform.Define( stage, "/group" )
4244+
cube = pxr.UsdGeom.Cube.Define( stage, "/group/proxy" )
4245+
cube.CreatePurposeAttr().Set( "proxy" )
4246+
4247+
bboxCache = pxr.UsdGeom.BBoxCache( pxr.Usd.TimeCode( 0 ), [ "default", "render", "proxy", "guide" ] )
4248+
modelAPI = pxr.UsdGeom.ModelAPI.Apply( stage.GetPrimAtPath( "/group" ) )
4249+
modelAPI.SetExtentsHint( modelAPI.ComputeExtentsHint( bboxCache ) )
4250+
4251+
stage.GetRootLayer().Save()
4252+
del stage
4253+
4254+
root = IECoreScene.SceneInterface.create( fileName, IECore.IndexedIO.OpenMode.Read )
4255+
self.assertFalse( root.hasBound() )
4256+
4257+
self.assertTrue( root.child( "group" ).hasBound() )
4258+
self.assertEqual( root.child( "group" ).readBound( 0 ), imath.Box3d( imath.V3d( -1 ), imath.V3d( 1 ) ) )
4259+
42144260
if __name__ == "__main__":
42154261
unittest.main()

0 commit comments

Comments
 (0)