Physics tests
This commit is contained in:
parent
98e78ca33e
commit
6d20041842
|
|
@ -14,6 +14,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/three": "^0.165.0",
|
"@types/three": "^0.165.0",
|
||||||
"three": "^0.165.0"
|
"three": "^0.165.0",
|
||||||
|
"three-mesh-bvh": "^0.7.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,115 @@
|
||||||
import * as Three from 'three';
|
import * as Three from 'three';
|
||||||
|
import * as CONST from '../consts';
|
||||||
|
import { AABB } from '../physics/common';
|
||||||
|
|
||||||
export default class Asset
|
const UP = new Three.Vector3(0, 0, 1);
|
||||||
|
|
||||||
|
const _position = new Three.Vector3();
|
||||||
|
const _rotation = new Three.Quaternion();
|
||||||
|
const _euler = new Three.Euler();
|
||||||
|
const _scale = new Three.Vector3();
|
||||||
|
|
||||||
|
export default class Asset implements AABB
|
||||||
{
|
{
|
||||||
#mat: Three.Matrix4;
|
mat: Three.Matrix4;
|
||||||
#layer: number;
|
layer: number;
|
||||||
|
|
||||||
ready: boolean = false;
|
selected: boolean = false;
|
||||||
|
|
||||||
_obj: Three.Object3D | undefined;
|
static instance = new Three.InstancedMesh(CONST.QUAD, new Three.MeshBasicMaterial({ color: new Three.Color( 0xffffff ) }), 2**14);
|
||||||
|
|
||||||
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.#layer = layer ?? 0;
|
this.layer = layer ?? 0;
|
||||||
|
}
|
||||||
|
get x1()
|
||||||
|
{
|
||||||
|
return this.mat.elements[0] * (-0.5) + this.mat.elements[4] * (-0.5) + this.mat.elements[12] * (1 / ( this.mat.elements[ 3 ] * -0.5 + + this.mat.elements[ 15 ] ));
|
||||||
|
}
|
||||||
|
get y1()
|
||||||
|
{
|
||||||
|
return this.mat.elements[1] * (-0.5) + this.mat.elements[5] * (-0.5) + this.mat.elements[13] * (1 / ( this.mat.elements[ 7 ] * -0.5 + + this.mat.elements[ 15 ] ));
|
||||||
|
}
|
||||||
|
get x2()
|
||||||
|
{
|
||||||
|
return this.mat.elements[0] * (0.5) + this.mat.elements[4] * (0.5) + this.mat.elements[12] * (1 / ( this.mat.elements[ 3 ] * 0.5 + + this.mat.elements[ 15 ] ));
|
||||||
|
}
|
||||||
|
get y2()
|
||||||
|
{
|
||||||
|
return this.mat.elements[1] * (0.5) + this.mat.elements[5] * (0.5) + this.mat.elements[13] * (1 / ( this.mat.elements[ 7 ] * 0.5 + + this.mat.elements[ 15 ] ));
|
||||||
|
}
|
||||||
|
move(x: number, y: number): Asset
|
||||||
|
{
|
||||||
|
this.mat.decompose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
_position.x += x;
|
||||||
|
_position.y += y;
|
||||||
|
|
||||||
|
this.mat.compose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
moveTo(x: number, y: number): Asset
|
||||||
|
{
|
||||||
|
this.mat.decompose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
_position.x = x;
|
||||||
|
_position.y = y;
|
||||||
|
|
||||||
|
this.mat.compose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
rotate(rad: number): Asset
|
||||||
|
{
|
||||||
|
this.mat.decompose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
_euler.setFromQuaternion(_rotation);
|
||||||
|
_euler.z += rad;
|
||||||
|
_rotation.setFromEuler(_euler);
|
||||||
|
|
||||||
|
this.mat.compose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
rotateTo(rad: number): Asset
|
||||||
|
{
|
||||||
|
this.mat.decompose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
_euler.setFromQuaternion(_rotation);
|
||||||
|
_euler.z = rad;
|
||||||
|
_rotation.setFromEuler(_euler);
|
||||||
|
|
||||||
|
this.mat.compose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
scale(x: number, y: number): Asset
|
||||||
|
{
|
||||||
|
this.mat.decompose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
_scale.x *= x;
|
||||||
|
_scale.y *= y;
|
||||||
|
|
||||||
|
this.mat.compose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
scaleTo(x: number, y: number): Asset
|
||||||
|
{
|
||||||
|
this.mat.decompose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
_scale.x = x;
|
||||||
|
_scale.y = y;
|
||||||
|
|
||||||
|
this.mat.compose(_position, _rotation, _scale);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
shapeTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): Asset
|
||||||
|
{
|
||||||
|
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
static init(): void {}
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,5 @@
|
||||||
import Asset from './asset.class';
|
import Asset from './asset.class';
|
||||||
import * as CONST from '../consts';
|
|
||||||
import * as Three from 'three';
|
|
||||||
|
|
||||||
export default class Sprite extends Asset
|
export default class Sprite extends Asset
|
||||||
{
|
{
|
||||||
static #material = new Three.RawShaderMaterial({
|
|
||||||
fragmentShader: "",
|
|
||||||
vertexShader: "",
|
|
||||||
});
|
|
||||||
static #mesh = CONST.QUAD;
|
|
||||||
static #instance = new Three.InstancedMesh(CONST.QUAD, Sprite.#material, 2**14);
|
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
const DEFAULT_BUCKET_SIZE = 64;
|
||||||
|
|
||||||
|
|
||||||
|
export class FastStack<T>
|
||||||
|
{
|
||||||
|
#arr: T[];
|
||||||
|
|
||||||
|
#pos: number; //Index of the last non empty value
|
||||||
|
|
||||||
|
#bucketSize: number;
|
||||||
|
#bucketCount: number = 1;
|
||||||
|
|
||||||
|
constructor(size?: number)
|
||||||
|
{
|
||||||
|
this.#bucketSize = size ?? DEFAULT_BUCKET_SIZE;
|
||||||
|
|
||||||
|
this.#arr = new Array(this.#bucketSize * this.#bucketCount);
|
||||||
|
this.#pos = 0;
|
||||||
|
}
|
||||||
|
push(item: T): void
|
||||||
|
{
|
||||||
|
if(this.#pos >= this.#arr.length)
|
||||||
|
this.#expand();
|
||||||
|
|
||||||
|
this.#arr[this.#pos] = item;
|
||||||
|
this.#pos++;
|
||||||
|
}
|
||||||
|
pop(): T
|
||||||
|
{
|
||||||
|
if(this.length === 0)
|
||||||
|
throw new Error("Empty queue.");
|
||||||
|
|
||||||
|
const item = this.#arr[this.#pos];
|
||||||
|
delete this.#arr[this.#pos];
|
||||||
|
this.#pos--;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
peek(): T
|
||||||
|
{
|
||||||
|
return this.#arr[this.#pos];
|
||||||
|
}
|
||||||
|
clear(): void
|
||||||
|
{
|
||||||
|
this.#pos = 0;
|
||||||
|
}
|
||||||
|
get length(): number
|
||||||
|
{
|
||||||
|
return this.#pos;
|
||||||
|
}
|
||||||
|
#expand(): void
|
||||||
|
{
|
||||||
|
if(this.length >= this.#bucketSize * this.#bucketCount)
|
||||||
|
this.#bucketCount++;
|
||||||
|
|
||||||
|
this.#arr.length = this.#bucketSize * this.#bucketCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class FastQueue<T>
|
||||||
|
{
|
||||||
|
#arr: T[];
|
||||||
|
|
||||||
|
#idx: number; //Index of the first non empty value
|
||||||
|
#pos: number; //Index of the last non empty value
|
||||||
|
|
||||||
|
#bucketSize: number;
|
||||||
|
#bucketCount: number = 1;
|
||||||
|
|
||||||
|
constructor(size?: number)
|
||||||
|
{
|
||||||
|
this.#bucketSize = size ?? DEFAULT_BUCKET_SIZE;
|
||||||
|
|
||||||
|
this.#arr = new Array(this.#bucketSize * this.#bucketCount);
|
||||||
|
this.#idx = 0;
|
||||||
|
this.#pos = 0;
|
||||||
|
}
|
||||||
|
push(item: T): void
|
||||||
|
{
|
||||||
|
if(this.#pos >= this.#arr.length)
|
||||||
|
this.#expand();
|
||||||
|
|
||||||
|
this.#arr[this.#pos] = item;
|
||||||
|
this.#pos++;
|
||||||
|
}
|
||||||
|
pull(): T
|
||||||
|
{
|
||||||
|
if(this.length === 0)
|
||||||
|
throw new Error("Empty queue.");
|
||||||
|
|
||||||
|
const item = this.#arr[this.#idx];
|
||||||
|
delete this.#arr[this.#idx];
|
||||||
|
this.#idx++;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
peek(): T
|
||||||
|
{
|
||||||
|
return this.#arr[this.#idx];
|
||||||
|
}
|
||||||
|
clear(): void
|
||||||
|
{
|
||||||
|
this.#idx = 0;
|
||||||
|
this.#pos = 0;
|
||||||
|
}
|
||||||
|
get length(): number
|
||||||
|
{
|
||||||
|
return this.#pos - this.#idx;
|
||||||
|
}
|
||||||
|
#shrink(): void
|
||||||
|
{
|
||||||
|
this.#arr.splice(0, this.#idx);
|
||||||
|
|
||||||
|
this.#arr.length = this.#bucketSize * this.#bucketCount;
|
||||||
|
this.#idx = 0;
|
||||||
|
}
|
||||||
|
#expand(): void
|
||||||
|
{
|
||||||
|
if(this.#idx !== 0)
|
||||||
|
this.#shrink();
|
||||||
|
|
||||||
|
if(this.length >= this.#bucketSize * this.#bucketCount)
|
||||||
|
this.#bucketCount++;
|
||||||
|
|
||||||
|
this.#arr.length = this.#bucketSize * this.#bucketCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class LinkedList<T>
|
||||||
|
{
|
||||||
|
#head: LinkedElmt<T> | null;
|
||||||
|
#tail: LinkedElmt<T> | null; //Extension
|
||||||
|
constructor()
|
||||||
|
{
|
||||||
|
this.#head = null;
|
||||||
|
this.#tail = null;
|
||||||
|
}
|
||||||
|
add(item: T): void
|
||||||
|
{
|
||||||
|
const tail = this.#tail;
|
||||||
|
|
||||||
|
this.#tail = { elmt: item, next: null, prev: tail };
|
||||||
|
|
||||||
|
if(tail) tail.next = this.#tail;
|
||||||
|
else this.#head = this.#tail;
|
||||||
|
}
|
||||||
|
pop(): T | null
|
||||||
|
{
|
||||||
|
if(this.#head === null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const head = this.#head;
|
||||||
|
this.#head = head.next;
|
||||||
|
|
||||||
|
if(head === this.#tail) this.#tail = null;
|
||||||
|
if(head.next) head.next.prev = null;
|
||||||
|
if(head.next && head.next === this.#tail) this.#tail = head.next;
|
||||||
|
|
||||||
|
return head.elmt;
|
||||||
|
}
|
||||||
|
remove(item: T): boolean
|
||||||
|
{
|
||||||
|
let current = this.#head;
|
||||||
|
while(current)
|
||||||
|
{
|
||||||
|
if(current.elmt === item)
|
||||||
|
{
|
||||||
|
const prev = current.prev;
|
||||||
|
const next = current.next;
|
||||||
|
|
||||||
|
if(prev) prev.next = next;
|
||||||
|
if(next) next.prev = prev;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
current = current.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
forEach(cb: (e: T, i: number) => void): void
|
||||||
|
{
|
||||||
|
let current = this.#head, i = 0;
|
||||||
|
while(current)
|
||||||
|
{
|
||||||
|
cb(current.elmt, i++);
|
||||||
|
|
||||||
|
current = current.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
toArray(): T[]
|
||||||
|
{
|
||||||
|
const result: T[] = [];
|
||||||
|
this.forEach(e => result.push(e));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
clear()
|
||||||
|
{
|
||||||
|
if(this.#head) this.#head.next = null;
|
||||||
|
if(this.#tail) this.#tail.prev = null;
|
||||||
|
|
||||||
|
this.#head = null;
|
||||||
|
this.#tail = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface LinkedElmt<T>
|
||||||
|
{
|
||||||
|
elmt: T;
|
||||||
|
|
||||||
|
next: LinkedElmt<T> | null;
|
||||||
|
prev: LinkedElmt<T> | null;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import * as Three from 'three';
|
import * as Three from 'three';
|
||||||
|
|
||||||
const QUAD = new Three.BufferGeometry();
|
const QUAD = new Three.BufferGeometry();
|
||||||
QUAD.setIndex( new Three.Float32BufferAttribute( [ 0, 2, 1, 2, 3, 1 ], 1 ) )
|
QUAD.setIndex( new Three.Uint16BufferAttribute( [ 0, 2, 1, 2, 3, 1 ], 1 ) )
|
||||||
QUAD.setAttribute( 'position', new Three.Float32BufferAttribute( [ -0.5, 0.5, 0, 0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0 ], 3 ) );
|
QUAD.setAttribute( 'position', new Three.Float32BufferAttribute( [ -0.5, 0.5, 0, 0.5, 0.5, 0, -0.5, -0.5, 0, 0.5, -0.5, 0 ], 3 ) );
|
||||||
QUAD.setAttribute( 'normal', new Three.Float32BufferAttribute( [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 ], 3 ) );
|
QUAD.setAttribute( 'normal', new Three.Float32BufferAttribute( [ 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1 ], 3 ) );
|
||||||
QUAD.setAttribute( 'uv', new Three.Float32BufferAttribute( [ 0, 1, 1, 1, 0, 0, 1, 0 ], 2 ) );
|
QUAD.setAttribute( 'uv', new Three.Float32BufferAttribute( [ 0, 1, 1, 1, 0, 0, 1, 0 ], 2 ) );
|
||||||
|
|
@ -9,3 +9,7 @@ QUAD.setAttribute( 'uv', new Three.Float32BufferAttribute( [ 0, 1, 1, 1, 0, 0, 1
|
||||||
export {
|
export {
|
||||||
QUAD
|
QUAD
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const FRUSTUMSIZE = 16;
|
||||||
|
|
||||||
|
export const MAX_DEPTH = 8;
|
||||||
51
src/main.ts
51
src/main.ts
|
|
@ -1,3 +1,54 @@
|
||||||
|
import * as THREE from 'three';
|
||||||
import Renderer from './renderer/renderer.class';
|
import Renderer from './renderer/renderer.class';
|
||||||
|
import Asset from './assets/asset.class';
|
||||||
|
import Quadtree from './physics/quadtree.class';
|
||||||
|
import { FRUSTUMSIZE } from './consts';
|
||||||
|
|
||||||
Renderer.init();
|
Renderer.init();
|
||||||
|
const quad = new Quadtree({x1: -FRUSTUMSIZE * Renderer.aspect, x2: FRUSTUMSIZE * Renderer.aspect, y1: -FRUSTUMSIZE, y2: FRUSTUMSIZE});
|
||||||
|
|
||||||
|
window.Asset = Asset;
|
||||||
|
|
||||||
|
const assets: Asset[] = [];
|
||||||
|
for(let i = 0; i < 1; i++)
|
||||||
|
{
|
||||||
|
assets[i] = new Asset(new THREE.Matrix4(), 1);
|
||||||
|
|
||||||
|
assets[i]
|
||||||
|
.move((Math.random() - 0.5) * FRUSTUMSIZE * Renderer.aspect, (Math.random() - 0.5) * FRUSTUMSIZE)
|
||||||
|
.rotate(Math.random() * Math.PI * 2)
|
||||||
|
.scale(Math.random() * 1.5 + 0.1, Math.random() * 1.5 + 0.1)
|
||||||
|
|
||||||
|
Asset.instance.setMatrixAt(i, assets[i].mat);
|
||||||
|
quad.insert(assets[i]);
|
||||||
|
|
||||||
|
console.log(assets[i]);
|
||||||
|
console.log("{ %s, %s, %s, %s }", assets[i].x1, assets[i].y1, assets[i].x2, assets[i].y2);
|
||||||
|
}
|
||||||
|
|
||||||
|
Asset.instance.count = assets.length;
|
||||||
|
|
||||||
|
Asset.instance.computeBoundingBox();
|
||||||
|
Asset.instance.computeBoundingSphere();
|
||||||
|
|
||||||
|
console.log(Asset.instance.boundingBox);
|
||||||
|
|
||||||
|
Renderer.scene.add(Asset.instance);
|
||||||
|
Renderer.render();
|
||||||
|
|
||||||
|
window.addEventListener('mousedown', drag);
|
||||||
|
window.addEventListener('mouseup', select);
|
||||||
|
window.addEventListener('mousemove', hover);
|
||||||
|
|
||||||
|
function drag(e: MouseEvent): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
function select(e: MouseEvent): void
|
||||||
|
{
|
||||||
|
console.log(e.clientX, e.clientY, (e.clientX / window.innerWidth - 0.5) * FRUSTUMSIZE * Renderer.aspect, - (e.clientY / window.innerHeight - 0.5) * FRUSTUMSIZE);
|
||||||
|
}
|
||||||
|
function hover(e: MouseEvent): void
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
export interface Point
|
||||||
|
{
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
export interface AABB
|
||||||
|
{
|
||||||
|
x1: number;
|
||||||
|
y1: number;
|
||||||
|
x2: number;
|
||||||
|
y2: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function intersects(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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,185 @@
|
||||||
|
import { LinkedList } from "../common";
|
||||||
|
import { AABB, Point, intersects } from "./common";
|
||||||
|
|
||||||
|
export default class Quadtree<T extends AABB>
|
||||||
|
{
|
||||||
|
#bounds: AABB;
|
||||||
|
#maxDepth: number = 8;
|
||||||
|
#maxElmts: number = 4;
|
||||||
|
#nodes: Node[];
|
||||||
|
#content: T[];
|
||||||
|
|
||||||
|
#dirty: boolean = false;
|
||||||
|
|
||||||
|
constructor(bounds: AABB, maxDepth?: number, maxElmts?: number)
|
||||||
|
{
|
||||||
|
this.#bounds = bounds;
|
||||||
|
this.#maxDepth = maxDepth ?? this.#maxDepth;
|
||||||
|
this.#maxElmts = maxElmts ?? this.#maxElmts;
|
||||||
|
|
||||||
|
this.#nodes = [];
|
||||||
|
this.#content = [];
|
||||||
|
|
||||||
|
this.#nodes.push({ children: new LinkedList<number>(), count: 0 });
|
||||||
|
}
|
||||||
|
fetch(point: Point): T[]
|
||||||
|
{
|
||||||
|
return this.query({x1: point.x, x2: point.x, y1: point.y, y2: point.y});
|
||||||
|
}
|
||||||
|
query(aabb: AABB): T[]
|
||||||
|
{
|
||||||
|
const result: number[] = [];
|
||||||
|
|
||||||
|
const leaves = this.#find_leaves(0, 0, this.#bounds, aabb);
|
||||||
|
for(let i = 0; i < leaves.length; i++)
|
||||||
|
{
|
||||||
|
const node = this.#nodes[leaves[i].index];
|
||||||
|
node.children.forEach(e => {
|
||||||
|
if(!result.includes(e) && intersects(this.#content[e], aabb))
|
||||||
|
result.push(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.map(e => this.#content[e]);
|
||||||
|
}
|
||||||
|
insert(item: T): number
|
||||||
|
{
|
||||||
|
const idx = this.#content.push(item) - 1;
|
||||||
|
this.#node_insert(0, 0, this.#bounds, idx);
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
remove(index: number): void
|
||||||
|
{
|
||||||
|
if(index >= this.#content.length)
|
||||||
|
throw new Error("Out of bound exception.");
|
||||||
|
|
||||||
|
const elmt = this.#content[index];
|
||||||
|
const leaves = this.#find_leaves(0, 0, this.#bounds, elmt);
|
||||||
|
|
||||||
|
for(let i = 0; i < leaves.length; i++)
|
||||||
|
{
|
||||||
|
if(this.#nodes[leaves[i].index].children.remove(index))
|
||||||
|
this.#nodes[leaves[i].index].count--;
|
||||||
|
}
|
||||||
|
this.#dirty = true;
|
||||||
|
}
|
||||||
|
cleanup(): void
|
||||||
|
{
|
||||||
|
//Only cleanup if it's dirty.
|
||||||
|
//Allows the system to call the function at each loop iteration.
|
||||||
|
if(!this.#dirty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const stack: number[] = [];
|
||||||
|
|
||||||
|
if(this.#nodes[0].count)
|
||||||
|
stack.push(0);
|
||||||
|
|
||||||
|
while(stack.length !== 0)
|
||||||
|
{
|
||||||
|
const node = this.#nodes[stack.pop()!];
|
||||||
|
|
||||||
|
let empty_leaves = 0;
|
||||||
|
|
||||||
|
node.children.forEach(e => {
|
||||||
|
const child = this.#nodes[e];
|
||||||
|
|
||||||
|
if(child.count === 0)
|
||||||
|
empty_leaves++;
|
||||||
|
else if(child.count === -1)
|
||||||
|
stack.push(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
if(empty_leaves === 4)
|
||||||
|
{
|
||||||
|
node.count = 0;
|
||||||
|
node.children.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#find_leaves(index: number, depth: number, bounds: AABB, elmtBounds: AABB): NodeProp[]
|
||||||
|
{
|
||||||
|
const stack: NodeProp[] = [], result: NodeProp[] = [];
|
||||||
|
stack.push({index: index, depth: depth, bounds: bounds});
|
||||||
|
|
||||||
|
//Fetch every nodes intersecting the element bounds
|
||||||
|
while(stack.length > 0)
|
||||||
|
{
|
||||||
|
const nodeProp = stack.pop()!;
|
||||||
|
|
||||||
|
//If the node contains elements
|
||||||
|
if(this.#nodes[nodeProp.index].count !== -1)
|
||||||
|
result.push({index: index, depth: depth, bounds: bounds});
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Check intersection on each 4 sides of the node
|
||||||
|
const children = this.#nodes[nodeProp.index].children.toArray();
|
||||||
|
const mx = bounds.x1 + (bounds.x2 - bounds.x1) / 2, my = bounds.y1 + (bounds.y2 - bounds.y1) / 2;
|
||||||
|
|
||||||
|
if(elmtBounds.y1 <= my)
|
||||||
|
{
|
||||||
|
if(elmtBounds.x1 <= mx)
|
||||||
|
stack.push({ index: children[0], depth: depth + 1, bounds: { x1: bounds.x1, x2: mx, y1: bounds.y1, y2: my }});
|
||||||
|
if(elmtBounds.x2 > mx)
|
||||||
|
stack.push({ index: children[1], depth: depth + 1, bounds: { x1: mx, x2: bounds.x2, y1: bounds.y1, y2: my }});
|
||||||
|
}
|
||||||
|
if(elmtBounds.y2 > my)
|
||||||
|
{
|
||||||
|
if(elmtBounds.x1 <= mx)
|
||||||
|
stack.push({ index: children[2], depth: depth + 1, bounds: { x1: bounds.x1, x2: mx, y1: my, y2: bounds.y2 }});
|
||||||
|
if(elmtBounds.x2 > mx)
|
||||||
|
stack.push({ index: children[3], depth: depth + 1, bounds: { x1: mx, x2: bounds.x2, y1: my, y2: bounds.y2 }});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#leaf_insert(index: number, depth: number, bounds: AABB, elmt: number): void
|
||||||
|
{
|
||||||
|
const node = this.#nodes[index];
|
||||||
|
node.children.add(elmt);
|
||||||
|
|
||||||
|
//Split if the max amount of element is reached and the max depth isn't
|
||||||
|
if(node.count == this.#maxElmts && depth < this.#maxDepth)
|
||||||
|
{
|
||||||
|
const elmts = node.children.toArray();
|
||||||
|
node.children.clear();
|
||||||
|
node.count = -1;
|
||||||
|
|
||||||
|
for(let i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
node.children.add(this.#nodes.push({ children: new LinkedList<number>(), count: 0 }) - 1);
|
||||||
|
}
|
||||||
|
for(let i = 0; i < elmts.length; i++)
|
||||||
|
{
|
||||||
|
this.#node_insert(index, depth, bounds, elmts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else //Otherwise, just increase the count
|
||||||
|
{
|
||||||
|
node.count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#node_insert(index: number, depth: number, bounds: AABB, elmt: number): void
|
||||||
|
{
|
||||||
|
const aabb = this.#content[elmt];
|
||||||
|
const leaves = this.#find_leaves(index, depth, bounds, aabb);
|
||||||
|
|
||||||
|
for(let i = 0; i < leaves.length; i++)
|
||||||
|
{
|
||||||
|
this.#leaf_insert(leaves[i].index, leaves[i].depth, leaves[i].bounds, elmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface NodeProp
|
||||||
|
{
|
||||||
|
index: number;
|
||||||
|
depth: number;
|
||||||
|
bounds: AABB;
|
||||||
|
}
|
||||||
|
interface Node
|
||||||
|
{
|
||||||
|
children: LinkedList<number>;
|
||||||
|
count: number; //The count is used to get the amount of T elements in the current node. If the Node only contains other nodes, the count is = -1.
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
import * as Three from 'three';
|
import * as Three from 'three';
|
||||||
import Asset from '../assets/asset.class';
|
import { FRUSTUMSIZE } from '../consts';
|
||||||
|
|
||||||
export default class Renderer
|
export default class Renderer
|
||||||
{
|
{
|
||||||
static #scene: Three.Scene;
|
static scene: Three.Scene;
|
||||||
static #camera: Three.OrthographicCamera;
|
static aspect: number;
|
||||||
|
|
||||||
|
static renderer: Three.WebGLRenderer;
|
||||||
|
static camera: Three.OrthographicCamera;
|
||||||
static init(): Boolean
|
static init(): Boolean
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
const canvas = document.createElement("canvas");
|
this.renderer = new Three.WebGLRenderer({ antialias: true });
|
||||||
canvas.addEventListener("webglcontextcreationerror", console.error);
|
this.renderer.setPixelRatio( window.devicePixelRatio );
|
||||||
const context = canvas.getContext("webgl2");
|
document.body.appendChild(this.renderer.domElement);
|
||||||
|
|
||||||
this.#renderer.setPixelRatio( window.devicePixelRatio );
|
this.scene = new Three.Scene();
|
||||||
document.body.appendChild(this.#renderer.domElement);
|
this.camera = new Three.OrthographicCamera();
|
||||||
|
this.camera.position.z = 500;
|
||||||
this.#scene = new Three.Scene();
|
|
||||||
this.#camera = new Three.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, 1, 1000 );
|
|
||||||
|
|
||||||
this.#resize();
|
this.#resize();
|
||||||
window.addEventListener("resize", this.#resize.bind(this));
|
window.addEventListener("resize", this.#resize.bind(this));
|
||||||
|
|
@ -31,17 +32,28 @@ export default class Renderer
|
||||||
}
|
}
|
||||||
static #resize(): void
|
static #resize(): void
|
||||||
{
|
{
|
||||||
this.#renderer.setSize( window.innerWidth, window.innerHeight );
|
const aspect = this.aspect = window.innerWidth / window.innerHeight;
|
||||||
this.#camera.left = window.innerWidth / - 2;
|
this.renderer.setSize( window.innerWidth, window.innerHeight );
|
||||||
this.#camera.right = window.innerWidth / 2;
|
|
||||||
this.#camera.top = window.innerHeight / 2;
|
this.camera.left = FRUSTUMSIZE * aspect / - 2;
|
||||||
this.#camera.bottom = window.innerHeight / - 2;
|
this.camera.right = FRUSTUMSIZE * aspect / 2;
|
||||||
|
this.camera.top = FRUSTUMSIZE / 2;
|
||||||
|
this.camera.bottom = FRUSTUMSIZE / - 2;
|
||||||
|
|
||||||
|
this.camera.updateProjectionMatrix();
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
static render(): void
|
static render(delta?: number): void
|
||||||
{
|
{
|
||||||
console.log(new Three.PlaneGeometry());
|
this.renderer.render(this.scene, this.camera);
|
||||||
this.#renderer.render(this.#scene, this.#camera);
|
}
|
||||||
|
static startRendering(): void
|
||||||
|
{
|
||||||
|
this.renderer.setAnimationLoop(Renderer.render.bind(Renderer));
|
||||||
|
}
|
||||||
|
static stopRendering(): void
|
||||||
|
{
|
||||||
|
this.renderer.setAnimationLoop(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue