schteppe / cannon.js

A lightweight 3D physics engine written in JavaScript.
http://schteppe.github.com/cannon.js
MIT License
4.64k stars 706 forks source link

Cannon.js Imported Terrain #434

Open ghost opened 4 years ago

ghost commented 4 years ago

Is there a way to have the vehicle raycast on an imported 3d terrain rather than creating a matrix of random numbers?

tomo0613 commented 4 years ago

Instead of random numbers, i did the following:

I created a height field in blender then extracted the height values as an array of arrays (with the help of a python script) in a way that i was able to paste it into my js function. https://github.com/tomo0613/offroadJS

tomo0613 commented 4 years ago

@baronwatts If still interested here's a function that basically does that: I did some experiments (same repo)

note:

function meshToHeightField(mesh) {
    const geometry = findGeometry(mesh);
    // positions coordinates are stored in a THREE.Float32BufferAttribute (array buffer [c0.x,c0.y,c0.z,c1.x, ...])
    const vertices = mapPositionBufferToVertices(geometry.getAttribute('position'));
    // if the the plane width equals to its length
    const rowCount = Math.sqrt(vertices.length);
    const columnCount = rowCount;

    geometry.computeBoundingBox();

    const minX = geometry.boundingBox.min.x;
    const maxX = geometry.boundingBox.max.x;
    const minZ = geometry.boundingBox.min.z;
    const maxZ = geometry.boundingBox.max.z;
    const gridWidth = maxX - minX;
    const gridLength = maxZ - minZ;
    // the scale is bit off, so it needs some adjustment (+ 0.1585)
    const gridElementSize = gridWidth / columnCount + 0.1585;

    // create grid
    const grid = [];
    for (let rowIndex = 0; rowIndex < rowCount; rowIndex++) {
        const row = [];

        for (let columnIndex = 0; columnIndex < columnCount; columnIndex++) {
            const vertexIndex = rowIndex * rowCount + columnIndex;
            const vertex = vertices[vertexIndex];

            row.push(vertex.y);
        }

        grid.push(row);
    }

    // create heightField from grid
    const heightFieldShape = new CANNON.Heightfield(grid, {elementSize: gridElementSize});
    const heightField = new CANNON.Body({mass: 0, shape: heightFieldShape});

    const q1 = new THREE.Quaternion();
    q1.setFromAxisAngle(new CANNON.Vec3(0, 0, 1), -Math.PI / 2);
    const q = new THREE.Quaternion();
    q.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), -Math.PI / 2);
    q.multiply(q1);

    heightField.quaternion.copy(q);
    heightField.position.set(
        -gridWidth / 2,
        0,
        gridLength / 2 - gridLength,
    );

    return heightField;

    function findGeometry(mesh) {
        let geometry;

        mesh.traverse((child) => {
            if (!geometry && child.type === 'Mesh' && child.geometry) {
                geometry = child.geometry;
            }
        });

        return geometry;
    }

    function mapPositionBufferToVertices(positionBuffer) {
        const vertexArray = [];
        const vertexCount = positionBuffer.count;

        for (let i = 0; i < vertexCount; i++) {        
            vertexArray.push(new THREE.Vector3(
                positionBuffer.getX(i),
                positionBuffer.getY(i),
                positionBuffer.getZ(i),
            ));
        }
        // vertices in a mesh are not in order, sort them by x & z position
        vertexArray.sort((a, b) => {
            if (a.z === b.z) {
                return (a.x < b.x) ? -1 : (a.x > b.x) ? 1 : 0;
            } else {
                return (a.z < b.z) ? -1 : 1;
            }
        });
        // filter duplicated vertices
        return vertexArray.filter((vertex, index) => {
            const nextVertex = vertexArray[index + 1];
            const duplicated = nextVertex 
                && vertex.x === nextVertex.x
                && vertex.y === nextVertex.y
                && vertex.z === nextVertex.z;

            return !duplicated;
        });
    }
}
tomo0613 commented 3 years ago

this is the way:

Loryhoof commented 2 years ago

https://github.com/Loryhoof/Mesh2Heightfield I hope this can help someone, its based on @tomo0613 's OffroadJS