schteppe / cannon.js

A lightweight 3D physics engine written in JavaScript.

Home Page:http://schteppe.github.com/cannon.js

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Raycaster Vehicle not working in the positive Z axis

JackBiofryd opened this issue · comments

Hey! So, I managed to make the Raycaster vehicle work properly, except for one huge bug. Whenever I try to drive with the vehicle in the positive z axis it glitches through the floor a bit down (about half of the wheels are below the plane) and slides to infinity. I have had this bug for a very very long time and I have no idea how to fix it. I don't think my code is the problem because I followed the examples completely. Any ideas? It might be because of the materials. Changing the default contact material changes how much the car slides (how fast), but no value fixes the bug.

Here is what the glitch looks like before it starts sliding:
123

Here's my code:
`
const canvas = document.querySelector('canvas.webgl');
// Scene
const scene = new THREE.Scene();

// Physics world
const world = new CANNON.World();
world.gravity.set(0, -9.81, 0);
world.defaultContactMaterial.friction = 0;
const groundMaterial = new CANNON.Material('groundMaterial');
const wheelMaterial = new CANNON.Material('wheelMaterial');
const wheelGroundContactMaterial = new CANNON.ContactMaterial(
wheelMaterial,
groundMaterial,
{
friction: 0.3,
restitution: 0,
contactEquationStiffness: 1000
}
);
world.addContactMaterial(wheelGroundContactMaterial);
// Performance
world.broadphase = new CANNON.SAPBroadphase(world);
world.allowSleep = true;

// Cameras
const camera = new THREE.PerspectiveCamera(
45,
window.innerWidth / window.innerHeight,
0.1,
1000
);
camera.position.set(10, 10, 10);

// Lights
const ambientLight = new THREE.AmbientLight(0xffffff, 0.7);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(10, 12, 0);

scene.add(ambientLight, directionalLight);

// Planes
const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(100, 100),
new THREE.MeshStandardMaterial({
metalness: 0.3,
roughness: 0.4,
color: '#777777'
})
);
plane.rotation.x = Math.PI * -0.5;

const planeBody = new CANNON.Body({
shape: new CANNON.Plane(),
mass: 0,
material: groundMaterial,
position: new CANNON.Vec3(0, 0, 0)
});
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI * -0.5);

world.addBody(planeBody);
scene.add(plane);

/**

  • 🔥 Physics & Vehicle 🔥
    */

// Vehicle physics body
const chassisShape = new CANNON.Box(new CANNON.Vec3(0.4, 0.25, 0.75));
const chassisBody = new CANNON.Body({ mass: 1500 });
chassisBody.addShape(chassisShape);
chassisBody.position.set(1, 2, -3);
chassisBody.angularVelocity.set(0, 0, 0); // Initial velocity

// Vehicle Three.js mesh
const chassisGeometry = new THREE.BoxBufferGeometry(0.8, 0.5, 1.5);
const chassisMaterial = new THREE.MeshStandardMaterial({
metalness: 0.8,
roughness: 0.4,
color: '#DF2800'
});
const chassisMesh = new THREE.Mesh(chassisGeometry, chassisMaterial);
scene.add(chassisMesh);
chassisMesh.castShadow = true;

// Parent vehicle object
const vehicle = new CANNON.RaycastVehicle({
chassisBody: chassisBody,
indexRightAxis: 0, // x
indexUpAxis: 1, // y
indexForwardAxis: 2 // z
});

// Wheels
const wheelOptions = {
radius: 0.2,
directionLocal: new CANNON.Vec3(0, -1, 0),
suspensionStiffness: 45,
suspensionRestLength: 0.4,
frictionSlip: 5,
dampingRelaxation: 2.3,
dampingCompression: 4.5,
maxSuspensionForce: 200000,
rollInfluence: 0.01,
axleLocal: new CANNON.Vec3(-1, 0, 0),
chassisConnectionPointLocal: new CANNON.Vec3(1, 1, 0),
maxSuspensionTravel: 0.25,
customSlidingRotationalSpeed: -30,
useCustomSlidingRotationalSpeed: true
};

const axlewidth = 0.4;
wheelOptions.chassisConnectionPointLocal.set(axlewidth, 0.1, -0.7);
vehicle.addWheel(wheelOptions);
wheelOptions.chassisConnectionPointLocal.set(-axlewidth, 0.1, -0.7);
vehicle.addWheel(wheelOptions);
wheelOptions.chassisConnectionPointLocal.set(axlewidth, 0.1, 0.7);
vehicle.addWheel(wheelOptions);
wheelOptions.chassisConnectionPointLocal.set(-axlewidth, 0.1, 0.7);
vehicle.addWheel(wheelOptions);

vehicle.addToWorld(world);

const wheelBodies = [],
wheelMeshes = [];

vehicle.wheelInfos.forEach(wheel => {
// Wheel physics body
const shape = new CANNON.Cylinder(
wheel.radius,
wheel.radius,
wheel.radius / 2,
20
);
const body = new CANNON.Body({ mass: 1, material: wheelMaterial });
var q = new CANNON.Quaternion();
q.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI / 2);
body.addShape(shape, new CANNON.Vec3(), q);
wheelBodies.push(body);

// Wheel Three.js mesh
const wheelGeometry = new THREE.CylinderGeometry(
	wheel.radius,
	wheel.radius,
	0.1,
	32
);
const wheelMeshMaterial = new THREE.MeshPhongMaterial({
	color: 0xff9852
});
const cylinder = new THREE.Mesh(wheelGeometry, wheelMeshMaterial);
cylinder.geometry.rotateZ(Math.PI / 2);
cylinder.castShadow = true;
wheelMeshes.push(cylinder);
scene.add(cylinder);

});

// Update the wheels to match the physics
world.addEventListener('postStep', function () {
chassisBody.wakeUp();

for (let i = 0; i < vehicle.wheelInfos.length; i++) {
	vehicle.updateWheelTransform(i);
	const t = vehicle.wheelInfos[i].worldTransform;

	console.log(t.position.y);

	// Update wheel physics bodies
	wheelBodies[i].position.copy(t.position);
	wheelBodies[i].quaternion.copy(t.quaternion);

	// Update wheel meshes
	wheelMeshes[i].position.copy(t.position);
	wheelMeshes[i].quaternion.copy(t.quaternion);
}

});

const moveCar = e => {
if (e.type !== 'keydown' && e.type !== 'keyup') return;

const keyup = e.type === 'keyup';

vehicle.setBrake(0, 0);
vehicle.setBrake(0, 1);
vehicle.setBrake(0, 2);
vehicle.setBrake(0, 3);

const engineForce = 2000,
	maxSteerVal = 0.3;

switch (e.keyCode) {
	case 38: // Forward
		vehicle.applyEngineForce(keyup ? 0 : -engineForce, 2);
		vehicle.applyEngineForce(keyup ? 0 : -engineForce, 3);
		break;

	case 40: // Backward
		vehicle.applyEngineForce(keyup ? 0 : engineForce, 0);
		vehicle.applyEngineForce(keyup ? 0 : engineForce, 1);
		break;

	case 39: // Right
		vehicle.setSteeringValue(keyup ? 0 : -maxSteerVal, 2);
		vehicle.setSteeringValue(keyup ? 0 : -maxSteerVal, 3);
		break;

	case 37: // Left
		vehicle.setSteeringValue(keyup ? 0 : maxSteerVal, 2);
		vehicle.setSteeringValue(keyup ? 0 : maxSteerVal, 3);
		break;
}

};
window.onkeydown = moveCar;
window.onkeyup = moveCar;

// Controls
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;

const h = new THREE.AxesHelper(10);
scene.add(h);

// Renderer
const renderer = new THREE.WebGLRenderer({
canvas,
antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.render(scene, camera);

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.camera.far = 20;

plane.receiveShadow = true;

// Animation
const clock = new THREE.Clock();
let oldTime = 0;

const tick = () => {
const elapsedTime = clock.getElapsedTime();
const frameTime = elapsedTime - oldTime;
oldTime = elapsedTime;

// Update controls
controls.update();

// Update physics
world.step(1 / 60, frameTime, 3);
chassisMesh.position.copy(chassisBody.position);
chassisMesh.quaternion.copy(chassisBody.quaternion);

// Render
renderer.render(scene, camera);
// Run tick on next frame
window.requestAnimationFrame(tick);

};

tick();

// Resize scene on window resize
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));

};

`

After trying things for so long, the bug was the rotation of the plane. My mistake. I replaced the planes with: `
const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry(100, 100),
new THREE.MeshStandardMaterial({
metalness: 0.3,
roughness: 0.4,
color: '#777777'
})
);
plane.rotation.x = Math.PI * -0.5;

const q = plane.quaternion;
const planeBody = new CANNON.Body({
shape: new CANNON.Plane(),
mass: 0,
material: groundMaterial,
position: new CANNON.Vec3(0, 0, 0),
quaternion: new CANNON.Quaternion(q.x, q.y, q.z, q.w)
});
planeBody.quaternion.setFromAxisAngle(new CANNON.Vec3(1, 0, 0), Math.PI * -0.5);

world.addBody(planeBody);
scene.add(plane);

`