Working on transform gizmo

This commit is contained in:
Peaceultime 2024-06-15 23:32:42 +02:00
parent 8f0100c466
commit c01f4bd8c6
6 changed files with 117 additions and 84 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -4,7 +4,7 @@
"version": "0.0.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "bunx --bun vite", "dev": "bunx --bun vite --port 3000 --cors",
"build": "tsc && vite build", "build": "tsc && vite build",
"preview": "vite preview" "preview": "vite preview"
}, },
@ -15,7 +15,6 @@
"dependencies": { "dependencies": {
"@types/three": "^0.165.0", "@types/three": "^0.165.0",
"stats.js": "^0.17.0", "stats.js": "^0.17.0",
"three": "^0.165.0", "three": "^0.165.0"
"three-mesh-bvh": "^0.7.5"
} }
} }

View File

@ -18,6 +18,12 @@ export default class Asset
//@ts-expect-error //@ts-expect-error
#aabb: AABB; #aabb: AABB;
#posX: number;
#posY: number;
#rot: number;
#scaleX: number;
#scaleY: number;
#index: number; #index: number;
#quad?: number; #quad?: number;
@ -27,6 +33,14 @@ export default class Asset
constructor(mat?: Three.Matrix4, layer?: number) constructor(mat?: Three.Matrix4, layer?: number)
{ {
this.mat = mat ?? new Three.Matrix4(); this.mat = mat ?? new Three.Matrix4();
this.mat.decompose(_position, _rotation, _scale);
this.#posX = _position.x;
this.#posY = _position.y;
this.#rot = _euler.setFromQuaternion(_rotation).z;
this.#scaleX = _position.x;
this.#scaleY = _position.y;
this.#updateAABB(); this.#updateAABB();
this.layer = layer ?? 0; this.layer = layer ?? 0;
@ -36,14 +50,6 @@ export default class Asset
{ {
return this.#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 { #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 };
@ -65,89 +71,53 @@ export default class Asset
y2: Math.max(y1, y2, y3, y4) y2: Math.max(y1, y2, y3, y4)
}; };
} }
move(x: number, y: number): Asset insert(quad: Quadtree): void
{ {
this.mat.decompose(_position, _rotation, _scale); this.#quad = quad.insert(this.#index, this.#aabb);
}
_position.x += x; remove(quad: Quadtree): void
_position.y += y; {
this.#quad !== undefined && quad.remove(this.#quad);
this.mat.compose(_position, _rotation, _scale);
this.#updateAABB();
return this;
} }
moveTo(x: number, y: number): Asset moveTo(x: number, y: number): Asset
{ {
this.mat.decompose(_position, _rotation, _scale); const e = this.mat.elements;
_position.x = x; e[12] = this.#posX = x;
_position.y = y; e[13] = this.#posY = y;
this.mat.compose(_position, _rotation, _scale);
this.#updateAABB(); this.#updateAABB();
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);
this.#updateAABB();
return this; return this;
} }
rotateTo(rad: number): Asset rotateTo(rad: number): Asset
{ {
this.mat.decompose(_position, _rotation, _scale); this.#rot = rad;
_euler.setFromQuaternion(_rotation); const e = this.mat.elements;
_euler.z = rad; const cos = Math.cos(rad), sin = Math.sin(rad);
_rotation.setFromEuler(_euler);
this.mat.compose(_position, _rotation, _scale); e[0] = cos * this.#scaleX;
e[1] = -sin * this.#scaleX;
e[4] = sin * this.#scaleY;
e[5] = cos * this.#scaleY;
this.#updateAABB(); this.#updateAABB();
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);
this.#updateAABB();
return this; return this;
} }
scaleTo(x: number, y: number): Asset scaleTo(x: number, y: number): Asset
{ {
this.mat.decompose(_position, _rotation, _scale); this.#scaleX = x;
this.#scaleY = y;
_scale.x = x; const e = this.mat.elements;
_scale.y = y; const cos = Math.cos(this.#rot), sin = Math.sin(this.#rot);
this.mat.compose(_position, _rotation, _scale); e[0] = cos * this.#scaleX;
e[1] = -sin * this.#scaleX;
e[4] = sin * this.#scaleY;
e[5] = cos * this.#scaleY;
this.#updateAABB(); this.#updateAABB();
return this;
}
shapeTo(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number): Asset
{
return this; return this;
} }
} }

