Mesh / Model into Canon Convex/Trimesh

Hi there I want to get a body on my imported 3d model i saw the three-to-cannon but i didn’t get to work. Im not using npm and I’m new to Javascript and I only see typescript module that i dont know how to use.

I already see some examples like this:

const icosahedronGeometry = new THREE.IcosahedronGeometry(1, 0)
const icosahedronMesh = new THREE.Mesh(icosahedronGeometry, normalMaterial)
icosahedronMesh.position.x = 1
icosahedronMesh.position.y = 3
icosahedronMesh.castShadow = true
scene.add(icosahedronMesh)
const position = icosahedronMesh.geometry.attributes.position.array
const icosahedronPoints: CANNON.Vec3[] = []
for (let i = 0; i < position.length; i += 3) {
    icosahedronPoints.push(
        new CANNON.Vec3(position[i], position[i + 1], position[i + 2])
    )
}
const icosahedronFaces: number[][] = []
for (let i = 0; i < position.length / 3; i += 3) {
    icosahedronFaces.push([i, i + 1, i + 2])
}
const icosahedronShape = new CANNON.ConvexPolyhedron({
    vertices: icosahedronPoints,
    faces: icosahedronFaces,
})
const icosahedronBody = new CANNON.Body({ mass: 1 })
icosahedronBody.addShape(icosahedronShape)
icosahedronBody.position.x = icosahedronMesh.position.x
icosahedronBody.position.y = icosahedronMesh.position.y
icosahedronBody.position.z = icosahedronMesh.position.z
world.addBody(icosahedronBody)

But is in typescript and im using only Javascript, can someone help me to make this work on javascript or another solution.
Thanks in advance!

I presume you got the code from this page - Physics with Cannon - Three.js Tutorials
There is a working example on that page where you can view the JavaScript source. Press the <>

const icosahedronGeometry = new THREE.IcosahedronGeometry(1, 0)
const icosahedronMesh = new THREE.Mesh(icosahedronGeometry, normalMaterial)
icosahedronMesh.position.x = 1
icosahedronMesh.position.y = 3
icosahedronMesh.castShadow = true
scene.add(icosahedronMesh)
let position = icosahedronMesh.geometry.attributes.position.array
const icosahedronPoints = []
for (let i = 0; i < position.length; i += 3) {
    icosahedronPoints.push(new CANNON.Vec3(position[i], position[i + 1], position[i + 2]))
}
const icosahedronFaces = []
for (let i = 0; i < position.length / 3; i += 3) {
    icosahedronFaces.push([i, i + 1, i + 2])
}
const icosahedronShape = new CANNON.ConvexPolyhedron({
    vertices: icosahedronPoints,
    faces: icosahedronFaces,
})
const icosahedronBody = new CANNON.Body({ mass: 1 })
icosahedronBody.addShape(icosahedronShape)
icosahedronBody.position.x = icosahedronMesh.position.x
icosahedronBody.position.y = icosahedronMesh.position.y
icosahedronBody.position.z = icosahedronMesh.position.z
world.addBody(icosahedronBody)

Thanks now I can get the icosahedron but when I use imported models I cannot get geometry.atributes.position.array always says undefined do you have some example with imported 3d models?

Thanks in advance!

Cannon Debug Renderer
Convex Geometry to CANNON.Trimesh
Finger Physics
FCS
Ball-VR

Did you have Canon Utils in JavaScript I cannot use it in Typescript?

all the working examples are written in JavaScript. Look for the <> in the working examples.
Typescript gets converted into JavaScript before it can run in your browser.

I get it but Im using Classes and I get this error in console.

THREE.BufferGeometry.computeBoundingSphere(): Computed radius is NaN. The "position" attribute is likely to have NaN values. 

Here is my code:

import * as THREE from './three/three.module.js';
import Stats from './three/stats.module.js';
// import {OrbitControls} from './three/OrbitControls.js';
import * as CANNON from './teste/cannon-es.js';
import CannonDebugger from './teste/cannon-es-debugger.js';
import {PointerLockControlsCannon} from './teste/PointerLockControlsCannon.js';
import {GLTFLoader} from "./three/GLTFLoader.js";
import CannonUtils from "./teste/cannonUtils.js";


