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

View File

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

View File

@ -14,13 +14,14 @@ 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);
for(let i = 0; i < 10000; i++)
for(let i = 0; i < 2; i++)
{
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))
.rotate(r.nextFloat(Math.PI * 2))
.scale(r.nextFloat(10, 30), r.nextFloat(10, 30));
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);

View File

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

View File

@ -2,16 +2,20 @@ import * as Three from "three";
import Asset from "../assets/asset.class";
import { Point } from "../physics/common";
import Renderer from "./renderer.class";
import { QUAD } from '../consts';
export default class Selector
{
static #assets: Asset[];
static #selected: boolean = false;
static #previousZoom: number = 1;
static #previewMesh: Three.Box3Helper;
static #ghostMesh: Three.Box3Helper;
static #selectionMesh: Three.Box3Helper;
static gizmoScale: Three.Mesh[];
static get selected(): boolean
{
return Selector.#selected;
@ -26,10 +30,31 @@ export default class Selector
Selector.#ghostMesh = 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.#ghostMesh);
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();
}
static preview(start: Point, end: Point): void
@ -64,16 +89,35 @@ export default class Selector
Selector.#selected = true;
Selector.#selectionMesh.box.setFromArray([
assets.map(e => e.aabb.x1).reduce((p, v) => Math.min(p, v), Infinity),
assets.map(e => e.aabb.y1).reduce((p, v) => Math.min(p, v), Infinity),
0,
assets.map(e => e.aabb.x2).reduce((p, v) => Math.max(p, v), -Infinity),
assets.map(e => e.aabb.y2).reduce((p, v) => Math.max(p, v), -Infinity),
0
]);
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (let i = 0; i < assets.length; i++)
{
minX = Math.min(minX, assets[i].aabb.x1);
minY = Math.min(minY, assets[i].aabb.y1);
maxX = Math.max(maxX, assets[i].aabb.x2);
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.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;
}
static toggle(assets: Asset[]): void
@ -100,5 +144,22 @@ export default class Selector
Selector.#previewMesh.visible = false;
Selector.#ghostMesh.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);
}
}
}