Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConvexPolyhedron collision issues #459

Open
AshDevFr opened this issue Mar 15, 2021 · 5 comments
Open

ConvexPolyhedron collision issues #459

AshDevFr opened this issue Mar 15, 2021 · 5 comments

Comments

@AshDevFr
Copy link

Hi, I have been trying to use CANNON.ConvexPolyhedron to handle collision between different type of objects.

When they collide with the plane or other type of shapes there is no issue but when two ConvexPolyhedron collide I get an error:

cannon.js:5085 Uncaught TypeError: Cannot read property 'x' of undefined
    at Vec3.copy (cannon.js:5085)
    at ConvexPolyhedron.clipFaceAgainstHull (cannon.js:8344)
    at ConvexPolyhedron.clipAgainstHull (cannon.js:8041)
    at Narrowphase.<computed>.Narrowphase.convexConvex (cannon.js:12266)
    at Narrowphase.getContacts (cannon.js:11256)
    at World.internalStep (cannon.js:13359)
    at World.step (cannon.js:13197)
    at animate (index.js:29)

This is how I create my shape:

const geo1 = new THREE.IcosahedronGeometry(2, 0);
const geo1Mesh = new THREE.Mesh(geo1, objectMaterial);
geo1Mesh.position.x = -15;
geo1Mesh.position.z = 0;
geo1Mesh.position.y = 3;
scene.add(geo1Mesh);

const geo1Body = new CANNON.Body({ mass: 10, material: objectBodyMaterial });
geo1Body.position.copy(geo1Mesh.position);
geo1Body.addShape(getPolyhedronShape(geo1Mesh));
world.addBody(geo1Body);

function getPolyhedronShape(mesh) {
  const position = mesh.geometry.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]);
  }

  return new CANNON.ConvexPolyhedron(points, faces);
}

I am using Three.js version 0.126.1 and Cannon.js version 0.6.2

I created a sandbox if anybody wants to take a look: https://codesandbox.io/s/hardcore-glitter-wxscw?file=/src/index.js

Am I missing something ?

Thank you for your time.

@Dannie226
Copy link

Dannie226 commented May 20, 2021

I have found that if you use an indexed geometry, and then loop through points and faces, convex on convex works pretty well most of the time. If you need to merge vertices, I would recommend BufferGeometryUtils or if you are using an import statement this will work. code for creating convex from indexed geometry:

function createFromIndexed(mesh){
  let geometry = mesh.geometry;
  geometry.deleteAttribute('normal');  
  //if not planning on putting textures on the mesh, you can delete the uv mapping for better vertice merging  
  //geometry.deleteAttribute('uv');  
  geometry = THREE.BufferGeometryUtils.mergeVertices(geometry); 
  //if using import statement  
  //geometry = BufferGeometryUtils.mergeVertices(geometry);  
  mesh.geometry = geometry;
  let position = geometry.attributes.position.array;  
  let geomFaces = geometry.index.array;   
  const points = [];  
  const faces = [];  
  for(var i = 0;i<position.length;i+=3){  
    points.push(new CANNON.Vec3(position[i],position[i+1],position[i+2]);  
  }  
  for(var i = 0;i<geomFaces.length;i+=3){  
    faces.push([geomFaces[i],geomFaces[i+1],geomFaces[i+2]);  
  }  
  return new CANNON.ConvexPolyhedron(points,faces);  
}

@Day-OS
Copy link

Day-OS commented Jul 28, 2021

Has this been solved yet? I'm getting the same problem.

@Dannie226
Copy link

Because of the way that cannon.js is programmed, if the convex triangles are not all connected, then convex-convex collision will not work. Which is why you can use either buffer geometry utils to merge the vertices, or make your own geometry that is indexed. unfortunately, you have to do one of the two for convex-convex collision, the answer stated above, does the one where you merge the vertices with buffer geometry utils.

@Rafapp
Copy link

Rafapp commented Aug 7, 2023

Greetings everyone.

Convex polyhedron never behaved correctly for me with more complex shapes. To fix this, I created a Python script with blender that parses the boxes in a collection, which you should name "Colliders," then it will print out json format text in the console, and using that json file I parse it and generate the proper box colliders in my game.

Here is the blender python script:

import bpy
import math

collection = bpy.data.collections.get("Colliders")

# Get all objects in the scene
all_objects = collection.objects

# Count meshes for formatting
meshCount = 0
for obj in all_objects:
    if obj.type == 'MESH':
        meshCount += 1

# Print mesh information using ID counter
objectID = 0

print("{")

for obj in all_objects:
    if obj.type == 'MESH':
        print("    \"box_" + str(objectID) + "\":{")
        
        # Position
        position = obj.location
        print("        \"position\":{")
        print("            \"x\":" + str(position.x) + ",")
        print("            \"y\":" + str(position.y) + ",")
        print("            \"z\":" + str(position.z))
        print("         },")
        
        # Scale
        scale = obj.scale
        print("        \"scale\":{")
        print("            \"x\":" + str(scale.x) + ",")
        print("            \"y\":" + str(scale.y) + ",")
        print("            \"z\":" + str(scale.z))
        print("         },")
        
        # Rotation (Euler)
        rotation = obj.rotation_euler
        print("        \"rotation\":{")
        print("            \"x\":" + str(math.degrees(rotation.x)) + ",")
        print("            \"y\":" + str(math.degrees(rotation.y)) + ",")
        print("            \"z\":" + str(math.degrees(rotation.z)))
        print("         }")
        if(objectID != meshCount - 1):
            print("    },\n")
        else:
            print("    }")
        
        objectID += 1
        
print("}")

That will generate the json text. Copy that from your console, paste it in a .json file, and parse it like so in a js file with cannon:

/*
 * Creating the level collision geometry
 */
async function GenerateLevelColliders(){
    
    // RigidBody
    levelRigidbody = new CANNON.Body({
        type: CANNON.Body.STATIC
    });

    // Parse the JSON file
    await fetch("levelColliders/level_" + currentLevel + ".json")
    .then(response => response.json())
    .then(data => {
        let boxID = 0;
        
        for (let boxKey in data) {
            if(data.hasOwnProperty(boxKey)){
                const box = data[boxKey];
                const position = box.position;
                const scale = box.scale;
                const rotation = box.rotation;

                let boxShape = new CANNON.Box(new CANNON.Vec3(scale.x, scale.y, scale.z));

                // Set the last box as the "checkpoint"
                if(boxID == Object.keys(data).length - 1){
                    // Create a specific body for final shape
                    levelEndRigidbody = new CANNON.Body({
                        type: CANNON.Body.STATIC
                    });

                    // Add its box
                    levelEndRigidbody.addShape(
                        boxShape,
                        new CANNON.Vec3(position.x , position.z, -position.y), // ~ Y is up in blender
                        EulerToQuaternion(rotation.x + 90, rotation.z, -rotation.y)
                    );

                } else {
                    // Add each other box given position, scale and rotation
                    levelRigidbody.addShape(
                        boxShape,
                        new CANNON.Vec3(position.x , position.z, -position.y), // ~ Y is up in blender
                        EulerToQuaternion(rotation.x + 90, rotation.z, -rotation.y)
                    );
                }
                boxID += 1;
            }
        }   
    })
    .catch(error => {
        console.error('Error parsing JSON file:', error);
    });

    // Add the bodies
    PhysicsWorld.addBody(levelRigidbody);
    PhysicsWorld.addBody(levelEndRigidbody);

    console.log("Finished loading: levelColliders/level_" + currentLevel + ".json ☑");
}

Keep in mind you must:

Hope this is helpful for game devs building larger levels with a bunch of colliders. The only downside to this is your colliders will use only boxes as primitives, but I'm sure this pipeline can be applied to spheres, and even tris.

@plainprince
Copy link

I know this issue is pretty old but I am now also getting a problem with convex hulls which is that my objects just completely fly away after they collide with something, is there a way to fix this without running external scripts? Also I am not familiar with blender's scripting, can anyone show me how this works with some screenshots? thx a lot :)
(please excuse my bad english)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants