schteppe/cannon.js

How to add a collision body to a model

OliverXH opened this issue · 19 comments

Using Cannonjs as the physical engine in Threejs, how to add collision bodies to the imported model.

Modeling with Blender

Hey @OliverXH! I hope you got your project working by now :)

For those here looking for help, here's an example of a glTF (.glb) blender-made-and-exported model loaded into a cannon environment.

This was made with the actively maintained use-cannon, however, the same methodology applies, if you're planning on using shapes that cannon already supports or approximating them yourself:

  1. Load your 3D model
  2. convert it to THREE.Geometry new THREE.Geometry().fromBufferGeometry(<your geo>)
  3. depending on the shape you're using, pass the dimensions of your geometry to a new cannon shape: for a convexpolyhedron (ensure that your model is actually convex!), pass the vertices array as is, and convert the faces array to Cannon.Vec3() before passing them
  4. Add the resulting body to your scene, as in the bunny demo

Good luck!

@stockhuman

Thank you very much for your answer.

I don't know if it's my fault, but I still have a lot of problems in the process of creating using this method, including the collision failure of some faces of the created rigid body. Here is my code:

    let shape, rigidBody, mesh, geometry;

    geometry = new THREE.Geometry().fromBufferGeometry(mesh.geometry);

    let scale = mesh.scale;

    let vertices = [], faces = [];

    // Add vertices
    for (let i = 0; i < geometry.vertices.length; i++) {

        let x = scale.x * geometry.vertices[i].x;
        let y = scale.y * geometry.vertices[i].y;
        let z = scale.z * geometry.vertices[i].z;

        vertices.push(new CANNON.Vec3(x, y, z));
    }

    for (let i = 0; i < geometry.faces.length; i++) {

        let a = geometry.faces[i].a;
        let b = geometry.faces[i].b;
        let c = geometry.faces[i].c;

        faces.push([a, b, c]);
    }

    console.log(vertices, faces);

    shape = new CANNON.ConvexPolyhedron(vertices, faces);

    rigidBody = new CANNON.Body({
        mass: 0,
        shape: shape
    });
    // rigidBody.position.copy(mesh.position);

My test model is a platform with slopes on both sides, and I drive a car to test it.

And what if there is a depression in the model?

The shape must be convex for the collision math to work. If there is a "depression in it", that sounds like the shape isn't convex, and this will lead to errors. You can also try exporting your model with the scale already baked in as desired, and omit the extra computation.

If you have a sandbox to post that might help.

@stockhuman

Is the effect of ConvexPolyhergon in Cannonjs the same as that of Mesh Collider in Unity when convex is checked? if so, can Cannonjs achieve the effect of Mesh Collider?

It is not. Cannon does not check if a mesh is Convex (it's actually a pending todo). You can use Three utilities to generate a convex mesh, but that will naturally result in different collisions than what you'd expect from your original model.

@stockhuman

Is there any way it can make a shape, like Mesh Collider in Unity?

I built a model of an overpass in blender, for which my model needs a more accurate collision body. I don't know if Cannonjs can achieve this level of accuracy. Is there any other way?

Screenshot of the model

QQ截图20200630130637

collision failure of some faces
QQ截图20200630152016

With geometries like this, it might be easier to use a navigation mesh.

@dirkk0

I'm sorry. I don't know what you mean.

My apologies, I should've explained that.

A navigation mesh is a special mesh that represents the ground your objects move on (for example the player). Then you restrict the player movement to this mesh. This lib does this and has more explanations:
https://github.com/donmccurdy/three-pathfinding

My point really was: I always thought that creating a NavMesh need of some complicated algorithm, which it is not. I found a quick way to create it manually in Blender (basically separating the ground, insetting it, splitting it and save it as another model).

It would take just a couple of minutes to do this with your model. I can create a quick tutorial if you want to.

@dirkk0

I'm glad you answered, and I'm interested, too, but I'm not talking about the pathfinding algorithm at the moment. I'm looking for a way to create a collision body for the imported model, whose shape is the shape of the model mesh.

This is a bit like mesh collider in unity .My project needs a more precise shape. Just like the overpass in my picture above.

I don't know if you understand or if you have a similar way.

I am not talking about pathfinding either. Maybe this example gets my point across:
https://navmesh-test.glitch.me/
(This is A-Frame, but this is also ThreeJS under the hood, and the same principle applies.)

You see that the player movement is constrained to the model -just not the 'real' model but an invisible navigation mesh (which was derived from the model, manually). But in this example you can't jump. This would be enough for an FPS, but not for a racing game that needs more physics than just this simple constraint.

Since I assume that you are creating something like a racing game, this won't be sufficient to you.
Still, you could create a very a) simple model that is b) invisible and is c) only needed for the collision.

