alt sim with high stability
This commit is contained in:
parent
46f6c08bc2
commit
79cc0fa677
|
|
@ -39,8 +39,6 @@ export class FolkRope extends AbstractArrow {
|
||||||
#lastTime = 0;
|
#lastTime = 0;
|
||||||
#currentTime = 0;
|
#currentTime = 0;
|
||||||
#deltaTime = 0;
|
#deltaTime = 0;
|
||||||
#previousDelta = 0;
|
|
||||||
#interval = 1000 / 60; // ms per frame
|
|
||||||
#gravity = { x: 0, y: 3000 };
|
#gravity = { x: 0, y: 3000 };
|
||||||
#points: RopePoint[] = [];
|
#points: RopePoint[] = [];
|
||||||
|
|
||||||
|
|
@ -83,32 +81,33 @@ export class FolkRope extends AbstractArrow {
|
||||||
this.draw();
|
this.draw();
|
||||||
};
|
};
|
||||||
|
|
||||||
#tick = (timestamp: number = performance.now()) => {
|
#dtAccumulator = 0;
|
||||||
this.#currentTime = timestamp;
|
#fixedTimestep = 1 / 60;
|
||||||
|
|
||||||
|
#tick = (timestamp: number = performance.now()) => {
|
||||||
this.#rAFId = requestAnimationFrame(this.#tick);
|
this.#rAFId = requestAnimationFrame(this.#tick);
|
||||||
|
|
||||||
this.#deltaTime = this.#currentTime - this.#lastTime;
|
const actualDelta = (timestamp - this.#lastTime) * 0.001;
|
||||||
|
this.#lastTime = timestamp;
|
||||||
if (this.#deltaTime > this.#interval) {
|
|
||||||
const dts = this.#deltaTime * 0.001; // delta time in seconds
|
|
||||||
|
|
||||||
|
// Accumulate delta time, but clamp to avoid spiral of death
|
||||||
|
this.#dtAccumulator = Math.min(this.#dtAccumulator + actualDelta, 0.2);
|
||||||
|
while (this.#dtAccumulator >= this.#fixedTimestep) {
|
||||||
for (const point of this.#points) {
|
for (const point of this.#points) {
|
||||||
this.#integratePoint(point, this.#gravity, dts, this.#previousDelta);
|
this.#integratePoint(point, this.#gravity);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let iteration = 0; iteration < 100; iteration++) {
|
// 3 constraint iterations is enough for fixed timestep
|
||||||
|
for (let iteration = 0; iteration < 3; iteration++) {
|
||||||
for (const point of this.#points) {
|
for (const point of this.#points) {
|
||||||
this.#constrainPoint(point);
|
this.#constrainPoint(point);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#previousDelta = dts;
|
this.#dtAccumulator -= this.#fixedTimestep;
|
||||||
|
|
||||||
this.draw();
|
|
||||||
|
|
||||||
this.#lastTime = this.#currentTime - (this.#deltaTime % this.#interval);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.draw();
|
||||||
};
|
};
|
||||||
|
|
||||||
override render(sourceRect: DOMRectReadOnly, targetRect: DOMRectReadOnly) {
|
override render(sourceRect: DOMRectReadOnly, targetRect: DOMRectReadOnly) {
|
||||||
|
|
@ -192,22 +191,16 @@ export class FolkRope extends AbstractArrow {
|
||||||
return points;
|
return points;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Integrate motion equations per node without taking into account relationship with other nodes...
|
#integratePoint(point: RopePoint, gravity: Vector2) {
|
||||||
#integratePoint(point: RopePoint, gravity: Vector2, dt: number, previousFrameDt: number) {
|
|
||||||
if (!point.isFixed) {
|
if (!point.isFixed) {
|
||||||
point.velocity = Vector.sub(point.pos, point.oldPos);
|
point.velocity = Vector.sub(point.pos, point.oldPos);
|
||||||
point.oldPos = { ...point.pos };
|
point.oldPos = { ...point.pos };
|
||||||
|
|
||||||
// Drastically improves stability
|
|
||||||
const timeCorrection = previousFrameDt !== 0.0 ? dt / previousFrameDt : 0.0;
|
|
||||||
|
|
||||||
const accel = Vector.add(gravity, { x: 0, y: point.mass });
|
const accel = Vector.add(gravity, { x: 0, y: point.mass });
|
||||||
|
const tsSq = this.#fixedTimestep * this.#fixedTimestep;
|
||||||
|
|
||||||
const velCoef = timeCorrection * point.damping;
|
point.pos.x += point.velocity.x * point.damping + accel.x * tsSq;
|
||||||
const accelCoef = Math.pow(dt, 2);
|
point.pos.y += point.velocity.y * point.damping + accel.y * tsSq;
|
||||||
|
|
||||||
point.pos.x += point.velocity.x * velCoef + accel.x * accelCoef;
|
|
||||||
point.pos.y += point.velocity.y * velCoef + accel.y * accelCoef;
|
|
||||||
} else {
|
} else {
|
||||||
point.velocity = Vector.zero();
|
point.velocity = Vector.zero();
|
||||||
point.oldPos = { ...point.pos };
|
point.oldPos = { ...point.pos };
|
||||||
|
|
@ -219,9 +212,13 @@ export class FolkRope extends AbstractArrow {
|
||||||
const applyConstraint = (p1: RopePoint, p2: RopePoint) => {
|
const applyConstraint = (p1: RopePoint, p2: RopePoint) => {
|
||||||
const delta = Vector.sub(p2.pos, p1.pos);
|
const delta = Vector.sub(p2.pos, p1.pos);
|
||||||
const len = Vector.mag(delta);
|
const len = Vector.mag(delta);
|
||||||
|
|
||||||
|
// Prevent division by zero
|
||||||
|
if (len < 0.0001) return;
|
||||||
|
|
||||||
const diff = len - p1.distanceToNextPoint;
|
const diff = len - p1.distanceToNextPoint;
|
||||||
const normal = Vector.normalized(delta);
|
const normal = Vector.normalized(delta);
|
||||||
const adjustment = Vector.scale(normal, diff * 0.25);
|
const adjustment = Vector.scale(normal, diff * 0.75);
|
||||||
|
|
||||||
if (!p1.isFixed) {
|
if (!p1.isFixed) {
|
||||||
p1.pos = Vector.add(p1.pos, adjustment);
|
p1.pos = Vector.add(p1.pos, adjustment);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue