Skip to content

RapierVehicle


WebGPU is not supported in your browser
Please upgrade to latest Chrome/Edge

ts
import { Object3D, LitMaterial, Engine3D, BoxGeometry, CylinderGeometry, MeshRenderer, Vector3, Color, KeyEvent, KeyCode, ComponentBase, AtmosphericComponent, CameraUtil, DirectLight, HoverCameraController, KelvinUtil, Scene3D, View3D } from "@orillusion/core";
import { Physics, Rigidbody, BodyType, CollisionShapeUtil, VehicleController } from "@orillusion/physics-rapier";

const RAD2DEG = 180 / Math.PI;

class VehicleDriver extends ComponentBase {
    public vehicle: VehicleController;
    private keys: Record<string, boolean> = {};
    public engineForce: number = 1500;
    public brakeForce: number = 100;
    public maxSteer: number = 0.5;

    public start() {
        const input = this.transform.scene3D.view.engine3D.inputSystem;
        input.addEventListener(KeyEvent.KEY_DOWN, (e: KeyEvent) => this.keys[e.keyCode] = true, this);
        input.addEventListener(KeyEvent.KEY_UP, (e: KeyEvent) => this.keys[e.keyCode] = false, this);
    }

    public onUpdate() {
        if (!this.vehicle || this.vehicle.numWheels() < 4) return;
        const fwd = this.keys[KeyCode.Key_W] ? this.engineForce : (this.keys[KeyCode.Key_S] ? -this.engineForce : 0);
        const brake = this.keys[KeyCode.Key_Space] ? this.brakeForce : 0;
        const steer = this.keys[KeyCode.Key_A] ? this.maxSteer : (this.keys[KeyCode.Key_D] ? -this.maxSteer : 0);

        this.vehicle.setEngineForce(fwd, 2);
        this.vehicle.setEngineForce(fwd, 3);
        for (let i = 0; i < 4; i++) this.vehicle.setBrake(brake, i);
        this.vehicle.setSteering(steer, 0);
        this.vehicle.setSteering(steer, 1);
    }
}

class WheelVisualSync extends ComponentBase {
    public vehicle: VehicleController;
    public steerPivots: Object3D[] = [];
    public spinPivots: Object3D[] = [];

    public onUpdate() {
        const ctrl = this.vehicle?.native;
        if (!ctrl) return;
        const n = Math.min(this.steerPivots.length, ctrl.numWheels());
        for (let i = 0; i < n; i++) {
            const conn = ctrl.wheelChassisConnectionPointCs(i);
            const dir = ctrl.wheelDirectionCs(i);
            const len = ctrl.wheelSuspensionLength(i) ?? 0;
            if (!conn || !dir) continue;
            const sp = this.steerPivots[i];
            sp.x = conn.x + dir.x * len;
            sp.y = conn.y + dir.y * len;
            sp.z = conn.z + dir.z * len;
            sp.rotationY = (ctrl.wheelSteering(i) ?? 0) * RAD2DEG;
            this.spinPivots[i].rotationX = (ctrl.wheelRotation(i) ?? 0) * RAD2DEG;
        }
    }
}

class Sample_RapierVehicle {
    async run() {
        await Physics.init();
        const engine = await Engine3D.init({ renderLoop: () => Physics.update() });
        let scene = new Scene3D();

        let atmosphericSky: AtmosphericComponent;
        atmosphericSky = scene.addComponent(AtmosphericComponent);

        let camera = CameraUtil.createCamera3DObject(scene);
        camera.perspective(60, engine.aspect, 1, 5000);

        let hoverCtrl = camera.object3D.addComponent(HoverCameraController);
        hoverCtrl.setCamera(-30, -15, 40);

        let view = new View3D();
        view.scene = scene;
        view.camera = camera;

        let lightObj3D = new Object3D();
        lightObj3D.x = 0;
        lightObj3D.y = 30;
        lightObj3D.z = -40;
        lightObj3D.rotationX = 35;
        lightObj3D.rotationY = 160;
        lightObj3D.rotationZ = 0;

        let light = lightObj3D.addComponent(DirectLight);
        light.lightColor = KelvinUtil.color_temperature_to_rgb(5355);
        light.castShadow = true;
        light.intensity = 4;
        light.enableCSM = true;

        scene.addChild(light.object3D);

        atmosphericSky.relativeTransform = light.transform;

        const floor = new Object3D();
        const fr = floor.addComponent(MeshRenderer);
        fr.geometry = new BoxGeometry(200, 0.2, 60);
        const fm = new LitMaterial(); fm.baseColor = new Color(0.35, 0.35, 0.4); fr.material = fm;
        const fb = floor.addComponent(Rigidbody);
        fb.bodyType = BodyType.Static;
        fb.shape = CollisionShapeUtil.createBoxShape(floor, new Vector3(200, 0.2, 60));
        fb.friction = 1.0;
        scene.addChild(floor);

        const chassis = new Object3D(); chassis.y = 2;
        const cmr = chassis.addComponent(MeshRenderer);
        cmr.geometry = new BoxGeometry(2, 0.6, 4);
        const cm = new LitMaterial(); cm.baseColor = new Color(0.8, 0.2, 0.2); cmr.material = cm;
        const crb = chassis.addComponent(Rigidbody);
        crb.bodyType = BodyType.Dynamic; crb.mass = 200;
        crb.shape = CollisionShapeUtil.createBoxShape(chassis, new Vector3(2, 0.6, 4));
        crb.linearDamping = 0.1;
        crb.angularDamping = 0.5;
        scene.addChild(chassis);

        const vc = chassis.addComponent(VehicleController);
        const wheelOpts = {
            suspensionRestLength: 0.3,
            radius: 0.4,
            stiffness: 30,
            dampingCompression: 4.4,
            dampingRelaxation: 2.3,
            frictionSlip: 1000,
        };
        vc.addWheel({ ...wheelOpts, chassisConnection: new Vector3(-1, -0.3, -1.5) }); // 0 FL
        vc.addWheel({ ...wheelOpts, chassisConnection: new Vector3(1, -0.3, -1.5) });  // 1 FR
        vc.addWheel({ ...wheelOpts, chassisConnection: new Vector3(-1, -0.3, 1.5) });  // 2 RL
        vc.addWheel({ ...wheelOpts, chassisConnection: new Vector3(1, -0.3, 1.5) });   // 3 RR

        const drv = chassis.addComponent(VehicleDriver);
        drv.vehicle = vc;

        const wheelMat = new LitMaterial();
        wheelMat.baseColor = new Color(0.08, 0.08, 0.08);
        const wheelGeo = new CylinderGeometry(wheelOpts.radius, wheelOpts.radius, 0.3, 24);
        const sync = chassis.addComponent(WheelVisualSync);
        sync.vehicle = vc;
        for (let i = 0; i < 4; i++) {
            const steer = new Object3D();
            const spin = new Object3D();
            const mesh = new Object3D();
            const mr = mesh.addComponent(MeshRenderer);
            mr.geometry = wheelGeo;
            mr.material = wheelMat;
            mesh.rotationZ = 90;
            spin.addChild(mesh);
            steer.addChild(spin);
            chassis.addChild(steer);
            sync.steerPivots.push(steer);
            sync.spinPivots.push(spin);
        }

        engine.startRenderView(view);
    }
}

new Sample_RapierVehicle().run();

Released under the MIT License