Starting to work on input controls

This commit is contained in:
Peaceultime 2024-06-16 22:59:17 +02:00
parent c01f4bd8c6
commit a70e599c14
10 changed files with 610 additions and 64 deletions

286
package-lock.json generated Normal file
View File

@ -0,0 +1,286 @@
{
"name": "vtt-mapper",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vtt-mapper",
"version": "0.0.0",
"dependencies": {
"@types/three": "^0.165.0",
"stats.js": "^0.17.0",
"three": "^0.165.0"
},
"devDependencies": {
"typescript": "^5.2.2",
"vite": "^5.2.0"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.20.2",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.18.0",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
]
},
"node_modules/@tweenjs/tween.js": {
"version": "23.1.2",
"license": "MIT"
},
"node_modules/@types/estree": {
"version": "1.0.5",
"dev": true,
"license": "MIT"
},
"node_modules/@types/stats.js": {
"version": "0.17.3",
"license": "MIT"
},
"node_modules/@types/three": {
"version": "0.165.0",
"license": "MIT",
"dependencies": {
"@tweenjs/tween.js": "~23.1.1",
"@types/stats.js": "*",
"@types/webxr": "*",
"fflate": "~0.8.2",
"meshoptimizer": "~0.18.1"
}
},
"node_modules/@types/webxr": {
"version": "0.5.16",
"license": "MIT"
},
"node_modules/esbuild": {
"version": "0.20.2",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.20.2",
"@esbuild/android-arm": "0.20.2",
"@esbuild/android-arm64": "0.20.2",
"@esbuild/android-x64": "0.20.2",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@esbuild/freebsd-arm64": "0.20.2",
"@esbuild/freebsd-x64": "0.20.2",
"@esbuild/linux-arm": "0.20.2",
"@esbuild/linux-arm64": "0.20.2",
"@esbuild/linux-ia32": "0.20.2",
"@esbuild/linux-loong64": "0.20.2",
"@esbuild/linux-mips64el": "0.20.2",
"@esbuild/linux-ppc64": "0.20.2",
"@esbuild/linux-riscv64": "0.20.2",
"@esbuild/linux-s390x": "0.20.2",
"@esbuild/linux-x64": "0.20.2",
"@esbuild/netbsd-x64": "0.20.2",
"@esbuild/openbsd-x64": "0.20.2",
"@esbuild/sunos-x64": "0.20.2",
"@esbuild/win32-arm64": "0.20.2",
"@esbuild/win32-ia32": "0.20.2",
"@esbuild/win32-x64": "0.20.2"
}
},
"node_modules/fflate": {
"version": "0.8.2",
"license": "MIT"
},
"node_modules/meshoptimizer": {
"version": "0.18.1",
"license": "MIT"
},
"node_modules/nanoid": {
"version": "3.3.7",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"bin": {
"nanoid": "bin/nanoid.cjs"
},
"engines": {
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
}
},
"node_modules/picocolors": {
"version": "1.0.1",
"dev": true,
"license": "ISC"
},
"node_modules/postcss": {
"version": "8.4.38",
"dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/postcss"
},
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"license": "MIT",
"dependencies": {
"nanoid": "^3.3.7",
"picocolors": "^1.0.0",
"source-map-js": "^1.2.0"
},
"engines": {
"node": "^10 || ^12 || >=14"
}
},
"node_modules/rollup": {
"version": "4.18.0",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.5"
},
"bin": {
"rollup": "dist/bin/rollup"
},
"engines": {
"node": ">=18.0.0",
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.18.0",
"@rollup/rollup-android-arm64": "4.18.0",
"@rollup/rollup-darwin-arm64": "4.18.0",
"@rollup/rollup-darwin-x64": "4.18.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
"@rollup/rollup-linux-arm-musleabihf": "4.18.0",
"@rollup/rollup-linux-arm64-gnu": "4.18.0",
"@rollup/rollup-linux-arm64-musl": "4.18.0",
"@rollup/rollup-linux-powerpc64le-gnu": "4.18.0",
"@rollup/rollup-linux-riscv64-gnu": "4.18.0",
"@rollup/rollup-linux-s390x-gnu": "4.18.0",
"@rollup/rollup-linux-x64-gnu": "4.18.0",
"@rollup/rollup-linux-x64-musl": "4.18.0",
"@rollup/rollup-win32-arm64-msvc": "4.18.0",
"@rollup/rollup-win32-ia32-msvc": "4.18.0",
"@rollup/rollup-win32-x64-msvc": "4.18.0",
"fsevents": "~2.3.2"
}
},
"node_modules/source-map-js": {
"version": "1.2.0",
"dev": true,
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/stats.js": {
"version": "0.17.0",
"license": "MIT"
},
"node_modules/three": {
"version": "0.165.0",
"license": "MIT"
},
"node_modules/typescript": {
"version": "5.4.5",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/vite": {
"version": "5.2.12",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.20.1",
"postcss": "^8.4.38",
"rollup": "^4.13.0"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || >=20.0.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || >=20.0.0",
"less": "*",
"lightningcss": "^1.21.0",
"sass": "*",
"stylus": "*",
"sugarss": "*",
"terser": "^5.4.0"
},
"peerDependenciesMeta": {
"@types/node": {
"optional": true
},
"less": {
"optional": true
},
"lightningcss": {
"optional": true
},
"sass": {
"optional": true
},
"stylus": {
"optional": true
},
"sugarss": {
"optional": true
},
"terser": {
"optional": true
}
}
}
}
}