let lastCallTime = performance.now();

class Application {
    constructor() {
        this.objects = [];
        this.createScene();
    }
    createScene() {
        this.scene = new THREE.Scene();
        this.camera = new THREE.PerspectiveCamera(60,
            window.innerWidth / window.innerHeight, 0.1, 1000);
        this.camera.position.y = 0;

        this.renderer = new THREE.WebGLRenderer({
            antialias: true,
            powerPreference: "high-performance",
        });
        this.renderer.outputEncoding = THREE.sRGBEncoding;
        this.renderer.autoClear = false;
        this.renderer.info.autoReset = false;
        this.renderer.setSize(window.innerWidth, window.innerHeight);
        this.renderer.shadowMap.enabled = false;
        this.renderer.shadowMap.type = THREE.VSMShadowMap;
        this.renderer.physicallyCorrectLights = true;
        this.renderer.setClearColor(0xcccccc);
        document.body.appendChild(this.renderer.domElement);
        this.stats = Stats()
        document.body.appendChild(this.stats.dom)





        this.world = new CANNON.World();
        this.world.defaultContactMaterial.contactEquationStiffness = 1e9;
        this.world.defaultContactMaterial.contactEquationRelaxation = 4

        this.cannonDebugger = new CannonDebugger(this.scene, this.world);

        const solver = new CANNON.GSSolver()
        solver.iterations = 7
        solver.tolerance = 0.1
        this.world.solver = new CANNON.SplitSolver(solver)
        // use this to test non-split solver
        // world.solver = solver

        this.world.gravity.set(0, -50, 0)

        // Create a slippery material (friction coefficient = 0.0)
        const physicsMaterial = new CANNON.Material('physics')
        const physics_physics = new CANNON.ContactMaterial(physicsMaterial, physicsMaterial, {
            friction: 0.0,
            restitution: 0.3,
        })

        // We must add the contact materials to the world
        this.world.addContactMaterial(physics_physics);

        const material = new THREE.MeshStandardMaterial( {color: 0x434c5e, side: THREE.DoubleSide} );


        const halfExtents = new CANNON.Vec3(100, 1, 100);
        const boxShape = new CANNON.Box(halfExtents);
        const boxGeometry = new THREE.BoxBufferGeometry(halfExtents.x * 2, halfExtents.y * 2, halfExtents.z * 2);
        this.boxBody = new CANNON.Body({ mass: 0 })
        this.boxBody.addShape(boxShape)
        this.boxMesh = new THREE.Mesh(boxGeometry, material)
        const x = 0
        const y = 0
        const z = 0
        this.boxBody.position.set(x, y, z)
        this.boxMesh.position.copy(this.boxBody.position)
        this.world.addBody(this.boxBody)
        this.scene.add(this.boxMesh)




        const radius = 0.3
        this.sphereShape = new CANNON.Sphere(radius)
        this.sphereBody = new CANNON.Body({ mass: 5, material: physicsMaterial })
        this.sphereBody.addShape(this.sphereShape)
        this.sphereBody.position.set(0, 5, 0)
        this.sphereBody.linearDamping = 0.9
        this.world.addBody(this.sphereBody)


        const normalMaterial = new THREE.MeshNormalMaterial()

        const icosahedronGeometry = new THREE.IcosahedronGeometry(1, 0)
        this.icosahedronMesh = new THREE.Mesh(icosahedronGeometry, normalMaterial)
        this.icosahedronMesh.position.x = 1
        this.icosahedronMesh.position.y = 3
        this.icosahedronMesh.castShadow = true
        this.scene.add(this.icosahedronMesh)
        let position = this.icosahedronMesh.geometry.attributes.position.array
        const icosahedronPoints = []
        for (let i = 0; i < position.length; i += 3) {
            icosahedronPoints.push(new CANNON.Vec3(position[i], position[i + 1], position[i + 2]))
        }
        const icosahedronFaces = []
        for (let i = 0; i < position.length / 3; i += 3) {
            icosahedronFaces.push([i, i + 1, i + 2])
        }
        const icosahedronShape = new CANNON.ConvexPolyhedron({
            vertices: icosahedronPoints,
            faces: icosahedronFaces,
        })
        this.icosahedronBody = new CANNON.Body({ mass: 1 })
        this.icosahedronBody.addShape(icosahedronShape)
        this.icosahedronBody.position.x = this.icosahedronMesh.position.x
        this.icosahedronBody.position.y = this.icosahedronMesh.position.y
        this.icosahedronBody.position.z = this.icosahedronMesh.position.z
        this.world.addBody(this.icosahedronBody)

        this.mundo = new THREE.Group();
        this.mundo.name = "world";
        this.scene.add(this.mundo)



        this.monkeyMesh;
        this.monkeyBody;
        const objLoader = new GLTFLoader()
        objLoader.load(
            './models/bench/bench.glb',
            (object) => {
                this.scene.add(object.scene)
                this.monkeyMesh = object.scene.children[0]
                this.monkeyMesh.material = normalMaterial
                this.monkeyMesh.position.x = -2
                this.monkeyMesh.position.y = 20
                const monkeyShape = CannonUtils.CreateTrimesh(this.monkeyMesh.geometry)
                // const monkeyShape = CannonUtils.CreateConvexPolyhedron(
                //     (monkeyMesh as THREE.Mesh).geometry
                // )
                this.monkeyBody = new CANNON.Body({ mass: 1 })
                this.monkeyBody.addShape(monkeyShape)
                // monkeyBody.addShape(cubeShape)
                // monkeyBody.addShape(sphereShape)
                // monkeyBody.addShape(cylinderShape)
                // monkeyBody.addShape(icosahedronShape)
                // monkeyBody.addShape(new CANNON.Plane())
                // monkeyBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2)
                this.monkeyBody.position.x = this.monkeyMesh.position.x
                this.monkeyBody.position.y = this.monkeyMesh.position.y
                this.monkeyBody.position.z = this.monkeyMesh.position.z
                this.world.addBody(this.monkeyBody)
                // monkeyLoaded = true
            },
            (xhr) => {
                console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
            },
            (error) => {
                console.log('An error happened')
            }
        )














        const ambientLight = new THREE.AmbientLight(0xfdffe1, 0.4);

        var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.6);
        hemiLight.position.set(0, 500, 0);

