diff --git a/src/assets/asset.class.ts b/src/assets/asset.class.ts index b03936f..63f8195 100644 --- a/src/assets/asset.class.ts +++ b/src/assets/asset.class.ts @@ -1,6 +1,7 @@ import * as Three from 'three'; import * as CONST from '../consts'; import { AABB } from '../physics/common'; +import Quadtree from '../physics/quadtree.class'; const UP = new Three.Vector3(0, 0, 1); @@ -9,26 +10,41 @@ const _rotation = new Three.Quaternion(); const _euler = new Three.Euler(); const _scale = new Three.Vector3(); -export default class Asset implements AABB +export default class Asset { mat: Three.Matrix4; layer: number; - #selected = false; - #dirty = false; - - //@ts-ignore + //@ts-expect-error #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) { this.mat = mat ?? new Three.Matrix4(); this.#updateAABB(); 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 e = this.mat.elements; @@ -49,18 +65,6 @@ export default class Asset implements AABB 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 { this.mat.decompose(_position, _rotation, _scale); diff --git a/src/common.ts b/src/common.ts index 90e23c9..f053586 100644 --- a/src/common.ts +++ b/src/common.ts @@ -52,7 +52,7 @@ export class FreeList } export class IntList { - #data: Uint32Array; + #data: number[]; #fields: number; #capacity: number = DEFAULT_SIZE; #length: number = 0; @@ -63,7 +63,7 @@ export class IntList if(fields <= 0) throw new Error("Invalid field count"); - this.#data = new Uint32Array(this.#capacity * fields); + this.#data = new Array(this.#capacity * fields); this.#fields = fields; } get length(): number @@ -105,8 +105,7 @@ export class IntList { this.#capacity *= 2; - //@ts-ignore - this.#data.buffer.transferToFixedLength(this.#capacity); + this.#data.length = this.#capacity * this.#fields; } return this.#length++; @@ -140,7 +139,22 @@ export class IntList 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 = {}; + for(let j = 0; j < this.#fields; ++i, ++j) + { + obj[names[j] ?? j] = this.#data[i]; + } + console.log(obj); + } } } export class Stack diff --git a/src/consts.ts b/src/consts.ts index dbf7a7d..34fa0d6 100644 --- a/src/consts.ts +++ b/src/consts.ts @@ -10,6 +10,7 @@ export { QUAD }; -export const FRUSTUMSIZE = 16; +export const RESOLUTION_X = 2048; +export const RESOLUTION_Y = 2048; export const MAX_DEPTH = 8; \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 96506ab..837995e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,45 +2,38 @@ import * as THREE from 'three'; import Renderer from './renderer/renderer.class'; import Asset from './assets/asset.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 { Random, clamp } from './common'; 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(); Input.init(Renderer.canvas); Selector.init(); 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++) { - assets[i] = new Asset(new THREE.Matrix4(), 1); + const asset = new Asset(new THREE.Matrix4(), 1); - assets[i] - .move(r.nextFloat(-0.5 * FRUSTUMSIZE * Renderer.aspect, 0.5 * FRUSTUMSIZE * Renderer.aspect), r.nextFloat(-0.5 * FRUSTUMSIZE, 0.5 * FRUSTUMSIZE)) + asset.move(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y)) .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); - quad.insert(i, assets[i]); + Asset.instance.setMatrixAt(i, asset.mat); + asset.insert(quad); } -Asset.instance.count = assets.length; +Asset.instance.count = Asset.assets.length; Asset.instance.computeBoundingBox(); Asset.instance.computeBoundingSphere(); Renderer.scene.add(Asset.instance); +//quad.debug(); Renderer.startRendering(); Input.onDragStart((_, button) => { if(button & 1) Selector.hide(); }); @@ -48,8 +41,8 @@ Input.onDragEnd((start, end, button) => { if(button & 1) { 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]); - console.log("Fetching %s out of %s elements in %sms", selection.length, assets.length, performance.now() - s); + 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, Asset.assets.length, performance.now() - s); if(Input.keys['Shift']) Selector.toggle(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) => { 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); else Selector.select(selection); } }); -Input.onWheel(delta => Renderer.zoom = clamp(Renderer.zoom * 1 + (delta * -0.001), 1, 5)); -Input.onMove(p => { if(!Input.dragging) Selector.ghost(assets[quad.fetch(p.x, p.y)[0]]); }); \ No newline at end of file +Input.onWheel(delta => Renderer.zoom = clamp(Renderer.zoom * 1 + (delta * -0.001), 0.9, 5)); +Input.onMove(p => { if (!Input.dragging) Selector.ghost(Asset.assets[quad.fetch(p.x, p.y)[0]]); }); \ No newline at end of file diff --git a/src/physics/common.ts b/src/physics/common.ts index 1319ccf..df49374 100644 --- a/src/physics/common.ts +++ b/src/physics/common.ts @@ -1,3 +1,5 @@ +import Asset from "../assets/asset.class"; + export interface Point { x: number; @@ -11,22 +13,12 @@ export interface AABB y2: number; } -export function intersectsObj(a: AABB, b: AABB | Point): 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 +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; +} +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); } \ No newline at end of file diff --git a/src/physics/quadtree.class.ts b/src/physics/quadtree.class.ts index b7546c1..20127ff 100644 --- a/src/physics/quadtree.class.ts +++ b/src/physics/quadtree.class.ts @@ -1,13 +1,8 @@ -import { FreeList, IntList, LinkedList } from "../common"; +import { IntList } from "../common"; import Renderer from "../renderer/renderer.class"; -import { AABB, intersects } from "./common"; +import { AABB, intersectsAsset } from "./common"; 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 { ENodeINext = 0, @@ -23,8 +18,13 @@ enum QuadConsts } 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 { #bounds: AABB; @@ -40,6 +40,11 @@ export default class Quadtree constructor(bounds: AABB, maxDepth?: number, maxElmts?: number) { 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.#maxElmts = maxElmts ?? this.#maxElmts; @@ -55,7 +60,7 @@ export default class Quadtree { _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 = {}; @@ -68,14 +73,11 @@ export default class Quadtree { const elmt = this.#enodes.get(node, QuadConsts.ENodeIElmt); - const left = this.#content.get(elmt, QuadConsts.ElmtILft), - right = this.#content.get(elmt, QuadConsts.ElmtIRgt), - top = this.#content.get(elmt, QuadConsts.ElmtITop), - bottom = this.#content.get(elmt, QuadConsts.ElmtIBtm); + const id = this.#content.get(elmt, QuadConsts.ElmtIId); - 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; } node = this.#enodes.get(node, QuadConsts.ENodeINext); @@ -88,10 +90,10 @@ export default class Quadtree { const index = this.#content.insert(); - this.#content.set(index, QuadConsts.ElmtILft, aabb.x1); - this.#content.set(index, QuadConsts.ElmtIRgt, aabb.x2); - this.#content.set(index, QuadConsts.ElmtITop, aabb.y1); - this.#content.set(index, QuadConsts.ElmtIBtm, aabb.y2); + this.#content.set(index, QuadConsts.ElmtILft, Math.floor(aabb.x1)); + this.#content.set(index, QuadConsts.ElmtIRgt, Math.floor(aabb.x2)); + this.#content.set(index, QuadConsts.ElmtITop, Math.floor(aabb.y1)); + this.#content.set(index, QuadConsts.ElmtIBtm, Math.floor(aabb.y2)); this.#content.set(index, QuadConsts.ElmtIId, id); 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 { - _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 left = _nodeProcess.get(last, QuadConsts.PropILft); - const right = _nodeProcess.get(last, QuadConsts.PropIRgt); - const top = _nodeProcess.get(last, QuadConsts.PropITop); - const bottom = _nodeProcess.get(last, QuadConsts.PropIBtm); - const depth = _nodeProcess.get(last, QuadConsts.PropIDpt); + const node = _nodes.get(last, QuadConsts.PropIIdx); + const left = _nodes.get(last, QuadConsts.PropILft); + const right = _nodes.get(last, QuadConsts.PropIRgt); + const top = _nodes.get(last, QuadConsts.PropITop); + const bottom = _nodes.get(last, QuadConsts.PropIBtm); + const depth = _nodes.get(last, QuadConsts.PropIDpt); - _nodeProcess.pop(); + _nodes.pop(); 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 (count === -1) @@ -217,14 +219,14 @@ export default class Quadtree const fc = this.#nodes.get(node, QuadConsts.NodeIFirst); const mx = left + (right - left) / 2, my = top + (bottom - top) / 2; - insert(_nodeProcess, fc + 0, depth + 1, left, mx, top, my); - insert(_nodeProcess, fc + 1, depth + 1, mx, right, top, my); - insert(_nodeProcess, fc + 2, depth + 1, left, mx, my, bottom); - insert(_nodeProcess, fc + 3, depth + 1, mx, right, my, bottom); + insert(_nodes, fc + 0, depth + 1, left, mx, top, my); + insert(_nodes, fc + 1, depth + 1, mx, right, top, my); + insert(_nodes, fc + 2, depth + 1, left, mx, my, bottom); + insert(_nodes, fc + 3, depth + 1, mx, right, my, bottom); } } } - render(): void + debug(): void { Renderer.scene.remove(...this.#debugRect); this.#debugRect = []; @@ -234,26 +236,30 @@ export default class Quadtree }); 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 { const leaves = new IntList(QuadConsts.PropCount); - _nodeProcess.clear(); - insert(_nodeProcess, index, depth, lleft, lright, ltop, lbottom); + _nodes.clear(); + 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 nodeRight = _nodeProcess.get(last, QuadConsts.PropIRgt); - const nodeTop = _nodeProcess.get(last, QuadConsts.PropITop); - const nodeBottom = _nodeProcess.get(last, QuadConsts.PropIBtm); - const nodeIndex = _nodeProcess.get(last, QuadConsts.PropIIdx); - const nodeDepth = _nodeProcess.get(last, QuadConsts.PropIDpt); + const nodeLeft = _nodes.get(last, QuadConsts.PropILft); + const nodeRight = _nodes.get(last, QuadConsts.PropIRgt); + const nodeTop = _nodes.get(last, QuadConsts.PropITop); + const nodeBottom = _nodes.get(last, QuadConsts.PropIBtm); + const nodeIndex = _nodes.get(last, QuadConsts.PropIIdx); + const nodeDepth = _nodes.get(last, QuadConsts.PropIDpt); - _nodeProcess.pop(); + _nodes.pop(); if(this.#nodes.get(nodeIndex, QuadConsts.NodeICount) !== -1) insert(leaves, nodeIndex, nodeDepth, nodeLeft, nodeRight, nodeTop, nodeBottom); @@ -266,16 +272,16 @@ export default class Quadtree if(etop <= my) { 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) - insert(_nodeProcess, fc + 1, nodeDepth + 1, mx, nodeRight, nodeTop, my); + insert(_nodes, fc + 1, nodeDepth + 1, mx, nodeRight, nodeTop, my); } if(ebottom > my) { 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) - 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); 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 { 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)); diff --git a/src/renderer/renderer.class.ts b/src/renderer/renderer.class.ts index 2632462..b810d0e 100644 --- a/src/renderer/renderer.class.ts +++ b/src/renderer/renderer.class.ts @@ -1,5 +1,5 @@ import * as Three from 'three'; -import { FRUSTUMSIZE } from '../consts'; +import { RESOLUTION_X, RESOLUTION_Y } from '../consts'; import Stats from 'stats.js'; import { Point } from '../physics/common'; @@ -65,17 +65,17 @@ export default class Renderer this.camera.position.y += y; } 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 { const aspect = this.aspect = window.innerWidth / window.innerHeight; this.renderer.setSize( window.innerWidth, window.innerHeight ); - this.camera.left = FRUSTUMSIZE * aspect / - 2 / this.#zoom; - this.camera.right = FRUSTUMSIZE * aspect / 2 / this.#zoom; - this.camera.top = FRUSTUMSIZE / 2 / this.#zoom; - this.camera.bottom = FRUSTUMSIZE / - 2 / this.#zoom; + this.camera.left = RESOLUTION_X * aspect / - 2 / this.#zoom; + this.camera.right = RESOLUTION_X * aspect / 2 / this.#zoom; + this.camera.top = RESOLUTION_Y / 2 / this.#zoom; + this.camera.bottom = RESOLUTION_Y / - 2 / this.#zoom; this.camera.updateProjectionMatrix(); diff --git a/src/renderer/selector.class.ts b/src/renderer/selector.class.ts index 3b85bf6..6a3b610 100644 --- a/src/renderer/selector.class.ts +++ b/src/renderer/selector.class.ts @@ -49,7 +49,7 @@ export default class Selector 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.visible = true; @@ -65,11 +65,11 @@ export default class Selector Selector.#selected = true; Selector.#selectionMesh.box.setFromArray([ - assets.map(e => e.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.x1).reduce((p, v) => Math.min(p, v), Infinity), + assets.map(e => e.aabb.y1).reduce((p, v) => Math.min(p, v), Infinity), 0, - assets.map(e => e.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.x2).reduce((p, v) => Math.max(p, v), -Infinity), + assets.map(e => e.aabb.y2).reduce((p, v) => Math.max(p, v), -Infinity), 0 ]); Selector.#selectionMesh.updateMatrix();