From 494bb3a32444088365e7db283a6c85ea480ca53c Mon Sep 17 00:00:00 2001 From: Lilia_Chen Date: Thu, 24 Jul 2025 06:28:49 +0100 Subject: [PATCH] fix(stage-ui): In the prod, the camera was not being set correct pos and target (#317) --- .../Scenarios/Settings/ModelSettings/VRM.vue | 2 +- .../stage-ui/src/components/Scenes/VRM.vue | 91 +++++++++++-------- .../src/components/Scenes/VRM/Model.vue | 12 ++- .../components/Scenes/VRM/OrbitControls.vue | 6 ++ 4 files changed, 68 insertions(+), 43 deletions(-) diff --git a/packages/stage-ui/src/components/Scenarios/Settings/ModelSettings/VRM.vue b/packages/stage-ui/src/components/Scenarios/Settings/ModelSettings/VRM.vue index 0567b3468..73c76b191 100644 --- a/packages/stage-ui/src/components/Scenarios/Settings/ModelSettings/VRM.vue +++ b/packages/stage-ui/src/components/Scenarios/Settings/ModelSettings/VRM.vue @@ -105,7 +105,7 @@ function urlUploadClick() { /> >() const camera = shallowRef(new THREE.PerspectiveCamera()) const controlsRef = ref>() - -onMounted(() => { - if (vrmContainerRef.value) { - camera.value.aspect = width.value / height.value - camera.value.fov = cameraFOV.value - camera.value.position.set( - cameraPosition.value.x, - cameraPosition.value.y, - cameraPosition.value.z, - ) - camera.value.updateProjectionMatrix() - } -}) +let isUpdatingCamera = true +// manage the sequence of the camera and controls initialization +const controlsReady = ref(false) +const modelReady = ref(false) +const sceneReady = ref(false) watch(cameraFOV, (newFov) => { if (camera.value) { @@ -55,31 +47,49 @@ watch(cameraFOV, (newFov) => { // camera.value.updateProjectionMatrix() // } // }) -function handleLoadModelProgress(val: number) { - if (val === 100 && camera.value && controlsRef.value && controlsRef.value.controls) { - // Set camera pos - camera.value.position.set( - cameraPosition.value.x, - cameraPosition.value.y, - cameraPosition.value.z, - ) - camera.value.updateProjectionMatrix() - - // Set camera target - controlsRef.value.controls.target.set( - modelOrigin.value.x, - modelOrigin.value.y, - modelOrigin.value.z, - ) - controlsRef.value.controls.update() - } +// If controls are ready +watch(() => controlsRef.value?.controls, (ctrl) => { + if (ctrl) + controlsReady.value = true +}) +// If model is ready +function handleLoadModelProgress() { + modelReady.value = true } +// Then start to set the camera postion and target +watch( + [controlsReady, modelReady], + ([ctrlOk, modelOk]) => { + if (ctrlOk && modelOk && camera.value && controlsRef.value && controlsRef.value.controls) { + isUpdatingCamera = true + try { + camera.value.aspect = width.value / height.value + camera.value.fov = cameraFOV.value + // Set camera target + controlsRef.value.setTarget(modelOrigin.value) + // Set camera position + camera.value.position.set( + cameraPosition.value.x, + cameraPosition.value.y, + cameraPosition.value.z, + ) + camera.value.updateProjectionMatrix() + controlsRef.value.controls.update() + } + finally { + isUpdatingCamera = false + sceneReady.value = true + } + } + }, +) // Bidirectional watch between slider and OrbitControls watch(() => controlsRef.value?.getDistance(), (newDistance) => { - if (newDistance !== undefined) { + if (!isUpdatingCamera && newDistance !== undefined && camera.value) { // To avoid floating point inaccuracies causing a feedback loop with the other watcher, // we can check if the distance has changed significantly. + isUpdatingCamera = true if (Math.abs(cameraDistance.value - newDistance) > 1e-6) { cameraDistance.value = newDistance cameraPosition.value = { @@ -88,10 +98,12 @@ watch(() => controlsRef.value?.getDistance(), (newDistance) => { z: camera.value.position.z, } } + isUpdatingCamera = false } }) watch(cameraDistance, (newDistance) => { - if (camera.value && controlsRef.value && controlsRef.value.controls) { + if (!isUpdatingCamera && camera.value && controlsRef.value && controlsRef.value.controls) { + isUpdatingCamera = true const newPosition = new THREE.Vector3() const target = controlsRef.value.controls.target const direction = new THREE.Vector3().subVectors(camera.value.position, target).normalize() @@ -108,6 +120,8 @@ watch(cameraDistance, (newDistance) => { z: newPosition.z, } } + + isUpdatingCamera = false }) defineExpose({ @@ -119,20 +133,21 @@ defineExpose({ diff --git a/packages/stage-ui/src/components/Scenes/VRM/Model.vue b/packages/stage-ui/src/components/Scenes/VRM/Model.vue index 1d0c18d2a..541763ead 100644 --- a/packages/stage-ui/src/components/Scenes/VRM/Model.vue +++ b/packages/stage-ui/src/components/Scenes/VRM/Model.vue @@ -23,6 +23,7 @@ const props = defineProps<{ const emit = defineEmits<{ (e: 'loadModelProgress', value: number): void (e: 'error', value: unknown): void + (e: 'modelReady'): void }>() let disposeBeforeRenderLoop: (() => void | undefined) @@ -78,6 +79,7 @@ onMounted(async () => { y: vrmModelCenter.y + vrmInitialCameraOffset.y, z: vrmModelCenter.z + vrmInitialCameraOffset.z, } + // cameraDistance.value = vrmInitialCameraOffset.length() // Set initial positions for model modelOrigin.value = { @@ -133,14 +135,14 @@ onMounted(async () => { hipsTrack.values[1], hipsTrack.values[2], ) - const delta = new Vector3().subVectors(animeHipPos, defaultHipPos) + const animeDelta = new Vector3().subVectors(animeHipPos, defaultHipPos) clip.tracks.forEach((track) => { if (track.name.endsWith('.position') && track instanceof VectorKeyframeTrack) { for (let i = 0; i < track.values.length; i += 3) { - track.values[i] -= delta.x - track.values[i + 1] -= delta.y - track.values[i + 2] -= delta.z + track.values[i] -= animeDelta.x + track.values[i + 1] -= animeDelta.y + track.values[i + 2] -= animeDelta.z } } }) @@ -164,6 +166,7 @@ onMounted(async () => { vrm.value = _vrm loadingModel.value = false + emit('modelReady') disposeBeforeRenderLoop = onBeforeRender(({ delta }) => { vrmAnimationMixer.value?.update(delta) @@ -201,6 +204,7 @@ defineExpose({ vrmEmote.value?.setEmotionWithResetAfter(expression, 1000) }, scene: computed(() => vrm.value?.scene), + lookAt: computed(() => vrm.value?.lookAt), }) const { pause, resume } = useLoop() diff --git a/packages/stage-ui/src/components/Scenes/VRM/OrbitControls.vue b/packages/stage-ui/src/components/Scenes/VRM/OrbitControls.vue index 6e01c8a4a..e8fbd5a5b 100644 --- a/packages/stage-ui/src/components/Scenes/VRM/OrbitControls.vue +++ b/packages/stage-ui/src/components/Scenes/VRM/OrbitControls.vue @@ -50,6 +50,12 @@ defineExpose({ controls, getDistance: () => controls.value?.getDistance(), update: () => controls.value?.update(), + setTarget: (target: { x: number, y: number, z: number }) => { + if (controls.value) { + controls.value.target.set(target.x, target.y, target.z) + controls.value.update() + } + }, })