diff --git a/src/components/Selector.jsx b/src/components/Selector.jsx index b9e54ae8..aecd0637 100644 --- a/src/components/Selector.jsx +++ b/src/components/Selector.jsx @@ -473,6 +473,24 @@ export default function Selector({confirmDialog, templateInfo, animationManager, return typeTraits; } + const rotateMeshVerticesY = (mesh, angle) => { + const vertices = mesh.geometry.attributes.position.array; + + for (let i = 0; i < vertices.length; i += 3) { + const x = vertices[i]; + const z = vertices[i + 2]; + + // Apply rotation around the Y-axis + const rotatedX = x * Math.cos(angle) - z * Math.sin(angle); + const rotatedZ = x * Math.sin(angle) + z * Math.cos(angle); + + vertices[i] = rotatedX; + vertices[i + 2] = rotatedZ; + } + + mesh.geometry.attributes.position.needsUpdate = true; + } + // once loaded, assign const itemAssign = (itemData) => { const item = itemData.item; @@ -508,7 +526,6 @@ export default function Selector({confirmDialog, templateInfo, animationManager, let vrm = null models.map((m)=>{ - // basic vrm setup (only if model is vrm) vrm = m.userData.vrm; @@ -600,7 +617,17 @@ export default function Selector({confirmDialog, templateInfo, animationManager, child.geometry.computeBoundsTree({strategy:SAH}); createFaceNormals(child.geometry) - if (child.isSkinnedMesh) createBoneDirection(child) + if (child.isSkinnedMesh) { + createBoneDirection(child) + if (vrm.meta?.metaVersion === '0'){ + VRMUtils.rotateVRM0( vrm ); + console.log("Loaded VRM0 file ", vrm); + for (let i =0; i < child.skeleton.bones.length;i++){ + child.skeleton.bones[i].userData.vrm0RestPosition = { ... child.skeleton.bones[i].position } + } + child.userData.isVRM0 = true; + } + } } }) diff --git a/src/components/TraitInformation.jsx b/src/components/TraitInformation.jsx index cd190f51..131b06ce 100644 --- a/src/components/TraitInformation.jsx +++ b/src/components/TraitInformation.jsx @@ -15,7 +15,7 @@ export default function TraitInformation({currentVRM, animationManager, lookatMa const [cullInDistance, setCullInDistance] = useState(0); const [cullLayer, setCullLayer] = useState(0); const [animationName, setAnimationName] = useState(animationManager.getCurrentAnimationName()); - const [hasMouseLook, setHasMouseLook] = useState(lookatManager.enabled); + const [hasMouseLook, setHasMouseLook] = useState(lookatManager.userActivated); useEffect(() => { if (currentVRM != null){ diff --git a/src/library/animationManager.js b/src/library/animationManager.js index 3084b992..71bec960 100644 --- a/src/library/animationManager.js +++ b/src/library/animationManager.js @@ -21,7 +21,6 @@ class AnimationControl { this.to = null; this.from = null; this.vrm = vrm; - this.animationManager = null; this.animationManager = animationManager; this.mixamoModel = null; @@ -159,7 +158,7 @@ export class AnimationManager{ this.curAnimID = 0; this.animationControls = []; this.started = false; - this.mouseLookEnabled = true; + this.mouseLookEnabled = false; this.mixamoModel = null; this.mixamoAnimations = null; diff --git a/src/library/lookatManager.js b/src/library/lookatManager.js index 4dfdde30..61a06698 100644 --- a/src/library/lookatManager.js +++ b/src/library/lookatManager.js @@ -9,10 +9,10 @@ export class LookAtManager { this.leftEyeBones = [] this.rightEyesBones = [] this.curMousePos = new THREE.Vector2() - this.enabled = true; this.hotzoneSection = getHotzoneSection() - this.enabled = true + this.enabled = false; + this.userActivated = false; this.lookInterest = 1 this.hasInterest = true this.interestSpeed = 0.3 @@ -58,7 +58,8 @@ export class LookAtManager { // }, 1000/60); } setActive(active){ - this.enabled = active; + console.log("is activating") + this.userActivated = active; } setCamera(camera){ this.camera = camera @@ -131,7 +132,7 @@ export class LookAtManager { const cameraRotationThreshold = localVector.z > 0.; // if camera rotation is not larger than 90 if (this.curMousePos.x > this.hotzoneSection.xStart && this.curMousePos.x < this.hotzoneSection.xEnd && this.curMousePos.y > this.hotzoneSection.yStart && this.curMousePos.y < this.hotzoneSection.yEnd && - cameraRotationThreshold && this.enabled) { + cameraRotationThreshold && this.enabled && this.userActivated) { this.neckBones.forEach(neck => { this._moveJoint(neck, this.maxLookPercent.neck) }) diff --git a/src/library/merge-geometry.js b/src/library/merge-geometry.js index 91651960..da5d9af9 100644 --- a/src/library/merge-geometry.js +++ b/src/library/merge-geometry.js @@ -31,6 +31,28 @@ export function cloneSkeleton(skinnedMesh) { return newSkeleton; } +function changeBoneHandedness(bone) { + console.log("isvrm0") + // Clone the bone and apply handedness change + const clone = bone.clone(false); + + // Reverse the X-axis scale to change handedness + const scale = clone.scale; + scale.x = -scale.x; + + // Reverse the rotation around the Y-axis to change handedness + const rotation = clone.rotation; + rotation.y = -rotation.y; + + // You might need to adjust the position as well depending on your specific use case. + // If the mesh is centered at the origin, this may not be necessary. + // If your mesh has been moved from the origin, you may need to adjust the position as well. + // clone.position.x = -clone.position.x; + + clone.position.set(0,0,0); + + return clone; +} 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 @@ -42,6 +64,7 @@ function createMergedSkeleton(meshes, scale){ let index = 0; meshes.forEach(mesh => { if (mesh.skeleton){ + const nonduparr = getOrderedNonDupArray(mesh.geometry.attributes.skinIndex.array); const boneArr = [] nonduparr.forEach(index => { @@ -62,13 +85,14 @@ function createMergedSkeleton(meshes, scale){ mesh.skeleton.bones.forEach((bone, boneInd) => { // only bones that are included in the previous array (used bones) if (boneArr.indexOf(bone)!==-1){ - const clone = boneClones.get(bone.name) + const clone = boneClones.get(bone.name); if (clone == null){ // no clone was found with the bone const boneData = { index, boneInverses:mesh.skeleton.boneInverses[boneInd], - bone: bone.clone(false), - parentName: bone.parent?.type == "Bone" ? bone.parent.name:null + bone: bone.clone(false), + parentName: bone.parent?.type == "Bone" ? bone.parent.name:null, + } index++ boneClones.set(bone.name, boneData); @@ -94,6 +118,10 @@ function createMergedSkeleton(meshes, scale){ newSkeleton.pose(); newSkeleton.bones.forEach(bn => { + const restPosition = bn.userData?.vrm0RestPosition; + if (restPosition){ + bn.position.set(-restPosition.x, restPosition.y, -restPosition.z); + } bn.position.set(bn.position.x *scale, bn.position.y*scale,bn.position.z*scale); }); return newSkeleton @@ -569,11 +597,20 @@ 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, scale }, isVrm0 = false) { // eslint-disable-next-line no-unused-vars let uvcount = 0; meshes.forEach(mesh => { uvcount += mesh.geometry.attributes.uv.count; + + if (mesh.userData?.isVRM0){ + for (let i = 0; i < mesh.geometry.attributes.position.array.length; i+=3){ + mesh.geometry.attributes.position.array[i] *= -1 + mesh.geometry.attributes.position.array[i+2] *= -1 + } + } }); const source = { meshes,