cannon.js icon indicating copy to clipboard operation
cannon.js copied to clipboard

How to add a collision body to a model

Open OliverXH opened this issue 4 years ago • 19 comments

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

Modeling with Blender

OliverXH avatar Apr 10 '20 01:04 OliverXH

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 avatar May 13 '20 16:05 stockhuman

@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?

OliverXH avatar Jun 29 '20 13:06 OliverXH

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 avatar Jun 29 '20 16:06 stockhuman

@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?

OliverXH avatar Jun 30 '20 01:06 OliverXH

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 avatar Jun 30 '20 03:06 stockhuman

@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

OliverXH avatar Jun 30 '20 05:06 OliverXH

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

dirkk0 avatar Jun 30 '20 06:06 dirkk0

@dirkk0

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

OliverXH avatar Jun 30 '20 09:06 OliverXH

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 avatar Jun 30 '20 11:06 dirkk0

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

OliverXH avatar Jun 30 '20 12:06 OliverXH

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.

dirkk0 avatar Jun 30 '20 13:06 dirkk0

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.

vik-vev avatar Jul 01 '20 14:07 vik-vev

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.

OliverXH avatar Jul 02 '20 03:07 OliverXH

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.

dirkk0 avatar Jul 02 '20 14:07 dirkk0

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?

OliverXH avatar Jul 02 '20 16:07 OliverXH

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 avatar Jul 04 '20 14:07 dirkk0

@dirkk0

Thanks a lot.

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

OliverXH avatar Jul 06 '20 14:07 OliverXH

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 avatar Apr 12 '23 05:04 Daudxu

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

Daudxu avatar Apr 12 '23 05:04 Daudxu