Skip to content

Commit f5b5e86

Browse files
chore(testbed): step at a fixed interval
1 parent b8b81f0 commit f5b5e86

File tree

3 files changed

+80
-136
lines changed

3 files changed

+80
-136
lines changed

testbed3d/src/Graphics.ts

Lines changed: 26 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import * as THREE from "three";
22
import {OrbitControls} from "three/examples/jsm/controls/OrbitControls";
33
import RAPIER from "@dimforge/rapier3d";
4+
import {Matrix4, Quaternion, Vector3} from "three";
45

56
const BOX_INSTANCE_INDEX = 0;
67
const BALL_INSTANCE_INDEX = 1;
@@ -14,7 +15,6 @@ interface InstanceDesc {
1415
groupId: number;
1516
instanceId: number;
1617
elementId: number;
17-
highlighted: boolean;
1818
scale?: THREE.Vector3;
1919
}
2020

@@ -61,9 +61,12 @@ function genHeightfieldGeometry(collider: RAPIER.Collider) {
6161
};
6262
}
6363

64+
const _position = new Vector3();
65+
const _rotation = new Quaternion();
66+
const _matrix = new Matrix4();
67+
6468
export class Graphics {
6569
raycaster: THREE.Raycaster;
66-
highlightedCollider: null | number;
6770
coll2instance: Map<number, InstanceDesc>;
6871
coll2mesh: Map<number, THREE.Mesh>;
6972
rb2colls: Map<number, Array<RAPIER.Collider>>;
@@ -79,7 +82,6 @@ export class Graphics {
7982

8083
constructor() {
8184
this.raycaster = new THREE.Raycaster();
82-
this.highlightedCollider = null;
8385
this.coll2instance = new Map();
8486
this.coll2mesh = new Map();
8587
this.rb2colls = new Map();
@@ -195,13 +197,9 @@ export class Graphics {
195197
});
196198
}
197199

198-
render(world: RAPIER.World, debugRender: boolean) {
200+
render(world: RAPIER.World, debugRender: boolean, alpha: number) {
199201
kk += 1;
200202
this.controls.update();
201-
// if (kk % 100 == 0) {
202-
// console.log(this.camera.position);
203-
// console.log(this.controls.target);
204-
// }
205203

206204
this.light.position.set(
207205
this.camera.position.x,
@@ -224,7 +222,7 @@ export class Graphics {
224222
this.lines.visible = false;
225223
}
226224

227-
this.updatePositions(world);
225+
this.updatePositions(world, alpha);
228226
this.renderer.render(this.scene, this.camera);
229227
}
230228

@@ -242,80 +240,37 @@ export class Graphics {
242240
this.controls.update();
243241
}
244242

245-
highlightInstanceId() {
246-
return this.colorPalette.length - 1;
247-
}
248-
249-
highlightCollider(handle: number) {
250-
if (handle == this.highlightedCollider)
251-
// Avoid flickering when moving the mouse on a single collider.
252-
return;
253-
254-
if (this.highlightedCollider != null) {
255-
let desc = this.coll2instance.get(this.highlightedCollider);
256-
257-
if (!!desc) {
258-
desc.highlighted = false;
259-
this.instanceGroups[desc.groupId][
260-
this.highlightInstanceId()
261-
].count = 0;
262-
}
263-
}
264-
if (handle != null) {
265-
let desc = this.coll2instance.get(handle);
266-
267-
if (!!desc) {
268-
if (desc.instanceId != 0)
269-
// Don't highlight static/kinematic bodies.
270-
desc.highlighted = true;
271-
}
272-
}
273-
this.highlightedCollider = handle;
274-
}
243+
updatePositions(world: RAPIER.World, alpha: number) {
244+
world.forEachCollider((collider) => {
245+
let gfx = this.coll2instance.get(collider.handle);
246+
let translation = collider.translation();
247+
let rotation = collider.rotation();
275248

276-
updatePositions(world: RAPIER.World) {
277-
world.forEachCollider((elt) => {
278-
let gfx = this.coll2instance.get(elt.handle);
279-
let translation = elt.translation();
280-
let rotation = elt.rotation();
249+
_position.set(translation.x, translation.y, translation.z);
250+
_rotation.set(rotation.x, rotation.y, rotation.z, rotation.w);
281251

282252
if (!!gfx) {
283253
let instance = this.instanceGroups[gfx.groupId][gfx.instanceId];
284-
dummy.scale.set(gfx.scale.x, gfx.scale.y, gfx.scale.z);
285-
dummy.position.set(translation.x, translation.y, translation.z);
286-
dummy.quaternion.set(
287-
rotation.x,
288-
rotation.y,
289-
rotation.z,
290-
rotation.w,
254+
255+
instance.getMatrixAt(gfx.elementId, _matrix);
256+
_matrix.decompose(
257+
dummy.position,
258+
dummy.quaternion,
259+
dummy.scale,
291260
);
261+
dummy.position.lerp(_position, alpha);
262+
dummy.quaternion.slerp(_rotation, alpha);
292263
dummy.updateMatrix();
293-
instance.setMatrixAt(gfx.elementId, dummy.matrix);
294-
295-
let highlightInstance =
296-
this.instanceGroups[gfx.groupId][
297-
this.highlightInstanceId()
298-
];
299-
if (gfx.highlighted) {
300-
highlightInstance.count = 1;
301-
highlightInstance.setMatrixAt(0, dummy.matrix);
302-
}
303264

265+
instance.setMatrixAt(gfx.elementId, dummy.matrix);
304266
instance.instanceMatrix.needsUpdate = true;
305-
highlightInstance.instanceMatrix.needsUpdate = true;
306267
}
307268

308-
let mesh = this.coll2mesh.get(elt.handle);
269+
let mesh = this.coll2mesh.get(collider.handle);
309270

310271
if (!!mesh) {
311-
mesh.position.set(translation.x, translation.y, translation.z);
312-
mesh.quaternion.set(
313-
rotation.x,
314-
rotation.y,
315-
rotation.z,
316-
rotation.w,
317-
);
318-
mesh.updateMatrix();
272+
mesh.position.lerp(_position, alpha);
273+
mesh.quaternion.slerp(_rotation, alpha);
319274
}
320275
});
321276
}
@@ -337,21 +292,6 @@ export class Graphics {
337292
this.colorIndex = 0;
338293
}
339294

340-
// applyModifications(RAPIER: RAPIER_API, world: RAPIER.World, modifications) {
341-
// if (!!modifications) {
342-
// modifications.addCollider.forEach(coll => {
343-
// let collider = world.getCollider(coll.handle);
344-
// this.addCollider(RAPIER, world, collider);
345-
// });
346-
// modifications.removeRigidBody.forEach(body => {
347-
// if (!!this.rb2colls.get(body.handle)) {
348-
// this.rb2colls.get(body.handle).forEach(coll => this.removeCollider(coll));
349-
// this.rb2colls.delete(body.handle);
350-
// }
351-
// });
352-
// }
353-
// }
354-
355295
removeRigidBody(body: RAPIER.RigidBody) {
356296
if (!!this.rb2colls.get(body.handle)) {
357297
this.rb2colls
@@ -399,7 +339,6 @@ export class Graphics {
399339
groupId: 0,
400340
instanceId: parent.isFixed() ? 0 : this.colorIndex + 1,
401341
elementId: 0,
402-
highlighted: false,
403342
};
404343

405344
switch (collider.shapeType()) {
@@ -495,12 +434,6 @@ export class Graphics {
495434
instance.count += 1;
496435
}
497436

498-
let highlightInstance =
499-
this.instanceGroups[instanceDesc.groupId][
500-
this.highlightInstanceId()
501-
];
502-
highlightInstance.count = 0;
503-
504437
let t = collider.translation();
505438
let r = collider.rotation();
506439
dummy.position.set(t.x, t.y, t.z);

testbed3d/src/Gui.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,6 @@ export class Gui {
5959
.add(simulationParameters, "numVelocityIter", 0, 20)
6060
.step(1)
6161
.listen();
62-
this.gui
63-
.add(simulationParameters, "numPositionIter", 0, 20)
64-
.step(1)
65-
.listen();
6662
this.gui
6763
.add(simulationParameters, "debugInfos")
6864
.listen()

testbed3d/src/Testbed.ts

Lines changed: 54 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {Graphics} from "./Graphics";
22
import {Gui} from "./Gui";
33
import type {DebugInfos} from "./Gui";
4-
import * as md5 from "md5";
4+
import md5 from "md5";
55
import type * as RAPIER from "@dimforge/rapier3d";
66

77
type RAPIER_API = typeof import("@dimforge/rapier3d");
@@ -13,7 +13,6 @@ class SimulationParameters {
1313
prevBackend: string;
1414
demo: string;
1515
numVelocityIter: number;
16-
numPositionIter: number;
1716
running: boolean;
1817
stepping: boolean;
1918
debugInfos: boolean;
@@ -30,7 +29,6 @@ class SimulationParameters {
3029
this.prevBackend = "rapier";
3130
this.demo = "collision groups";
3231
this.numVelocityIter = 4;
33-
this.numPositionIter = 1;
3432
this.running = true;
3533
this.stepping = false;
3634
this.debugRender = false;
@@ -60,6 +58,8 @@ export class Testbed {
6058
lastMessageTime: number;
6159
snap: Uint8Array;
6260
snapStepId: number;
61+
time: number;
62+
accumulator: number;
6363

6464
constructor(RAPIER: RAPIER_API, builders: Builders) {
6565
let backends = ["rapier"];
@@ -72,6 +72,8 @@ export class Testbed {
7272
this.demoToken = 0;
7373
this.mouse = {x: 0, y: 0};
7474
this.events = new RAPIER.EventQueue(true);
75+
this.time = 0;
76+
this.accumulator = 0;
7577

7678
this.switchToDemo(builders.keys().next().value);
7779

@@ -89,7 +91,7 @@ export class Testbed {
8991
this.preTimestepAction = null;
9092
this.world = world;
9193
this.world.maxVelocityIterations = this.parameters.numVelocityIter;
92-
// this.world.maxPositionIterations = this.parameters.numPositionIter;
94+
this.world.timestep = 1 / 60;
9395
this.demoToken += 1;
9496
this.stepId = 0;
9597
this.gui.resetTiming();
@@ -139,51 +141,64 @@ export class Testbed {
139141
}
140142

141143
run() {
142-
if (this.parameters.running || this.parameters.stepping) {
143-
this.world.maxVelocityIterations = this.parameters.numVelocityIter;
144-
// this.world.maxPositionIterations = this.parameters.numPositionIter;
145-
146-
if (!!this.preTimestepAction) {
147-
this.preTimestepAction(this.graphics);
144+
const time = performance.now();
145+
const fixedStep = this.world.timestep;
146+
const frameTime = Math.min(0.25, (time - this.time) / 1000);
147+
148+
this.time = time;
149+
this.accumulator += frameTime;
150+
151+
// Run physics at a fixed update interval
152+
while (this.accumulator >= fixedStep) {
153+
if (this.parameters.running || this.parameters.stepping) {
154+
this.world.maxVelocityIterations =
155+
this.parameters.numVelocityIter;
156+
157+
if (!!this.preTimestepAction) {
158+
this.preTimestepAction(this.graphics);
159+
}
160+
161+
this.world.step(this.events);
162+
this.gui.setTiming(performance.now() - time);
163+
this.stepId += 1;
164+
165+
if (!!this.parameters.debugInfos) {
166+
let t0 = performance.now();
167+
let snapshot = this.world.takeSnapshot();
168+
let t1 = performance.now();
169+
let snapshotTime = t1 - t0;
170+
171+
let debugInfos: DebugInfos = {
172+
token: this.demoToken,
173+
stepId: this.stepId,
174+
worldHash: "",
175+
worldHashTime: 0,
176+
snapshotTime: 0,
177+
};
178+
t0 = performance.now();
179+
debugInfos.worldHash = md5(snapshot);
180+
t1 = performance.now();
181+
let worldHashTime = t1 - t0;
182+
183+
debugInfos.worldHashTime = worldHashTime;
184+
debugInfos.snapshotTime = snapshotTime;
185+
186+
this.gui.setDebugInfos(debugInfos);
187+
}
148188
}
149189

150-
let t0 = new Date().getTime();
151-
this.world.step(this.events);
152-
this.gui.setTiming(new Date().getTime() - t0);
153-
this.stepId += 1;
154-
155-
if (!!this.parameters.debugInfos) {
156-
let t0 = performance.now();
157-
let snapshot = this.world.takeSnapshot();
158-
let t1 = performance.now();
159-
let snapshotTime = t1 - t0;
160-
161-
let debugInfos: DebugInfos = {
162-
token: this.demoToken,
163-
stepId: this.stepId,
164-
worldHash: "",
165-
worldHashTime: 0,
166-
snapshotTime: 0,
167-
};
168-
t0 = performance.now();
169-
debugInfos.worldHash = md5(snapshot);
170-
t1 = performance.now();
171-
let worldHashTime = t1 - t0;
172-
173-
debugInfos.worldHashTime = worldHashTime;
174-
debugInfos.snapshotTime = snapshotTime;
175-
176-
this.gui.setDebugInfos(debugInfos);
177-
}
190+
this.accumulator -= fixedStep;
178191
}
179192

193+
const alpha = this.accumulator / fixedStep;
194+
180195
if (this.parameters.stepping) {
181196
this.parameters.running = false;
182197
this.parameters.stepping = false;
183198
}
184199

185200
this.gui.stats.begin();
186-
this.graphics.render(this.world, this.parameters.debugRender);
201+
this.graphics.render(this.world, this.parameters.debugRender, alpha);
187202
this.gui.stats.end();
188203

189204
requestAnimationFrame(() => this.run());

0 commit comments

Comments
 (0)