View File

@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "bunx --bun vite --port 3000 --cors",
"dev": "bunx --bun vite",
"build": "tsc && vite build",
"preview": "vite preview"
},

View File

@ -2,8 +2,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);
import { FreeList } from '../common';
const _position = new Three.Vector3();
const _rotation = new Three.Quaternion();
@ -14,6 +13,7 @@ export default class Asset
{
mat: Three.Matrix4;
layer: number;
selected: boolean = false;
//@ts-expect-error
#aabb: AABB;
@ -28,7 +28,8 @@ export default class Asset
#quad?: number;
static instance: Three.InstancedMesh = new Three.InstancedMesh(CONST.QUAD, new Three.MeshBasicMaterial({ color: new Three.Color(0xffffff) }), 1000000);
static assets: Asset[] = [];
static quadtree: Quadtree = new Quadtree({ x1: -CONST.RESOLUTION_X / 2, x2: CONST.RESOLUTION_X / 2, y1: -CONST.RESOLUTION_Y / 2, y2: CONST.RESOLUTION_Y / 2 }, 6, 10);
static assets: FreeList<Asset> = new FreeList<Asset>();
constructor(mat?: Three.Matrix4, layer?: number)
{
@ -44,12 +45,26 @@ export default class Asset
this.#updateAABB();
this.layer = layer ?? 0;
this.#index = Asset.assets.push(this) - 1;
this.#index = Asset.assets.insert(this);
}
get aabb(): AABB
{
get aabb(): AABB {
return this.#aabb;
}
get posX(): number {
return this.#posX;
}
get posY(): number {
return this.#posY;
}
get rot(): number {
return this.#rot;
}
get scaleX(): number {
return this.#scaleX;
}
get scaleY(): number {
return this.#scaleY;
}
#updateAABB(): void {
const aabb = { x1: -0.5, x2: 0.5, y1: -0.5, y2: 0.5 };
@ -71,13 +86,32 @@ export default class Asset
y2: Math.max(y1, y2, y3, y4)
};
}
insert(quad: Quadtree): void
insert(): Asset
{
this.#quad = quad.insert(this.#index, this.#aabb);
this.#quad = Asset.quadtree.insert(this.#index, this.#aabb);
return this;
}
remove(quad: Quadtree): void
remove(): Asset
{
this.#quad !== undefined && quad.remove(this.#quad);
this.#quad !== undefined && Asset.quadtree.remove(this.#quad);
Asset.assets.erase(this.#index);
Asset.instance.setMatrixAt(this.#index, this.mat.identity());
Asset.instance.instanceMatrix.needsUpdate = true;
return this;
}
update(quad: boolean = true): void
{
this.#updateAABB();
Asset.instance.setMatrixAt(this.#index, this.mat);
Asset.instance.instanceMatrix.needsUpdate = true;
if(quad)
{
this.#quad !== undefined && Asset.quadtree.remove(this.#quad);
this.#quad = Asset.quadtree.insert(this.#index, this.#aabb);
}
}
moveTo(x: number, y: number): Asset
{
@ -86,7 +120,6 @@ export default class Asset
e[12] = this.#posX = x;
e[13] = this.#posY = y;
this.#updateAABB();
return this;
}
rotateTo(rad: number): Asset
@ -101,7 +134,6 @@ export default class Asset
e[4] = sin * this.#scaleY;
e[5] = cos * this.#scaleY;
this.#updateAABB();
return this;
}
scaleTo(x: number, y: number): Asset
@ -117,7 +149,6 @@ export default class Asset
e[4] = sin * this.#scaleY;
e[5] = cos * this.#scaleY;
this.#updateAABB();
return this;
}
}

View File

@ -1,4 +1,5 @@
const DEFAULT_SIZE = 128;
const MAX_BYTE_LENGTH = 1024*1024*16;
export class FreeList<T>
{
@ -17,10 +18,12 @@ export class FreeList<T>
{
if(this.#free.empty)
{
++this.#length;
return this.#data.push(element) - 1;
}
else
{
++this.#length;
const index = this.#free.pop()!;
this.#data[index] = element;
return index;
@ -30,12 +33,45 @@ export class FreeList<T>
{
delete this.#data[n];
this.#free.add(n);
this.#length--;
}
clear(): void
{
this.#data.length = 0;
this.#free.clear();
}
forEach(cb: (e: T, i: number) => void): void {
let i = 0, offset = 0;
for (; i + offset < this.#data.length; ++i) {
while (this.#data[i + offset] === undefined && i + offset < this.#data.length)
++offset;
if (i + offset >= this.#data.length)
break;
cb(this.#data[i + offset], i + offset);
}
if (i !== this.#length)
throw new Error("Something wants wrong.");
}
toArray(): T[]
{
let i = 0, offset = 0, arr = [];
for(; i + offset < this.#data.length; ++i)
{
while (this.#data[i + offset] === undefined && i + offset < this.#data.length)
++offset;
if (i + offset >= this.#data.length)
break;
arr.push(this.#data[i + offset]);
}
if(i !== this.#length)
throw new Error("Something wants wrong.");
return arr;
}
get(index: number): T
{
@ -52,7 +88,7 @@ export class FreeList<T>
}
export class IntList
{
#data: number[];
#data: Int32Array;
#fields: number;
#capacity: number = DEFAULT_SIZE;
#length: number = 0;
@ -63,7 +99,8 @@ export class IntList
if(fields <= 0)
throw new Error("Invalid field count");
this.#data = new Array(this.#capacity * fields);
//@ts-ignore
this.#data = new Int32Array(new ArrayBuffer(this.#capacity * fields * Int32Array.BYTES_PER_ELEMENT, { maxByteLength: MAX_BYTE_LENGTH }));
this.#fields = fields;
}
get length(): number
@ -101,11 +138,20 @@ export class IntList
{
const pos = (this.#length + 1) * this.#fields;
if(pos > this.#capacity)
if(pos > this.#capacity * this.#fields)
{
this.#capacity *= 2;
this.#data.length = this.#capacity * this.#fields;
//@ts-ignore
if(this.#data.buffer.resizable)
{
//@ts-ignore
this.#data.buffer.resize(clamp(this.#capacity * this.#fields * Int32Array.BYTES_PER_ELEMENT, 1, MAX_BYTE_LENGTH));
}
else
{
throw new Error("Cannot resize the buffer");
}
}
return this.#length++;
@ -139,7 +185,7 @@ export class IntList
toArray(): number[]
{
return this.#data.slice(0, this.#length);
return [...this.#data.slice(0, this.#length)];
}
//DEBUG

View File

@ -1,30 +1,29 @@
import * as THREE from 'three';
import Renderer from './renderer/renderer.class';
import Asset from './assets/asset.class';
import Quadtree from './physics/quadtree.class';
import { RESOLUTION_X, RESOLUTION_Y } from './consts';
import Input from './renderer/input.class';
import { Random, clamp } from './common';
import Selector from './renderer/selector.class';
export enum DragMode
{
pan, move, select, scale, rotate
}
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);
let dragMode = DragMode.pan, dragFrom: number | undefined;
for(let i = 0; i < 2; i++)
for(let i = 0; i < 10; i++)
{
const asset = new Asset(new THREE.Matrix4(), 1);
asset
.rotateTo(r.nextFloat(Math.PI * 2))
.scaleTo(r.nextFloat(100, 300), r.nextFloat(100, 300))
.moveTo(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y));
Asset.instance.setMatrixAt(i, asset.mat);
asset.insert(quad);
new Asset(new THREE.Matrix4(), 1).rotateTo(r.nextFloat(Math.PI * 2))
.scaleTo(r.nextFloat(10, 30), r.nextFloat(10, 30))
.moveTo(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y))
.update();
}
Asset.instance.count = Asset.assets.length;
@ -34,37 +33,94 @@ Asset.instance.computeBoundingSphere();
Renderer.scene.add(Asset.instance);
//quad.debug();
Renderer.startRendering();
Input.onDragStart((_, button) => { if(button & 1) Selector.hide(); });
Input.onDragEnd((s, e, button) => {
Input.onDragStart((s, button) => {
if(button & 1)
{
const n = performance.now();
const selection = quad.query({x1: Math.min(s.x, e.x), x2: Math.max(s.x, e.x), y1: Math.min(s.y, e.y), y2: Math.max(s.y, e.y)}).map(e => Asset.assets[e]);
console.log("Fetching %s out of %s elements in %sms", selection.length, Asset.assets.length, performance.now() - n);
if(Input.keys['Shift']) Selector.toggle(selection);
else Selector.select(selection);
if(Selector.selected)
{
const result = Selector.mousemove(s.x, s.y);
dragMode = result.mode;
dragFrom = result.details;
}
else
{
dragMode = DragMode.select;
Selector.hide();
}
}
else
{
dragMode = DragMode.pan;
}
});
Input.onDragEnd((s, e, _) => {
if(dragMode === DragMode.select)
{
const n = performance.now();
const selection = Asset.quadtree.query({x1: Math.min(s.x, e.x), x2: Math.max(s.x, e.x), y1: Math.min(s.y, e.y), y2: Math.max(s.y, e.y)}).map(e => Asset.assets.get(e));
console.log("Fetching %s out of %s elements in %sms", selection.length, Asset.assets.length, performance.now() - n);
if(Input.keys['shift']) Selector.toggle(selection);
else Selector.select(selection);
}
else
{
Selector.selection.forEach(e => e.update());
}
});
Input.onDrag((delta, start, end, _) => {
if (dragMode === DragMode.select)
{
Selector.preview(start, end);
}
else if (dragMode === DragMode.move && Selector.selected)
{
Selector.move(delta.x, delta.y);
}
else if (dragMode === DragMode.scale && Selector.selected)
{
Selector.scale(dragFrom!, delta.x, delta.y);
}
else if(dragMode === DragMode.pan)
{
Renderer.move(-delta.x, -delta.y);
}
});
Input.onDrag((delta, start, end, button) => { if(button & 1) Selector.preview(start, end); else Renderer.move(-delta.x, -delta.y); });
Input.onClick((p, button) => {
if(button & 1)
{
const selection = quad.fetch(p.x, p.y)[0];
const selection = Asset.quadtree.fetch(p.x, p.y)[0];
if (selection === undefined)
{
if (!Input.keys['Shift'])
if (!Input.keys['shift'])
Selector.clear();
dragMode = DragMode.select;
return;
}
if (Input.keys['Shift']) Selector.toggle([Asset.assets[selection]]);
else Selector.select([Asset.assets[selection]]);
if (Input.keys['shift']) Selector.toggle([Asset.assets.get(selection)]);
else Selector.select([Asset.assets.get(selection)]);
}
});
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]]); });
Input.onMove(p => {
if (!Input.dragging)
{
Selector.ghost(Asset.assets.get(Asset.quadtree.fetch(p.x, p.y)[0]));
Selector.mousemove(p.x, p.y);
}
});
Input.onInput((input) => {
if(input === 'delete')
{
Selector.selection.forEach(e => e.remove());
Selector.clear();
}
else if(input === 'a' && Input.keys['control'])
{
Selector.select(Asset.assets.toArray());
}
})

View File

@ -19,6 +19,12 @@ export function intersects(aLeft: number, aTop: number, aRight: number, aBottom:
}
export function intersectsAsset(left: number, top: number, right: number, bottom: number, id: number): boolean
{
const aabb = Asset.assets[id].aabb;
const aabb = Asset.assets.get(id).aabb;
return intersects(left, top, right, bottom, aabb.x1, aabb.y1, aabb.x2, aabb.y2);
}
export function sqrtLen(x1: number, y1: number, x2: number, y2: number) {
return Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2);
}
export function len(x1: number, y1: number, x2: number, y2: number) {
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
}

View File

@ -34,7 +34,6 @@ export default class Quadtree
#enodes: IntList = new IntList(2);
#content: IntList = new IntList(5);
#dirty: boolean = true;
#debugRect: THREE.Box3Helper[] = [];
constructor(bounds: AABB, maxDepth?: number, maxElmts?: number)
@ -126,7 +125,7 @@ export default class Quadtree
else
this.#enodes.set(prev, QuadConsts.ENodeINext, next);
this.#nodes.set(prop, QuadConsts.NodeICount, this.#nodes.get(prop, QuadConsts.NodeICount - 1));
this.#nodes.set(prop, QuadConsts.NodeICount, this.#nodes.get(prop, QuadConsts.NodeICount) - 1);
}
}
@ -138,19 +137,13 @@ export default class Quadtree
this.#content.clear();
this.#enodes.clear();
this.#dirty = false;
//DEBUG
this.#debugRect.forEach(e => e.parent?.remove(e));
this.#debugRect = [];
}
cleanup(): void
{
//Only cleanup if it's dirty.
//Allows the system to call the function at each loop iteration.
if(!this.#dirty)
return;
let updated = false;
_process.clear();
if(this.#nodes.get(0, QuadConsts.NodeICount) === -1)
@ -170,7 +163,7 @@ export default class Quadtree
const count = this.#nodes.get(current, QuadConsts.NodeICount);
if(count == 0) //Count the amount of empty leaves
if(count === 0) //Count the amount of empty leaves
++empty
else if(count === -1) //Add this node to the check process if it's a branch
_process.set(_process.push(), 0, current);
@ -178,7 +171,7 @@ export default class Quadtree
if(empty === 4)
{
//Because of the way the IntList is made, it's preferable to erase in the reversed order
//Because of the way the IntList is made, it's preferable to erase in the reversed order as the last erased index becomes the first available index.
this.#nodes.erase(fc + 3);
this.#nodes.erase(fc + 2);
this.#nodes.erase(fc + 1);
@ -187,8 +180,15 @@ export default class Quadtree
//The branch becomes a empty leaf
this.#nodes.set(node, QuadConsts.NodeICount, 0);
this.#nodes.set(node, QuadConsts.NodeIFirst, -1);
updated = true;
}
}
if(updated)
{
//this.debug();
}
}
traverse(cb: (index: number, depth: number, left: number, top: number, right: number, bottom: number, leaf: boolean) => void): void
{
@ -258,6 +258,11 @@ export default class Quadtree
const nodeBottom = _nodes.get(last, QuadConsts.PropIBtm);
const nodeIndex = _nodes.get(last, QuadConsts.PropIIdx);
const nodeDepth = _nodes.get(last, QuadConsts.PropIDpt);
if (nodeIndex === -1) {
console.log(leaves, last, nodeIndex, nodeDepth);
debugger;
}
_nodes.pop();

View File

@ -167,12 +167,12 @@ export default class Input
}
static #keydown(e: KeyboardEvent): void
{
Input.#keys[e.key] = true;
Input.#keys[e.key.toLowerCase()] = true;
}
static #keyup(e: KeyboardEvent): void
{
Input.#keys[e.key] = false;
Input.#keys[e.key.toLowerCase()] = false;
Input.#inputCb && Input.#inputCb(e.key);
Input.#inputCb && Input.#inputCb(e.key.toLowerCase());
}
}

View File

@ -3,7 +3,18 @@ import { RESOLUTION_X, RESOLUTION_Y } from '../consts';
import Stats from 'stats.js';
import { Point } from '../physics/common';
import Selector from './selector.class';
import Asset from '../assets/asset.class';
export enum CursorType
{
default = "default",
pointer = "pointer",
move = "move",
updown = "ns-resize",
leftright = "ew-resize",
nesw = "nesw-resize",
nwse = "nwse-resize",
}
export default class Renderer
{
static scene: Three.Scene;
@ -87,6 +98,7 @@ export default class Renderer
this.#stats.begin();
Selector.update();
this.renderer.render(this.scene, this.camera);
Asset.quadtree.cleanup();
this.#stats.end();
}
static startRendering(): void
@ -97,4 +109,8 @@ export default class Renderer
{
this.renderer.setAnimationLoop(null);
}
static cursor(type?: CursorType): void
{
Renderer.canvas.style.setProperty("cursor", type ?? CursorType.default);
}
}

View File

@ -1,12 +1,22 @@
import * as Three from "three";
import Asset from "../assets/asset.class";
import { Point } from "../physics/common";
import Renderer from "./renderer.class";
import { Point, intersects, intersectsAsset, sqrtLen } from "../physics/common";
import Renderer, { CursorType } from "./renderer.class";
import { QUAD } from '../consts';
import { DragMode } from "../main";
const _vector = new Three.Vector3();
const orientation = {
0: { x: -1, y: -1 },
1: { x: -1, y: 1 },
2: { x: -1, y: 1 },
3: { x: -1, y: 1 },
};
export default class Selector
{
static #assets: Asset[];
static #assets: Asset[] = [];
static #selected: boolean = false;
static #previousZoom: number = 1;
@ -68,6 +78,8 @@ export default class Selector
}
static ghost(asset: Asset): void
{
Renderer.cursor(asset && !asset.selected ? CursorType.pointer : undefined);
if(!asset)
{
Selector.#ghostMesh.visible = false;
@ -81,11 +93,18 @@ export default class Selector
}
static select(assets: Asset[]): void
{
for (let i = 0; i < Selector.#assets.length; i++) {
Selector.#assets[i].selected = false;
}
Selector.#assets = assets;
Selector.hide();
if(assets.length <= 0)
{
Selector.#selected = false;
return;
}
Selector.#selected = true;
@ -96,6 +115,8 @@ export default class Selector
minY = Math.min(minY, assets[i].aabb.y1);
maxX = Math.max(maxX, assets[i].aabb.x2);
maxY = Math.max(maxY, assets[i].aabb.y2);
assets[i].selected = true;
}
const scale = Math.max((maxX - minX) / 2, (maxY - minY) / 2)
@ -125,6 +146,7 @@ export default class Selector
for(let i = 0; i < assets.length; i++)
{
const index = Selector.#assets.indexOf(assets[i]);
assets[i].selected = false;
if(index === -1)
Selector.#assets.push(assets[i]);
@ -135,7 +157,11 @@ export default class Selector
}
static clear(): void
{
for(let i = 0; i < Selector.#assets.length; i++)
Selector.#assets[i].selected = false;
Selector.#assets = [];
Selector.#selected = false;
Selector.hide();
}
@ -162,4 +188,78 @@ export default class Selector
Selector.gizmoScale[3].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
}
}
static mousemove(x: number, y: number): { mode: DragMode, details?: number }
{
if(!Selector.selected)
return { mode: DragMode.select };
const minX = Selector.#selectionMesh.box.min.x,
minY = Selector.#selectionMesh.box.min.y,
maxX = Selector.#selectionMesh.box.max.x,
maxY = Selector.#selectionMesh.box.max.y;
if (sqrtLen(x, y, Selector.gizmoScale[0].position.x, Selector.gizmoScale[0].position.y) <= 500 / Renderer.zoom)
{
Renderer.cursor(CursorType.nesw);
return { mode: DragMode.scale, details: 0 };
}
else if (sqrtLen(x, y, Selector.gizmoScale[1].position.x, Selector.gizmoScale[1].position.y) <= 500 / Renderer.zoom)
{
Renderer.cursor(CursorType.nwse);
return { mode: DragMode.scale, details: 1 };
}
else if (sqrtLen(x, y, Selector.gizmoScale[2].position.x, Selector.gizmoScale[2].position.y) <= 500 / Renderer.zoom)
{
Renderer.cursor(CursorType.nwse);
return { mode: DragMode.scale, details: 2 };
}
else if (sqrtLen(x, y, Selector.gizmoScale[3].position.x, Selector.gizmoScale[3].position.y) <= 500 / Renderer.zoom)
{
Renderer.cursor(CursorType.nesw);
return { mode: DragMode.scale, details: 3 };
}
else if (intersects(x, y, x, y, minX, minY, maxX, maxY))
{
Renderer.cursor(CursorType.move);
return { mode: DragMode.move };
}
return { mode: DragMode.select };
}
static move(x: number, y: number): void
{
Selector.selection.forEach(e => e.moveTo(e.posX + x, e.posY + y).update(false));
Asset.instance.computeBoundingBox();
Asset.instance.computeBoundingSphere();
Selector.#selectionMesh.box.translate(_vector.set(x, y, 0));
Selector.#ghostMesh.box.translate(_vector.set(x, y, 0));
Selector.gizmoScale[0].position.add({ x: x, y: y, z: 0 });
Selector.gizmoScale[1].position.add({ x: x, y: y, z: 0 });
Selector.gizmoScale[2].position.add({ x: x, y: y, z: 0 });
Selector.gizmoScale[3].position.add({ x: x, y: y, z: 0 });
}
static scale(dragFrom: number, x: number, y: number): void
{
console.log(dragFrom, x, y);
//@ts-ignore
x *= orientation[dragFrom].x;
//@ts-ignore
y *= orientation[dragFrom].y;
Selector.selection.forEach(e => e.scaleTo(e.scaleX + x, e.scaleY + y).moveTo(e.posX - x / 2, e.posY - y / 2).update(false));
Selector.#selectionMesh.box.translate(_vector.set(-x / 2, -y / 2, 0));
Selector.gizmoScale[0].position.add({ x: -x / 2, y: -y / 2, z: 0 });
Selector.gizmoScale[1].position.add({ x: -x / 2, y: -y / 2, z: 0 });
Selector.gizmoScale[2].position.add({ x: -x / 2, y: -y / 2, z: 0 });
Selector.gizmoScale[3].position.add({ x: -x / 2, y: -y / 2, z: 0 });
Asset.instance.computeBoundingBox();
Asset.instance.computeBoundingSphere();
}
}