        var dirLight = new THREE.DirectionalLight(0xffffff, 2);
        dirLight.position.set(-2.3, 3, 3);

        dirLight.position.multiplyScalar(50);
        dirLight.name = "dirlight";

        dirLight.castShadow = true;
        dirLight.shadow.mapSize.width = dirLight.shadow.mapSize.height = 1024 * 2;

        var d = 350;

        dirLight.shadow.camera.left = -d;
        dirLight.shadow.camera.right = d;
        dirLight.shadow.camera.top = d;
        dirLight.shadow.camera.bottom = -d;

        dirLight.shadow.camera.far = 3500;
        dirLight.shadow.bias = -0.0001;
        dirLight.shadow.mapSize.width = 512 * 4;
        dirLight.shadow.mapSize.height = 512 * 4;

        this.scene.add(dirLight);
        this.scene.add(hemiLight);
        this.scene.add(ambientLight)

        this.controls = new PointerLockControlsCannon(this.camera, this.sphereBody);
        this.scene.add(this.controls.getObject())


        this.render();






        // this.controls = new OrbitControls(this.camera, this.renderer.domElement);
        // this.controls = new PointerLockControlsCannon(this.camera, this.sphereBody);
        // this.scene.add(this.controls.getObject())
        const instructions = document.getElementById('instructions')
        instructions.addEventListener('click', () => {
           this.controls.lock()
        })

