diff --git a/src/components/ExportMenu.jsx b/src/components/ExportMenu.jsx index 97b17871..8c5d575c 100644 --- a/src/components/ExportMenu.jsx +++ b/src/components/ExportMenu.jsx @@ -14,7 +14,7 @@ export const ExportMenu = ({getFaceScreenshot}) => { // Translate hook const { t } = useContext(LanguageContext); const [name] = React.useState(localStorage.getItem("name") || defaultName) - const { model, avatar } = useContext(SceneContext) + const { model, avatar,templateInfo } = useContext(SceneContext) return ( @@ -46,7 +46,8 @@ export const ExportMenu = ({getFaceScreenshot}) => { className={styles.button} onClick={() => { const screenshot = getFaceScreenshot(); - downloadVRM(model, avatar, name, screenshot, 4096, true) + console.log(templateInfo.exportScale) + downloadVRM(model, avatar, name, screenshot, 4096,templateInfo.exportScale||1, true) }} /> diff --git a/src/library/download-utils.js b/src/library/download-utils.js index d6a0a477..d382e2e4 100644 --- a/src/library/download-utils.js +++ b/src/library/download-utils.js @@ -61,34 +61,35 @@ function getUnopotimizedGLB (avatarToDownload){ return unoptimizedGLB; } -function getOptimizedGLB(avatarToDownload, atlasSize, isVrm0 = false){ +function getOptimizedGLB(avatarToDownload, atlasSize, scale = 1, isVrm0 = false){ const avatarToDownloadClone = cloneAvatarModel(avatarToDownload) return combine({ transparentColor: new Color(1,1,1), avatar: avatarToDownloadClone, atlasSize, + scale }, isVrm0) } -export async function getGLBBlobData(avatarToDownload, atlasSize = 4096, optimized = true){ +export async function getGLBBlobData(avatarToDownload, atlasSize = 4096, optimized = true, scale = 1){ const model = await (optimized ? - getOptimizedGLB(avatarToDownload, atlasSize) : + getOptimizedGLB(avatarToDownload, atlasSize,scale) : getUnopotimizedGLB(avatarToDownload)) const glb = await parseGLB(model); return new Blob([glb], { type: 'model/gltf-binary' }); } -export async function getVRMBlobData(avatarToDownload, avatar, screenshot = null, atlasSize = 4096, isVrm0 = false){ - const model = await getOptimizedGLB(avatarToDownload, atlasSize, isVrm0) +export async function getVRMBlobData(avatarToDownload, avatar, screenshot = null, atlasSize = 4096, scale = 1, isVrm0 = false){ + const model = await getOptimizedGLB(avatarToDownload, atlasSize,scale, isVrm0) const vrm = await parseVRM(model, avatar, screenshot, isVrm0); // save it as glb now return new Blob([vrm], { type: 'model/gltf-binary' }); } // returns a promise with the parsed data -async function getGLBData(avatarToDownload, atlasSize = 4096, optimized = true){ +async function getGLBData(avatarToDownload, atlasSize = 4096, optimized = true, scale = 1){ if (optimized){ - const model = await getOptimizedGLB(avatarToDownload, atlasSize) + const model = await getOptimizedGLB(avatarToDownload, atlasSize,scale) return parseGLB(model); } else{ @@ -96,17 +97,17 @@ async function getGLBData(avatarToDownload, atlasSize = 4096, optimized = true) return parseGLB(model); } } -async function getVRMData(avatarToDownload, avatar, screenshot = null, atlasSize = 4096, isVrm0 = false){ +async function getVRMData(avatarToDownload, avatar, screenshot = null, atlasSize = 4096, scale = 1, isVrm0 = false){ - const vrmModel = await getOptimizedGLB(avatarToDownload, atlasSize, isVrm0); + const vrmModel = await getOptimizedGLB(avatarToDownload, atlasSize, scale, isVrm0); return parseVRM(vrmModel,avatar,screenshot, isVrm0) } -export async function downloadVRM(avatarToDownload, avatar, fileName = "", screenshot = null, atlasSize = 4096, isVrm0 = false){ +export async function downloadVRM(avatarToDownload, avatar, fileName = "", screenshot = null, atlasSize = 4096, scale = 1, isVrm0 = false){ const downloadFileName = `${ fileName && fileName !== "" ? fileName : "AvatarCreatorModel" }` - getVRMData(avatarToDownload, avatar, screenshot, atlasSize, isVrm0).then((vrm)=>{ + getVRMData(avatarToDownload, avatar, screenshot, atlasSize,scale, isVrm0).then((vrm)=>{ saveArrayBuffer(vrm, `${downloadFileName}.vrm`) }) } @@ -116,7 +117,7 @@ export async function downloadGLB(avatarToDownload, optimized = true, fileName }` const model = optimized ? - await getOptimizedGLB(avatarToDownload, atlasSize): + await getOptimizedGLB(avatarToDownload, atlasSize, scale): getUnopotimizedGLB(avatarToDownload) parseGLB(model) diff --git a/src/library/merge-geometry.js b/src/library/merge-geometry.js index c1cca3f4..1895227f 100644 --- a/src/library/merge-geometry.js +++ b/src/library/merge-geometry.js @@ -31,7 +31,7 @@ export function cloneSkeleton(skinnedMesh) { return newSkeleton; } -function createMergedSkeleton(meshes){ +function createMergedSkeleton(meshes, scale){ /* user should be careful with naming convetions in custom bone names out from humanoids vrm definition, for example ones that come from head (to add hair movement), should start with vrm's connected bone followed by the number of the bone in reference to the base bone (head > head_hair_00 > head_hair_01), @@ -98,7 +98,7 @@ function createMergedSkeleton(meshes){ newSkeleton.pose(); newSkeleton.bones.forEach(bn => { - bn.position.set(bn.position.x *0.7, bn.position.y*0.7,bn.position.z*0.7); + bn.position.set(bn.position.x *scale, bn.position.y*scale,bn.position.z*scale); }); return newSkeleton } @@ -168,14 +168,14 @@ function removeUnusedAttributes(attribute,arrayMatch){ return new BufferAttribute(typedArr,attribute.itemSize,attribute.normalized) } -export async function combine({ transparentColor, avatar, atlasSize = 4096 }, isVrm0 = false) { +export async function combine({ transparentColor, avatar, atlasSize = 4096, scale = 1 }, isVrm0 = false) { const { bakeObjects, textures, vrmMaterial } = await createTextureAtlas({ transparentColor, atlasSize, meshes: findChildrenByType(avatar, "SkinnedMesh")}); // if (vrmMaterial != null) // vrmMaterial.userData.textureProperties = {_MainTex:0, _ShadeTexture:0 const meshes = bakeObjects.map((bakeObject) => bakeObject.mesh); - const newSkeleton = createMergedSkeleton(meshes); + const newSkeleton = createMergedSkeleton(meshes, scale); meshes.forEach((mesh) => { const geometry = mesh.geometry; @@ -221,7 +221,7 @@ export async function combine({ transparentColor, avatar, atlasSize = 4096 }, is } }); - const { dest } = mergeGeometry({ meshes },isVrm0); + const { dest } = mergeGeometry({ meshes, scale },isVrm0); const geometry = new THREE.BufferGeometry(); if (isVrm0){ @@ -238,9 +238,9 @@ export async function combine({ transparentColor, avatar, atlasSize = 4096 }, is const vertices = geometry.attributes.position.array; for (let i = 0; i < vertices.length; i += 3) { - vertices[i] *= 0.7; - vertices[i + 1] *= 0.7; - vertices[i + 2] *= 0.7; + vertices[i] *= scale; + vertices[i + 1] *= scale; + vertices[i + 2] *= scale; } const material = new THREE.MeshStandardMaterial({ @@ -337,7 +337,7 @@ function mergeSourceMorphTargetDictionaries({ sourceMorphTargetDictionaries }) { }); return destMorphTargetDictionary; } -function mergeSourceMorphAttributes({ meshes, sourceMorphTargetDictionaries, sourceMorphAttributes, destMorphTargetDictionary, }, isVrm0 = false) { +function mergeSourceMorphAttributes({ meshes, sourceMorphTargetDictionaries, sourceMorphAttributes, destMorphTargetDictionary, scale}, isVrm0 = false) { const propertyNameSet = new Set(); // e.g. ["position", "normal"] const allSourceMorphAttributes = Array.from(sourceMorphAttributes.values()); allSourceMorphAttributes.forEach((sourceMorphAttributes) => { @@ -380,9 +380,9 @@ function mergeSourceMorphAttributes({ meshes, sourceMorphTargetDictionaries, sou } } for (let j = 0; j < buffArr.length; j+=3){ - buffArr[j] *= 0.7; - buffArr[j+1] *= 0.7; - buffArr[j+2] *= 0.7; + buffArr[j] *= scale; + buffArr[j+1] *= scale; + buffArr[j+2] *= scale; } } }); @@ -573,7 +573,7 @@ function mergeSourceIndices({ meshes }) { // function remapAnimationClips({ animationClips, sourceMorphTargetDictionaries, meshes, destMorphTargetDictionary }) { // return animationClips.map((clip) => new THREE.AnimationClip(clip.name, clip.duration, clip.tracks.map((track) => remapKeyframeTrack({ track, sourceMorphTargetDictionaries, meshes, destMorphTargetDictionary })), clip.blendMode)); // } -export function mergeGeometry({ meshes }, isVrm0 = false) { +export function mergeGeometry({ meshes, scale }, isVrm0 = false) { // eslint-disable-next-line no-unused-vars let uvcount = 0; meshes.forEach(mesh => { @@ -605,6 +605,7 @@ export function mergeGeometry({ meshes }, isVrm0 = false) { sourceMorphAttributes: source.morphAttributes, sourceMorphTargetDictionaries: source.morphTargetDictionaries, destMorphTargetDictionary, + scale, },isVrm0); dest.morphTargetInfluences = mergeMorphTargetInfluences({ meshes, diff --git a/src/library/utils.js b/src/library/utils.js index 4d5ff0b6..c166616b 100644 --- a/src/library/utils.js +++ b/src/library/utils.js @@ -79,7 +79,7 @@ export const cullHiddenMeshes = (avatar) => { CullHiddenFaces(models) } -export async function getModelFromScene(avatarScene, format = 'glb', skinColor = new THREE.Color(1, 1, 1)) { +export async function getModelFromScene(avatarScene, format = 'glb', skinColor = new THREE.Color(1, 1, 1), scale = 1) { if (format && format === 'glb') { const exporter = new GLTFExporter(); const options = { @@ -91,7 +91,7 @@ export async function getModelFromScene(avatarScene, format = 'glb', skinColor = maxTextureSize: 1024 || Infinity }; - const avatar = await combine({ transparentColor: skinColor, avatar: avatarScene }); + const avatar = await combine({ transparentColor: skinColor, avatar: avatarScene, scale:scale }); const glb = await new Promise((resolve) => exporter.parse(avatar, resolve, (error) => console.error("Error getting model", error), options)); return new Blob([glb], { type: 'model/gltf-binary' });