Create CANNON body from THREE. Mesh

56 Views Asked by At

I have a THREE.js plane that the points are moved up and down using Perlin noise to make terrain. Now I need to add physics to the camera to stop it from just falling through the plane.

I'm hoping to make a function that can retrieve the vertices and faces out of the mesh and copy them over to a CANNON body as a Convex Polyhedron. Here's what I have so far but doesn't work:

function setPhysicsGeometry(mesh, body) {
    const vertices = mesh.geometry.vertices;
    const shape = new CANNON.ConvexPolyhedron();

    vertices.forEach(function(vertex) {
        // Adjust vertex position based on the physics properties
        vertex.z = 0; // Assuming initial z position is 0

        // Add the vertex to the shape
        shape.vertices.push(new CANNON.Vec3(vertex.x, vertex.y, vertex.z));
    });

    // Compute the faces of the shape
    const faces = [];
    for (let i = 0; i < mesh.geometry.faces.length; i++) {
        const face = mesh.geometry.faces[i];
        faces.push([face.a, face.b, face.c]);
    }
    shape.faces = faces;

    // Update the shape's bounding box
    shape.updateNormals();
    shape.updateEdges();

    // Assign the shape to the body
    body.addShape(shape);
}

This is an error that I get: Uncaught TypeError: shape.updateNormals is not a function

Same thing happens for shape.updateEdges()

Is this a problem with those two functions or am I over simplifying something that is difficult/impossible.

Thanks

UPDATE: I have made a function that sort of works but its super slow:

function createShape(geo) {
    const bufferedGeo = new THREE.BufferGeometry().fromGeometry(geo); // Turn geometry into buffer geo

    let position = bufferedGeo.attributes.position.array
    const points = []

    for (let i = 0; i < position.length; i += 3) {
        points.push(new CANNON.Vec3(position[i], position[i + 1], position[i + 2]))
    }
    const faces = []
    
    for (let i = 0; i < position.length / 3; i += 3) {
        faces.push([i, i + 1, i + 2])
    }

    const shape = new CANNON.ConvexPolyhedron(points, faces)
    
    return shape
}

Here is an error I get that shows that the normals are being ordered in the wrong order:

.faceNormals[1011] = Vec3(-0.38237948261368415,0.4673447646702331,-0.7971040096570934) looks like it points into the shape? The vertices follow. Make sure they are ordered CCW around the normal, using the right hand rule.

I don't know how to fix it since they are just taken from the geometry

1

There are 1 best solutions below

0
Owen Odroski On

I solved it (Not completely, still bugs with the physics but that's a different problem).

Thanks to This Guy for sending me an amazing example of where I got the solution from. I was attempting to use ConvexPolyhedron to get my own body shape. I think it is still possible to do that but ordering the faces and normals in the correct syntax is really difficult. Instead, I used Trimesh which was much simpler.

However, in the example, he used new THREE.TorusKnotGeometry() and then went straight into createTrimesh(). For me this would return an error because the THREE.js geometry was not a buffer geometry. So I changed with a simple function and it returned no errors. Still physics wouldn't work on the object. So after what felt like two long I copied the position and quartation from the THREE.js mesh to the CANNON body and it works pretty well. Here is the completed code:

Code to Copy position and quartation

function copy(threeMesh, cannonBody) {
    // Copy position
    const position = new CANNON.Vec3(
        threeMesh.position.x,
        threeMesh.position.y,
        threeMesh.position.z
    );
    cannonBody.position.copy(position);

    // Copy rotation
    const quaternion = new CANNON.Quaternion(
        threeMesh.quaternion.x,
        threeMesh.quaternion.y,
        threeMesh.quaternion.z,
        threeMesh.quaternion.w
    );
    cannonBody.quaternion.copy(quaternion);
}

Code to create Trimesh:

function buffer(geo) {
    const bufferedGeo = new THREE.BufferGeometry().fromGeometry(geo);
    return bufferedGeo;
}

function CreateTrimesh(geometry) {
    const vertices = buffer(geometry).attributes.position.array
    const indices = Object.keys(vertices).map(Number)
    return new CANNON.Trimesh(vertices, indices)
}

And Mesh and Body code:

var platformGeometry = createCustomGeometry(terrain, size + 1, size + 1)
var platformMaterial = new THREE.MeshPhongMaterial({ color: 0x00ff00, wireframe: false, side: THREE.DoubleSide});
var platform = new THREE.Mesh(platformGeometry, platformMaterial);
platform.receiveShadow = true;
platform.castShadow = true
platform.position.set(0, 0, 0)
platform.rotation.x = Math.PI / 2
scene.add(platform);

var platShape = CreateTrimesh(platformGeometry, platform)
var platBody = new CANNON.Body({ mass: 0, shape: platShape });
copy(platform, platBody)
world.addBody(platBody)