        this.controls.addEventListener('lock', () => {
            this.controls.enabled = true
            instructions.style.display = 'none'
        })

        this.controls.addEventListener('unlock', () => {
            this.controls.enabled = false
            instructions.style.display = null
        })
    }
    render() {
        requestAnimationFrame((t) => {
            if (this.time === null) {
                this.time = t;
            }

            this.render();
            this.renderer.render(this.scene, this.camera);
            this.update(t-this.time);
            this.time = t;
        });
    }

    update(timeElapsed){
        const delta = timeElapsed * 0.001;
        this.objects.forEach((object) => {
            // if(object instanceof PortalCreator){
            // // }else if(object instanceof AnimatedModel){
            // //     object.update(delta);
            // } else{
            object.update();
            // }
        });
        const time = performance.now() / 1000
        const dt = time - lastCallTime
        lastCallTime = time


        this.world.fixedStep(1/60, dt);
        this.boxMesh.position.copy(this.boxBody.position);
        this.boxMesh.quaternion.copy(this.boxBody.quaternion);
        this.icosahedronMesh.position.copy(this.icosahedronBody.position);
        this.icosahedronMesh.quaternion.copy(this.icosahedronBody.quaternion);
        // this.monkeyMesh.position.set(
        //     this.monkeyBody.position.x,
        //     this.monkeyBody.position.y,
        //     this.monkeyBody.position.z
        // )
        // this.monkeyMesh.quaternion.set(
        //     this.monkeyBody.quaternion.x,
        //     this.monkeyBody.quaternion.y,
        //     this.monkeyBody.quaternion.z,
        //     this.monkeyBody.quaternion.w
        // )
        this.cannonDebugger.update();
        this.controls.update(dt);
        this.stats.update();

    }

    add(mesh) {
        if (Array.isArray(mesh)){
            for(var index in mesh){
                this.objects.push(mesh[index]);
                this.mundo.add( mesh[index].getMesh() );
            }
        }
        else{
            this.objects.push(mesh);
            this.mundo.add(mesh.getMesh());
        }
    }
}

let app = new Application();
let objs = [

];
app.add(objs);

Thanks for your help!

Two of my examples in my links use classes. I won’t write your code for you. But your employer could try to convince me.

No problem! Still thanks for your help <3

Now i know my problem, the problem is that my models / meshes have children so i get the error do you know any way to transform models into only 1 mesh or create a convex to a mesh with childrens.

Thanks for your attencion!

There is no simple way that works for everything.
Get good at doing things manually.
I discuss that on this page : Trimeshes, ConvexPolyhedrons and Compound Shapes

Here are some more examples of manually applying customised solutions.
Kick Boxing
Glasses
Random Number Generator

I got the same issues. I presume you were updating THREE.Mesh.position from Cannon world like the examples, then NaN value maybe resulted from the world.step(delta) function at delta = 0. Try delta += 0.00001 as a simple workaround and check if this helps.

I use ‘three-to-cannon’;

example

  addMeshToWorldViaThreeToCannon = (meshItem: any, type = ShapeType.MESH) => {
    const { shape, offset, orientation } = threeToCannon(meshItem, { type });
    const meshCannon = new CANNON.Body({
      shape,
      position: new CANNON.Vec3(meshItem.position.x, meshItem.position.y, meshItem.position.z),
      type: CANNON.Body.STATIC,
    });

    meshCannon.quaternion.copy(meshItem.quaternion);
    this.world.addBody(meshCannon);
  };

Make sure your model is hull model

Trimesh is really the best approach for this. It’s fast, works with pretty much any Three.js mesh, and lets you create a Cannon.js body without needing npm or TypeScript. Unlike convex hulls, which try to simplify your mesh into a single convex shape and can kill performance on complex models while also being less accurate, trimesh uses the actual geometry, so it handles collisions precisely without unnecessary compromises. For imported models, especially ones with lots of detail or concave parts, trimesh is much more reliable and efficient, and it’s simple enough to use directly in plain JavaScript.