diff --git a/packages/frontend/src/utility/room/engine.ts b/packages/frontend/src/utility/room/engine.ts index 4ce56c6c01..fa5280c02a 100644 --- a/packages/frontend/src/utility/room/engine.ts +++ b/packages/frontend/src/utility/room/engine.ts @@ -32,7 +32,7 @@ import { reactive, ref, shallowRef, triggerRef, watch } from 'vue'; import { genId } from '../id.js'; import { deepClone } from '../clone.js'; import { getObjectDef } from './object-defs.js'; -import { HorizontalCameraKeyboardMoveInput, findMaterial } from './utility.js'; +import { HorizontalCameraKeyboardMoveInput, applyMorphTargetsToMesh, findMaterial } from './utility.js'; import * as sound from '@/utility/sound.js'; // babylonのドメイン知識は持たない @@ -199,7 +199,7 @@ class ModelManager { return; } - const toMerge = [] as BABYLON.Mesh[]; + const _toMerge = [] as BABYLON.Mesh[]; for (const mesh of childMeshes) { let fixedMesh = mesh; @@ -223,7 +223,15 @@ class ModelManager { } fixedMesh.isVisible = false; - toMerge.push(fixedMesh); + _toMerge.push(fixedMesh); + } + + const toMerge = [] as BABYLON.Mesh[]; + for (const mesh of _toMerge) { + const newMesh = mesh.clone(mesh.name + '_bakeMerged'); + applyMorphTargetsToMesh(newMesh); + //newMesh.bakeCurrentTransformIntoVertices(); + toMerge.push(newMesh); } const merged = BABYLON.Mesh.MergeMeshes(toMerge, false, true, undefined, false, true); diff --git a/packages/frontend/src/utility/room/utility.ts b/packages/frontend/src/utility/room/utility.ts index 925a4f7d84..5d42fee019 100644 --- a/packages/frontend/src/utility/room/utility.ts +++ b/packages/frontend/src/utility/room/utility.ts @@ -491,39 +491,55 @@ export function findMaterial(rootMesh: BABYLON.AbstractMesh, keyword: string): B throw new Error(`Material with keyword "${keyword}" not found`); } -// merge all child meshes into the root mesh -export function bakeMesh(rootMesh: BABYLON.Mesh) { - const childMeshes = rootMesh.getChildMeshes(); - - const toMerge = [] as BABYLON.Mesh[]; - for (const mesh of childMeshes) { - let fixedMesh = mesh; - - if (mesh instanceof BABYLON.InstancedMesh) { - const sourceMesh = mesh.sourceMesh; - const newMesh = sourceMesh.clone(mesh.name + '_baked'); - - newMesh.position = mesh.position.clone(); - if (mesh.rotationQuaternion) { - newMesh.rotationQuaternion = mesh.rotationQuaternion.clone(); - } else { - newMesh.rotation = mesh.rotation.clone(); - } - newMesh.scaling = mesh.scaling.clone(); - - newMesh.parent = mesh.parent; - - mesh.dispose(); - - fixedMesh = newMesh; - } - - toMerge.push(fixedMesh); +export function applyMorphTargetsToMesh(mesh: BABYLON.Mesh) { + if (!mesh.morphTargetManager) { + return; } - const merged = BABYLON.Mesh.MergeMeshes(toMerge, true, true, undefined, false, true); + const morphTargetManager = mesh.morphTargetManager; + const positions = mesh.getVerticesData(BABYLON.VertexBuffer.PositionKind); - merged.setParent(rootMesh); + if (!positions) { + return; + } - return merged; + // Create a copy of the original positions to work with + const finalPositions = positions.slice(); + + // Apply each morph target + for (let targetIndex = 0; targetIndex < morphTargetManager.numTargets; targetIndex++) { + const target = morphTargetManager.getTarget(targetIndex); + const influence = target.influence; + + if (influence === 0) { + continue; + } + + // Get the morph target positions + const targetPositions = target.getPositions(); + + if (!targetPositions || targetPositions.length !== positions.length) { + console.warn(`Morph target ${targetIndex} has invalid position data`); + continue; + } + + // Apply the morph target influence to each vertex + for (let i = 0; i < positions.length; i++) { + finalPositions[i] += (targetPositions[i] - positions[i]) * influence; + } + } + + // Update the mesh with the morphed positions + mesh.setVerticesData(BABYLON.VertexBuffer.PositionKind, finalPositions); + + // Update normals if available + const normals = mesh.getVerticesData(BABYLON.VertexBuffer.NormalKind); + if (normals) { + // For simplicity, we'll just recompute the normals + mesh.createNormals(true); + } + + // Refresh the mesh + mesh.refreshBoundingInfo(); + mesh.computeWorldMatrix(true); }