diff --git a/packages/three-vrm/src/VRMUtils/combineMorphs.ts b/packages/three-vrm/src/VRMUtils/combineMorphs.ts index 6387eb640..303924c9e 100644 --- a/packages/three-vrm/src/VRMUtils/combineMorphs.ts +++ b/packages/three-vrm/src/VRMUtils/combineMorphs.ts @@ -21,9 +21,17 @@ function collectMeshes(scene: THREE.Group): Set { function combineMorph( positionAttributes: (THREE.BufferAttribute | THREE.InterleavedBufferAttribute)[], - binds: Iterable, + binds: Set, morphTargetsRelative: boolean, -): THREE.BufferAttribute { +): THREE.BufferAttribute | THREE.InterleavedBufferAttribute { + // if there is only one morph target and the weight is 1.0, we can use the original as-is + if (binds.size === 1) { + const bind = binds.values().next().value!; + if (bind.weight === 1.0) { + return positionAttributes[bind.index]; + } + } + const newArray = new Float32Array(positionAttributes[0].count * 3); let weightSum = 0.0; @@ -108,14 +116,18 @@ export function combineMorphs(vrm: VRMCore): void { continue; } + // prevent cloning morph attributes + const originalMorphAttributes = mesh.geometry.morphAttributes; + mesh.geometry.morphAttributes = {}; + const geometry = mesh.geometry.clone(); mesh.geometry = geometry; const morphTargetsRelative = geometry.morphTargetsRelative; - const hasPMorph = geometry.morphAttributes.position != null; - const hasNMorph = geometry.morphAttributes.normal != null; + const hasPMorph = originalMorphAttributes.position != null; + const hasNMorph = originalMorphAttributes.normal != null; - const morphAttributes: typeof geometry.morphAttributes = {}; + const morphAttributes: typeof originalMorphAttributes = {}; const morphTargetDictionary: typeof mesh.morphTargetDictionary = {}; const morphTargetInfluences: typeof mesh.morphTargetInfluences = []; @@ -130,10 +142,10 @@ export function combineMorphs(vrm: VRMCore): void { let i = 0; for (const [name, bindSet] of nameBindSetMap) { if (hasPMorph) { - morphAttributes.position[i] = combineMorph(geometry.morphAttributes.position, bindSet, morphTargetsRelative); + morphAttributes.position[i] = combineMorph(originalMorphAttributes.position, bindSet, morphTargetsRelative); } if (hasNMorph) { - morphAttributes.normal[i] = combineMorph(geometry.morphAttributes.normal, bindSet, morphTargetsRelative); + morphAttributes.normal[i] = combineMorph(originalMorphAttributes.normal, bindSet, morphTargetsRelative); } expressionMap?.[name].addBind(