Optimize quadtree

This commit is contained in:
Peaceultime 2024-06-13 22:40:23 +02:00
parent 1d907c8ab9
commit 209a070464
8 changed files with 136 additions and 126 deletions

View File

@ -1,6 +1,7 @@
import * as Three from 'three'; import * as Three from 'three';
import * as CONST from '../consts'; import * as CONST from '../consts';
import { AABB } from '../physics/common'; import { AABB } from '../physics/common';
import Quadtree from '../physics/quadtree.class';
const UP = new Three.Vector3(0, 0, 1); const UP = new Three.Vector3(0, 0, 1);
@ -9,26 +10,41 @@ const _rotation = new Three.Quaternion();
const _euler = new Three.Euler(); const _euler = new Three.Euler();
const _scale = new Three.Vector3(); const _scale = new Three.Vector3();
export default class Asset implements AABB export default class Asset
{ {
mat: Three.Matrix4; mat: Three.Matrix4;
layer: number; layer: number;
#selected = false; //@ts-expect-error
#dirty = false;
//@ts-ignore
#aabb: AABB; #aabb: AABB;
static instance = new Three.InstancedMesh(CONST.QUAD, new Three.MeshBasicMaterial({ color: new Three.Color( 0xffffff ) }), 1000000); #index: number;
#quad?: number;
static instance: Three.InstancedMesh = new Three.InstancedMesh(CONST.QUAD, new Three.MeshBasicMaterial({ color: new Three.Color(0xffffff) }), 1000000);
static assets: Asset[] = [];
constructor(mat?: Three.Matrix4, layer?: number) constructor(mat?: Three.Matrix4, layer?: number)
{ {
this.mat = mat ?? new Three.Matrix4(); this.mat = mat ?? new Three.Matrix4();
this.#updateAABB(); this.#updateAABB();
this.layer = layer ?? 0; this.layer = layer ?? 0;
this.#index = Asset.assets.push(this) - 1;
} }
#updateAABB() { get aabb(): AABB
{
return this.#aabb;
}
insert(quad: Quadtree): void
{
this.#quad = quad.insert(this.#index, this.#aabb);
}
remove(quad: Quadtree): void
{
this.#quad !== undefined && quad.remove(this.#quad);
}
#updateAABB(): void {
const aabb = { x1: -0.5, x2: 0.5, y1: -0.5, y2: 0.5 }; const aabb = { x1: -0.5, x2: 0.5, y1: -0.5, y2: 0.5 };
const e = this.mat.elements; const e = this.mat.elements;
@ -49,18 +65,6 @@ export default class Asset implements AABB
y2: Math.max(y1, y2, y3, y4) y2: Math.max(y1, y2, y3, y4)
}; };
} }
get x1() {
return this.#aabb.x1;
}
get y1() {
return this.#aabb.y1;
}
get x2() {
return this.#aabb.x2;
}
get y2() {
return this.#aabb.y2;
}
move(x: number, y: number): Asset move(x: number, y: number): Asset
{ {
this.mat.decompose(_position, _rotation, _scale); this.mat.decompose(_position, _rotation, _scale);

View File

@ -52,7 +52,7 @@ export class FreeList<T>
} }
export class IntList export class IntList
{ {
#data: Uint32Array; #data: number[];
#fields: number; #fields: number;
#capacity: number = DEFAULT_SIZE; #capacity: number = DEFAULT_SIZE;
#length: number = 0; #length: number = 0;
@ -63,7 +63,7 @@ export class IntList
if(fields <= 0) if(fields <= 0)
throw new Error("Invalid field count"); throw new Error("Invalid field count");
this.#data = new Uint32Array(this.#capacity * fields); this.#data = new Array(this.#capacity * fields);
this.#fields = fields; this.#fields = fields;
} }
get length(): number get length(): number
@ -105,8 +105,7 @@ export class IntList
{ {
this.#capacity *= 2; this.#capacity *= 2;
//@ts-ignore this.#data.length = this.#capacity * this.#fields;
this.#data.buffer.transferToFixedLength(this.#capacity);
} }
return this.#length++; return this.#length++;
@ -140,7 +139,22 @@ export class IntList
toArray(): number[] toArray(): number[]
{ {
return Array.from(this.#data); return this.#data.slice(0, this.#length);
}
//DEBUG
printReadable(names: string[] = []): void
{
const size = this.#length * this.#fields;
for(let i = 0; i < size; i)
{
const obj: Record<string, number> = {};
for(let j = 0; j < this.#fields; ++i, ++j)
{
obj[names[j] ?? j] = this.#data[i];
}
console.log(obj);
}
} }
} }
export class Stack<T> export class Stack<T>

