Getting different results with ComputeWorldBound for '/' when root is xform vs SkelRoot


I have attached two files with very similar stages. The main difference is that boxes_skel.usda has one of the boxes bound to a UsdSkel and, for that, the root prim has been converted from a xform to a SkelRoot.

The script provided will open both stages and calculate their extents by calling ComputeWorldBound on the root prim, then print it values.
I expected the results of this function call to be the same for both scenarios since the objects in the scene didn’t change transforms from one file to the other.

Given the difference is results, my question is:
Is there a difference when calculating word bounds when the root prim has changed from xform to a skelroot?

boxes.usda (4.4 KB)
boxes_skel.usda (5.7 KB)

from pxr import Usd, UsdGeom

stage = Usd.Stage.Open('boxes.usda')
prim = stage.GetPrimAtPath('/')

imageable = UsdGeom.Imageable(prim)
time = Usd.TimeCode.Default() # The time at which we compute the bounding box
bound = imageable.ComputeWorldBound(time, UsdGeom.Tokens.default_)
bound_range = bound.ComputeAlignedBox()

stage2 = Usd.Stage.Open('boxes_skel.usda')
prim2 = stage2.GetPrimAtPath('/')

imageable2 = UsdGeom.Imageable(prim2)
bound2 = imageable2.ComputeWorldBound(time, UsdGeom.Tokens.default_)
bound_range2 = bound2.ComputeAlignedBox()


What happens if you remove the SkelAnimation from your file? I wonder if that gets taken into account at all

I’ve updated the usd file removing the skelanimation, but no changes in the results. It gives the same extents as the one with animation, which is different from the initial one.
I’ve attached a new file without the animation prim. To clarify, even though there’s an animation prim, there are no animations in the stage.

boxes_no_anim.usda (5.2 KB)

It seems that SkelRoot prims derive their extents from the Skeleton prims (

Perhaps that should be mentioned at Universal Scene Description: Schemas In-Depth, which talks more about explicitly authoring skinned extents on the SkelRoot

That is a great point, @cameronwhite , and I’ll also note there is a bug in the example - it should be UsdGeomXform rather than UsdGeomTransform. Would you create an Issue for it?

To @barbalt 's question, yes SkelRoot provides an extent computation that UsdGeomBBoxCache is supposed to call when extents are unauthored, though an example that @dhruvgovil provided in an off-channel recently made me question whether that was working. The computation is not inexpensive, however, so we promote the pattern (especially when UsdSkel is being used for crowds purposes) of pre-authoring extents by using skelRootPrim.ComputeExtentFromPlugins() at each timeSample of animation, and using the result to author extents.

I’ve submitted Improve the documentation for UsdSkelRoot by cameronwhite · Pull Request #2695 · PixarAnimationStudios/OpenUSD · GitHub for improving these docs

Thanks @cameronwhite and @spiff
I think there could be more to it, though…
I’m not sure if this is expected, but using skelRootPrim.ComputeExtentFromPlugins() only seems to consider the extents of the joints themselves and it doesn’t consider the extents of the skinned geometry. So, any geometry that bigger than the joints, would be outside the bounding box. This is specially the case if you consider, for example, a character walking a longer distance outside their initial bounding box.
It’s my understanding that we have to also remove the extents from the skinned mesh. So, to go around that problem I had to traverse the entire stage, calculate the extent for each prim and union with the results of skelRootPrim.ComputeExtentFromPlugins()

That sounds incorrect (or unexpected) to me @barbalt . This code attempts to compute a padding factor by max’ing the extent of all skinned prims, and applying it to the extent of the posed joints at the requested time, and extents on the skinned prims would definitely be useful for performance. This is all heuristic, and we are open to improvements in the algorithm.

Hi Spiff,
Sorry to keep coming back to this. You mentioned that calculates the extents of skinned meshes, but when there are regular meshes under the same skelroot, wouldn’t those not be considered for the extents, in this case?

On a related note, when I click on a skinned Mesh (which I had the extents removed to work with the skelroot extents) it still shows the extents at the start position. Is this expected? I would imagine that it shouldn’t show the box like we see on the image below:

this is how I calculated the extents for this example above:

skelRootExtents = skelRoot.CreateExtentAttr();
static const auto includePurposes = { UsdGeomTokens->default_, UsdGeomTokens->renderVisibility,
	UsdGeomTokens->proxy };
for (size_t i = 0; i < usdTimeSamples.size(); ++i)
	const pxr::UsdTimeCode& timeCode = usdTimeSamples[i];
	VtVec3fArray ext(2);
	if (UsdGeomBoundable::ComputeExtentFromPlugins(skelRoot, timeCode, &ext))
		skelRootExtents.Set(ext, timeCode);

Hi @barbalt , lots going on here…

Firstly, on “non-skinned geometry under a SkelRoot not participating properly in the computed extent”: we could definitely do a better job here without adding a ton of cost, and it makes sense for us to do so since imaging such geometry already works fine. If you would file an Issue on OpenUSD, we’ll have a look!

Secondly, on “extents for individual skinned meshes being way off”: this is unfortunately expected because there isn’t a reasonable way for UsdSkel to tell UsdGeom that a Mesh’s extents should be computed differently when it has a SkelBindingAPI schema applied to it, so we just get the rest-pose. This is something we may be able to improve when OpenExec is ready. Anecdotally, we don’t suffer as much from this at Pixar, because for various pipeline reasons, we always encode the gross, root-level animation for each agent in the SkelRoot’s Xformable affine transformation - so the un-deformed meshes are still in roughly the right spot. But I believe that some external authoring (and glTF-conversion) tools are putting the gross motion into the root-level bone of the SkelAnimation, which has the virtue of allowing each animation to be fully self-contained and switchable just by binding a different SkelAnimation, but does throw off even the SkelRoot’s extents computation. I think we might be able to make progress on that latter aspect, by adding a property on the SkelRoot schema that identifies whether the root bone/joint should be considered a full local-to-parent transformation and thus excluded from extent computations.

Thirdly, it still very much defies my expectations that you should need to remove extents from the skinned meshes for the SkelRoot’s ComputeExtentFromPlugin to work properly. If that is what you are still experiencing, would you be willing to file a small repro in an Issue with that? That’s definitely leaving runtime performance on the table…

Thank you, spiff. I really appreciate your time to explain all this. It all makes sense.
I’ll create the issues for those edge cases.