From aeb65c292521cf600ceaf0ee1dec7b4336b95c68 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 17 Oct 2023 17:08:04 -0600 Subject: [PATCH 1/4] add ui elements --- src/components/TraitInformation.jsx | 12 +++++++ src/components/TraitInformation.module.css | 41 ++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/src/components/TraitInformation.jsx b/src/components/TraitInformation.jsx index f6fdc0d3..12369704 100644 --- a/src/components/TraitInformation.jsx +++ b/src/components/TraitInformation.jsx @@ -131,6 +131,18 @@ export default function TraitInformation({currentVRM, animationManager}){ onClick={nextAnimation} > +
+
+
+ Mouse Follow +
+ +
+
+ diff --git a/src/components/TraitInformation.module.css b/src/components/TraitInformation.module.css index 9e1eed72..dee01a70 100644 --- a/src/components/TraitInformation.module.css +++ b/src/components/TraitInformation.module.css @@ -98,4 +98,45 @@ .anim-button:hover { opacity: 1; +} + +/* Hide the default checkbox */ +.custom-checkbox input[type="checkbox"] { + display: none; +} + +/* Style the custom checkbox */ +.custom-checkbox .checkbox-container { + display: inline-block; + width: 20px; + height: 20px; + border: 2px solid #284b39; /* Change border color as needed */ + border-radius: 5px; + cursor: pointer; +} + +.custom-checkbox .checkbox-container.checked { + background-color: #5eb086; /* Change background color when checked */ +} + +.custom-checkbox .checkbox-container .checkmark { + display: none; +} + +/* Style the checkmark when the checkbox is checked */ +.custom-checkbox input[type="checkbox"]:checked + .checkbox-container { + background-color: #5eb086; /* Change background color when checked */ +} + +.custom-checkbox input[type="checkbox"]:checked + .checkbox-container .checkmark { + display: block; +} + +.checkboxHolder { + display: flex; + gap: 20px; + align-items: center; + justify-content: flex-start; + align-content: center; + height: 40px; } \ No newline at end of file From 5283c74050dc0753d5bf1962b3e9b613a8b630e9 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 17 Oct 2023 17:23:46 -0600 Subject: [PATCH 2/4] disable mouse look on checkbox selected --- src/components/Editor.jsx | 2 +- src/components/TraitInformation.jsx | 16 ++++++++++++++-- src/library/lookatManager.js | 6 +++++- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/components/Editor.jsx b/src/components/Editor.jsx index 5acf8ace..d24aaa56 100644 --- a/src/components/Editor.jsx +++ b/src/components/Editor.jsx @@ -115,7 +115,7 @@ export default function Editor({confirmDialog,animationManager, blinkManager, lo - + ) } diff --git a/src/components/TraitInformation.jsx b/src/components/TraitInformation.jsx index 12369704..d9043cf1 100644 --- a/src/components/TraitInformation.jsx +++ b/src/components/TraitInformation.jsx @@ -5,7 +5,7 @@ import { SceneContext } from "../context/SceneContext"; import Slider from "./Slider"; import { cullHiddenMeshes } from "../library/utils"; -export default function TraitInformation({currentVRM, animationManager}){ +export default function TraitInformation({currentVRM, animationManager, lookatManager}){ const { displayTraitOption, avatar @@ -15,6 +15,7 @@ export default function TraitInformation({currentVRM, animationManager}){ const [cullInDistance, setCullInDistance] = useState(0); const [cullLayer, setCullLayer] = useState(0); const [animationName, setAnimationName] = useState(animationManager.getCurrentAnimationName()); + const [hasMouseLook, setHasMouseLook] = useState(lookatManager.enabled); useEffect(() => { if (currentVRM != null){ @@ -47,6 +48,7 @@ export default function TraitInformation({currentVRM, animationManager}){ }; const handleCullLayerChange = (event) => { + console.log(lookatManager.enabled); if (currentVRM?.data){ setCullLayer(event.target.value); currentVRM.data.cullingLayer = event.target.value; @@ -62,6 +64,11 @@ export default function TraitInformation({currentVRM, animationManager}){ await animationManager.loadPreviousAnimation(); setAnimationName(animationManager.getCurrentAnimationName()); } + const handleMouseLookEnable = (event) => { + setHasMouseLook(event.target.checked); + lookatManager.setActive(event.target.checked); + // Perform any additional actions or logic based on the checkbox state change + }; return ( displayTraitOption != null ? ( @@ -134,10 +141,15 @@ export default function TraitInformation({currentVRM, animationManager}){
+ Mouse Follow
diff --git a/src/library/lookatManager.js b/src/library/lookatManager.js index 4e745f44..4dfdde30 100644 --- a/src/library/lookatManager.js +++ b/src/library/lookatManager.js @@ -9,6 +9,7 @@ export class LookAtManager { this.leftEyeBones = [] this.rightEyesBones = [] this.curMousePos = new THREE.Vector2() + this.enabled = true; this.hotzoneSection = getHotzoneSection() this.enabled = true @@ -56,6 +57,9 @@ export class LookAtManager { // this.update(); // }, 1000/60); } + setActive(active){ + this.enabled = active; + } setCamera(camera){ this.camera = camera } @@ -127,7 +131,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) { + cameraRotationThreshold && this.enabled) { this.neckBones.forEach(neck => { this._moveJoint(neck, this.maxLookPercent.neck) }) From d74de16ad6a5e6a90aaf09445c379a17bd233e4a Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 17 Oct 2023 19:02:37 -0600 Subject: [PATCH 3/4] restore character initial animation --- src/components/TraitInformation.jsx | 1 + src/library/animationManager.js | 43 +++++++++++++++++++++-------- src/library/loadMixamoAnimation.js | 2 -- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/components/TraitInformation.jsx b/src/components/TraitInformation.jsx index d9043cf1..c7f570ec 100644 --- a/src/components/TraitInformation.jsx +++ b/src/components/TraitInformation.jsx @@ -67,6 +67,7 @@ export default function TraitInformation({currentVRM, animationManager, lookatMa const handleMouseLookEnable = (event) => { setHasMouseLook(event.target.checked); lookatManager.setActive(event.target.checked); + animationManager.enableMouseLook(event.target.checked); // Perform any additional actions or logic based on the checkbox state change }; diff --git a/src/library/animationManager.js b/src/library/animationManager.js index b2b29bd0..76e6185e 100644 --- a/src/library/animationManager.js +++ b/src/library/animationManager.js @@ -23,6 +23,11 @@ class AnimationControl { this.vrm = vrm; this.animationManager = null; this.animationManager = animationManager; + this.mixamoModel = null; + + this.neckBone = vrm?.humanoid?.humanBones?.neck; + this.spineBone = vrm?.humanoid?.humanBones?.spine; + this.setAnimations(animations); @@ -42,18 +47,30 @@ class AnimationControl { this.actions[curIdx].time = animationManager.getToActionTime(); this.actions[curIdx].play(); } - setAnimations(animations, mixamoModel){ + + setMouseLookEnabled(mouseLookEnabled){ + this.setAnimations(this.animations, this.mixamoModel, mouseLookEnabled); + } + + setAnimations(animations, mixamoModel, mouseLookEnabled = null){ + mouseLookEnabled = mouseLookEnabled == null ? this.animationManager.mouseLookEnabled : mouseLookEnabled; + this.animations = animations; this.mixer.stopAllAction(); if (mixamoModel != null){ - if (this.vrm != null) + if (this.vrm != null){ animations = [getMixamoAnimation(animations, mixamoModel , this.vrm)] + this.mixamoModel = mixamoModel; + } // modify animations } - animations[0].tracks.map((track, index) => { - if(track.name === "neck.quaternion" || track.name === "spine.quaternion"){ - animations[0].tracks.splice(index, 1) - } - }) + if (mouseLookEnabled){ + animations[0].tracks.map((track, index) => { + if(track.name === "neck.quaternion" || track.name === "spine.quaternion"){ + animations[0].tracks.splice(index, 1) + } + }) + } + this.actions = []; for (let i =0; i < animations.length;i++){ @@ -106,6 +123,7 @@ export class AnimationManager{ this.curAnimID = 0; this.animationControls = []; this.started = false; + this.mouseLookEnabled = true; this.mixamoModel = null; this.mixamoAnimations = null; @@ -122,14 +140,17 @@ export class AnimationManager{ }, 1000/30); } - + enableMouseLook(enable){ + this.mouseLookEnabled = enable; + this.animationControls.forEach(animControls => { + animControls.setMouseLookEnabled(enable); + }); + } async loadAnimation(paths, isfbx = true, pathBase = "", name = ""){ - console.log(paths) const path = pathBase + (pathBase != "" ? "/":"") + getAsArray(paths)[0]; name = name == "" ? getFileNameWithoutExtension(path) : name; this.currentAnimationName = name; - console.log(this.currentAnimationName); const loader = isfbx ? fbxLoader : gltfLoader; const animationModel = await loader.loadAsync(path); // if we have mixamo animations store the model @@ -153,7 +174,7 @@ export class AnimationManager{ else{ //cons this.animationControls.forEach(animationControl => { - animationControl.setAnimations(animationModel.animations, this.mixamoModel) + animationControl.setAnimations(animationModel.animations, this.mixamoModel, this.mouseLookEnabled) }); } diff --git a/src/library/loadMixamoAnimation.js b/src/library/loadMixamoAnimation.js index 1b51e703..bf7b0353 100644 --- a/src/library/loadMixamoAnimation.js +++ b/src/library/loadMixamoAnimation.js @@ -10,7 +10,6 @@ import { VRMRigMapMixamo } from './VRMRigMapMixamo.js'; */ export function getMixamoAnimation( animations, model, vrm ) { const clip = THREE.AnimationClip.findByName( animations, 'mixamo.com' ); // extract the AnimationClip - const tracks = []; // KeyframeTracks compatible with VRM will be added here const restRotationInverse = new THREE.Quaternion(); @@ -24,7 +23,6 @@ export function getMixamoAnimation( animations, model, vrm ) { const vrmRootY = vrm.scene.getWorldPosition( _vec3 ).y; const vrmHipsHeight = Math.abs( vrmHipsY - vrmRootY ); const hipsPositionScale = vrmHipsHeight / motionHipsHeight; - clip.tracks.forEach( ( origTrack ) => { const track = origTrack.clone(); // Convert each tracks for VRM use, and push to `tracks` From 26e722852157622a77b760e0118f166ffe62c199 Mon Sep 17 00:00:00 2001 From: memelotsqui Date: Tue, 17 Oct 2023 19:55:49 -0600 Subject: [PATCH 4/4] smooth transition for animations --- src/components/TraitInformation.module.css | 4 +- src/library/animationManager.js | 44 ++++++++++++++++++++-- src/library/loadMixamoAnimation.js | 2 + 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/components/TraitInformation.module.css b/src/components/TraitInformation.module.css index dee01a70..5708bc45 100644 --- a/src/components/TraitInformation.module.css +++ b/src/components/TraitInformation.module.css @@ -134,9 +134,9 @@ .checkboxHolder { display: flex; - gap: 20px; + gap: 30px; align-items: center; - justify-content: flex-start; + justify-content: center; align-content: center; height: 40px; } \ No newline at end of file diff --git a/src/library/animationManager.js b/src/library/animationManager.js index 76e6185e..3084b992 100644 --- a/src/library/animationManager.js +++ b/src/library/animationManager.js @@ -25,6 +25,9 @@ class AnimationControl { this.animationManager = animationManager; this.mixamoModel = null; + this.fadeOutActions = null; + this.newAnimationWeight = 1; + this.neckBone = vrm?.humanoid?.humanBones?.neck; this.spineBone = vrm?.humanoid?.humanBones?.spine; @@ -55,14 +58,23 @@ class AnimationControl { setAnimations(animations, mixamoModel, mouseLookEnabled = null){ mouseLookEnabled = mouseLookEnabled == null ? this.animationManager.mouseLookEnabled : mouseLookEnabled; this.animations = animations; - this.mixer.stopAllAction(); + //this.mixer.stopAllAction(); if (mixamoModel != null){ if (this.vrm != null){ - animations = [getMixamoAnimation(animations, mixamoModel , this.vrm)] - this.mixamoModel = mixamoModel; + const mixamoAnimation = getMixamoAnimation(animations, mixamoModel , this.vrm); + if (mixamoAnimation){ + animations = [mixamoAnimation] + this.mixamoModel = mixamoModel; + } } - // modify animations + } else{ + const cloneAnims = []; + animations.forEach(animation => { + cloneAnims.push(animation.clone()); + }); + animations = cloneAnims; } + // modify animations if (mouseLookEnabled){ animations[0].tracks.map((track, index) => { if(track.name === "neck.quaternion" || track.name === "spine.quaternion"){ @@ -71,15 +83,39 @@ class AnimationControl { }) } + this.fadeOutActions = this.actions; this.actions = []; + this.newAnimationWeight = 0; for (let i =0; i < animations.length;i++){ this.actions.push(this.mixer.clipAction(animations[i])); } + this.actions[0].weight = 0; this.actions[0].play(); } update(weightIn,weightOut){ + if (this.fadeOutActions != null){ + this.newAnimationWeight += 1/5; + this.fadeOutActions.forEach(action => { + action.weight = 1 - this.newAnimationWeight; + }); + + if (this.newAnimationWeight >= 1){ + this.newAnimationWeight = 1; + this.fadeOutActions.forEach(action => { + action.weight = 0; + action.stop(); + }); + this.fadeOutActions = null; + } + + this.actions.forEach(action => { + action.weight = this.newAnimationWeight; + }); + + } + if (this.from != null) { this.from.weight = weightOut; } diff --git a/src/library/loadMixamoAnimation.js b/src/library/loadMixamoAnimation.js index bf7b0353..a4c97d0c 100644 --- a/src/library/loadMixamoAnimation.js +++ b/src/library/loadMixamoAnimation.js @@ -10,6 +10,8 @@ import { VRMRigMapMixamo } from './VRMRigMapMixamo.js'; */ export function getMixamoAnimation( animations, model, vrm ) { const clip = THREE.AnimationClip.findByName( animations, 'mixamo.com' ); // extract the AnimationClip + if (clip == null) + return null; const tracks = []; // KeyframeTracks compatible with VRM will be added here const restRotationInverse = new THREE.Quaternion();