From 6d05be2fecfd04d9ca786655914e3773960b49d6 Mon Sep 17 00:00:00 2001 From: goynov Date: Wed, 25 Mar 2026 16:34:38 +0200 Subject: [PATCH] #76 --- src/components/GameDesigner/GameDesigner.vue | 83 +++++++++++++++----- src/lib/GameEngine.js | 2 +- src/lib/GameManager.js | 5 +- src/lib/MeshUtils.js | 8 +- src/pages/manage/games/[[id]].vue | 2 +- 5 files changed, 77 insertions(+), 23 deletions(-) diff --git a/src/components/GameDesigner/GameDesigner.vue b/src/components/GameDesigner/GameDesigner.vue index 9df600a..596fa40 100644 --- a/src/components/GameDesigner/GameDesigner.vue +++ b/src/components/GameDesigner/GameDesigner.vue @@ -16,19 +16,29 @@
- - - + + + - - - + + + - - - + + + + + + +
+
Width: {{ (currentObject.scale.x * objectInfo.width).toFixed(2) }}
+
Height: {{ (currentObject.scale.y * objectInfo.height).toFixed(2) }}
+
Depth: {{ (currentObject.scale.z * objectInfo.depth).toFixed(2) }}
+
@@ -84,6 +94,7 @@ export default { pointerDownTime: 0, renderType: 'ST', cameraType: 'perspective', + uniformScale: true, version: 0 } }, @@ -98,8 +109,8 @@ export default { }); manager = await new GameManager(engine, this.modelValue, null, { onObjectLoad:(sceneObjects, data, object3d, source)=>{ - ['position', 'scale', 'rotation', 'visible'].forEach(p=>{ - sceneObjects[data.id][p] = object3d[p]; + ['position', 'scale', 'rotation'].forEach(p=>{ + sceneObjects[data.id][p] = object3d[p].clone(); }) sceneObjects[data.id].__o = markRaw(object3d); sceneObjects[data.id].__animations = markRaw(source.animations || []); @@ -124,6 +135,19 @@ export default { manager = null; }, + computed:{ + objectInfo(){ + if (this.currentObject.__o.userData?.bbox){ + let b = this.currentObject.__o.userData.bbox; + return { + width: b.max.x - b.min.x, + height: b.max.y - b.min.y, + depth: b.max.z - b.min.z + } + } + } + }, + watch:{ mode(n){ engine.transformControls.setMode(n) @@ -137,22 +161,49 @@ export default { }else{ engine.setCameraOrthographic(); } + }, + 'currentObject':{ + deep: true, + handler(v){ + this.handleScale(this.currentObject, this.currentObject.__o) + } } }, methods:{ async loadScene(sceneId){ - engine.transformControls.removeEventListener('change', this.update) await manager.loadScene(sceneId) this.currentScene = manager.scenarioData.scenes.find(sc=>sc.data.id == sceneId); this.sceneObjects = this.modelValue.scenes?.[sceneId]?.objects - engine.transformControls.addEventListener('change', this.update) + engine.transformControls.addEventListener('objectChange', this.handleTransformControlsChange) + }, + + handleScale(src, dst){ + if (this.uniformScale){ + for (let p of ['x', 'y', 'z']){ + if (src.scale[p] != dst.scale[p]) { + let k = src.scale[p]/dst.scale[p]; + ['x', 'y', 'z'].filter(c=>src.scale[c] == dst.scale[c]).forEach(c=>{ + src.scale[c] = dst.scale[c] * k; + engine.transformControls._scaleStart[c] = src.scale[c]; + }) + break; + } + } + } + ['position', 'scale', 'rotation'].forEach(p=>{ + dst[p].copy(src[p]); + }) + }, + + handleTransformControlsChange(){ + this.handleScale(this.currentObject.__o, this.currentObject); }, selectObject(oid){ this.currentObject = this.sceneObjects[oid]; engine.transformControls.attach(this.currentObject.__o); - engine.gizmo.target = this.currentObject.__o.position; + engine.gizmo.target = this.currentObject.position; engine.camera.updateProjectionMatrix(); this.objectAnimations = this.currentObject?.__animations?.map(a => ({ name: a.name, id: a.uuid, a @@ -189,10 +240,6 @@ export default { engine.resize(r.clientWidth, r.clientHeight); //this.zoom = Math.min(r.clientWidth / this.viewBox.w, r.clientHeight / this.viewBox.h); }, - - update(){ - this.version ++; - } } } \ No newline at end of file diff --git a/src/lib/GameEngine.js b/src/lib/GameEngine.js index 2866bdc..733c842 100644 --- a/src/lib/GameEngine.js +++ b/src/lib/GameEngine.js @@ -26,7 +26,7 @@ THREE.Cache.enabled = true const assetPath = '/asset/default/'; const defaultLightIntensity = 11; -const sceneScale = 1//.33; +const sceneScale = 1.33; class GameEngine extends EventManager{ diff --git a/src/lib/GameManager.js b/src/lib/GameManager.js index 358860f..e84e3fe 100644 --- a/src/lib/GameManager.js +++ b/src/lib/GameManager.js @@ -19,7 +19,10 @@ class GameManager{ this.loadScene = async function(sceneId){ let scene = scenarioData.scenes.find(sc=>sc.data.id == sceneId); - let sceneObjects = gameData.scenes?.[sceneId]?.objects || {}; + gameData.scenes = gameData.scenes || {}; + gameData.scenes[sceneId] = gameData.scenes[sceneId] || {}; + gameData.scenes[sceneId].objects = gameData.scenes[sceneId].objects || {}; + let sceneObjects = gameData.scenes[sceneId].objects; engine.tm?.setGame(gameData.id); let intro; await engine.resetScene(); diff --git a/src/lib/MeshUtils.js b/src/lib/MeshUtils.js index 8122f88..8135397 100644 --- a/src/lib/MeshUtils.js +++ b/src/lib/MeshUtils.js @@ -38,8 +38,12 @@ class MeshUtils { getBoundingBox(object){ let bb = new Box3().setFromObject(object); - bb.min.multiplyScalar(1 / this.engine.scale); - bb.max.multiplyScalar(1 / this.engine.scale); + let scale = new Vector3(1,1,1); + object.traverseAncestors(o=>{ + scale.multiply(o.scale); + }); + bb.min.divide(scale); + bb.max.divide(scale); return bb; } diff --git a/src/pages/manage/games/[[id]].vue b/src/pages/manage/games/[[id]].vue index 37e0739..c8cf605 100644 --- a/src/pages/manage/games/[[id]].vue +++ b/src/pages/manage/games/[[id]].vue @@ -62,7 +62,7 @@ export default { async save(params) { this.loading = true; try { - this.debug('saving', this.object) + //this.debug('saving', this.object) let result = await this.$api.game.save(this.object); this.object.id = result.data.object.id; if (this.id == 'add') {