View File

@ -10,6 +10,7 @@ export {
QUAD QUAD
}; };
export const FRUSTUMSIZE = 16; export const RESOLUTION_X = 2048;
export const RESOLUTION_Y = 2048;
export const MAX_DEPTH = 8; export const MAX_DEPTH = 8;

View File

@ -2,45 +2,38 @@ import * as THREE from 'three';
import Renderer from './renderer/renderer.class'; import Renderer from './renderer/renderer.class';
import Asset from './assets/asset.class'; import Asset from './assets/asset.class';
import Quadtree from './physics/quadtree.class'; import Quadtree from './physics/quadtree.class';
import { FRUSTUMSIZE } from './consts'; import { RESOLUTION_X, RESOLUTION_Y } from './consts';
import Input from './renderer/input.class'; import Input from './renderer/input.class';
import { Random, clamp } from './common'; import { Random, clamp } from './common';
import Selector from './renderer/selector.class'; import Selector from './renderer/selector.class';
if(!ArrayBuffer.prototype.hasOwnProperty("transferToFixedLength"))
{
throw new Error("Your web browser doesn't includes the latest features needed to make this website work properly.\nPlease upgrade your browser and try again.");
}
Renderer.init(); Renderer.init();
Input.init(Renderer.canvas); Input.init(Renderer.canvas);
Selector.init(); Selector.init();
const r = new Random(0); const r = new Random(0);
const quad = new Quadtree({ x1: -RESOLUTION_X / 2, x2: RESOLUTION_X / 2, y1: -RESOLUTION_Y / 2, y2: RESOLUTION_Y / 2 }, 6, 10);
const quad = new Quadtree({x1: -FRUSTUMSIZE * Renderer.aspect / 2, x2: FRUSTUMSIZE * Renderer.aspect / 2, y1: -FRUSTUMSIZE / 2, y2: FRUSTUMSIZE / 2});
const assets: Asset[] = [];
for(let i = 0; i < 10000; i++) for(let i = 0; i < 10000; i++)
{ {
assets[i] = new Asset(new THREE.Matrix4(), 1); const asset = new Asset(new THREE.Matrix4(), 1);
assets[i] asset.move(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y))
.move(r.nextFloat(-0.5 * FRUSTUMSIZE * Renderer.aspect, 0.5 * FRUSTUMSIZE * Renderer.aspect), r.nextFloat(-0.5 * FRUSTUMSIZE, 0.5 * FRUSTUMSIZE))
.rotate(r.nextFloat(Math.PI * 2)) .rotate(r.nextFloat(Math.PI * 2))
.scale(r.nextFloat(0.01, 0.15), r.nextFloat(0.01, 0.15)) .scale(r.nextFloat(10, 30), r.nextFloat(10, 30));
Asset.instance.setMatrixAt(i, assets[i].mat); Asset.instance.setMatrixAt(i, asset.mat);
quad.insert(i, assets[i]); asset.insert(quad);
} }
Asset.instance.count = assets.length; Asset.instance.count = Asset.assets.length;
Asset.instance.computeBoundingBox(); Asset.instance.computeBoundingBox();
Asset.instance.computeBoundingSphere(); Asset.instance.computeBoundingSphere();
Renderer.scene.add(Asset.instance); Renderer.scene.add(Asset.instance);
//quad.debug();
Renderer.startRendering(); Renderer.startRendering();
Input.onDragStart((_, button) => { if(button & 1) Selector.hide(); }); Input.onDragStart((_, button) => { if(button & 1) Selector.hide(); });
@ -48,8 +41,8 @@ Input.onDragEnd((start, end, button) => {
if(button & 1) if(button & 1)
{ {
const s = performance.now(); const s = performance.now();
const selection = quad.query({x1: Math.min(start.x, end.x), x2: Math.max(start.x, end.x), y1: Math.min(start.y, end.y), y2: Math.max(start.y, end.y)}).map(e => assets[e]); const selection = quad.query({x1: Math.min(start.x, end.x), x2: Math.max(start.x, end.x), y1: Math.min(start.y, end.y), y2: Math.max(start.y, end.y)}).map(e => Asset.assets[e]);
console.log("Fetching %s out of %s elements in %sms", selection.length, assets.length, performance.now() - s); console.log("Fetching %s out of %s elements in %sms", selection.length, Asset.assets.length, performance.now() - s);
if(Input.keys['Shift']) Selector.toggle(selection); if(Input.keys['Shift']) Selector.toggle(selection);
else Selector.select(selection); else Selector.select(selection);
@ -59,11 +52,11 @@ Input.onDrag((delta, start, end, button) => { if(button & 1) Selector.preview(st
Input.onClick((point, button) => { Input.onClick((point, button) => {
if(button & 1) if(button & 1)
{ {
const selection = quad.fetch(point.x, point.y).map(e => assets[e]); const selection = quad.fetch(point.x, point.y).map(e => Asset.assets[e]);
if(Input.keys['Shift']) Selector.toggle(selection); if(Input.keys['Shift']) Selector.toggle(selection);
else Selector.select(selection); else Selector.select(selection);
} }
}); });
Input.onWheel(delta => Renderer.zoom = clamp(Renderer.zoom * 1 + (delta * -0.001), 1, 5)); Input.onWheel(delta => Renderer.zoom = clamp(Renderer.zoom * 1 + (delta * -0.001), 0.9, 5));
Input.onMove(p => { if(!Input.dragging) Selector.ghost(assets[quad.fetch(p.x, p.y)[0]]); }); Input.onMove(p => { if (!Input.dragging) Selector.ghost(Asset.assets[quad.fetch(p.x, p.y)[0]]); });

View File

@ -1,3 +1,5 @@
import Asset from "../assets/asset.class";
export interface Point export interface Point
{ {
x: number; x: number;
@ -11,22 +13,12 @@ export interface AABB
y2: number; y2: number;
} }
export function intersectsObj(a: AABB, b: AABB | Point): Boolean export function intersects(aLeft: number, aTop: number, aRight: number, aBottom: number, bLeft: number, bTop: number, bRight: number, bBottom: number): boolean
{
if(b.hasOwnProperty("x") && b.hasOwnProperty("y"))
{
b = b as Point;
return a.x1 <= b.x && a.x2 >= b.x && a.y1 <= b.y && a.y2 >= b.y;
}
else
{
b = b as AABB;
return a.x1 <= b.x2 && a.x2 >= b.x1 && a.y1 <= b.y2 && a.y2 >= b.y1;
}
}
export function intersects(aLeft: number, aTop: number, aRight: number, aBottom: number, bLeft: number, bTop: number, bRight: number, bBottom: number): Boolean
{ {
return aLeft <= bRight && aRight >= bLeft && aTop <= bBottom && aBottom >= bTop; return aLeft <= bRight && aRight >= bLeft && aTop <= bBottom && aBottom >= bTop;
}
export function intersectsAsset(left: number, top: number, right: number, bottom: number, id: number): boolean
{
const aabb = Asset.assets[id].aabb;
return intersects(left, top, right, bottom, aabb.x1, aabb.y1, aabb.x2, aabb.y2);
} }

View File

@ -1,13 +1,8 @@
import { FreeList, IntList, LinkedList } from "../common"; import { IntList } from "../common";
import Renderer from "../renderer/renderer.class"; import Renderer from "../renderer/renderer.class";
import { AABB, intersects } from "./common"; import { AABB, intersectsAsset } from "./common";
import * as THREE from 'three'; import * as THREE from 'three';
/**
* A node that contains elments is called a leaf. Its count is equals to the amount of children it holds.
* A node that contains other nodes is called a branch. Its count is equals to -1.
* The AABB of each node isn't stored and is computed on the go. (Not memory friendly in JS ?)
*/
enum QuadConsts enum QuadConsts
{ {
ENodeINext = 0, ENodeINext = 0,
@ -23,8 +18,13 @@ enum QuadConsts
} }
const _process = new IntList(1); const _process = new IntList(1);
const _nodeProcess = new IntList(QuadConsts.PropCount); const _nodes = new IntList(QuadConsts.PropCount);
/**
* A node that contains elments is called a leaf. Its count is equals to the amount of children it holds.
* A node that contains other nodes is called a branch. Its count is equals to -1.
* The AABB of each node isn't stored and is computed on the go.
*/
export default class Quadtree export default class Quadtree
{ {
#bounds: AABB; #bounds: AABB;
@ -40,6 +40,11 @@ export default class Quadtree
constructor(bounds: AABB, maxDepth?: number, maxElmts?: number) constructor(bounds: AABB, maxDepth?: number, maxElmts?: number)
{ {
this.#bounds = bounds; this.#bounds = bounds;
this.#bounds.x1 = Math.round(this.#bounds.x1);
this.#bounds.x2 = Math.round(this.#bounds.x2);
this.#bounds.y1 = Math.round(this.#bounds.y1);
this.#bounds.y2 = Math.round(this.#bounds.y2);
this.#maxDepth = maxDepth ?? this.#maxDepth; this.#maxDepth = maxDepth ?? this.#maxDepth;
this.#maxElmts = maxElmts ?? this.#maxElmts; this.#maxElmts = maxElmts ?? this.#maxElmts;
@ -55,7 +60,7 @@ export default class Quadtree
{ {
_process.clear(); _process.clear();
const leaves = this.#findLeaves(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, aabb.x1, aabb.x2, aabb.y1, aabb.y2); const leaves = this.#findLeaves(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, Math.floor(aabb.x1), Math.floor(aabb.x2), Math.floor(aabb.y1), Math.floor(aabb.y2));
const tmp: Record<number, boolean> = {}; const tmp: Record<number, boolean> = {};
@ -68,14 +73,11 @@ export default class Quadtree
{ {
const elmt = this.#enodes.get(node, QuadConsts.ENodeIElmt); const elmt = this.#enodes.get(node, QuadConsts.ENodeIElmt);
const left = this.#content.get(elmt, QuadConsts.ElmtILft), const id = this.#content.get(elmt, QuadConsts.ElmtIId);
right = this.#content.get(elmt, QuadConsts.ElmtIRgt),
top = this.#content.get(elmt, QuadConsts.ElmtITop),
bottom = this.#content.get(elmt, QuadConsts.ElmtIBtm);
if(!tmp[elmt] && intersects(aabb.x1, aabb.y1, aabb.x2, aabb.y2, left, top, right, bottom)) if (!tmp[elmt] && intersectsAsset(aabb.x1, aabb.y1, aabb.x2, aabb.y2, id))
{ {
_process.set(_process.push(), 0, elmt); _process.set(_process.push(), 0, id);
tmp[elmt] = true; tmp[elmt] = true;
} }
node = this.#enodes.get(node, QuadConsts.ENodeINext); node = this.#enodes.get(node, QuadConsts.ENodeINext);
@ -88,10 +90,10 @@ export default class Quadtree
{ {
const index = this.#content.insert(); const index = this.#content.insert();
this.#content.set(index, QuadConsts.ElmtILft, aabb.x1); this.#content.set(index, QuadConsts.ElmtILft, Math.floor(aabb.x1));
this.#content.set(index, QuadConsts.ElmtIRgt, aabb.x2); this.#content.set(index, QuadConsts.ElmtIRgt, Math.floor(aabb.x2));
this.#content.set(index, QuadConsts.ElmtITop, aabb.y1); this.#content.set(index, QuadConsts.ElmtITop, Math.floor(aabb.y1));
this.#content.set(index, QuadConsts.ElmtIBtm, aabb.y2); this.#content.set(index, QuadConsts.ElmtIBtm, Math.floor(aabb.y2));
this.#content.set(index, QuadConsts.ElmtIId, id); this.#content.set(index, QuadConsts.ElmtIId, id);
this.#insertNode(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, index); this.#insertNode(0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2, index);
@ -190,26 +192,26 @@ export default class Quadtree
} }
traverse(cb: (index: number, depth: number, left: number, top: number, right: number, bottom: number, leaf: boolean) => void): void traverse(cb: (index: number, depth: number, left: number, top: number, right: number, bottom: number, leaf: boolean) => void): void
{ {
_nodeProcess.clear(); _nodes.clear();
insert(_nodeProcess, 0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2); insert(_nodes, 0, 0, this.#bounds.x1, this.#bounds.x2, this.#bounds.y1, this.#bounds.y2);
while(_nodeProcess.length > 0) while(_nodes.length > 0)
{ {
const last = _nodeProcess.length - 1; const last = _nodes.length - 1;
const node = _nodeProcess.get(last, QuadConsts.PropIIdx); const node = _nodes.get(last, QuadConsts.PropIIdx);
const left = _nodeProcess.get(last, QuadConsts.PropILft); const left = _nodes.get(last, QuadConsts.PropILft);
const right = _nodeProcess.get(last, QuadConsts.PropIRgt); const right = _nodes.get(last, QuadConsts.PropIRgt);
const top = _nodeProcess.get(last, QuadConsts.PropITop); const top = _nodes.get(last, QuadConsts.PropITop);
const bottom = _nodeProcess.get(last, QuadConsts.PropIBtm); const bottom = _nodes.get(last, QuadConsts.PropIBtm);
const depth = _nodeProcess.get(last, QuadConsts.PropIDpt); const depth = _nodes.get(last, QuadConsts.PropIDpt);
_nodeProcess.pop(); _nodes.pop();
const count = this.#nodes.get(node, QuadConsts.NodeICount); const count = this.#nodes.get(node, QuadConsts.NodeICount);
cb(left, top, right, bottom, node, depth, count !== -1); cb(node, depth, left, top, right, bottom, count !== -1);
//If it's a branch //If it's a branch
if (count === -1) if (count === -1)
@ -217,14 +219,14 @@ export default class Quadtree
const fc = this.#nodes.get(node, QuadConsts.NodeIFirst); const fc = this.#nodes.get(node, QuadConsts.NodeIFirst);
const mx = left + (right - left) / 2, my = top + (bottom - top) / 2; const mx = left + (right - left) / 2, my = top + (bottom - top) / 2;
insert(_nodeProcess, fc + 0, depth + 1, left, mx, top, my); insert(_nodes, fc + 0, depth + 1, left, mx, top, my);
insert(_nodeProcess, fc + 1, depth + 1, mx, right, top, my); insert(_nodes, fc + 1, depth + 1, mx, right, top, my);
insert(_nodeProcess, fc + 2, depth + 1, left, mx, my, bottom); insert(_nodes, fc + 2, depth + 1, left, mx, my, bottom);
insert(_nodeProcess, fc + 3, depth + 1, mx, right, my, bottom); insert(_nodes, fc + 3, depth + 1, mx, right, my, bottom);
} }
} }
} }
render(): void debug(): void
{ {
Renderer.scene.remove(...this.#debugRect); Renderer.scene.remove(...this.#debugRect);
this.#debugRect = []; this.#debugRect = [];
@ -234,26 +236,30 @@ export default class Quadtree
}); });
Renderer.scene.add(...this.#debugRect); Renderer.scene.add(...this.#debugRect);
// this.#nodes.printReadable(["first", "count"]);
// this.#enodes.printReadable(["index", "next"]);
// this.#content.printReadable(["left", "right", "top", "bottom", "index", "depth"]);
} }
#findLeaves(index: number, depth: number, lleft: number, lright: number, ltop: number, lbottom: number, eleft: number, eright: number, etop: number, ebottom: number): IntList #findLeaves(index: number, depth: number, lleft: number, lright: number, ltop: number, lbottom: number, eleft: number, eright: number, etop: number, ebottom: number): IntList
{ {
const leaves = new IntList(QuadConsts.PropCount); const leaves = new IntList(QuadConsts.PropCount);
_nodeProcess.clear(); _nodes.clear();
insert(_nodeProcess, index, depth, lleft, lright, ltop, lbottom); insert(_nodes, index, depth, lleft, lright, ltop, lbottom);
while (_nodeProcess.length > 0) while (_nodes.length > 0)
{ {
const last = _nodeProcess.length - 1; const last = _nodes.length - 1;
const nodeLeft = _nodeProcess.get(last, QuadConsts.PropILft); const nodeLeft = _nodes.get(last, QuadConsts.PropILft);
const nodeRight = _nodeProcess.get(last, QuadConsts.PropIRgt); const nodeRight = _nodes.get(last, QuadConsts.PropIRgt);
const nodeTop = _nodeProcess.get(last, QuadConsts.PropITop); const nodeTop = _nodes.get(last, QuadConsts.PropITop);
const nodeBottom = _nodeProcess.get(last, QuadConsts.PropIBtm); const nodeBottom = _nodes.get(last, QuadConsts.PropIBtm);
const nodeIndex = _nodeProcess.get(last, QuadConsts.PropIIdx); const nodeIndex = _nodes.get(last, QuadConsts.PropIIdx);
const nodeDepth = _nodeProcess.get(last, QuadConsts.PropIDpt); const nodeDepth = _nodes.get(last, QuadConsts.PropIDpt);
_nodeProcess.pop(); _nodes.pop();
if(this.#nodes.get(nodeIndex, QuadConsts.NodeICount) !== -1) if(this.#nodes.get(nodeIndex, QuadConsts.NodeICount) !== -1)
insert(leaves, nodeIndex, nodeDepth, nodeLeft, nodeRight, nodeTop, nodeBottom); insert(leaves, nodeIndex, nodeDepth, nodeLeft, nodeRight, nodeTop, nodeBottom);
@ -266,16 +272,16 @@ export default class Quadtree
if(etop <= my) if(etop <= my)
{ {
if(eleft <= mx) //Add a new if(eleft <= mx) //Add a new
insert(_nodeProcess, fc + 0, nodeDepth + 1, nodeLeft, mx, nodeTop, my); insert(_nodes, fc + 0, nodeDepth + 1, nodeLeft, mx, nodeTop, my);
if(eright > mx) if(eright > mx)
insert(_nodeProcess, fc + 1, nodeDepth + 1, mx, nodeRight, nodeTop, my); insert(_nodes, fc + 1, nodeDepth + 1, mx, nodeRight, nodeTop, my);
} }
if(ebottom > my) if(ebottom > my)
{ {
if(eleft <= mx) if(eleft <= mx)
insert(_nodeProcess, fc + 2, nodeDepth + 1, nodeLeft, mx, my, nodeBottom); insert(_nodes, fc + 2, nodeDepth + 1, nodeLeft, mx, my, nodeBottom);
if(eright > mx) if(eright > mx)
insert(_nodeProcess, fc + 3, nodeDepth + 1, mx, nodeRight, my, nodeBottom); insert(_nodes, fc + 3, nodeDepth + 1, mx, nodeRight, my, nodeBottom);
} }
} }
} }
@ -328,14 +334,14 @@ export default class Quadtree
this.#nodes.set(index, QuadConsts.NodeICount, -1); this.#nodes.set(index, QuadConsts.NodeICount, -1);
for(let i = 0; i < _process.length; ++i) for(let i = 0; i < _process.length; ++i)
this.#insertNode(index, depth, left, top, right, bottom, _process.get(i, 0)); this.#insertNode(index, depth, left, right, top, bottom, _process.get(i, 0));
} }
else else
{ {
this.#nodes.set(index, QuadConsts.NodeICount, this.#nodes.get(index, QuadConsts.NodeICount) + 1); this.#nodes.set(index, QuadConsts.NodeICount, this.#nodes.get(index, QuadConsts.NodeICount) + 1);
} }
} }
#insertNode(index: number, depth: number, left: number, top: number, right: number, bottom: number, elmt: number): void #insertNode(index: number, depth: number, left: number, right: number, top: number, bottom: number, elmt: number): void
{ {
const leaves = this.#findLeaves(index, depth, left, right, top, bottom, this.#content.get(elmt, QuadConsts.ElmtILft), this.#content.get(elmt, QuadConsts.ElmtIRgt), this.#content.get(elmt, QuadConsts.ElmtITop), this.#content.get(elmt, QuadConsts.ElmtIBtm)); const leaves = this.#findLeaves(index, depth, left, right, top, bottom, this.#content.get(elmt, QuadConsts.ElmtILft), this.#content.get(elmt, QuadConsts.ElmtIRgt), this.#content.get(elmt, QuadConsts.ElmtITop), this.#content.get(elmt, QuadConsts.ElmtIBtm));

View File

@ -1,5 +1,5 @@
import * as Three from 'three'; import * as Three from 'three';
import { FRUSTUMSIZE } from '../consts'; import { RESOLUTION_X, RESOLUTION_Y } from '../consts';
import Stats from 'stats.js'; import Stats from 'stats.js';
import { Point } from '../physics/common'; import { Point } from '../physics/common';
@ -65,17 +65,17 @@ export default class Renderer
this.camera.position.y += y; this.camera.position.y += y;
} }
static screenSpaceToCameraSpace(x: number, y: number, omit: boolean = false): Point { static screenSpaceToCameraSpace(x: number, y: number, omit: boolean = false): Point {
return { x: ((x / window.innerWidth - 0.5) * FRUSTUMSIZE * this.aspect) / this.#zoom + (omit ? 0 : this.#pos.x), y: (- (y / window.innerHeight - 0.5) * FRUSTUMSIZE) / this.zoom + (omit ? 0 : this.#pos.y) }; return { x: ((x / window.innerWidth - 0.5) * RESOLUTION_X * this.aspect) / this.#zoom + (omit ? 0 : this.#pos.x), y: (- (y / window.innerHeight - 0.5) * RESOLUTION_Y) / this.zoom + (omit ? 0 : this.#pos.y) };
} }
static #resize(): void static #resize(): void
{ {
const aspect = this.aspect = window.innerWidth / window.innerHeight; const aspect = this.aspect = window.innerWidth / window.innerHeight;
this.renderer.setSize( window.innerWidth, window.innerHeight ); this.renderer.setSize( window.innerWidth, window.innerHeight );
this.camera.left = FRUSTUMSIZE * aspect / - 2 / this.#zoom; this.camera.left = RESOLUTION_X * aspect / - 2 / this.#zoom;
this.camera.right = FRUSTUMSIZE * aspect / 2 / this.#zoom; this.camera.right = RESOLUTION_X * aspect / 2 / this.#zoom;
this.camera.top = FRUSTUMSIZE / 2 / this.#zoom; this.camera.top = RESOLUTION_Y / 2 / this.#zoom;
this.camera.bottom = FRUSTUMSIZE / - 2 / this.#zoom; this.camera.bottom = RESOLUTION_Y / - 2 / this.#zoom;
this.camera.updateProjectionMatrix(); this.camera.updateProjectionMatrix();

View File

@ -49,7 +49,7 @@ export default class Selector
return; return;
} }
Selector.#ghostMesh.box.setFromArray([asset.x1, asset.y1, 0, asset.x2, asset.y2, 0]); Selector.#ghostMesh.box.setFromArray([asset.aabb.x1, asset.aabb.y1, 0, asset.aabb.x2, asset.aabb.y2, 0]);
Selector.#ghostMesh.updateMatrix(); Selector.#ghostMesh.updateMatrix();
Selector.#ghostMesh.visible = true; Selector.#ghostMesh.visible = true;
@ -65,11 +65,11 @@ export default class Selector
Selector.#selected = true; Selector.#selected = true;
Selector.#selectionMesh.box.setFromArray([ Selector.#selectionMesh.box.setFromArray([
assets.map(e => e.x1).reduce((p, v) => Math.min(p, v), Infinity), assets.map(e => e.aabb.x1).reduce((p, v) => Math.min(p, v), Infinity),
assets.map(e => e.y1).reduce((p, v) => Math.min(p, v), Infinity), assets.map(e => e.aabb.y1).reduce((p, v) => Math.min(p, v), Infinity),
0, 0,
assets.map(e => e.x2).reduce((p, v) => Math.max(p, v), -Infinity), assets.map(e => e.aabb.x2).reduce((p, v) => Math.max(p, v), -Infinity),
assets.map(e => e.y2).reduce((p, v) => Math.max(p, v), -Infinity), assets.map(e => e.aabb.y2).reduce((p, v) => Math.max(p, v), -Infinity),
0 0
]); ]);
Selector.#selectionMesh.updateMatrix(); Selector.#selectionMesh.updateMatrix();