the-world-engine
Version:
three.js based, unity like game engine for browser
382 lines (341 loc) • 13 kB
JavaScript
import { b2_linearSlop, b2_maxPolygonVertices } from "../common/b2_settings.js";
import { b2Abs, b2Max, b2Vec2, b2Rot, b2Transform, b2Sweep } from "../common/b2_math.js";
import { b2Timer } from "../common/b2_timer.js";
import { b2Distance, b2DistanceInput, b2DistanceOutput, b2DistanceProxy, b2SimplexCache } from "./b2_distance.js";
export let b2_toiTime = 0;
export let b2_toiMaxTime = 0;
export let b2_toiCalls = 0;
export let b2_toiIters = 0;
export let b2_toiMaxIters = 0;
export let b2_toiRootIters = 0;
export let b2_toiMaxRootIters = 0;
export function b2_toi_reset() {
b2_toiTime = 0;
b2_toiMaxTime = 0;
b2_toiCalls = 0;
b2_toiIters = 0;
b2_toiMaxIters = 0;
b2_toiRootIters = 0;
b2_toiMaxRootIters = 0;
}
const b2TimeOfImpact_s_xfA = new b2Transform;
const b2TimeOfImpact_s_xfB = new b2Transform;
const b2TimeOfImpact_s_pointA = new b2Vec2;
const b2TimeOfImpact_s_pointB = new b2Vec2;
const b2TimeOfImpact_s_normal = new b2Vec2;
const b2TimeOfImpact_s_axisA = new b2Vec2;
const b2TimeOfImpact_s_axisB = new b2Vec2;
export class b2TOIInput {
constructor() {
this.proxyA = new b2DistanceProxy;
this.proxyB = new b2DistanceProxy;
this.sweepA = new b2Sweep;
this.sweepB = new b2Sweep;
this.tMax = 0;
}
}
export var b2TOIOutputState;
(function(t) {
t[t["e_unknown"] = 0] = "e_unknown";
t[t["e_failed"] = 1] = "e_failed";
t[t["e_overlapped"] = 2] = "e_overlapped";
t[t["e_touching"] = 3] = "e_touching";
t[t["e_separated"] = 4] = "e_separated";
})(b2TOIOutputState || (b2TOIOutputState = {}));
export class b2TOIOutput {
constructor() {
this.state = b2TOIOutputState.e_unknown;
this.t = 0;
}
}
export var b2SeparationFunctionType;
(function(t) {
t[t["e_unknown"] = -1] = "e_unknown";
t[t["e_points"] = 0] = "e_points";
t[t["e_faceA"] = 1] = "e_faceA";
t[t["e_faceB"] = 2] = "e_faceB";
})(b2SeparationFunctionType || (b2SeparationFunctionType = {}));
export class b2SeparationFunction {
constructor() {
this.m_sweepA = new b2Sweep;
this.m_sweepB = new b2Sweep;
this.m_type = b2SeparationFunctionType.e_unknown;
this.m_localPoint = new b2Vec2;
this.m_axis = new b2Vec2;
}
Initialize(t, e, s, i, o, n) {
this.m_proxyA = e;
this.m_proxyB = i;
const c = t.count;
this.m_sweepA.Copy(s);
this.m_sweepB.Copy(o);
const b = b2TimeOfImpact_s_xfA;
const a = b2TimeOfImpact_s_xfB;
this.m_sweepA.GetTransform(b, n);
this.m_sweepB.GetTransform(a, n);
if (c === 1) {
this.m_type = b2SeparationFunctionType.e_points;
const e = this.m_proxyA.GetVertex(t.indexA[0]);
const s = this.m_proxyB.GetVertex(t.indexB[0]);
const i = b2Transform.MulXV(b, e, b2TimeOfImpact_s_pointA);
const o = b2Transform.MulXV(a, s, b2TimeOfImpact_s_pointB);
b2Vec2.SubVV(o, i, this.m_axis);
const n = this.m_axis.Normalize();
this.m_localPoint.SetZero();
return n;
} else if (t.indexA[0] === t.indexA[1]) {
this.m_type = b2SeparationFunctionType.e_faceB;
const e = this.m_proxyB.GetVertex(t.indexB[0]);
const s = this.m_proxyB.GetVertex(t.indexB[1]);
b2Vec2.CrossVOne(b2Vec2.SubVV(s, e, b2Vec2.s_t0), this.m_axis).SelfNormalize();
const i = b2Rot.MulRV(a.q, this.m_axis, b2TimeOfImpact_s_normal);
b2Vec2.MidVV(e, s, this.m_localPoint);
const o = b2Transform.MulXV(a, this.m_localPoint, b2TimeOfImpact_s_pointB);
const n = this.m_proxyA.GetVertex(t.indexA[0]);
const c = b2Transform.MulXV(b, n, b2TimeOfImpact_s_pointA);
let m = b2Vec2.DotVV(b2Vec2.SubVV(c, o, b2Vec2.s_t0), i);
if (m < 0) {
this.m_axis.SelfNeg();
m = -m;
}
return m;
} else {
this.m_type = b2SeparationFunctionType.e_faceA;
const e = this.m_proxyA.GetVertex(t.indexA[0]);
const s = this.m_proxyA.GetVertex(t.indexA[1]);
b2Vec2.CrossVOne(b2Vec2.SubVV(s, e, b2Vec2.s_t0), this.m_axis).SelfNormalize();
const i = b2Rot.MulRV(b.q, this.m_axis, b2TimeOfImpact_s_normal);
b2Vec2.MidVV(e, s, this.m_localPoint);
const o = b2Transform.MulXV(b, this.m_localPoint, b2TimeOfImpact_s_pointA);
const n = this.m_proxyB.GetVertex(t.indexB[0]);
const c = b2Transform.MulXV(a, n, b2TimeOfImpact_s_pointB);
let m = b2Vec2.DotVV(b2Vec2.SubVV(c, o, b2Vec2.s_t0), i);
if (m < 0) {
this.m_axis.SelfNeg();
m = -m;
}
return m;
}
}
FindMinSeparation(t, e, s) {
const i = b2TimeOfImpact_s_xfA;
const o = b2TimeOfImpact_s_xfB;
this.m_sweepA.GetTransform(i, s);
this.m_sweepB.GetTransform(o, s);
switch (this.m_type) {
case b2SeparationFunctionType.e_points:
{
const s = b2Rot.MulTRV(i.q, this.m_axis, b2TimeOfImpact_s_axisA);
const n = b2Rot.MulTRV(o.q, b2Vec2.NegV(this.m_axis, b2Vec2.s_t0), b2TimeOfImpact_s_axisB);
t[0] = this.m_proxyA.GetSupport(s);
e[0] = this.m_proxyB.GetSupport(n);
const c = this.m_proxyA.GetVertex(t[0]);
const b = this.m_proxyB.GetVertex(e[0]);
const a = b2Transform.MulXV(i, c, b2TimeOfImpact_s_pointA);
const m = b2Transform.MulXV(o, b, b2TimeOfImpact_s_pointB);
const p = b2Vec2.DotVV(b2Vec2.SubVV(m, a, b2Vec2.s_t0), this.m_axis);
return p;
}
case b2SeparationFunctionType.e_faceA:
{
const s = b2Rot.MulRV(i.q, this.m_axis, b2TimeOfImpact_s_normal);
const n = b2Transform.MulXV(i, this.m_localPoint, b2TimeOfImpact_s_pointA);
const c = b2Rot.MulTRV(o.q, b2Vec2.NegV(s, b2Vec2.s_t0), b2TimeOfImpact_s_axisB);
t[0] = -1;
e[0] = this.m_proxyB.GetSupport(c);
const b = this.m_proxyB.GetVertex(e[0]);
const a = b2Transform.MulXV(o, b, b2TimeOfImpact_s_pointB);
const m = b2Vec2.DotVV(b2Vec2.SubVV(a, n, b2Vec2.s_t0), s);
return m;
}
case b2SeparationFunctionType.e_faceB:
{
const s = b2Rot.MulRV(o.q, this.m_axis, b2TimeOfImpact_s_normal);
const n = b2Transform.MulXV(o, this.m_localPoint, b2TimeOfImpact_s_pointB);
const c = b2Rot.MulTRV(i.q, b2Vec2.NegV(s, b2Vec2.s_t0), b2TimeOfImpact_s_axisA);
e[0] = -1;
t[0] = this.m_proxyA.GetSupport(c);
const b = this.m_proxyA.GetVertex(t[0]);
const a = b2Transform.MulXV(i, b, b2TimeOfImpact_s_pointA);
const m = b2Vec2.DotVV(b2Vec2.SubVV(a, n, b2Vec2.s_t0), s);
return m;
}
default:
t[0] = -1;
e[0] = -1;
return 0;
}
}
Evaluate(t, e, s) {
const i = b2TimeOfImpact_s_xfA;
const o = b2TimeOfImpact_s_xfB;
this.m_sweepA.GetTransform(i, s);
this.m_sweepB.GetTransform(o, s);
switch (this.m_type) {
case b2SeparationFunctionType.e_points:
{
const s = this.m_proxyA.GetVertex(t);
const n = this.m_proxyB.GetVertex(e);
const c = b2Transform.MulXV(i, s, b2TimeOfImpact_s_pointA);
const b = b2Transform.MulXV(o, n, b2TimeOfImpact_s_pointB);
const a = b2Vec2.DotVV(b2Vec2.SubVV(b, c, b2Vec2.s_t0), this.m_axis);
return a;
}
case b2SeparationFunctionType.e_faceA:
{
const t = b2Rot.MulRV(i.q, this.m_axis, b2TimeOfImpact_s_normal);
const s = b2Transform.MulXV(i, this.m_localPoint, b2TimeOfImpact_s_pointA);
const n = this.m_proxyB.GetVertex(e);
const c = b2Transform.MulXV(o, n, b2TimeOfImpact_s_pointB);
const b = b2Vec2.DotVV(b2Vec2.SubVV(c, s, b2Vec2.s_t0), t);
return b;
}
case b2SeparationFunctionType.e_faceB:
{
const e = b2Rot.MulRV(o.q, this.m_axis, b2TimeOfImpact_s_normal);
const s = b2Transform.MulXV(o, this.m_localPoint, b2TimeOfImpact_s_pointB);
const n = this.m_proxyA.GetVertex(t);
const c = b2Transform.MulXV(i, n, b2TimeOfImpact_s_pointA);
const b = b2Vec2.DotVV(b2Vec2.SubVV(c, s, b2Vec2.s_t0), e);
return b;
}
default:
return 0;
}
}
}
const b2TimeOfImpact_s_timer = new b2Timer;
const b2TimeOfImpact_s_cache = new b2SimplexCache;
const b2TimeOfImpact_s_distanceInput = new b2DistanceInput;
const b2TimeOfImpact_s_distanceOutput = new b2DistanceOutput;
const b2TimeOfImpact_s_fcn = new b2SeparationFunction;
const b2TimeOfImpact_s_indexA = [ 0 ];
const b2TimeOfImpact_s_indexB = [ 0 ];
const b2TimeOfImpact_s_sweepA = new b2Sweep;
const b2TimeOfImpact_s_sweepB = new b2Sweep;
export function b2TimeOfImpact(t, e) {
const s = b2TimeOfImpact_s_timer.Reset();
++b2_toiCalls;
t.state = b2TOIOutputState.e_unknown;
t.t = e.tMax;
const i = e.proxyA;
const o = e.proxyB;
const n = b2Max(b2_maxPolygonVertices, b2Max(i.m_count, o.m_count));
const c = b2TimeOfImpact_s_sweepA.Copy(e.sweepA);
const b = b2TimeOfImpact_s_sweepB.Copy(e.sweepB);
c.Normalize();
b.Normalize();
const a = e.tMax;
const m = i.m_radius + o.m_radius;
const p = b2Max(b2_linearSlop, m - 3 * b2_linearSlop);
const _ = .25 * b2_linearSlop;
let r = 0;
const f = 20;
let T = 0;
const I = b2TimeOfImpact_s_cache;
I.count = 0;
const O = b2TimeOfImpact_s_distanceInput;
O.proxyA.Copy(e.proxyA);
O.proxyB.Copy(e.proxyB);
O.useRadii = false;
for (;;) {
const e = b2TimeOfImpact_s_xfA;
const s = b2TimeOfImpact_s_xfB;
c.GetTransform(e, r);
b.GetTransform(s, r);
O.transformA.Copy(e);
O.transformB.Copy(s);
const m = b2TimeOfImpact_s_distanceOutput;
b2Distance(m, I, O);
if (m.distance <= 0) {
t.state = b2TOIOutputState.e_overlapped;
t.t = 0;
break;
}
if (m.distance < p + _) {
t.state = b2TOIOutputState.e_touching;
t.t = r;
break;
}
const h = b2TimeOfImpact_s_fcn;
h.Initialize(I, i, c, o, b, r);
let u = false;
let l = a;
let x = 0;
for (;;) {
const e = b2TimeOfImpact_s_indexA;
const s = b2TimeOfImpact_s_indexB;
let i = h.FindMinSeparation(e, s, l);
if (i > p + _) {
t.state = b2TOIOutputState.e_separated;
t.t = a;
u = true;
break;
}
if (i > p - _) {
r = l;
break;
}
let o = h.Evaluate(e[0], s[0], r);
if (o < p - _) {
t.state = b2TOIOutputState.e_failed;
t.t = r;
u = true;
break;
}
if (o <= p + _) {
t.state = b2TOIOutputState.e_touching;
t.t = r;
u = true;
break;
}
let c = 0;
let b = r;
let m = l;
for (;;) {
let t = 0;
if (c & 1) {
t = b + (p - o) * (m - b) / (i - o);
} else {
t = .5 * (b + m);
}
++c;
++b2_toiRootIters;
const n = h.Evaluate(e[0], s[0], t);
if (b2Abs(n - p) < _) {
l = t;
break;
}
if (n > p) {
b = t;
o = n;
} else {
m = t;
i = n;
}
if (c === 50) {
break;
}
}
b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, c);
++x;
if (x === n) {
break;
}
}
++T;
++b2_toiIters;
if (u) {
break;
}
if (T === f) {
t.state = b2TOIOutputState.e_failed;
t.t = r;
break;
}
}
b2_toiMaxIters = b2Max(b2_toiMaxIters, T);
const h = s.GetMilliseconds();
b2_toiMaxTime = b2Max(b2_toiMaxTime, h);
b2_toiTime += h;
}