View File

@ -14,13 +14,14 @@ Selector.init();
const r = new Random(0); const r = new Random(0);
const quad = new Quadtree({ x1: -RESOLUTION_X / 2, x2: RESOLUTION_X / 2, y1: -RESOLUTION_Y / 2, y2: RESOLUTION_Y / 2 }, 6, 10); const quad = new Quadtree({ x1: -RESOLUTION_X / 2, x2: RESOLUTION_X / 2, y1: -RESOLUTION_Y / 2, y2: RESOLUTION_Y / 2 }, 6, 10);
for(let i = 0; i < 10000; i++) for(let i = 0; i < 2; i++)
{ {
const asset = new Asset(new THREE.Matrix4(), 1); const asset = new Asset(new THREE.Matrix4(), 1);
asset.move(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y)) asset
.rotate(r.nextFloat(Math.PI * 2)) .rotateTo(r.nextFloat(Math.PI * 2))
.scale(r.nextFloat(10, 30), r.nextFloat(10, 30)); .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.instance.setMatrixAt(i, asset.mat);
asset.insert(quad); asset.insert(quad);

View File

@ -2,6 +2,7 @@ import * as Three from 'three';
import { RESOLUTION_X, RESOLUTION_Y } from '../consts'; import { RESOLUTION_X, RESOLUTION_Y } from '../consts';
import Stats from 'stats.js'; import Stats from 'stats.js';
import { Point } from '../physics/common'; import { Point } from '../physics/common';
import Selector from './selector.class';
export default class Renderer export default class Renderer
{ {
@ -84,6 +85,7 @@ export default class Renderer
static render(delta?: number): void static render(delta?: number): void
{ {
this.#stats.begin(); this.#stats.begin();
Selector.update();
this.renderer.render(this.scene, this.camera); this.renderer.render(this.scene, this.camera);
this.#stats.end(); this.#stats.end();
} }

View File

@ -2,16 +2,20 @@ import * as Three from "three";
import Asset from "../assets/asset.class"; import Asset from "../assets/asset.class";
import { Point } from "../physics/common"; import { Point } from "../physics/common";
import Renderer from "./renderer.class"; import Renderer from "./renderer.class";
import { QUAD } from '../consts';
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 #previewMesh: Three.Box3Helper; static #previewMesh: Three.Box3Helper;
static #ghostMesh: Three.Box3Helper; static #ghostMesh: Three.Box3Helper;
static #selectionMesh: Three.Box3Helper; static #selectionMesh: Three.Box3Helper;
static gizmoScale: Three.Mesh[];
static get selected(): boolean static get selected(): boolean
{ {
return Selector.#selected; return Selector.#selected;
@ -25,10 +29,31 @@ export default class Selector
Selector.#previewMesh = new Three.Box3Helper(new Three.Box3(), 0x2980B9); Selector.#previewMesh = new Three.Box3Helper(new Three.Box3(), 0x2980B9);
Selector.#ghostMesh = new Three.Box3Helper(new Three.Box3(), 0xffffff); Selector.#ghostMesh = new Three.Box3Helper(new Three.Box3(), 0xffffff);
Selector.#selectionMesh = new Three.Box3Helper(new Three.Box3(), 0xffffff); Selector.#selectionMesh = new Three.Box3Helper(new Three.Box3(), 0xffffff);
Selector.gizmoScale = [];
Selector.gizmoScale[0] = new Three.Mesh(QUAD, new Three.MeshBasicMaterial({ depthTest: false, depthWrite: false, fog: false, toneMapped: false, transparent: true, color: 0x3498DB, opacity: 0.5 }));
Selector.gizmoScale[1] = new Three.Mesh(QUAD, Selector.gizmoScale[0].material);
Selector.gizmoScale[2] = new Three.Mesh(QUAD, Selector.gizmoScale[0].material);
Selector.gizmoScale[3] = new Three.Mesh(QUAD, Selector.gizmoScale[0].material);
Selector.gizmoScale[0].renderOrder = Infinity;
Selector.gizmoScale[1].renderOrder = Infinity;
Selector.gizmoScale[2].renderOrder = Infinity;
Selector.gizmoScale[3].renderOrder = Infinity;
Selector.gizmoScale[0].name = "Gizmo for scale (TL)";
Selector.gizmoScale[1].name = "Gizmo for scale (TR)";
Selector.gizmoScale[2].name = "Gizmo for scale (BL)";
Selector.gizmoScale[3].name = "Gizmo for scale (BR)";
Renderer.scene.add(Selector.#previewMesh); Renderer.scene.add(Selector.#previewMesh);
Renderer.scene.add(Selector.#ghostMesh); Renderer.scene.add(Selector.#ghostMesh);
Renderer.scene.add(Selector.#selectionMesh); Renderer.scene.add(Selector.#selectionMesh);
Renderer.scene.add(Selector.gizmoScale[0]);
Renderer.scene.add(Selector.gizmoScale[1]);
Renderer.scene.add(Selector.gizmoScale[2]);
Renderer.scene.add(Selector.gizmoScale[3]);
Selector.hide(); Selector.hide();
} }
@ -64,16 +89,35 @@ export default class Selector
Selector.#selected = true; Selector.#selected = true;
Selector.#selectionMesh.box.setFromArray([ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
assets.map(e => e.aabb.x1).reduce((p, v) => Math.min(p, v), Infinity), for (let i = 0; i < assets.length; i++)
assets.map(e => e.aabb.y1).reduce((p, v) => Math.min(p, v), Infinity), {
0, minX = Math.min(minX, assets[i].aabb.x1);
assets.map(e => e.aabb.x2).reduce((p, v) => Math.max(p, v), -Infinity), minY = Math.min(minY, assets[i].aabb.y1);
assets.map(e => e.aabb.y2).reduce((p, v) => Math.max(p, v), -Infinity), maxX = Math.max(maxX, assets[i].aabb.x2);
0 maxY = Math.max(maxY, assets[i].aabb.y2);
]); }
const scale = Math.max((maxX - minX) / 2, (maxY - minY) / 2)
Selector.#selectionMesh.box.setFromArray([minX, minY, 0, maxX, maxY, 0]);
Selector.#selectionMesh.updateMatrix(); Selector.#selectionMesh.updateMatrix();
Selector.gizmoScale[0].position.set(minX, minY, 0);
Selector.gizmoScale[1].position.set(maxX, minY, 0);
Selector.gizmoScale[2].position.set(minX, maxY, 0);
Selector.gizmoScale[3].position.set(maxX, maxY, 0);
Selector.gizmoScale[0].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[1].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[2].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[3].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[0].visible = true;
Selector.gizmoScale[1].visible = true;
Selector.gizmoScale[2].visible = true;
Selector.gizmoScale[3].visible = true;
Selector.#selectionMesh.visible = true; Selector.#selectionMesh.visible = true;
} }
static toggle(assets: Asset[]): void static toggle(assets: Asset[]): void
@ -100,5 +144,22 @@ export default class Selector
Selector.#previewMesh.visible = false; Selector.#previewMesh.visible = false;
Selector.#ghostMesh.visible = false; Selector.#ghostMesh.visible = false;
Selector.#selectionMesh.visible = false; Selector.#selectionMesh.visible = false;
Selector.gizmoScale[0].visible = false;
Selector.gizmoScale[1].visible = false;
Selector.gizmoScale[2].visible = false;
Selector.gizmoScale[3].visible = false;
}
static update(): void
{
if (Selector.#previousZoom !== Renderer.zoom && Selector.#selected)
{
Selector.#previousZoom = Renderer.zoom;
Selector.gizmoScale[0].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[1].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[2].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
Selector.gizmoScale[3].scale.set(20 / Renderer.zoom, 20 / Renderer.zoom, 1);
}
} }
} }