diff --git a/bun.lockb b/bun.lockb index a6ce7f5..453f990 100644 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 2977f61..40a65a9 100644 --- a/package.json +++ b/package.json @@ -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" } } diff --git a/src/assets/asset.class.ts b/src/assets/asset.class.ts index 63f8195..361a832 100644 --- a/src/assets/asset.class.ts +++ b/src/assets/asset.class.ts @@ -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; } } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 4d603f4..846e852 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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); diff --git a/src/renderer/renderer.class.ts b/src/renderer/renderer.class.ts index b810d0e..9f4789e 100644 --- a/src/renderer/renderer.class.ts +++ b/src/renderer/renderer.class.ts @@ -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(); } diff --git a/src/renderer/selector.class.ts b/src/renderer/selector.class.ts index 6a3b610..0234094 100644 --- a/src/renderer/selector.class.ts +++ b/src/renderer/selector.class.ts @@ -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; @@ -25,10 +29,31 @@ export default class Selector Selector.#previewMesh = new Three.Box3Helper(new Three.Box3(), 0x2980B9); 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(); } @@ -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); + } } } \ No newline at end of file