Starting to work on input controls
This commit is contained in:
parent
c01f4bd8c6
commit
a70e599c14
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "bunx --bun vite --port 3000 --cors",
|
"dev": "bunx --bun vite",
|
||||||
"build": "tsc && vite build",
|
"build": "tsc && vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,7 @@ 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';
|
import Quadtree from '../physics/quadtree.class';
|
||||||
|
import { FreeList } from '../common';
|
||||||
const UP = new Three.Vector3(0, 0, 1);
|
|
||||||
|
|
||||||
const _position = new Three.Vector3();
|
const _position = new Three.Vector3();
|
||||||
const _rotation = new Three.Quaternion();
|
const _rotation = new Three.Quaternion();
|
||||||
|
|
@ -14,6 +13,7 @@ export default class Asset
|
||||||
{
|
{
|
||||||
mat: Three.Matrix4;
|
mat: Three.Matrix4;
|
||||||
layer: number;
|
layer: number;
|
||||||
|
selected: boolean = false;
|
||||||
|
|
||||||
//@ts-expect-error
|
//@ts-expect-error
|
||||||
#aabb: AABB;
|
#aabb: AABB;
|
||||||
|
|
@ -28,7 +28,8 @@ export default class Asset
|
||||||
#quad?: number;
|
#quad?: number;
|
||||||
|
|
||||||
static instance: Three.InstancedMesh = new Three.InstancedMesh(CONST.QUAD, new Three.MeshBasicMaterial({ color: new Three.Color(0xffffff) }), 1000000);
|
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)
|
constructor(mat?: Three.Matrix4, layer?: number)
|
||||||
{
|
{
|
||||||
|
|
@ -44,12 +45,26 @@ export default class Asset
|
||||||
this.#updateAABB();
|
this.#updateAABB();
|
||||||
this.layer = layer ?? 0;
|
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;
|
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 {
|
#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 };
|
||||||
|
|
||||||
|
|
@ -71,13 +86,32 @@ export default class Asset
|
||||||
y2: Math.max(y1, y2, y3, y4)
|
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
|
moveTo(x: number, y: number): Asset
|
||||||
{
|
{
|
||||||
|
|
@ -86,7 +120,6 @@ export default class Asset
|
||||||
e[12] = this.#posX = x;
|
e[12] = this.#posX = x;
|
||||||
e[13] = this.#posY = y;
|
e[13] = this.#posY = y;
|
||||||
|
|
||||||
this.#updateAABB();
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
rotateTo(rad: number): Asset
|
rotateTo(rad: number): Asset
|
||||||
|
|
@ -101,7 +134,6 @@ export default class Asset
|
||||||
e[4] = sin * this.#scaleY;
|
e[4] = sin * this.#scaleY;
|
||||||
e[5] = cos * this.#scaleY;
|
e[5] = cos * this.#scaleY;
|
||||||
|
|
||||||
this.#updateAABB();
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
scaleTo(x: number, y: number): Asset
|
scaleTo(x: number, y: number): Asset
|
||||||
|
|
@ -117,7 +149,6 @@ export default class Asset
|
||||||
e[4] = sin * this.#scaleY;
|
e[4] = sin * this.#scaleY;
|
||||||
e[5] = cos * this.#scaleY;
|
e[5] = cos * this.#scaleY;
|
||||||
|
|
||||||
this.#updateAABB();
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
const DEFAULT_SIZE = 128;
|
const DEFAULT_SIZE = 128;
|
||||||
|
const MAX_BYTE_LENGTH = 1024*1024*16;
|
||||||
|
|
||||||
export class FreeList<T>
|
export class FreeList<T>
|
||||||
{
|
{
|
||||||
|
|
@ -17,10 +18,12 @@ export class FreeList<T>
|
||||||
{
|
{
|
||||||
if(this.#free.empty)
|
if(this.#free.empty)
|
||||||
{
|
{
|
||||||
|
++this.#length;
|
||||||
return this.#data.push(element) - 1;
|
return this.#data.push(element) - 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
++this.#length;
|
||||||
const index = this.#free.pop()!;
|
const index = this.#free.pop()!;
|
||||||
this.#data[index] = element;
|
this.#data[index] = element;
|
||||||
return index;
|
return index;
|
||||||
|
|
@ -30,12 +33,45 @@ export class FreeList<T>
|
||||||
{
|
{
|
||||||
delete this.#data[n];
|
delete this.#data[n];
|
||||||
this.#free.add(n);
|
this.#free.add(n);
|
||||||
|
this.#length--;
|
||||||
}
|
}
|
||||||
clear(): void
|
clear(): void
|
||||||
{
|
{
|
||||||
this.#data.length = 0;
|
this.#data.length = 0;
|
||||||
this.#free.clear();
|
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
|
get(index: number): T
|
||||||
{
|
{
|
||||||
|
|
@ -52,7 +88,7 @@ export class FreeList<T>
|
||||||
}
|
}
|
||||||
export class IntList
|
export class IntList
|
||||||
{
|
{
|
||||||
#data: number[];
|
#data: Int32Array;
|
||||||
#fields: number;
|
#fields: number;
|
||||||
#capacity: number = DEFAULT_SIZE;
|
#capacity: number = DEFAULT_SIZE;
|
||||||
#length: number = 0;
|
#length: number = 0;
|
||||||
|
|
@ -63,7 +99,8 @@ export class IntList
|
||||||
if(fields <= 0)
|
if(fields <= 0)
|
||||||
throw new Error("Invalid field count");
|
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;
|
this.#fields = fields;
|
||||||
}
|
}
|
||||||
get length(): number
|
get length(): number
|
||||||
|
|
@ -101,11 +138,20 @@ export class IntList
|
||||||
{
|
{
|
||||||
const pos = (this.#length + 1) * this.#fields;
|
const pos = (this.#length + 1) * this.#fields;
|
||||||
|
|
||||||
if(pos > this.#capacity)
|
if(pos > this.#capacity * this.#fields)
|
||||||
{
|
{
|
||||||
this.#capacity *= 2;
|
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++;
|
return this.#length++;
|
||||||
|
|
@ -139,7 +185,7 @@ export class IntList
|
||||||
|
|
||||||
toArray(): number[]
|
toArray(): number[]
|
||||||
{
|
{
|
||||||
return this.#data.slice(0, this.#length);
|
return [...this.#data.slice(0, this.#length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
//DEBUG
|
//DEBUG
|
||||||
|
|
|
||||||
110
src/main.ts
110
src/main.ts
|
|
@ -1,30 +1,29 @@
|
||||||
import * as THREE from 'three';
|
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 { RESOLUTION_X, RESOLUTION_Y } 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';
|
||||||
|
|
||||||
|
export enum DragMode
|
||||||
|
{
|
||||||
|
pan, move, select, scale, rotate
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
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);
|
new Asset(new THREE.Matrix4(), 1).rotateTo(r.nextFloat(Math.PI * 2))
|
||||||
|
.scaleTo(r.nextFloat(10, 30), r.nextFloat(10, 30))
|
||||||
asset
|
.moveTo(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y))
|
||||||
.rotateTo(r.nextFloat(Math.PI * 2))
|
.update();
|
||||||
.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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Asset.instance.count = Asset.assets.length;
|
Asset.instance.count = Asset.assets.length;
|
||||||
|
|
@ -34,37 +33,94 @@ 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((s, button) => {
|
||||||
Input.onDragEnd((s, e, button) => {
|
|
||||||
if(button & 1)
|
if(button & 1)
|
||||||
{
|
{
|
||||||
const n = performance.now();
|
if(Selector.selected)
|
||||||
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);
|
const result = Selector.mousemove(s.x, s.y);
|
||||||
|
dragMode = result.mode;
|
||||||
if(Input.keys['Shift']) Selector.toggle(selection);
|
dragFrom = result.details;
|
||||||
else Selector.select(selection);
|
}
|
||||||
|
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) => {
|
Input.onClick((p, button) => {
|
||||||
if(button & 1)
|
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 (selection === undefined)
|
||||||
{
|
{
|
||||||
if (!Input.keys['Shift'])
|
if (!Input.keys['shift'])
|
||||||
Selector.clear();
|
Selector.clear();
|
||||||
|
dragMode = DragMode.select;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Input.keys['Shift']) Selector.toggle([Asset.assets[selection]]);
|
if (Input.keys['shift']) Selector.toggle([Asset.assets.get(selection)]);
|
||||||
else Selector.select([Asset.assets[selection]]);
|
else Selector.select([Asset.assets.get(selection)]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Input.onWheel(delta => Renderer.zoom = clamp(Renderer.zoom * 1 + (delta * -0.001), 0.9, 5));
|
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());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
@ -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
|
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);
|
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));
|
||||||
|
}
|
||||||
|
|
@ -34,7 +34,6 @@ export default class Quadtree
|
||||||
#enodes: IntList = new IntList(2);
|
#enodes: IntList = new IntList(2);
|
||||||
#content: IntList = new IntList(5);
|
#content: IntList = new IntList(5);
|
||||||
|
|
||||||
#dirty: boolean = true;
|
|
||||||
#debugRect: THREE.Box3Helper[] = [];
|
#debugRect: THREE.Box3Helper[] = [];
|
||||||
|
|
||||||
constructor(bounds: AABB, maxDepth?: number, maxElmts?: number)
|
constructor(bounds: AABB, maxDepth?: number, maxElmts?: number)
|
||||||
|
|
@ -126,7 +125,7 @@ export default class Quadtree
|
||||||
else
|
else
|
||||||
this.#enodes.set(prev, QuadConsts.ENodeINext, next);
|
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.#content.clear();
|
||||||
this.#enodes.clear();
|
this.#enodes.clear();
|
||||||
|
|
||||||
this.#dirty = false;
|
|
||||||
|
|
||||||
//DEBUG
|
//DEBUG
|
||||||
this.#debugRect.forEach(e => e.parent?.remove(e));
|
this.#debugRect.forEach(e => e.parent?.remove(e));
|
||||||
this.#debugRect = [];
|
this.#debugRect = [];
|
||||||
}
|
}
|
||||||
cleanup(): void
|
cleanup(): void
|
||||||
{
|
{
|
||||||
//Only cleanup if it's dirty.
|
let updated = false;
|
||||||
//Allows the system to call the function at each loop iteration.
|
|
||||||
if(!this.#dirty)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_process.clear();
|
_process.clear();
|
||||||
|
|
||||||
if(this.#nodes.get(0, QuadConsts.NodeICount) === -1)
|
if(this.#nodes.get(0, QuadConsts.NodeICount) === -1)
|
||||||
|
|
@ -170,7 +163,7 @@ export default class Quadtree
|
||||||
|
|
||||||
const count = this.#nodes.get(current, QuadConsts.NodeICount);
|
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
|
++empty
|
||||||
else if(count === -1) //Add this node to the check process if it's a branch
|
else if(count === -1) //Add this node to the check process if it's a branch
|
||||||
_process.set(_process.push(), 0, current);
|
_process.set(_process.push(), 0, current);
|
||||||
|
|
@ -178,7 +171,7 @@ export default class Quadtree
|
||||||
|
|
||||||
if(empty === 4)
|
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 + 3);
|
||||||
this.#nodes.erase(fc + 2);
|
this.#nodes.erase(fc + 2);
|
||||||
this.#nodes.erase(fc + 1);
|
this.#nodes.erase(fc + 1);
|
||||||
|
|
@ -187,8 +180,15 @@ export default class Quadtree
|
||||||
//The branch becomes a empty leaf
|
//The branch becomes a empty leaf
|
||||||
this.#nodes.set(node, QuadConsts.NodeICount, 0);
|
this.#nodes.set(node, QuadConsts.NodeICount, 0);
|
||||||
this.#nodes.set(node, QuadConsts.NodeIFirst, -1);
|
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
|
traverse(cb: (index: number, depth: number, left: number, top: number, right: number, bottom: number, leaf: boolean) => void): void
|
||||||
{
|
{
|
||||||
|
|
@ -259,6 +259,11 @@ export default class Quadtree
|
||||||
const nodeIndex = _nodes.get(last, QuadConsts.PropIIdx);
|
const nodeIndex = _nodes.get(last, QuadConsts.PropIIdx);
|
||||||
const nodeDepth = _nodes.get(last, QuadConsts.PropIDpt);
|
const nodeDepth = _nodes.get(last, QuadConsts.PropIDpt);
|
||||||
|
|
||||||
|
if (nodeIndex === -1) {
|
||||||
|
console.log(leaves, last, nodeIndex, nodeDepth);
|
||||||
|
debugger;
|
||||||
|
}
|
||||||
|
|
||||||
_nodes.pop();
|
_nodes.pop();
|
||||||
|
|
||||||
if(this.#nodes.get(nodeIndex, QuadConsts.NodeICount) !== -1)
|
if(this.#nodes.get(nodeIndex, QuadConsts.NodeICount) !== -1)
|
||||||
|
|
|
||||||
|
|
@ -167,12 +167,12 @@ export default class Input
|
||||||
}
|
}
|
||||||
static #keydown(e: KeyboardEvent): void
|
static #keydown(e: KeyboardEvent): void
|
||||||
{
|
{
|
||||||
Input.#keys[e.key] = true;
|
Input.#keys[e.key.toLowerCase()] = true;
|
||||||
}
|
}
|
||||||
static #keyup(e: KeyboardEvent): void
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -3,7 +3,18 @@ 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';
|
||||||
import Selector from './selector.class';
|
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
|
export default class Renderer
|
||||||
{
|
{
|
||||||
static scene: Three.Scene;
|
static scene: Three.Scene;
|
||||||
|
|
@ -87,6 +98,7 @@ export default class Renderer
|
||||||
this.#stats.begin();
|
this.#stats.begin();
|
||||||
Selector.update();
|
Selector.update();
|
||||||
this.renderer.render(this.scene, this.camera);
|
this.renderer.render(this.scene, this.camera);
|
||||||
|
Asset.quadtree.cleanup();
|
||||||
this.#stats.end();
|
this.#stats.end();
|
||||||
}
|
}
|
||||||
static startRendering(): void
|
static startRendering(): void
|
||||||
|
|
@ -97,4 +109,8 @@ export default class Renderer
|
||||||
{
|
{
|
||||||
this.renderer.setAnimationLoop(null);
|
this.renderer.setAnimationLoop(null);
|
||||||
}
|
}
|
||||||
|
static cursor(type?: CursorType): void
|
||||||
|
{
|
||||||
|
Renderer.canvas.style.setProperty("cursor", type ?? CursorType.default);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,22 @@
|
||||||
import * as Three from "three";
|
import * as Three from "three";
|
||||||
import Asset from "../assets/asset.class";
|
import Asset from "../assets/asset.class";
|
||||||
import { Point } from "../physics/common";
|
import { Point, intersects, intersectsAsset, sqrtLen } from "../physics/common";
|
||||||
import Renderer from "./renderer.class";
|
import Renderer, { CursorType } from "./renderer.class";
|
||||||
import { QUAD } from '../consts';
|
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
|
export default class Selector
|
||||||
{
|
{
|
||||||
static #assets: Asset[];
|
static #assets: Asset[] = [];
|
||||||
static #selected: boolean = false;
|
static #selected: boolean = false;
|
||||||
static #previousZoom: number = 1;
|
static #previousZoom: number = 1;
|
||||||
|
|
||||||
|
|
@ -68,6 +78,8 @@ export default class Selector
|
||||||
}
|
}
|
||||||
static ghost(asset: Asset): void
|
static ghost(asset: Asset): void
|
||||||
{
|
{
|
||||||
|
Renderer.cursor(asset && !asset.selected ? CursorType.pointer : undefined);
|
||||||
|
|
||||||
if(!asset)
|
if(!asset)
|
||||||
{
|
{
|
||||||
Selector.#ghostMesh.visible = false;
|
Selector.#ghostMesh.visible = false;
|
||||||
|
|
@ -81,11 +93,18 @@ export default class Selector
|
||||||
}
|
}
|
||||||
static select(assets: Asset[]): void
|
static select(assets: Asset[]): void
|
||||||
{
|
{
|
||||||
|
for (let i = 0; i < Selector.#assets.length; i++) {
|
||||||
|
Selector.#assets[i].selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
Selector.#assets = assets;
|
Selector.#assets = assets;
|
||||||
Selector.hide();
|
Selector.hide();
|
||||||
|
|
||||||
if(assets.length <= 0)
|
if(assets.length <= 0)
|
||||||
|
{
|
||||||
|
Selector.#selected = false;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Selector.#selected = true;
|
Selector.#selected = true;
|
||||||
|
|
||||||
|
|
@ -96,6 +115,8 @@ export default class Selector
|
||||||
minY = Math.min(minY, assets[i].aabb.y1);
|
minY = Math.min(minY, assets[i].aabb.y1);
|
||||||
maxX = Math.max(maxX, assets[i].aabb.x2);
|
maxX = Math.max(maxX, assets[i].aabb.x2);
|
||||||
maxY = Math.max(maxY, assets[i].aabb.y2);
|
maxY = Math.max(maxY, assets[i].aabb.y2);
|
||||||
|
|
||||||
|
assets[i].selected = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const scale = Math.max((maxX - minX) / 2, (maxY - minY) / 2)
|
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++)
|
for(let i = 0; i < assets.length; i++)
|
||||||
{
|
{
|
||||||
const index = Selector.#assets.indexOf(assets[i]);
|
const index = Selector.#assets.indexOf(assets[i]);
|
||||||
|
assets[i].selected = false;
|
||||||
|
|
||||||
if(index === -1)
|
if(index === -1)
|
||||||
Selector.#assets.push(assets[i]);
|
Selector.#assets.push(assets[i]);
|
||||||
|
|
@ -135,7 +157,11 @@ export default class Selector
|
||||||
}
|
}
|
||||||
static clear(): void
|
static clear(): void
|
||||||
{
|
{
|
||||||
|
for(let i = 0; i < Selector.#assets.length; i++)
|
||||||
|
Selector.#assets[i].selected = false;
|
||||||
|
|
||||||
Selector.#assets = [];
|
Selector.#assets = [];
|
||||||
|
Selector.#selected = false;
|
||||||
|
|
||||||
Selector.hide();
|
Selector.hide();
|
||||||
}
|
}
|
||||||
|
|
@ -162,4 +188,78 @@ export default class Selector
|
||||||
Selector.gizmoScale[3].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue