Is your feature request related to a problem? Please describe.
The local bounds of a SkinnedMeshRenderer are computed once at load time from mesh.bounds (the bind-pose POSITION AABB) and only follow the rootBone transform at runtime. This is correct and tight for the bind pose, but the bounding box does not track the deformation produced by the other joints during animation.
When an animation moves a joint far from its bind pose — large rotation, translation, or especially scale — the skinned vertices move outside the cached bounds, while the bounds stay frozen in the bind-pose shape. The box becomes too small and the renderer can be incorrectly frustum-culled (the character is on screen but disappears) or mis-picked.
Verified with a minimal repro: a mesh bound to a single joint, bind pose bounds [-1, 1]. After scaling that joint by 50 at runtime, the real skinned vertices reach [-50, 50], but renderer.bounds stays at [-1, 1] — 50x too small.
Note this is not a bug in the load-time computation (PR #3027 made the bind-pose bounds correct, including the case where rootBone is outside skin.joints). It is an inherent limitation of approximating the whole skinned mesh by a single rootBone-rigid box. Conformant bind-pose bounds are exact; the gap is purely the runtime/animated case.
Describe the solution you'd like
A way to make skinned bounds cover the animated range, opt-in so the default O(1) path is unaffected. Since we have an editor with an import/bake stage, the cleanest option is to bake an expanded local bounds offline (covering the mesh's animation poses) into the asset, and read it at runtime with no extra per-frame cost.
Describe alternatives you've considered
- Runtime per-frame recompute (opt-in flag): iterate skinned vertices each frame to produce an exact AABB. Most accurate, but O(vertices) per frame — only worth enabling for meshes that genuinely need it.
- Manual bounds extension: let the asset/author specify a positive/negative margin to inflate the bind-pose box enough to contain extreme poses. Cheapest, but the margin must be chosen by hand.
Additional context
- The accurate-but-expensive direction (covering animation) requires traversing skinned vertices — there is no cheap "look at bones only" estimate that is both tight and never-too-small, because the AABB is not preserved under the per-vertex skinning transform.
- Baking only the bind pose has no benefit for conformant assets: at bind pose
joint.world · IBM = I, so the skinned shape equals mesh.bounds and a bind-pose bake reproduces the current result exactly. The value is specifically in covering animation poses.
- Suggested scope: editor-side bake of an animation-covering local bounds, with the runtime falling back to the current rootBone-space box when no baked bounds are present.
Related: #3027 (load-time skin rootBone bounds fix — this issue is the follow-up runtime-accuracy work).
Is your feature request related to a problem? Please describe.
The local bounds of a
SkinnedMeshRendererare computed once at load time frommesh.bounds(the bind-pose POSITION AABB) and only follow therootBonetransform at runtime. This is correct and tight for the bind pose, but the bounding box does not track the deformation produced by the other joints during animation.When an animation moves a joint far from its bind pose — large rotation, translation, or especially scale — the skinned vertices move outside the cached bounds, while the bounds stay frozen in the bind-pose shape. The box becomes too small and the renderer can be incorrectly frustum-culled (the character is on screen but disappears) or mis-picked.
Verified with a minimal repro: a mesh bound to a single joint, bind pose bounds
[-1, 1]. After scaling that joint by 50 at runtime, the real skinned vertices reach[-50, 50], butrenderer.boundsstays at[-1, 1]— 50x too small.Note this is not a bug in the load-time computation (PR #3027 made the bind-pose bounds correct, including the case where
rootBoneis outsideskin.joints). It is an inherent limitation of approximating the whole skinned mesh by a single rootBone-rigid box. Conformant bind-pose bounds are exact; the gap is purely the runtime/animated case.Describe the solution you'd like
A way to make skinned bounds cover the animated range, opt-in so the default O(1) path is unaffected. Since we have an editor with an import/bake stage, the cleanest option is to bake an expanded local bounds offline (covering the mesh's animation poses) into the asset, and read it at runtime with no extra per-frame cost.
Describe alternatives you've considered
Additional context
joint.world · IBM = I, so the skinned shape equalsmesh.boundsand a bind-pose bake reproduces the current result exactly. The value is specifically in covering animation poses.Related: #3027 (load-time skin rootBone bounds fix — this issue is the follow-up runtime-accuracy work).