Can someone please explain to me how the faces work (from a geometric sense)?
How does the array of numbers represent a face? Is it suppose to be a normal vector through the face or something?

UPDATE: Just saw @OliverXH code and realized you could just copy the vertices and faces from the THREE mesh to the CANNON body. Oh my goodness. This helps so much. Don't even need to know what the face arrays mean.

After searching in many ways, I found it a bit impractical to do so.
It still requires visualization to create collision bodies for such complex models, but the video I saw on YouTube is curious about how he created collision bodies for these complex geometry.

https://youtu.be/RAOOoV_TBq4

The comment said that Blender should be used to export physical data, but I am still a little curious about how to do this.

Thanks for bringing this video to my attention. I remember I found this project once, and then forget the link.
Then: as I understand it, he parses the glTF files and adds Cannon objects on the fly. He claims he added custom properties but I cannot see them in the .glb files, but maybe they were lost in the re-export. So he creates an invisible set of Cannon objects for physics only.

Forgive me, I still don't understand exactly how this works. Did he redefine the export file or did he add other data to the file?

Have you made any progress on this project?

I don't know how exactly this guy did, but I can explain what I did in a hopefully similar solution.
I create Blender files with a certain naming convention for the objects, let's say 'box_' like in box_003.
Then I parse the nodes of the exported glTF file and create objects (I created A-Frame entities, you will want to create Cannon objects):

mesh.traverse((node) => {
  if (node.name.slice(0, 4) == "box_") {
    console.log(node.name, node.uuid, node.position);
    console.log("found box", node.name);
    let object = scene.getObjectByProperty("name", node.name);
    // create Cannon box
  }
}

I think he does something similar here:
https://github.com/swift502/Sketchbook/blob/740943d68aed8952bf1f6e86abbcd3b7ff9599ff/src/lib/utils/three-to-cannon.js

@dirkk0

Thanks a lot.

I'm trying to add some features to threejs' editor, I want to add collision bodies through visual operations.

Daudxu commented

Hey @OliverXH! I hope you got your project working by now :)

For those here looking for help, here's an example of a glTF (.glb) blender-made-and-exported model loaded into a cannon environment.

This was made with the actively maintained use-cannon, however, the same methodology applies, if you're planning on using shapes that cannon already supports or approximating them yourself:

  1. Load your 3D model
  2. convert it to THREE.Geometry new THREE.Geometry().fromBufferGeometry(<your geo>)
  3. depending on the shape you're using, pass the dimensions of your geometry to a new cannon shape: for a convexpolyhedron (ensure that your model is actually convex!), pass the vertices array as is, and convert the faces array to Cannon.Vec3() before passing them
  4. Add the resulting body to your scene, as in the bunny demo

Good luck!

How to put cannon-es.js into the GLB ground

Daudxu commented

Hey @OliverXH! I hope you got your project working by now :)

For those here looking for help, here's an example of a glTF (.glb) blender-made-and-exported model loaded into a cannon environment.

This was made with the actively maintained use-cannon, however, the same methodology applies, if you're planning on using shapes that cannon already supports or approximating them yourself:

  1. Load your 3D model
  2. convert it to THREE.Geometry new THREE.Geometry().fromBufferGeometry(<your geo>)
  3. depending on the shape you're using, pass the dimensions of your geometry to a new cannon shape: for a convexpolyhedron (ensure that your model is actually convex!), pass the vertices array as is, and convert the faces array to Cannon.Vec3() before passing them
  4. Add the resulting body to your scene, as in the bunny demo

Good luck!
How to use a model as a ground in a physics engine