the-world-engine
Version:
three.js based, unity like game engine for browser
544 lines (528 loc) • 15.6 kB
JavaScript
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;