The cannon-es program experiences crashes every time two Convex polyhedrons collide with each other

Currently, I'm working on implementing a dice roller using three.js and cannon-es. Everything runs smoothly when there's only one dice, rolling against the ground plane in a satisfactory manner.

However, the trouble arises when I add a second dice. While both dices roll fine against the ground plane, the simulation crashes when the two CANNON.ConvexPolyhedron objects collide.

After the collision, I continually receive this error message:

cannon-es.js:920 Uncaught TypeError: Cannot read properties of undefined (reading 'x')
    at Vec3.copy (cannon-es.js:920:21)
    at ConvexPolyhedron.clipFaceAgainstHull (cannon-es.js:2662:24)
    at ConvexPolyhedron.clipAgainstHull (cannon-es.js:2404:12)
    at Narrowphase.convexConvex (cannon-es.js:10916:10)
    at Narrowphase.getContacts (cannon-es.js:10608:33)
    at World.internalStep (cannon-es.js:12649:22)
    at World.step (cannon-es.js:12515:12)
    at updatePhysics (rolling.svelte:154:11)
    at animate (rolling.svelte:142:5)

To load a stl file of a twenty-sided dice, I use an STL loader from three.js and convert it to a cannon body like this:

  function createConvexHull(mesh: THREE.Mesh) {
    const position = mesh.geometry.attributes.position.array
    const points: CANNON.Vec3[] = []
    const faces: number[][] = []
    for (let i = 0; i < position.length; i += 3) {
      points.push(new CANNON.Vec3(position[i], position[i + 1], position[i + 2]))
    }
    for (let i = 0; i < position.length / 3; i += 3) {
      faces.push([i, i + 1, i + 2])
    }
    const convexGeometry = new CANNON.ConvexPolyhedron({
      vertices: points,
      faces: faces
    })
    const body = new CANNON.Body({ mass: 1 })
    body.addShape(convexGeometry)
    return body
  }

Despite attempting to compute vertex normals on the Three.js mesh and merging vertices before creating the cannon body, the issue persists without any resolution.

Any advice or guidance on what might be going wrong would be greatly appreciated.

Answer №1

Creating accurate collision detection for complex shapes in a convex polyhedron was a challenge for me. To solve this issue, I developed a Python script using Blender that extracts information from boxes in a collection named "Colliders." This script outputs the data in JSON format in the console, which I then use to generate precise box colliders for my game.

Below is the Blender Python script:

import bpy
import math

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

# Fetch 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("}")

This script will generate the JSON text. Copy the output from your console, save it as a .json file, and then parse it in a JavaScript file using Cannon.js as shown below:

