Skip to content Skip to sidebar Skip to footer

Aframe-physics-extras: Collision Of Dynamic Entity With Static Gltf Model

I'm trying to catch collision event between a dynamic sphere and a static gltf model. I'm building the gltf entity the following way: const template = document.createElement('a

Solution 1:

0. using setAttribute

setAttribute won't handle a list like:

template.setAttribute('body', 'type:static; shape:none');

Rather provide an object of the new attributes:

element.setAttribute("name", {
  "property 1": "value 1",
  "property 2": "value 2"
}); 

1. dynamic body/shape set-up

That being said, you can create a custom static cylinder like this:

element.setAttribute("body", {
   "type": "static",
   "shape": "none"
})

element.setAttribute("shape__cylinder", {
   'shape': 'cylinder',
   "height": 1.5,
   "radiusTop": 0.1,
   "radiusBottom": 0.2
})

check it out in this fiddle


2. dynamic shape for models

As for creating a dynamic shape for a gltf model. Personally i had no luck using cannon, although it worked well with the ammo driver. On the other hand I've had a huge FPS drop (on ~older mobile devices), so If possible, try using simple collision meshes for performance sake.

You can get a bounding box of a skinned model with a simple function I made:

let box = new THREE.Box3()
THREE.Box3Utils.fromSkinnedMesh(skinnedMesh, box);
// box should be the bounding box of the skinned mesh

3. animated models

I'd highly recommend creating simple collision shapes and attaching them to a certain bone in the model. A starting point could be this component:

<a-gltf-modelbone-collider="bone: boneName; halfExtents: 0.15 0.15 0.15">

The bounding box approach will be very complicated since:
  • You'd have to compute a bounding box for an non-rotated mesh
  • apply the rotation to the result (bounding boxes are world-aligned)
  • update the body on each tick, or even remove the shape and add it anew (source)

You can see both approaches in this example

Solution 2:

Meanwhile I was able to implement animated collision mesh for a gltf model. I used a helper function implemented by @Piotr Adam Milewski here. See more details about the function in this answer. Any other suggestions to improve performance are appreciated.

AFRAME.registerComponent('animated-collision-shape', {
    init: function () {
        this.nodeMap = {};

        this.el.setAttribute('body', 'type: static; shape: none;')

        this.el.addEventListener('model-loaded', () => {
            const size = new THREE.Vector3();
            let box = new THREE.Box3().setFromObject(this.el.object3D);
            box.getSize(size);
            this.offset = new CANNON.Vec3(0, size.y / 2, 0);

            let mesh = this.el.getObject3D("mesh");
            mesh.traverse(node => {
                if (node.isSkinnedMesh) {
                    this.skinnedMesh = node;
                    this.nodeMap[node.uuid] = {
                        mesh: node,
                        box: new THREE.Box3()
                    }
                }
            });

            if (!Object.keys(this.nodeMap).length) {
                this.nodeMap[0] = {
                    mesh: this.el.object3D,
                    box: new THREE.Box3()
                };
            }

            this.el.components["body"].shouldUpdateBody = true;
        })
    },

    remove: function () {
        this.removeBoxes();
    },

    tick: (function () {
        const size = new THREE.Vector3();
        let common_box_uuid = null;

        return function tick() {
            if (
                !Object.keys(this.nodeMap).length ||
                !this.el.body) {
                return;
            }

            let combine = this.data.combine === true

            let i = 0;
            for (let uuid inthis.nodeMap) {
                // Non - skinned caseif (!this.nodeMap[uuid].mesh.isSkinnedMesh) {
                    this.nodeMap[uuid].box.setFromObject(this.el.object3D);
                    return;
                }
                // skinned model. Either separate boxes, or combinedif (common_box_uuid && combine) {
                    utils.SkinnedMeshBBox.expandAABB(this.nodeMap[uuid].mesh, this.nodeMap[common_box_uuid].box);
                } else {
                    utils.SkinnedMeshBBox.getAABB(this.nodeMap[uuid].mesh, this.nodeMap[uuid].box);
                    common_box_uuid = uuid
                }

                if (isFinite(this.nodeMap[common_box_uuid].box.max.x)) {
                    this.nodeMap[common_box_uuid].box.getSize(size);
                    if (this.el.body.shapes[i]) {
                        this.el.body.shapes[i].halfExtents = new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2);
                        this.el.body.shapes[i].updateConvexPolyhedronRepresentation();
                    } else {
                        let shape = new CANNON.Box(new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2))
                        this.el.body.addShape(shape, this.offset, shape.orientation);
                    }
                    i++;
                }
            }

            this.el.components["body"].shouldUpdateWireframe = true;
        };
    })()
})

Post a Comment for "Aframe-physics-extras: Collision Of Dynamic Entity With Static Gltf Model"