From ea80ec3aa63d7e4d4e89025b1f4d9b0aeadfb2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=98=D0=9D?= Date: Sun, 31 May 2026 13:37:44 +0300 Subject: [PATCH] =?UTF-8?q?fix(09):=20studs=20=D0=BD=D0=B5=20=D1=80=D0=B0?= =?UTF-8?q?=D1=81=D1=82=D1=8F=D0=B3=D0=B8=D0=B2=D0=B0=D1=8E=D1=82=D1=81?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=B8=20scale-drag=20=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BC=D0=B8=D1=82=D0=B8=D0=B2=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit При scale-гизмо mesh.scaling тянул faceUV → studs превращались в полосы. Фикс: во время drag прячем studs-текстуру (плоский цвет), в dragEnd меш пересоздаётся с правильным faceUV. _recreateMesh для studs пересоздаёт материал заново (свежий тайлинг + восстановление текстуры). GizmoController: + onDrag (live) колбэк для scale. Co-Authored-By: Claude Opus 4.8 --- src/editor/engine/BabylonScene.js | 22 ++++++++++++++++++++ src/editor/engine/GizmoController.js | 8 +++++++ src/editor/engine/PrimitiveManager.js | 30 ++++++++++----------------- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/editor/engine/BabylonScene.js b/src/editor/engine/BabylonScene.js index 15902b4..b82b57e 100644 --- a/src/editor/engine/BabylonScene.js +++ b/src/editor/engine/BabylonScene.js @@ -1332,6 +1332,9 @@ export class BabylonScene { // При окончании drag — синхронизируем this._gizmo.setOnDragEnd(() => this._onGizmoDragEnd()); + // Во время scale-drag — live-обновление тайлинга studs (кружки одного + // размера, не растягиваются пока тянешь гизмо). + this._gizmo.setOnDrag((mode) => { if (mode === 'scale') this._onGizmoScaleDrag(); }); // Привязка гизмо к выделенному this.selection.setOnSelectionChange((sel) => this._updateGizmoForSelection(sel)); @@ -3547,6 +3550,25 @@ export class BabylonScene { * Гизмо манипулировал объектом — синхронизируем через SelectionManager. * Тип операции (move/rotate/scale) определяется по режиму гизмо. */ + /** + * Во время scale-drag studs-примитива: текстура (faceUV) растягивается + * вместе с mesh.scaling и кружки превращаются в полосы. Чтобы не показывать + * это — временно прячем diffuse/bump-текстуру (плоский цвет). В dragEnd меш + * пересоздаётся с правильным faceUV и текстура возвращается. + */ + _onGizmoScaleDrag() { + if (!this.selection) return; + const sel = this.selection.getSelection?.(); + if (!sel || sel.type !== 'primitive' || sel.material !== 'studs') return; + const mat = sel.mesh?.material; + if (mat && mat.diffuseTexture && !mat._studsHidden) { + mat._studsStash = { diffuse: mat.diffuseTexture, bump: mat.bumpTexture }; + mat.diffuseTexture = null; + mat.bumpTexture = null; + mat._studsHidden = true; + } + } + _onGizmoDragEnd() { if (!this.selection || !this._gizmo) return; const sel = this.selection.getSelection(); diff --git a/src/editor/engine/GizmoController.js b/src/editor/engine/GizmoController.js index 6de0d49..af2c08f 100644 --- a/src/editor/engine/GizmoController.js +++ b/src/editor/engine/GizmoController.js @@ -38,6 +38,7 @@ export class GizmoController { setOnDragEnd(cb) { this._onDragEnd = cb; } setOnDragStart(cb) { this._onDragStart = cb; } + setOnDrag(cb) { this._onDrag = cb; } /** Главный метод — переключить режим. */ setMode(mode) { @@ -88,6 +89,13 @@ export class GizmoController { gizmo.onDragStartObservable.add(() => { if (this._onDragStart) this._onDragStart(); }); + // onDrag (в процессе перетаскивания) — для live-обновления (напр. тайлинг + // studs, чтобы кружки не растягивались пока тянешь scale-гизмо). + if (gizmo.onDragObservable) { + gizmo.onDragObservable.add(() => { + if (this._onDrag) this._onDrag(mode); + }); + } gizmo.onDragEndObservable.add(() => { if (this._onDragEnd) this._onDragEnd(); }); diff --git a/src/editor/engine/PrimitiveManager.js b/src/editor/engine/PrimitiveManager.js index 54dfd52..b666569 100644 --- a/src/editor/engine/PrimitiveManager.js +++ b/src/editor/engine/PrimitiveManager.js @@ -843,7 +843,15 @@ export class PrimitiveManager { const newMesh = this._createMeshForType(typeDef, data.id, data.sx, data.sy, data.sz, data.material, data.studDensity); newMesh.position = oldPos; if (oldRot) newMesh.rotation = oldRot; - newMesh.material = oldMat; + // studs — материал пересоздаём заново (свежий faceUV/тайлинг + текстура + // могла быть временно спрятана во время scale-drag). Иначе переносим старый. + if (data.material === 'studs') { + newMesh._studsDims = { type: data.type, sx: data.sx, sy: data.sy, sz: data.sz, density: data.studDensity }; + this._applyMaterial(newMesh, typeDef, data.color, data.material); + try { oldMat?.dispose(); } catch (e) { /* ignore */ } + } else { + newMesh.material = oldMat; + } newMesh.isPickable = true; newMesh.metadata = { ...oldMesh.metadata }; newMesh.setEnabled(data.visible); @@ -853,24 +861,8 @@ export class PrimitiveManager { catch (e) { /* ignore */ } data.mesh = newMesh; - newMesh._studsDims = { type: data.type, sx: data.sx, sy: data.sy, sz: data.sz, density: data.studDensity }; - // studs-материал: пересчитать тайлинг под новый размер меша. - // Куб уже пересоздан с новым faceUV (тайлинг в геометрии) — uScale=1. - // Для остальных форм пересчитываем uScale/vScale по размеру. - if (data.material === 'studs' && oldMat && oldMat.diffuseTexture) { - if (data.type === 'cube' || data.type === 'trigger') { - oldMat.diffuseTexture.uScale = oldMat.diffuseTexture.vScale = 1; - if (oldMat.bumpTexture) oldMat.bumpTexture.uScale = oldMat.bumpTexture.vScale = 1; - } else { - const tile = _studsTiling(data.type, data.sx, data.sy, data.sz, data.studDensity); - oldMat.diffuseTexture.uScale = tile.u; - oldMat.diffuseTexture.vScale = tile.v; - if (oldMat.bumpTexture) { - oldMat.bumpTexture.uScale = tile.u; - oldMat.bumpTexture.vScale = tile.v; - } - } - } + // _studsDims и материал studs уже выставлены выше (через _applyMaterial + // на новом меше с правильным faceUV/тайлингом). Для не-studs ничего не надо. } /** Удалить инстанс. */