Three.js: GLTFLoader: Very large bounding box w/ morph targets

Created on 4 Mar 2020  路  10Comments  路  Source: mrdoob/three.js

I exported origami paper folding animation which was made using the extension AnimAll in blender to three.js and the materials wont show up and the animations array would be empty.

The problem is similar to this: https://blender.stackexchange.com/questions/49815/blender-scene-animation-exported-to-three-js-without-bones-or-skinning

Bug Loaders

Most helpful comment

I think it's helpful that GLTFLoader can save the application load time by precomputing the bounding box without scanning vertices, but maybe we're being too conservative by including morph targets? Some options:

The issue as I understand it isn't that we aren't scanning vertices, but that we're assuming that the worst case box is when all weights are at their extremes (1)? So we're correctly estimating the impact of each individual target, but assuming that they are all applied simultaneously. If you do set all morph weights to 1, you are probably going to end up with a box 250x the size :D

But yeah this is wildly suboptimal if the application uses morph targets for keyframe animations. I agree that option (b) seems like a reasonable compromise, although it will sometimes result in boxes that are slightly too small.

Perhaps it's a better default, and if anyone notices we can improve this by providing an option to compute data conservatively. Fundamentally the correct solution here is probably to store per-target boxes and let three.js dynamically recompute the box based on runtime supplied weights (and something similar is required to compute a correct box for skinned objects...), but that's a very large core change that has many implications.

All 10 comments

Can you please share the exported glb and blend file in this thread? Also ensure to use the latest version of Blender.

https://drive.google.com/open?id=1K3m_0bWm6_4KckSu9MrWxvSey0vt4qZQ

i also tried with fbx and obj, but no results

This .glb file does not contain any animation, you can confirm on https://gltf-viewer.donmccurdy.com/ and https://sandbox.babylonjs.com/. So the issue is not in GLTFLoader.

Vertex animation isn't supported by engines, or by glTF, so you'll need to bake the animation into a series of morph targets. You can do this by:

  1. Enable the MDD addon in Blender (Edit -> Preferences -> Add-ons).
  2. Export to MDD (point cache).
  3. Import the MDD file back into Blender.
  4. Export to glTF again.

The result should work, but feel free to ask for help on the forums if you get stuck: https://discourse.threejs.org/.

i also tried with fbx and obj, but no results

The suggestions above should also work for FBX, but the OBJ format doesn't support animation at all.

After some discussion in the forums (https://discourse.threejs.org/t/vertex-animation-not-playing-correctly-in-three-js/13309) I'm reopening this, there is at least one issue going on here that is arguably a bug in GLTFLoader:

We construct a very conservative bounding box for geometry with morph targets, taking the worst case scenario where _all_ morph targets are applied simultaneously:

https://github.com/mrdoob/three.js/blob/9cd486bd4fc2f19d2817ed5f93ac0cf736dfe43c/examples/js/loaders/GLTFLoader.js#L2249

However, this leads to a problem in models with morph target sequences (e.g. anything generated by the Blender->MDD->Blender->glTF workflow I suggested above). The model has 250 morph targets, and adding them all up gives you a bounding box that's ~250 times larger in some dimensions than it should be. The immediate effect here is that https://gltf-viewer.donmccurdy.com/ does a very poor job positioning the camera.

I think it's helpful that GLTFLoader can save the application load time by precomputing the bounding box without scanning vertices, but maybe we're being too conservative by including morph targets? Some options:

(a) Ignore morph targets here, just use the static pose.
(b) Use the extremes of all morph targets, rather than the sums.
(c) Consider only worst-case sum of 4-8 morph targets, since that's all that could be applied simultaneously. Box would still be too large for this particular model, by a more reasonable 4x-8x.
(d) Require user input to change the behavior.

Note that BufferGeometry.computeBoundingBox does the same thing as GLTFLoader. I think I'm leaning toward option (b).

/cc @zeux

I think it's helpful that GLTFLoader can save the application load time by precomputing the bounding box without scanning vertices, but maybe we're being too conservative by including morph targets? Some options:

The issue as I understand it isn't that we aren't scanning vertices, but that we're assuming that the worst case box is when all weights are at their extremes (1)? So we're correctly estimating the impact of each individual target, but assuming that they are all applied simultaneously. If you do set all morph weights to 1, you are probably going to end up with a box 250x the size :D

But yeah this is wildly suboptimal if the application uses morph targets for keyframe animations. I agree that option (b) seems like a reasonable compromise, although it will sometimes result in boxes that are slightly too small.

Perhaps it's a better default, and if anyone notices we can improve this by providing an option to compute data conservatively. Fundamentally the correct solution here is probably to store per-target boxes and let three.js dynamically recompute the box based on runtime supplied weights (and something similar is required to compute a correct box for skinned objects...), but that's a very large core change that has many implications.

I agree. @Mugen87 do you have a preference here? I suppose this is similar to bounding box questions for InstancedMesh and SkinnedMesh we've discussed.

I think I'm leaning toward option (b).

Okay, let's see how this works with GLTFLoader.

@Mugen87 Did you mean wait before updating BufferGeometry as well? Or do both in one release?

I would start with GLTFLoader and wait for any reactions.

Was this page helpful?
0 / 5 - 0 ratings