Sfoglia il codice sorgente

Merge pull request #85 from Tresjs/feature/80-useanimation-composable-for-animationmixer

feat(cientos) useanimation composable for animationmixer
Alvaro Saburido 2 anni fa
parent
commit
c18f6a834d

+ 4 - 1
docs/.vitepress/config.ts

@@ -63,7 +63,10 @@ export default defineConfig({
           { text: 'Introduction', link: '/cientos/' },
           {
             text: 'Abstractions',
-            items: [{ text: 'Text3D', link: '/cientos/abstractions/text-3d' }],
+            items: [
+              { text: 'Text3D', link: '/cientos/abstractions/text-3d' },
+              { text: 'useAnimations', link: '/cientos/abstractions/use-animations' },
+            ],
           },
           {
             text: 'Controls',

+ 21 - 0
docs/cientos/abstractions/use-animations.md

@@ -0,0 +1,21 @@
+# useAnimations <Badge type="warning" text="^1.5.0" />
+
+`useAnimation` is a composable that returns a `shallowReactive` with all the models actions based on the animations provided. It is a wrapper around the [AnimationMixer](https://threejs.org/docs/#api/en/animation/AnimationMixer) class.
+
+<StackBlitzEmbed projectId="tresjs-use-animations" />
+
+## Usage
+
+```ts
+import { useAnimations } from '@tresjs/cientos'
+
+const { scene: model, animations } = await useGLTF('/models/ugly-naked-bunny.gltf')
+
+// Animations [ { name: 'Greeting'}, { name: 'Idle' } ]
+
+const { actions, mixer } = useAnimations(animations, model)
+
+let currentAction = actions.Greeting
+
+currentAction.play()
+```

+ 30 - 0
packages/cientos/src/core/useAnimations.ts

@@ -0,0 +1,30 @@
+import { AnimationAction, AnimationClip, AnimationMixer, Object3D, Scene } from 'three'
+import { useRenderLoop } from '@tresjs/core'
+import { ref, Ref, shallowReactive } from 'vue'
+
+export function useAnimations<T extends AnimationClip>(
+  animations: T[],
+  modelRef?: Scene | Ref<Object3D | undefined | null>,
+) {
+  const reference: Ref<Object3D> = ref(modelRef) as Ref<Object3D>
+
+  const mixer = new AnimationMixer(reference.value)
+
+  const actions = shallowReactive<{ [key: string]: AnimationAction }>({})
+
+  animations.forEach(animation => {
+    const action = mixer.clipAction(animation, reference.value)
+    actions[animation.name] = action
+  })
+
+  const { onLoop } = useRenderLoop()
+
+  onLoop(({ delta }) => {
+    mixer.update(delta)
+  })
+
+  return {
+    actions,
+    mixer,
+  }
+}

+ 1 - 0
packages/cientos/src/core/useGLTF/index.ts

@@ -8,6 +8,7 @@ export interface GLTFLoaderOptions {
 }
 
 export interface GLTFResult {
+  animations: Array<THREE.AnimationClip>
   nodes: Array<TresObject>
   materials: Array<THREE.Material>
   scene: THREE.Scene

+ 2 - 1
packages/cientos/src/index.ts

@@ -1,6 +1,7 @@
 import OrbitControls from './core/OrbitControls.vue'
 import TransformControls from './core/TransformControls.vue'
 import { useTweakPane } from './core/useTweakPane'
+import { useAnimations } from './core/useAnimations'
 import { GLTFModel } from './core/useGLTF/component'
 import { FBXModel } from './core/useFBX/component'
 import Text3D from './core/Text3D.vue'
@@ -9,4 +10,4 @@ import Plane from './core/Plane.vue'
 export * from './core/useGLTF'
 export * from './core/useFBX'
 export * from './types'
-export { OrbitControls, TransformControls, useTweakPane, GLTFModel, FBXModel, Text3D, Plane }
+export { OrbitControls, TransformControls, useTweakPane, GLTFModel, FBXModel, Text3D, Plane, useAnimations }

BIN
packages/tres/public/models/ugly-naked-bunny/Face_29.png


BIN
packages/tres/public/models/ugly-naked-bunny/assets/Face_1.png


BIN
packages/tres/public/models/ugly-naked-bunny/assets/Face_2.png


BIN
packages/tres/public/models/ugly-naked-bunny/assets/Face_3.png


BIN
packages/tres/public/models/ugly-naked-bunny/assets/Face_4.png


BIN
packages/tres/public/models/ugly-naked-bunny/assets/Face_5.png


File diff suppressed because it is too large
+ 3350 - 0
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny-animated.gltf


File diff suppressed because it is too large
+ 371 - 0
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny-join.gltf


BIN
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.bin


BIN
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.glb


File diff suppressed because it is too large
+ 3350 - 0
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.gltf


+ 2 - 2
packages/tres/src/App.vue

@@ -1,12 +1,12 @@
 <script setup lang="ts">
 import { useTweakPane } from '@tresjs/cientos'
-import TheBasic from '/@/components/TheBasic.vue'
+import AnimatedModel from '/@/components/AnimatedModel.vue'
 
 useTweakPane()
 </script>
 
 <template>
   <Suspense>
-    <TheBasic />
+    <AnimatedModel />
   </Suspense>
 </template>

+ 61 - 0
packages/tres/src/components/AnimatedModel.vue

@@ -0,0 +1,61 @@
+<script setup lang="ts">
+import { Color, sRGBEncoding } from 'three'
+
+/* import { OrbitControls, useTweakPane, useGLTF, useAnimations } from '../../../cientos/src/' */
+import { OrbitControls, useTweakPane, useGLTF, useAnimations } from '@tresjs/cientos'
+
+const bgColor = new Color('#F78B3D')
+
+const { pane } = useTweakPane()
+
+const { scene: model, animations } = await useGLTF('/models/ugly-naked-bunny/ugly-naked-bunny-animated.gltf')
+
+const { actions, mixer } = useAnimations(animations, model)
+
+let currentAction = actions.Greeting
+
+currentAction.play()
+
+pane
+  .addBlade({
+    view: 'list',
+    label: 'scene',
+    options: Object.keys(actions).map(name => ({
+      text: name,
+      value: name,
+    })),
+    value: 'Greeting',
+  })
+  .on('change', ({ value }) => {
+    if (currentAction) {
+      currentAction.stop()
+    }
+
+    currentAction = actions[value]
+
+    currentAction.play()
+  })
+
+console.log({ animations, actions, mixer })
+</script>
+
+<template>
+  <Suspense>
+    <TresCanvas
+      :clear-color="bgColor"
+      shadows
+      alpha
+      window-size
+      power-preference="high-performance"
+      :output-encoding="sRGBEncoding"
+    >
+      <OrbitControls />
+      <TresPerspectiveCamera :position="8" :fov="45" :near="0.1" :far="10000" />
+      <TresScene :fog="bgColor">
+        <TresAmbientLight :color="0xffffff" :intensity="0.75" />
+        <TresMesh v-bind="model" />
+        <!--   <FBXModel ref="jeepRef" path="/models/low-poly-truck/Jeep_done.fbx" /> -->
+      </TresScene>
+    </TresCanvas>
+  </Suspense>
+</template>

+ 9 - 10
packages/tres/src/core/useRenderLoop/index.ts

@@ -1,12 +1,11 @@
-import { TresState } from './../useTres/index'
-import { useTres } from '/@/core/'
 import { createEventHook, EventHookOn, Fn, useRafFn } from '@vueuse/core'
 import { Ref } from 'vue'
+import { Clock } from 'three'
 
 export interface RenderLoop {
   delta: number
   elapsed: number
-  state: TresState
+  clock: Clock
 }
 
 export interface UseRenderLoopReturn {
@@ -22,22 +21,22 @@ const onBeforeLoop = createEventHook<RenderLoop>()
 const onLoop = createEventHook<RenderLoop>()
 const onAfterLoop = createEventHook<RenderLoop>()
 
+const clock = new Clock()
 let delta = 0
 let elapsed = 0
+
 const { pause, resume, isActive } = useRafFn(
   () => {
-    const { state } = useTres()
-    onBeforeLoop.trigger({ delta, elapsed, state })
-    onLoop.trigger({ delta, elapsed, state })
-    onAfterLoop.trigger({ delta, elapsed, state })
+    onBeforeLoop.trigger({ delta, elapsed, clock })
+    onLoop.trigger({ delta, elapsed, clock })
+    onAfterLoop.trigger({ delta, elapsed, clock })
   },
   { immediate: false },
 )
 
 onAfterLoop.on(() => {
-  const { state } = useTres()
-  delta = state.clock.getDelta()
-  elapsed = state.clock.getElapsedTime()
+  delta = clock.getDelta()
+  elapsed = clock.getElapsedTime()
 })
 
 export function useRenderLoop(): UseRenderLoopReturn {

+ 1 - 1
packages/tres/src/core/useScene/component.ts

@@ -17,7 +17,7 @@ export const Scene = defineComponent({
 
     provide('local-scene', scene)
 
-    onLoop(() => {
+    onLoop(({ clock }) => {
       if (renderer?.value && activeCamera?.value && scene?.value) {
         renderer.value.render(scene?.value, activeCamera.value)
       }

Some files were not shown because too many files changed in this diff