Skip to content

Skinned mesh bounds are too small during animation (only track rootBone, not joint deformation) #3033

Description

@GuoLei1990

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).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrenderingRendering related functions

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions