UNPKG

the-world-engine

Version:

three.js based, unity like game engine for browser

544 lines (528 loc) 15.6 kB
import { b2_aabbExtension, b2_aabbMultiplier } from "../common/b2_settings.js"; import { b2Abs, b2Min, b2Max, b2Vec2 } from "../common/b2_math.js"; import { b2GrowableStack } from "../common/b2_growable_stack.js"; import { b2AABB, b2RayCastInput, b2TestOverlapAABB } from "./b2_collision.js"; function verify(e) { if (e === null) { throw new Error; } return e; } export class b2TreeNode { constructor(e = 0) { this.m_id = 0; this.aabb = new b2AABB; this._userData = null; this.parent = null; this.child1 = null; this.child2 = null; this.height = 0; this.moved = false; this.m_id = e; } get userData() { if (this._userData === null) { throw new Error; } return this._userData; } set userData(e) { if (this._userData !== null) { throw new Error; } this._userData = e; } Reset() { this._userData = null; } IsLeaf() { return this.child1 === null; } } export class b2DynamicTree { constructor() { this.m_root = null; this.m_freeList = null; this.m_insertionCount = 0; this.m_stack = new b2GrowableStack(256); } Query(e, t) { const n = this.m_stack.Reset(); n.Push(this.m_root); while (n.GetCount() > 0) { const i = n.Pop(); if (i === null) { continue; } if (i.aabb.TestOverlap(e)) { if (i.IsLeaf()) { const e = t(i); if (!e) { return; } } else { n.Push(i.child1); n.Push(i.child2); } } } } QueryPoint(e, t) { const n = this.m_stack.Reset(); n.Push(this.m_root); while (n.GetCount() > 0) { const i = n.Pop(); if (i === null) { continue; } if (i.aabb.TestContain(e)) { if (i.IsLeaf()) { const e = t(i); if (!e) { return; } } else { n.Push(i.child1); n.Push(i.child2); } } } } RayCast(e, t) { const n = e.p1; const i = e.p2; const s = b2Vec2.SubVV(i, n, b2DynamicTree.s_r); s.Normalize(); const r = b2Vec2.CrossOneV(s, b2DynamicTree.s_v); const c = b2Vec2.AbsV(r, b2DynamicTree.s_abs_v); let l = e.maxFraction; const o = b2DynamicTree.s_segmentAABB; let b = n.x + l * (i.x - n.x); let a = n.y + l * (i.y - n.y); o.lowerBound.x = b2Min(n.x, b); o.lowerBound.y = b2Min(n.y, a); o.upperBound.x = b2Max(n.x, b); o.upperBound.y = b2Max(n.y, a); const u = this.m_stack.Reset(); u.Push(this.m_root); while (u.GetCount() > 0) { const s = u.Pop(); if (s === null) { continue; } if (!b2TestOverlapAABB(s.aabb, o)) { continue; } const f = s.aabb.GetCenter(); const h = s.aabb.GetExtents(); const y = b2Abs(b2Vec2.DotVV(r, b2Vec2.SubVV(n, f, b2Vec2.s_t0))) - b2Vec2.DotVV(c, h); if (y > 0) { continue; } if (s.IsLeaf()) { const r = b2DynamicTree.s_subInput; r.p1.Copy(e.p1); r.p2.Copy(e.p2); r.maxFraction = l; const c = t(r, s); if (c === 0) { return; } if (c > 0) { l = c; b = n.x + l * (i.x - n.x); a = n.y + l * (i.y - n.y); o.lowerBound.x = b2Min(n.x, b); o.lowerBound.y = b2Min(n.y, a); o.upperBound.x = b2Max(n.x, b); o.upperBound.y = b2Max(n.y, a); } } else { u.Push(s.child1); u.Push(s.child2); } } } AllocateNode() { if (this.m_freeList !== null) { const e = this.m_freeList; this.m_freeList = e.parent; e.parent = null; e.child1 = null; e.child2 = null; e.height = 0; e.moved = false; return e; } return new b2TreeNode(b2DynamicTree.s_node_id++); } FreeNode(e) { e.parent = this.m_freeList; e.child1 = null; e.child2 = null; e.height = -1; e.Reset(); this.m_freeList = e; } CreateProxy(e, t) { const n = this.AllocateNode(); const i = b2_aabbExtension; const s = b2_aabbExtension; n.aabb.lowerBound.x = e.lowerBound.x - i; n.aabb.lowerBound.y = e.lowerBound.y - s; n.aabb.upperBound.x = e.upperBound.x + i; n.aabb.upperBound.y = e.upperBound.y + s; n.userData = t; n.height = 0; n.moved = true; this.InsertLeaf(n); return n; } DestroyProxy(e) { this.RemoveLeaf(e); this.FreeNode(e); } MoveProxy(e, t, n) { const i = b2DynamicTree.MoveProxy_s_fatAABB; const s = b2_aabbExtension; const r = b2_aabbExtension; i.lowerBound.x = t.lowerBound.x - s; i.lowerBound.y = t.lowerBound.y - r; i.upperBound.x = t.upperBound.x + s; i.upperBound.y = t.upperBound.y + r; const c = b2_aabbMultiplier * n.x; const l = b2_aabbMultiplier * n.y; if (c < 0) { i.lowerBound.x += c; } else { i.upperBound.x += c; } if (l < 0) { i.lowerBound.y += l; } else { i.upperBound.y += l; } const o = e.aabb; if (o.Contains(t)) { const e = b2DynamicTree.MoveProxy_s_hugeAABB; e.lowerBound.x = i.lowerBound.x - 4 * s; e.lowerBound.y = i.lowerBound.y - 4 * r; e.upperBound.x = i.upperBound.x + 4 * s; e.upperBound.y = i.upperBound.y + 4 * r; if (e.Contains(o)) { return false; } } this.RemoveLeaf(e); e.aabb.Copy(i); this.InsertLeaf(e); e.moved = true; return true; } InsertLeaf(e) { ++this.m_insertionCount; if (this.m_root === null) { this.m_root = e; this.m_root.parent = null; return; } const t = e.aabb; let n = this.m_root; while (!n.IsLeaf()) { const e = verify(n.child1); const i = verify(n.child2); const s = n.aabb.GetPerimeter(); const r = b2DynamicTree.s_combinedAABB; r.Combine2(n.aabb, t); const c = r.GetPerimeter(); const l = 2 * c; const o = 2 * (c - s); let b; const a = b2DynamicTree.s_aabb; let u; let f; if (e.IsLeaf()) { a.Combine2(t, e.aabb); b = a.GetPerimeter() + o; } else { a.Combine2(t, e.aabb); u = e.aabb.GetPerimeter(); f = a.GetPerimeter(); b = f - u + o; } let h; if (i.IsLeaf()) { a.Combine2(t, i.aabb); h = a.GetPerimeter() + o; } else { a.Combine2(t, i.aabb); u = i.aabb.GetPerimeter(); f = a.GetPerimeter(); h = f - u + o; } if (l < b && l < h) { break; } if (b < h) { n = e; } else { n = i; } } const i = n.parent; const s = this.AllocateNode(); s.parent = i; s.aabb.Combine2(t, n.aabb); s.height = n.height + 1; if (i !== null) { if (i.child1 === n) { i.child1 = s; } else { i.child2 = s; } s.child1 = n; s.child2 = e; n.parent = s; e.parent = s; } else { s.child1 = n; s.child2 = e; n.parent = s; e.parent = s; this.m_root = s; } let r = e.parent; while (r !== null) { r = this.Balance(r); const e = verify(r.child1); const t = verify(r.child2); r.height = 1 + b2Max(e.height, t.height); r.aabb.Combine2(e.aabb, t.aabb); r = r.parent; } } RemoveLeaf(e) { if (e === this.m_root) { this.m_root = null; return; } const t = verify(e.parent); const n = t && t.parent; const i = verify(t.child1 === e ? t.child2 : t.child1); if (n !== null) { if (n.child1 === t) { n.child1 = i; } else { n.child2 = i; } i.parent = n; this.FreeNode(t); let e = n; while (e !== null) { e = this.Balance(e); const t = verify(e.child1); const n = verify(e.child2); e.aabb.Combine2(t.aabb, n.aabb); e.height = 1 + b2Max(t.height, n.height); e = e.parent; } } else { this.m_root = i; i.parent = null; this.FreeNode(t); } } Balance(e) { if (e.IsLeaf() || e.height < 2) { return e; } const t = verify(e.child1); const n = verify(e.child2); const i = n.height - t.height; if (i > 1) { const i = verify(n.child1); const s = verify(n.child2); n.child1 = e; n.parent = e.parent; e.parent = n; if (n.parent !== null) { if (n.parent.child1 === e) { n.parent.child1 = n; } else { n.parent.child2 = n; } } else { this.m_root = n; } if (i.height > s.height) { n.child2 = i; e.child2 = s; s.parent = e; e.aabb.Combine2(t.aabb, s.aabb); n.aabb.Combine2(e.aabb, i.aabb); e.height = 1 + b2Max(t.height, s.height); n.height = 1 + b2Max(e.height, i.height); } else { n.child2 = s; e.child2 = i; i.parent = e; e.aabb.Combine2(t.aabb, i.aabb); n.aabb.Combine2(e.aabb, s.aabb); e.height = 1 + b2Max(t.height, i.height); n.height = 1 + b2Max(e.height, s.height); } return n; } if (i < -1) { const i = verify(t.child1); const s = verify(t.child2); t.child1 = e; t.parent = e.parent; e.parent = t; if (t.parent !== null) { if (t.parent.child1 === e) { t.parent.child1 = t; } else { t.parent.child2 = t; } } else { this.m_root = t; } if (i.height > s.height) { t.child2 = i; e.child1 = s; s.parent = e; e.aabb.Combine2(n.aabb, s.aabb); t.aabb.Combine2(e.aabb, i.aabb); e.height = 1 + b2Max(n.height, s.height); t.height = 1 + b2Max(e.height, i.height); } else { t.child2 = s; e.child1 = i; i.parent = e; e.aabb.Combine2(n.aabb, i.aabb); t.aabb.Combine2(e.aabb, s.aabb); e.height = 1 + b2Max(n.height, i.height); t.height = 1 + b2Max(e.height, s.height); } return t; } return e; } GetHeight() { if (this.m_root === null) { return 0; } return this.m_root.height; } static GetAreaNode(e) { if (e === null) { return 0; } if (e.IsLeaf()) { return 0; } let t = e.aabb.GetPerimeter(); t += b2DynamicTree.GetAreaNode(e.child1); t += b2DynamicTree.GetAreaNode(e.child2); return t; } GetAreaRatio() { if (this.m_root === null) { return 0; } const e = this.m_root; const t = e.aabb.GetPerimeter(); const n = b2DynamicTree.GetAreaNode(this.m_root); return n / t; } static ComputeHeightNode(e) { if (e === null) { return 0; } if (e.IsLeaf()) { return 0; } const t = b2DynamicTree.ComputeHeightNode(e.child1); const n = b2DynamicTree.ComputeHeightNode(e.child2); return 1 + b2Max(t, n); } ComputeHeight() { const e = b2DynamicTree.ComputeHeightNode(this.m_root); return e; } ValidateStructure(e) { if (e === null) { return; } if (e === this.m_root) {} if (e.IsLeaf()) { return; } const t = verify(e.child1); const n = verify(e.child2); this.ValidateStructure(t); this.ValidateStructure(n); } ValidateMetrics(e) { if (e === null) { return; } if (e.IsLeaf()) { return; } const t = verify(e.child1); const n = verify(e.child2); const i = b2DynamicTree.s_aabb; i.Combine2(t.aabb, n.aabb); this.ValidateMetrics(t); this.ValidateMetrics(n); } Validate() {} static GetMaxBalanceNode(e, t) { if (e === null) { return t; } if (e.height <= 1) { return t; } const n = verify(e.child1); const i = verify(e.child2); const s = b2Abs(i.height - n.height); return b2Max(t, s); } GetMaxBalance() { const e = b2DynamicTree.GetMaxBalanceNode(this.m_root, 0); return e; } RebuildBottomUp() { this.Validate(); } static ShiftOriginNode(e, t) { if (e === null) { return; } if (e.height <= 1) { return; } const n = e.child1; const i = e.child2; b2DynamicTree.ShiftOriginNode(n, t); b2DynamicTree.ShiftOriginNode(i, t); e.aabb.lowerBound.SelfSub(t); e.aabb.upperBound.SelfSub(t); } ShiftOrigin(e) { b2DynamicTree.ShiftOriginNode(this.m_root, e); } } b2DynamicTree.s_r = new b2Vec2; b2DynamicTree.s_v = new b2Vec2; b2DynamicTree.s_abs_v = new b2Vec2; b2DynamicTree.s_segmentAABB = new b2AABB; b2DynamicTree.s_subInput = new b2RayCastInput; b2DynamicTree.s_combinedAABB = new b2AABB; b2DynamicTree.s_aabb = new b2AABB; b2DynamicTree.s_node_id = 0; b2DynamicTree.MoveProxy_s_fatAABB = new b2AABB; b2DynamicTree.MoveProxy_s_hugeAABB = new b2AABB;