Working on transform gizmo
This commit is contained in:
parent
8f0100c466
commit
c01f4bd8c6
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue