diff --git a/src/assets/asset.class.ts b/src/assets/asset.class.ts index f163d8c..8e21903 100644 --- a/src/assets/asset.class.ts +++ b/src/assets/asset.class.ts @@ -4,18 +4,13 @@ import { AABB } from '../physics/common'; import Quadtree from '../physics/quadtree.class'; import { FreeList, clamp } from '../common'; -const _position = new Three.Vector3(); -const _rotation = new Three.Quaternion(); -const _euler = new Three.Euler(); -const _scale = new Three.Vector3(); +const _mat = new Three.Matrix4(); export default class Asset { - mat: Three.Matrix4; - layer: number; selected: boolean = false; - //@ts-expect-error + #layer: number; #aabb: AABB; #posX: number; @@ -23,8 +18,9 @@ export default class Asset #rot: number; #scaleX: number; #scaleY: number; - #shearX: number; - #shearY: number; + + #sin: number; + #cos: number; #index: number; #quad?: number; @@ -33,29 +29,38 @@ export default class 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 = new FreeList(); - constructor(mat?: Three.Matrix4, layer?: number) + constructor(posX?: number, posY?: number, scaleX?: number, scaleY?: number, rotation?: number, layer?: number) { - //TODO: Remake the value computation to include shear determination - 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 = _scale.x; - this.#scaleY = _scale.y; - - this.#shearX = 0; - this.#shearY = 0; - - this.#updateAABB(); - this.layer = layer ?? 0; + this.#posX = posX ?? 0, this.#posY = posY ?? 0, this.#scaleX = scaleX ?? 0, this.#scaleY = scaleY ?? 0; + this.#rot = rotation ?? 0, this.#cos = Math.cos(this.#rot), this.#sin = Math.sin(this.#rot); + this.#layer = layer ?? 0; this.#index = Asset.assets.insert(this); + + console.log(this); + + Asset.instance.instanceMatrix.setComponent(this.#index, 0, this.#cos * this.#scaleX); + Asset.instance.instanceMatrix.setComponent(this.#index, 1, this.#sin * this.#scaleX); + Asset.instance.instanceMatrix.setComponent(this.#index, 4, -this.#sin * this.#scaleY); + Asset.instance.instanceMatrix.setComponent(this.#index, 5, this.#cos * this.#scaleY); + Asset.instance.instanceMatrix.setComponent(this.#index, 12, this.#posX); + Asset.instance.instanceMatrix.setComponent(this.#index, 13, this.#posY); + Asset.instance.instanceMatrix.addUpdateRange(this.#index * 16, 16); + Asset.instance.instanceMatrix.needsUpdate = true; + + this.#aabb = { + x1: -(this.#scaleX * Math.abs(this.#cos) + this.#scaleY * Math.abs(this.#sin)) / 2 + this.#posX, + x2: (this.#scaleX * Math.abs(this.#cos) + this.#scaleY * Math.abs(this.#sin)) / 2 + this.#posX, + y1: -(this.#scaleX * Math.abs(this.#sin) + this.#scaleY * Math.abs(this.#cos)) / 2 + this.#posY, + y2: (this.#scaleX * Math.abs(this.#sin) + this.#scaleY * Math.abs(this.#cos)) / 2 + this.#posY, + } } get aabb(): AABB { return this.#aabb; } + get layer(): number { + return this.#layer; + } get posX(): number { return this.#posX; } @@ -71,27 +76,6 @@ export default class Asset get scaleY(): number { return this.#scaleY; } - #updateAABB(): void { - const aabb = { x1: -0.5, x2: 0.5, y1: -0.5, y2: 0.5 }; - - const e = this.mat.elements; - - const x1 = aabb.x1 * e[0] + aabb.y1 * e[4] + e[12]; - const x2 = aabb.x2 * e[0] + aabb.y2 * e[4] + e[12]; - const y1 = aabb.x1 * e[1] + aabb.y1 * e[5] + e[13]; - const y2 = aabb.x2 * e[1] + aabb.y2 * e[5] + e[13]; - const x3 = aabb.x2 * e[0] + aabb.y1 * e[4] + e[12]; - const x4 = aabb.x1 * e[0] + aabb.y2 * e[4] + e[12]; - const y3 = aabb.x2 * e[1] + aabb.y1 * e[5] + e[13]; - const y4 = aabb.x1 * e[1] + aabb.y2 * e[5] + e[13]; - - this.#aabb = { - x1: Math.min(x1, x2, x3, x4), - x2: Math.max(x1, x2, x3, x4), - y1: Math.min(y1, y2, y3, y4), - y2: Math.max(y1, y2, y3, y4) - }; - } insert(): Asset { this.#quad = Asset.quadtree.insert(this.#index, this.#aabb); @@ -102,18 +86,17 @@ export default class Asset { this.#quad !== undefined && Asset.quadtree.remove(this.#quad); Asset.assets.erase(this.#index); - Asset.instance.setMatrixAt(this.#index, this.mat.identity()); + Asset.instance.setMatrixAt(this.#index, _mat); + Asset.instance.instanceMatrix.addUpdateRange(this.#index * 16, 16); Asset.instance.instanceMatrix.needsUpdate = true; return this; } - update(quad: boolean = true): Asset + update(updateQuad: boolean = true): Asset { - this.#updateAABB(); - Asset.instance.setMatrixAt(this.#index, this.mat); Asset.instance.instanceMatrix.needsUpdate = true; - if(quad) + if(updateQuad) { this.#quad !== undefined && Asset.quadtree.remove(this.#quad); this.#quad = Asset.quadtree.insert(this.#index, this.#aabb); @@ -123,83 +106,51 @@ export default class Asset } moveTo(x: number, y: number): Asset { - const e = this.mat.elements; + this.#aabb.x1 -= this.#posX - x; + this.#aabb.x2 -= this.#posX - x; + this.#aabb.y1 -= this.#posY - y; + this.#aabb.y2 -= this.#posY - y; - e[12] = this.#posX = x; - e[13] = this.#posY = y; + this.#posX = x, this.#posY = y; + + Asset.instance.instanceMatrix.setComponent(this.#index, 12, x); + Asset.instance.instanceMatrix.setComponent(this.#index, 13, y); return this; } rotateTo(rad: number): Asset { - this.#rot = rad % (Math.PI * 2); + this.#rot = (rad + Math.PI) % (2*Math.PI) - Math.PI, this.#cos = Math.cos(this.#rot), this.#sin = Math.sin(this.#rot); - const e = this.mat.elements; - const cos = Math.cos(rad), sin = Math.sin(rad), tanX = Math.tan(this.#shearX), tanY = Math.tan(this.#shearY); + Asset.instance.instanceMatrix.setComponent(this.#index, 0, this.#cos * this.#scaleX); + Asset.instance.instanceMatrix.setComponent(this.#index, 1, this.#sin * this.#scaleX); + Asset.instance.instanceMatrix.setComponent(this.#index, 4, -this.#sin * this.#scaleY); + Asset.instance.instanceMatrix.setComponent(this.#index, 5, this.#cos * this.#scaleY); - e[0] = cos * this.#scaleX + -sin * this.#shearY; - e[1] = sin * this.#scaleX + cos * this.#shearX; - e[4] = cos * this.#shearX + -sin * this.#scaleY; - e[5] = sin * this.#shearX + cos * this.#scaleY; - - return this; - } - scaleTo(x: number, y: number): Asset - { - x = clamp(x, 0.1, CONST.RESOLUTION_X); - y = clamp(y, 0.1, CONST.RESOLUTION_Y); - - this.#scaleX = x; - this.#scaleY = y; - - const e = this.mat.elements; - const cos = Math.cos(this.#rot), sin = Math.sin(this.#rot); - - e[0] = cos * this.#scaleX + -sin * this.#shearY; - e[1] = sin * this.#scaleX + cos * this.#shearX; - e[4] = cos * this.#shearX + -sin * this.#scaleY; - e[5] = sin * this.#shearX + cos * this.#scaleY; - - return this; - } - shearTo(x: number, y: number): Asset - { - x = clamp(x, 0.1, CONST.RESOLUTION_X); - y = clamp(y, 0.1, CONST.RESOLUTION_Y); - - this.#shearX = x; - this.#shearY = y; - - const e = this.mat.elements; - const cos = Math.cos(this.#rot), sin = Math.sin(this.#rot); - - e[0] = cos * this.#scaleX + -sin * this.#shearY; - e[1] = sin * this.#scaleX + cos * this.#shearX; - e[4] = cos * this.#shearX + -sin * this.#scaleY; - e[5] = sin * this.#shearX + cos * this.#scaleY; + this.#aabb.x1 = -(this.#scaleX * Math.abs(this.#cos) + this.#scaleY * Math.abs(this.#sin)) / 2 + this.#posX; + this.#aabb.x2 = (this.#scaleX * Math.abs(this.#cos) + this.#scaleY * Math.abs(this.#sin)) / 2 + this.#posX; + this.#aabb.y1 = -(this.#scaleX * Math.abs(this.#sin) + this.#scaleY * Math.abs(this.#cos)) / 2 + this.#posY; + this.#aabb.y2 = (this.#scaleX * Math.abs(this.#sin) + this.#scaleY * Math.abs(this.#cos)) / 2 + this.#posY; return this; } matchAABB(aabb: AABB): Asset { + const oldAABB = this.#aabb; + this.#aabb = aabb; - this.#scaleX = aabb.x2 - aabb.x1; - this.#scaleY = aabb.y2 - aabb.y1; + this.#posX = this.#aabb.x1 + (this.#aabb.x2 - this.#aabb.x1) / 2, this.#posY = this.#aabb.y1 + (this.#aabb.y2 - this.#aabb.y1) / 2; + this.#scaleX *= (this.#aabb.x2 - this.#aabb.x1) / (oldAABB.x2 - oldAABB.x1), this.#scaleY *= (this.#aabb.y2 - this.#aabb.y1) / (oldAABB.y2 - oldAABB.y1); - this.#posX = aabb.x1 + this.#scaleX / 2; - this.#posY = aabb.y1 + this.#scaleY / 2; + console.log(this.#posX, this.#posY, this.#scaleX, this.#scaleY); - const e = this.mat.elements; - const cos = Math.cos(this.#rot), sin = Math.sin(this.#rot); - - e[0] = cos * this.#scaleX + -sin * this.#shearY; - e[1] = sin * this.#scaleX + cos * this.#shearX; - e[4] = cos * this.#shearX + -sin * this.#scaleY; - e[5] = sin * this.#shearX + cos * this.#scaleY; - - e[12] = this.#posX; - e[13] = this.#posY; + Asset.instance.instanceMatrix.setComponent(this.#index, 0, this.#cos * this.#scaleX); + Asset.instance.instanceMatrix.setComponent(this.#index, 1, this.#sin * this.#scaleX); + Asset.instance.instanceMatrix.setComponent(this.#index, 4, -this.#sin * this.#scaleY); + Asset.instance.instanceMatrix.setComponent(this.#index, 5, this.#cos * this.#scaleY); + Asset.instance.instanceMatrix.setComponent(this.#index, 12, this.#posX); + Asset.instance.instanceMatrix.setComponent(this.#index, 13, this.#posY); return this; } diff --git a/src/main.ts b/src/main.ts index fd812df..50ec2a3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,23 +18,12 @@ Selector.init(); const r = new Random(0); let dragMode = DragMode.pan, dragFrom: number | undefined; -/*for(let i = 0; i < 10; i++) -{ - new Asset(new THREE.Matrix4(), 1).rotateTo(r.nextFloat(Math.PI * 2)) - .scaleTo(r.nextFloat(10, 30), r.nextFloat(10, 30)) - .moveTo(r.nextInt(-0.5 * RESOLUTION_X, 0.5 * RESOLUTION_X), r.nextInt(-0.5 * RESOLUTION_Y, 0.5 * RESOLUTION_Y)) - .update(); -}*/ -window.asset = new Asset().moveTo(0, 0).rotateTo(Math.PI / 3).scaleTo(20, 20).update(); -new Asset().moveTo(40, 0).rotateTo(0).scaleTo(20, 20).update(); -new Asset().moveTo(-40, 0).rotateTo(0).scaleTo(20, 20).update(); +new Asset(0, 0, 20, 10, -Math.PI / 4).update(); window.Selector = Selector; Asset.instance.count = Asset.assets.length; - -Asset.instance.computeBoundingBox(); -Asset.instance.computeBoundingSphere(); +Asset.instance.frustumCulled = false; Renderer.scene.add(Asset.instance); @@ -63,9 +52,7 @@ Input.onDragStart((s, button) => { 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); @@ -86,7 +73,7 @@ Input.onDrag((delta, start, end, _) => { } else if (dragMode === DragMode.rotate && Selector.selected) { - Selector.rotate(delta.x, delta.y); + Selector.rotate(end.x - delta.x, end.y - delta.y, end.x, end.y); } else if (dragMode === DragMode.scale && Selector.selected) { diff --git a/src/renderer/selector.class.ts b/src/renderer/selector.class.ts index 5529a81..aae7865 100644 --- a/src/renderer/selector.class.ts +++ b/src/renderer/selector.class.ts @@ -238,10 +238,7 @@ export default class Selector 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)); @@ -250,14 +247,18 @@ export default class Selector Selector.gizmoScale[2].position.add({ x: x, y: y, z: 0 }); Selector.gizmoScale[3].position.add({ x: x, y: y, z: 0 }); } - static rotate(x: number, y: number): void + static rotate(startX: number, startY: number, endX: number, endY: number): void { - _vector.set(x, y, 0).normalize() - const rad = Math.atan2(-_vector.y, _vector.x); - Selector.selection.forEach(e => e.rotateTo(e.rot + rad).update(false)); + const centerX = Selector.gizmoScale[0].position.x + (Selector.gizmoScale[3].position.x - Selector.gizmoScale[0].position.x) / 2, + centerY = Selector.gizmoScale[0].position.y + (Selector.gizmoScale[3].position.y - Selector.gizmoScale[0].position.y) / 2; - Asset.instance.computeBoundingBox(); - Asset.instance.computeBoundingSphere(); + startX -= centerX; + startY -= centerY; + endX -= centerX; + endY -= centerY; + + const rad = Math.atan2(startX*endY-startY*endX,startX*endX+startY*endY); + Selector.selection.forEach(e => e.rotateTo(e.rot + rad).update(false)); } static scale(dragFrom: number, x: number, y: number): void { @@ -274,9 +275,6 @@ export default class Selector e.matchAABB(aabb).update(false); }); - Asset.instance.computeBoundingBox(); - Asset.instance.computeBoundingSphere(); - Selector.#selectionMesh.box.setFromPoints([Selector.gizmoScale[0].position, Selector.gizmoScale[3].position]); Selector.#ghostMesh.box.setFromPoints([Selector.gizmoScale[0].position, Selector.gizmoScale[3].position]); }