Search code examples
javascriptaframe

AFrame: position offset units when adding new Object3D to the current one


I'm trying to add an Object3D to my gltf model and place it above the model. I'm doing it the following way:

this.el.addEventListener('model-loaded', () => {

            this.bar = new MyCustomObject3D();

            const size = new THREE.Vector3();
            let box = new THREE.Box3().setFromObject(this.el.object3D);
            box.getSize(size)
            let height = size.y + 1;

            this.bar.position.set(0, height, 0);
            this.el.setObject3D("bar", this.bar);
            // same result: 
            // this.el.object3D.add(this.bar); 
        })

The height is 2 and if I placed an element with this position into root (i.e. scene) it would be placed correctly right above the model. But when I add it to the Object3D it's being placed somewhere below the model on height ~0.5. Only by multiplying the height by 25 I could achieve the right position. So how to calculate the exact offset needed to place the new Object3D above the model without multiplying it to a random number?

UPDATE:

Adding reproducible example. Note the width and height I had to pass to GLTF model.


Solution

  • One way of placing objects above a model, would be grabbing its bounding box, and placing an object above it.

    In general, it it simple - just like you did it:

    let box = new THREE.Box3().setFromObject(this.el.object3D);
    box.getSize(size)
    let height = size.y + 1;
    this.bar.position.set(0, height, 0);
    

    But in this case - the bounding box is off. Way off. The minimum is way too low, and the maximum is somewhere in the middle. Why is that? (tldr: check it out here)

    The cuprit is: skinning. The model is transformed by its bones - which is a form of vertex displacement that happens on the GPU (vertex shader), and has nothing to do with the geometry (source).

    Here is some visual aid - the model with its armature: enter image description here And without the armature applied: enter image description here

    Now we see why the box is off - its corresponding to the bottom picture! So we need to re-create what the bones are doing to the geometry:

    1. The hard route

    1. You need to take a THREE.Box3.
    2. Iterate through each geometry point of the model
    3. Apply the bone transform to each point (it is done here - but not available in a-frame 1.0.4)
    4. Expand the THREE.Box3

    2. The easy route
    While looking into this, I've made a utility function THREE.Box3Utils.fromSkinnedMesh(mesh, box3); - box3 will be the bounding box of the model at the time when the function is called. The function is a part of this repo. Its used on this example.