How to set "extent" attribute of custom prim?

Hi, guys
I created a custom primadapter inherirted from UsdImagingPrimAdapter. I generated some curves in my custom prim. But canot set the extent of the prim. I overrided the GetExtent() method like below. But the extent in usdviewer is still empty. Anyone any idea?

GfRange3d myPrimAdapter::GetExtent(UsdPrim const& usdPrim, 
                                     SdfPath const& cachePath, 
                                     UsdTimeCode time) const
{
auto myextent=computeMyExtent();
// I print variable myextent here and see the right value.
return myextent;
}

I also overrided ComputeExtent method of UsdGeomGprim.

What is the difference of GetExtent in primAdapter and ComputeExtent in UsdGeomGprim?

The extents displayed by usdview have nothing to do with Hydra - they come from the intrinsic notion of extent defined by the UsdGeomBoundable schema class. You’re in uncharted waters trying to add a new geometry type just to usdImaging without basing it on a new “custom” USD schema that you create. If you do create a new USD schema, then you can provision your extent computation into your schema’s .cpp file, using UsdGeomRegisterComputeExtentFunction - you’ll find examples of how it’s used in the UsdGeom source files.

If you do this, then you won’t need to override the primAdapter’s GetExtent() at all, and clients that use UsdGeomBBoxCache (like usdview) will bound your prims properly.

Hi Thanks, Sebastian
I added the ComputeExtent like below. Basically following the cube.cpp in usd source. And I see the “World Bounding Box” has the right value “Min[-1,-1,-1] Max[2,2,2]”. But the “extent” attribute is still empty, why?

bool
MySchema::ComputeExtent(VtVec3fArray* extent)
{
   extent->resize(2);
   (*extent)[0]=GfVec3f(-1);
   (*extent)[1]=GfVec3f(2);
   std::cout<<"-----------Copmute Non Transform extent..."<<std::endl;
   return true;
}


bool
MySchema::ComputeExtent(const GfMatrix4d& transform,
   VtVec3fArray* extent)
{
   extent->resize(2);
   (*extent)[0]=GfVec3f(-1);
   (*extent)[1]=GfVec3f(2);
   std::cout<<"-----------Copmute Transform extent..."<<std::endl;
   return true;
}


static bool
_ComputeExtent(
   const UsdGeomBoundable& boundable,
   const UsdTimeCode& time,
   const GfMatrix4d* transform,
   VtVec3fArray* extent)
{   
   const MySchema testSchema(boundable);
   if (!TF_VERIFY(testSchema)) {
       return false;
   }


   if (transform) {
       return MySchema::ComputeExtent(*transform, extent);
   } else {
       return MySchema::ComputeExtent(extent);
   }
}

TF_REGISTRY_FUNCTION(UsdGeomBoundable)
{
   UsdGeomRegisterComputeExtentFunction<MySchema>(
       _ComputeExtent);
}

In order to be fast and scalable, USD currently takes the position that “resolving the value of an attribute” (i.e. UsdAttribute::Get()) involves no computation other than walking through the “composition graph” of sites that contribute opinions to the property to find the strongest authored opinion. Attributes defined by a USD schema can optionally provide a “fallback opinion” that Get() will return when there are no authored opinions (such as a Sphere.radius fallback of 1.0).

But there is no mechanism for attaching general/custom computations to an attribute’s value resolution. As such, the ComputeExtent() mechanism that you plugged into is bespoke, and only clients that know about it and opt-in to using it will benefit from it. The UsdGeomBBoxCache and Hydra/UsdImaging both do.

Our nascent OpenExec project will provide us with a way to attach computations to attributes, but we will be very deliberate about where and how we use it in the existing USD schemas, to ensure we retain scalability.

Thank you so much,Sebastian.
So do you mean currently i cannot set “extent”?
For the prim type “cube”, I can see the “extent” attribute updates when changing “height”,“radius”. Want to know how it works.

You can absolutely set extent! And use your computation to do it! So, for a non-time-varying scene you are constructing, once you have added your custom prim and configured the attributes that govern its size, in python you can:

myPrim.CreateExtentAttr(UsdGeom.Boundable.ComputeExtentFromPlugins(myPrim, Usd.TimeCode.Default()))

There is a non-static ComputeExtent() method, but it would be tricky to use if your prim were animated and you needed to cache out extent at multiple timeSamples. The static computation will always give you the accurate result.

Hi Sebastian, maybe I misdescribed my question . By “set extent”, I mean how to make the “extent” attribute in usdviewer non-empty.
With the static ComputeExtent function, i can set the “World Bounding Box” correctly and the bouding box was also drew correctly in the viewer, but the “extent” attribute is empty. Think they should be the same.
Below is the screenshot.

I understand. The only way to have a non-empty extent attribute in today’s OpenUSD is to explicitly author it in the scene, as I described in my last post (though how your software actually computes it is up to it… you don’t need to use the ComputeExtentFromPlugins method)

Really appreciate it Sebastian.
Got a last question. Is it possible to use GetExtent() method in UsdImagingGprimAdapter to compute the extent and make it display in the “World Bounding Box” attribute instead of what I am using now?

If you’re willing to modify USD source code, you can change the python computation in usdviewq that produces the “World Bounding Box” to do whatever you want. I don’t really recommend that, though, and I don’t think Hydra allows you to grab a UsdPrim’s adapter from outside…

Ok, Thank you Sebastian. You are always helpful.

Hi Spiff
I got a new question.
Currently the only way for us to set the World Bounding Box is to provide the bbox data in UsdGeomRegisterComputeExtentFunction in the Schema. But we are generating our points data in Adapter, meanwhile we can get the right bounding box from the points. The question is what is the best way to pass the bounding box data in Adapter to Schema? What we are doing now is save the bouding box data in a global variable like unordered_map whose key is the prim path and get the data in Schema. The reason for this is I dont want to do the computation(which is expensive) again in Schema just for the bounding box. Do you have any suggestions?

Hi @JerryK. It’s not uncommon for primitives in OpenUSD to have different granularities of bounding computations with the schema being the “coarsest”. For example, Mesh doesn’t apply subdivision surface refinement when computing bounds even though it would likely make the bounds tighter.

Is there a faster / coarser algorithm that could be used for the schema?

Thank you Matthew, got your point. Will think about it.