/*
 * 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));

                if(boxID == Object.keys(data).length - 1){
                    levelEndRigidbody = new CANNON.Body({
                        type: CANNON.Body.STATIC
                    });

                    levelEndRigidbody.addShape(
                        boxShape,
                        new CANNON.Vec3(position.x , position.z, -position.y),
                        EulerToQuaternion(rotation.x + 90, rotation.z, -rotation.y)
                    );

                } else {
                    levelRigidbody.addShape(
                        boxShape,
                        new CANNON.Vec3(position.x , position.z, -position.y),
                        EulerToQuaternion(rotation.x + 90, rotation.z, -rotation.y)
                    );
                }
                boxID += 1;
            }
        }   
    })
    .catch(error => {
        console.error('Error parsing JSON file:', error);
    });

    PhysicsWorld.addBody(levelRigidbody);
    PhysicsWorld.addBody(levelEndRigidbody);

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

Remember to:

  • Run Blender to view console output ()
  • Create one box in Blender per collider and refrain from applying transforms
  • Name your collection in Blender as "Colliders"

This method can be beneficial for game developers working on larger levels with multiple colliders. While the approach is limited to using box primitives for colliders, it can potentially be adapted for other shapes such as spheres and triangles.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the process for retrieving data from mongoDB and displaying it by year in a single row?

I have received an array of data from MongoDB, which includes id, userName, and date. The date consists of years and months. My goal is to display this data categorized by years. How can I construct a query to the database that will show the data for all y ...

For each array element that is pushed, create and return an empty object

I am encountering an issue with an array where the objects are being generated by a push operation within a function. Despite successfully viewing the objects directly in the array, when I attempt to use forEach to count how many times each id uses the ser ...

Display a JSON object on a web browser

I am trying to display a JSON object on a web browser using HTML. The object is already in a text file and has been properly formatted for readability. My goal is to maintain the same formatting when displaying it on the browser. ...

Switch between accordion items

Currently, I have a React Js accordion in which clicking on an item opens the panel. To close it, you need to click on another item. However, I am looking to enhance this functionality by allowing the active panel to be closed after clicking on the AccButt ...

Retrieve any webpage using AJAX

Hey there! I'm a beginner in AJAX and have a question that may seem basic to some. I understand that you can set up a page to respond to an AJAX call, but is it possible to request any page with AJAX? In other words, can all the functionalities of a ...

Ways to conceal an element when it appears in the initial section of a page

I currently have a single-page website consisting of 4 sections which are not scrollable. To navigate between these sections, I have implemented a burger menu button with a fixed position to ensure it remains visible on all sections except the first one. ...

In React, components will consistently render the initial code

I have a chat application that requires authentication and uses cookies. Here's what I've been attempting: class AppHeader extends React.Component { constructor(props) { super(props) } render() { if (cookies.get(' ...

Angular code is failing to send a signal to the server following a $http.post request

I've been using angular for about a week now and I've been struggling to solve this issue. I have a service that wraps around $http because multiple controllers make calls to the same URL. On one particular page, there is a lot of server-side bus ...

using a route object to update the path in Nuxt

I need to navigate to a specific URL: myApp.com/search-page?name%5Bquery%5D=value The code snippet below works perfectly when I'm on the homepage myApp.com: this.$router.push({ path: "search-page", query: { name: { query: `${this.value} ...

How to set default props in Vue Select component?

I have been utilizing the vue-multiselect plugin from this website: Given that I plan to use it frequently, I am interested in establishing some default props. For instance, my aim is to have the selectLabel prop set as an empty string and possibly con ...

(Javascript) Extracting specific values from JSON objects using a loop

JSON: [ { "id": 1, "name": "One", "value": "Hello 1", "enabled": true, }, { "id": 2, "name": "Two", "value": "Hello 2", "e ...

Exploring the properties within a MongoDB document using mapping functionality

I am trying to retrieve data from a document using Node.js and encountering an issue where the map operator is returning data with similar names twice. I am wondering if I can use filter instead of map to address this problem. Here is the code I am using t ...

A guide on importing gLTF files into three.js

I've been having some trouble uploading a gLTF file using three.js. Despite fixing some errors, I'm still greeted with a black screen. One solution was adding this to my chrome target directory: ‘path to your chrome installation\chrome.exe ...

Updating a div using PHP elements

Currently, I'm using a webcam to capture images for a project related to learning. My goal is to showcase the recently taken photos. After taking a photo, it gets stored in a folder. To display all the collected photos within a <div>, I need to ...

Is there a way to verify the existence of an Array Field?

For JavaScript, the code would look like this: if (array[x] != undefined && array[x][y] != undefined) { array[x][y] = "foo"; } Is there an equivalent simple method for achieving this in Java? I have tried checking if the field is null or no ...

Error: The program encountered a type error while trying to access the '0' property of an undefined or null reference

I am a beginner in the world of coding and I am currently working on creating an application that allows users to add items to their order. My goal is to have the quantity of an item increase when it is selected multiple times, rather than listing the same ...

Exploring the concept of D3 data binding key accessor

Exploring D3 for the first time, I am working on a basic example to grasp the concept of data binding. My setup includes an array of colors, a function to add a color, and a function to remove a color based on index. However, I'm facing issues with t ...

Implementing Node.JS ajax to update current JSON information

I am seeking assistance in updating data within a JSON file using NODE.JS. Currently, my method adds the data with the same ID as expected. However, upon receiving the data back, it eliminates the last duplicate because it encounters the old value first. I ...

The jQuery ajax request was unsuccessful in connecting to the remote server

I've tried researching and troubleshooting, but I still can't figure out why the Ajax code is not functioning correctly. Here is my JavaScript code: $(document).ready(function(){ $("#tform").submit(function() { var varUserName ...

Utilizing React Hook Form for Interactive Slider and User Input Controls

Whenever I move the Slider, I want to retrieve the same value as when I enter a new value in the Input field. However, the current setup is not functioning correctly. <StyledSlider {register(NAME ...