UNPKG

the-world-engine

Version:

three.js based, unity like game engine for browser

611 lines (557 loc) 16.5 kB
import { b2_epsilon, b2_epsilon_sq, b2_polygonRadius, b2_linearSlop } from "../common/b2_settings.js"; import { b2Max, b2Vec2, b2Rot, b2Transform } from "../common/b2_math.js"; export class b2DistanceProxy { constructor() { this.m_buffer = b2Vec2.MakeArray(2); this.m_vertices = this.m_buffer; this.m_count = 0; this.m_radius = 0; } Copy(t) { if (t.m_vertices === t.m_buffer) { this.m_vertices = this.m_buffer; this.m_buffer[0].Copy(t.m_buffer[0]); this.m_buffer[1].Copy(t.m_buffer[1]); } else { this.m_vertices = t.m_vertices; } this.m_count = t.m_count; this.m_radius = t.m_radius; return this; } Reset() { this.m_vertices = this.m_buffer; this.m_count = 0; this.m_radius = 0; return this; } SetShape(t, s) { t.SetupDistanceProxy(this, s); } SetVerticesRadius(t, s, e) { this.m_vertices = t; this.m_count = s; this.m_radius = e; } GetSupport(t) { let s = 0; let e = b2Vec2.DotVV(this.m_vertices[0], t); for (let i = 1; i < this.m_count; ++i) { const c = b2Vec2.DotVV(this.m_vertices[i], t); if (c > e) { s = i; e = c; } } return s; } GetSupportVertex(t) { let s = 0; let e = b2Vec2.DotVV(this.m_vertices[0], t); for (let i = 1; i < this.m_count; ++i) { const c = b2Vec2.DotVV(this.m_vertices[i], t); if (c > e) { s = i; e = c; } } return this.m_vertices[s]; } GetVertexCount() { return this.m_count; } GetVertex(t) { return this.m_vertices[t]; } } export class b2SimplexCache { constructor() { this.metric = 0; this.count = 0; this.indexA = [ 0, 0, 0 ]; this.indexB = [ 0, 0, 0 ]; } Reset() { this.metric = 0; this.count = 0; return this; } } export class b2DistanceInput { constructor() { this.proxyA = new b2DistanceProxy; this.proxyB = new b2DistanceProxy; this.transformA = new b2Transform; this.transformB = new b2Transform; this.useRadii = false; } Reset() { this.proxyA.Reset(); this.proxyB.Reset(); this.transformA.SetIdentity(); this.transformB.SetIdentity(); this.useRadii = false; return this; } } export class b2DistanceOutput { constructor() { this.pointA = new b2Vec2; this.pointB = new b2Vec2; this.distance = 0; this.iterations = 0; } Reset() { this.pointA.SetZero(); this.pointB.SetZero(); this.distance = 0; this.iterations = 0; return this; } } export class b2ShapeCastInput { constructor() { this.proxyA = new b2DistanceProxy; this.proxyB = new b2DistanceProxy; this.transformA = new b2Transform; this.transformB = new b2Transform; this.translationB = new b2Vec2; } } export class b2ShapeCastOutput { constructor() { this.point = new b2Vec2; this.normal = new b2Vec2; this.lambda = 0; this.iterations = 0; } } export let b2_gjkCalls = 0; export let b2_gjkIters = 0; export let b2_gjkMaxIters = 0; export function b2_gjk_reset() { b2_gjkCalls = 0; b2_gjkIters = 0; b2_gjkMaxIters = 0; } export class b2SimplexVertex { constructor() { this.wA = new b2Vec2; this.wB = new b2Vec2; this.w = new b2Vec2; this.a = 0; this.indexA = 0; this.indexB = 0; } Copy(t) { this.wA.Copy(t.wA); this.wB.Copy(t.wB); this.w.Copy(t.w); this.a = t.a; this.indexA = t.indexA; this.indexB = t.indexB; return this; } } export class b2Simplex { constructor() { this.m_v1 = new b2SimplexVertex; this.m_v2 = new b2SimplexVertex; this.m_v3 = new b2SimplexVertex; this.m_vertices = []; this.m_count = 0; this.m_vertices[0] = this.m_v1; this.m_vertices[1] = this.m_v2; this.m_vertices[2] = this.m_v3; } ReadCache(t, s, e, i, c) { this.m_count = t.count; const n = this.m_vertices; for (let h = 0; h < this.m_count; ++h) { const o = n[h]; o.indexA = t.indexA[h]; o.indexB = t.indexB[h]; const b = s.GetVertex(o.indexA); const r = i.GetVertex(o.indexB); b2Transform.MulXV(e, b, o.wA); b2Transform.MulXV(c, r, o.wB); b2Vec2.SubVV(o.wB, o.wA, o.w); o.a = 0; } if (this.m_count > 1) { const s = t.metric; const e = this.GetMetric(); if (e < .5 * s || 2 * s < e || e < b2_epsilon) { this.m_count = 0; } } if (this.m_count === 0) { const t = n[0]; t.indexA = 0; t.indexB = 0; const h = s.GetVertex(0); const o = i.GetVertex(0); b2Transform.MulXV(e, h, t.wA); b2Transform.MulXV(c, o, t.wB); b2Vec2.SubVV(t.wB, t.wA, t.w); t.a = 1; this.m_count = 1; } } WriteCache(t) { t.metric = this.GetMetric(); t.count = this.m_count; const s = this.m_vertices; for (let e = 0; e < this.m_count; ++e) { t.indexA[e] = s[e].indexA; t.indexB[e] = s[e].indexB; } } GetSearchDirection(t) { switch (this.m_count) { case 1: return b2Vec2.NegV(this.m_v1.w, t); case 2: { const s = b2Vec2.SubVV(this.m_v2.w, this.m_v1.w, t); const e = b2Vec2.CrossVV(s, b2Vec2.NegV(this.m_v1.w, b2Vec2.s_t0)); if (e > 0) { return b2Vec2.CrossOneV(s, t); } else { return b2Vec2.CrossVOne(s, t); } } default: return t.SetZero(); } } GetClosestPoint(t) { switch (this.m_count) { case 0: return t.SetZero(); case 1: return t.Copy(this.m_v1.w); case 2: return t.Set(this.m_v1.a * this.m_v1.w.x + this.m_v2.a * this.m_v2.w.x, this.m_v1.a * this.m_v1.w.y + this.m_v2.a * this.m_v2.w.y); case 3: return t.SetZero(); default: return t.SetZero(); } } GetWitnessPoints(t, s) { switch (this.m_count) { case 0: break; case 1: t.Copy(this.m_v1.wA); s.Copy(this.m_v1.wB); break; case 2: t.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x; t.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y; s.x = this.m_v1.a * this.m_v1.wB.x + this.m_v2.a * this.m_v2.wB.x; s.y = this.m_v1.a * this.m_v1.wB.y + this.m_v2.a * this.m_v2.wB.y; break; case 3: s.x = t.x = this.m_v1.a * this.m_v1.wA.x + this.m_v2.a * this.m_v2.wA.x + this.m_v3.a * this.m_v3.wA.x; s.y = t.y = this.m_v1.a * this.m_v1.wA.y + this.m_v2.a * this.m_v2.wA.y + this.m_v3.a * this.m_v3.wA.y; break; default: break; } } GetMetric() { switch (this.m_count) { case 0: return 0; case 1: return 0; case 2: return b2Vec2.DistanceVV(this.m_v1.w, this.m_v2.w); case 3: return b2Vec2.CrossVV(b2Vec2.SubVV(this.m_v2.w, this.m_v1.w, b2Vec2.s_t0), b2Vec2.SubVV(this.m_v3.w, this.m_v1.w, b2Vec2.s_t1)); default: return 0; } } Solve2() { const t = this.m_v1.w; const s = this.m_v2.w; const e = b2Vec2.SubVV(s, t, b2Simplex.s_e12); const i = -b2Vec2.DotVV(t, e); if (i <= 0) { this.m_v1.a = 1; this.m_count = 1; return; } const c = b2Vec2.DotVV(s, e); if (c <= 0) { this.m_v2.a = 1; this.m_count = 1; this.m_v1.Copy(this.m_v2); return; } const n = 1 / (c + i); this.m_v1.a = c * n; this.m_v2.a = i * n; this.m_count = 2; } Solve3() { const t = this.m_v1.w; const s = this.m_v2.w; const e = this.m_v3.w; const i = b2Vec2.SubVV(s, t, b2Simplex.s_e12); const c = b2Vec2.DotVV(t, i); const n = b2Vec2.DotVV(s, i); const h = n; const o = -c; const b = b2Vec2.SubVV(e, t, b2Simplex.s_e13); const r = b2Vec2.DotVV(t, b); const a = b2Vec2.DotVV(e, b); const _ = a; const p = -r; const l = b2Vec2.SubVV(e, s, b2Simplex.s_e23); const V = b2Vec2.DotVV(s, l); const u = b2Vec2.DotVV(e, l); const f = u; const x = -V; const w = b2Vec2.CrossVV(i, b); const S = w * b2Vec2.CrossVV(s, e); const m = w * b2Vec2.CrossVV(e, t); const C = w * b2Vec2.CrossVV(t, s); if (o <= 0 && p <= 0) { this.m_v1.a = 1; this.m_count = 1; return; } if (h > 0 && o > 0 && C <= 0) { const t = 1 / (h + o); this.m_v1.a = h * t; this.m_v2.a = o * t; this.m_count = 2; return; } if (_ > 0 && p > 0 && m <= 0) { const t = 1 / (_ + p); this.m_v1.a = _ * t; this.m_v3.a = p * t; this.m_count = 2; this.m_v2.Copy(this.m_v3); return; } if (h <= 0 && x <= 0) { this.m_v2.a = 1; this.m_count = 1; this.m_v1.Copy(this.m_v2); return; } if (_ <= 0 && f <= 0) { this.m_v3.a = 1; this.m_count = 1; this.m_v1.Copy(this.m_v3); return; } if (f > 0 && x > 0 && S <= 0) { const t = 1 / (f + x); this.m_v2.a = f * t; this.m_v3.a = x * t; this.m_count = 2; this.m_v1.Copy(this.m_v3); return; } const k = 1 / (S + m + C); this.m_v1.a = S * k; this.m_v2.a = m * k; this.m_v3.a = C * k; this.m_count = 3; } } b2Simplex.s_e12 = new b2Vec2; b2Simplex.s_e13 = new b2Vec2; b2Simplex.s_e23 = new b2Vec2; const b2Distance_s_simplex = new b2Simplex; const b2Distance_s_saveA = [ 0, 0, 0 ]; const b2Distance_s_saveB = [ 0, 0, 0 ]; const b2Distance_s_p = new b2Vec2; const b2Distance_s_d = new b2Vec2; const b2Distance_s_normal = new b2Vec2; const b2Distance_s_supportA = new b2Vec2; const b2Distance_s_supportB = new b2Vec2; export function b2Distance(t, s, e) { ++b2_gjkCalls; const i = e.proxyA; const c = e.proxyB; const n = e.transformA; const h = e.transformB; const o = b2Distance_s_simplex; o.ReadCache(s, i, n, c, h); const b = o.m_vertices; const r = 20; const a = b2Distance_s_saveA; const _ = b2Distance_s_saveB; let p = 0; let l = 0; while (l < r) { p = o.m_count; for (let t = 0; t < p; ++t) { a[t] = b[t].indexA; _[t] = b[t].indexB; } switch (o.m_count) { case 1: break; case 2: o.Solve2(); break; case 3: o.Solve3(); break; default: break; } if (o.m_count === 3) { break; } const t = o.GetSearchDirection(b2Distance_s_d); if (t.LengthSquared() < b2_epsilon_sq) { break; } const s = b[o.m_count]; s.indexA = i.GetSupport(b2Rot.MulTRV(n.q, b2Vec2.NegV(t, b2Vec2.s_t0), b2Distance_s_supportA)); b2Transform.MulXV(n, i.GetVertex(s.indexA), s.wA); s.indexB = c.GetSupport(b2Rot.MulTRV(h.q, t, b2Distance_s_supportB)); b2Transform.MulXV(h, c.GetVertex(s.indexB), s.wB); b2Vec2.SubVV(s.wB, s.wA, s.w); ++l; ++b2_gjkIters; let e = false; for (let t = 0; t < p; ++t) { if (s.indexA === a[t] && s.indexB === _[t]) { e = true; break; } } if (e) { break; } ++o.m_count; } b2_gjkMaxIters = b2Max(b2_gjkMaxIters, l); o.GetWitnessPoints(t.pointA, t.pointB); t.distance = b2Vec2.DistanceVV(t.pointA, t.pointB); t.iterations = l; o.WriteCache(s); if (e.useRadii) { const s = i.m_radius; const e = c.m_radius; if (t.distance > s + e && t.distance > b2_epsilon) { t.distance -= s + e; const i = b2Vec2.SubVV(t.pointB, t.pointA, b2Distance_s_normal); i.Normalize(); t.pointA.SelfMulAdd(s, i); t.pointB.SelfMulSub(e, i); } else { const s = b2Vec2.MidVV(t.pointA, t.pointB, b2Distance_s_p); t.pointA.Copy(s); t.pointB.Copy(s); t.distance = 0; } } } const b2ShapeCast_s_n = new b2Vec2; const b2ShapeCast_s_simplex = new b2Simplex; const b2ShapeCast_s_wA = new b2Vec2; const b2ShapeCast_s_wB = new b2Vec2; const b2ShapeCast_s_v = new b2Vec2; const b2ShapeCast_s_p = new b2Vec2; const b2ShapeCast_s_pointA = new b2Vec2; const b2ShapeCast_s_pointB = new b2Vec2; export function b2ShapeCast(t, s) { t.iterations = 0; t.lambda = 1; t.normal.SetZero(); t.point.SetZero(); const e = s.proxyA; const i = s.proxyB; const c = b2Max(e.m_radius, b2_polygonRadius); const n = b2Max(i.m_radius, b2_polygonRadius); const h = c + n; const o = s.transformA; const b = s.transformB; const r = s.translationB; const a = b2ShapeCast_s_n.Set(0, 0); let _ = 0; const p = b2ShapeCast_s_simplex; p.m_count = 0; const l = p.m_vertices; let V = e.GetSupport(b2Rot.MulTRV(o.q, b2Vec2.NegV(r, b2Vec2.s_t1), b2Vec2.s_t0)); let u = b2Transform.MulXV(o, e.GetVertex(V), b2ShapeCast_s_wA); let f = i.GetSupport(b2Rot.MulTRV(b.q, r, b2Vec2.s_t0)); let x = b2Transform.MulXV(b, i.GetVertex(f), b2ShapeCast_s_wB); const w = b2Vec2.SubVV(u, x, b2ShapeCast_s_v); const S = b2Max(b2_polygonRadius, h - b2_polygonRadius); const m = .5 * b2_linearSlop; const C = 20; let k = 0; while (k < C && w.Length() - S > m) { t.iterations += 1; V = e.GetSupport(b2Rot.MulTRV(o.q, b2Vec2.NegV(w, b2Vec2.s_t1), b2Vec2.s_t0)); u = b2Transform.MulXV(o, e.GetVertex(V), b2ShapeCast_s_wA); f = i.GetSupport(b2Rot.MulTRV(b.q, w, b2Vec2.s_t0)); x = b2Transform.MulXV(b, i.GetVertex(f), b2ShapeCast_s_wB); const s = b2Vec2.SubVV(u, x, b2ShapeCast_s_p); w.Normalize(); const c = b2Vec2.DotVV(w, s); const n = b2Vec2.DotVV(w, r); if (c - S > _ * n) { if (n <= 0) { return false; } _ = (c - S) / n; if (_ > 1) { return false; } a.Copy(w).SelfNeg(); p.m_count = 0; } const h = l[p.m_count]; h.indexA = f; h.wA.Copy(x).SelfMulAdd(_, r); h.indexB = V; h.wB.Copy(u); h.w.Copy(h.wB).SelfSub(h.wA); h.a = 1; p.m_count += 1; switch (p.m_count) { case 1: break; case 2: p.Solve2(); break; case 3: p.Solve3(); break; default: } if (p.m_count === 3) { return false; } p.GetClosestPoint(w); ++k; } if (k === 0) { return false; } const D = b2ShapeCast_s_pointA; const R = b2ShapeCast_s_pointB; p.GetWitnessPoints(D, R); if (w.LengthSquared() > 0) { a.Copy(w).SelfNeg(); a.Normalize(); } t.normal.Copy(a); t.lambda = _; t.iterations = k; return true; }