SOURCE / PINNED RELEASE
Made of little things.
Supersonic RC Revive
- Release
- 1ba42f1ca1d6…
- Author-recorded commit
- baecad10b1cd…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ye5ptcxfyyxl5vjvdjar2ua3f0hynkjzpx552mu5snj3qmx5pzjscpknpr/wss%3A%2F%2Fgit.napplet.soy%2F/n-143146b0d6f
Archive hash verified: 90d22b206672eba4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Port of "ParentScript 3 - havokManager".
import { Vector3 } from 'three';
import { HavokWorld, type HavokRigidBody, type HavokOptions, type StaticBody } from '../physics/havok.js';
/**
* pRB.friction = 10 (car.ls). Against the scenery's 0.3 the original behaves
* like the lower value: landings keep their speed as in the logs, while the
* geometric mean (1.73) used before scrubbed off up to a third of it.
*/
export const CHASSIS_CONTACT_FRICTION = 10;
export class HavokManager {
readonly havok: HavokWorld;
private readonly forces: Array<[HavokRigidBody, Vector3]> = [];
private readonly forcesOnPoint: Array<[HavokRigidBody, Vector3, Vector3]> = [];
constructor(statics: StaticBody[], gravity: Vector3, scale: number, overrides: Partial<HavokOptions> = {}) {
const options: HavokOptions = {
scale,
gravity,
contactFriction: CHASSIS_CONTACT_FRICTION,
forceModel: 'impulse',
...overrides,
};
// pHavok.Initialize(p3d); dragParameters = [0, 0]
this.havok = new HavokWorld(options, statics);
// pHavok.gravity = pHavok.gravity * 0.2 ; every static restitution = 0
this.havok.gravity = this.havok.gravity.multiplyScalar(0.2);
}
update(tDifMs: number): void {
let tDif = tDifMs / 1000;
if (tDif > 0.015) tDif = 0.015;
if (tDif < 0.003) tDif = 0.003;
for (const [rb, force] of this.forces) rb.applyForce(force);
// The Lingo stores [tRB, tPoint, tForce] but callers pass (rb, force, point);
// the positional call rb.applyForceAtPoint(tForce[2], tForce[3]) is (force, point).
for (const [rb, force, point] of this.forcesOnPoint) rb.applyForceAtPoint(force, point);
this.havok.step(tDif * 2.5, 7);
this.forces.length = 0;
this.forcesOnPoint.length = 0;
}
removeBody(rb: HavokRigidBody): void {
for (const queue of [this.forces, this.forcesOnPoint]) {
for (let i = queue.length - 1; i >= 0; i--) if (queue[i][0] === rb) queue.splice(i, 1);
}
this.havok.removeBody(rb);
}
applyForceAtPoint(rb: HavokRigidBody, force: Vector3, point: Vector3): void {
this.forcesOnPoint.push([rb, force.clone(), point.clone()]);
}
/** Drop the forces queued for the next step (test harness experiments). */
clearQueuedForces(): void {
this.forces.length = 0;
this.forcesOnPoint.length = 0;
}
applyForce(rb: HavokRigidBody, force: Vector3): void {
this.forces.push([rb, force.clone()]);
}
getSimTime(): number {
return this.havok.simTime;
}
destroy(): void {
this.havok.free();
}
}
