Files
2026-05-27 23:29:53 +01:00

4.4 KiB
Raw Permalink Blame History

OpenSpec Change: enterprise-gaming-vehicle-physics

  • Change ID: enterprise-gaming-vehicle-physics
  • Package: @suaveplan/vehicle-physics
  • Category: gaming/rigs
  • Target path: gaming/rigs/vehicle-physics
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/vehicle-physics.md

Summary

Deliver @suaveplan/vehicle-physics on the genesis branch from scratch at enterprise quality. The package provides engine-agnostic vehicle physics using Pacejka Magic Formula tire model, spring-damper suspension per wheel, multi-wheel drivetrain (FWD/RWD/AWD), differential, braking, and aerodynamic drag. All math is pure TypeScript with no runtime deps — physics backends plug in via interface. Genesis adds Zod config schemas, structured errors, injectable clock, OTel telemetry, and property tests verifying tire force symmetry and suspension convergence.


Current-state audit (genesis)

Package does not exist on genesis yet — fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) VehiclePhysicsError; VEHICLE.* codes
@suaveplan/types (workspace:^) Clock
@suaveplan/events (workspace:^) vehicle:collision, vehicle:flip, vehicle:stall events
zod (catalog:forms) VehicleConfig schema
@opentelemetry/api (catalog:observability) Step duration histogram

Public API surface

function createVehicle(config: VehicleConfig): Vehicle

interface VehicleConfig {
  mass: number
  wheelBase: number
  trackWidth: number
  centerOfMassY: number
  engine: EngineConfig
  transmission: TransmissionConfig
  wheels: WheelConfig[]
  aerodynamics?: AeroConfig
  backend?: VehiclePhysicsBackend
  clock?: Clock
}

interface EngineConfig {
  maxTorque: number           // Nm
  maxRpm: number
  torqueCurve?: [rpm: number, torque: number][]
  idleRpm?: number
}

interface TransmissionConfig {
  type: 'FWD' | 'RWD' | 'AWD'
  gearRatios: number[]
  finalDrive: number
  differentialSlip?: number
}

interface WheelConfig {
  position: Vec3
  radius: number
  suspensionRestLength: number
  suspensionStiffness: number
  suspensionDamping: number
  tireGripLateral: number
  tireGripLongitudinal: number
}

interface Vehicle {
  update(dt: number, input: VehicleInput): void
  teleport(position: Vec3, rotation: Quat): void
  applyImpulse(impulse: Vec3): void
  flip(): void
  readonly position: Vec3
  readonly rotation: Quat
  readonly velocity: Vec3
  readonly speed: number        // m/s
  readonly rpm: number
  readonly gear: number
  readonly wheelsGrounded: boolean[]
}

interface VehicleInput {
  throttle: number    // 0–1
  brake: number       // 0–1
  steering: number    // -1 to 1
  handbrake: boolean
  clutch?: number
}

Error codes

type VehicleErrorCode =
  | 'VEHICLE_INVALID_CONFIG'
  | 'VEHICLE_BACKEND_NOT_SET'
  | 'VEHICLE_UPDATE_FAILED'
  | 'VEHICLE_INVALID_GEAR'

New additions for genesis

  1. Pacejka tire model — lateral and longitudinal force from slip angle / slip ratio; configurable B/C/D/E coefficients.
  2. Automatic gear shifting — RPM-based shift logic with configurable up/down thresholds.
  3. Aerodynamic drag + downforce — 0.5 * rho * Cd * A * v² drag; downforce scales with speed².
  4. Suspension raycast — VehiclePhysicsBackend.castRay() for each wheel per tick; ground contact from hit distance.

File layout

gaming/rigs/vehicle-physics/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/index.ts
    types/common.ts
    types/common.md
    core/
      vehicle/
        vehicle.ts
        vehicle.md
        vehicle.types.ts
        vehicle.test.ts
        index.ts
    models/
      pacejka/
        pacejka.ts
        pacejka.md
        pacejka.test.ts
        index.ts
      suspension/
        suspension.ts
        suspension.md
        suspension.test.ts
        index.ts
      drivetrain/
        drivetrain.ts
        drivetrain.md
        drivetrain.test.ts
        index.ts
    backends/
      null-backend/
        null-backend.ts
        null-backend.md
        null-backend.test.ts
        index.ts
  tests/
    property.test.ts

Impact

Downstream: @suaveplan/r3f-vehicle wires this to Rapier. Prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events.