浏览代码

Merge branch 'main' into feature/playground

Alvaro 2 年之前
父节点
当前提交
d791a375b4
共有 25 个文件被更改,包括 7271 次插入17 次删除
  1. 4 1
      docs/.vitepress/config.ts
  2. 1 1
      docs/api/composables.md
  3. 21 0
      docs/cientos/abstractions/use-animations.md
  4. 30 0
      packages/cientos/src/core/useAnimations.ts
  5. 1 0
      packages/cientos/src/core/useGLTF/index.ts
  6. 2 1
      packages/cientos/src/index.ts
  7. 二进制
      packages/tres/public/models/ugly-naked-bunny/Face_29.png
  8. 二进制
      packages/tres/public/models/ugly-naked-bunny/assets/Face_1.png
  9. 二进制
      packages/tres/public/models/ugly-naked-bunny/assets/Face_2.png
  10. 二进制
      packages/tres/public/models/ugly-naked-bunny/assets/Face_3.png
  11. 二进制
      packages/tres/public/models/ugly-naked-bunny/assets/Face_4.png
  12. 二进制
      packages/tres/public/models/ugly-naked-bunny/assets/Face_5.png
  13. 3350 0
      packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny-animated.gltf
  14. 371 0
      packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny-join.gltf
  15. 二进制
      packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.bin
  16. 二进制
      packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.glb
  17. 3350 0
      packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.gltf
  18. 2 2
      packages/tres/src/App.vue
  19. 61 0
      packages/tres/src/components/AnimatedModel.vue
  20. 60 0
      packages/tres/src/components/TheBasic.vue
  21. 2 2
      packages/tres/src/core/useInstanceCreator/index.ts
  22. 12 7
      packages/tres/src/core/useRenderLoop/index.ts
  23. 2 1
      packages/tres/src/core/useRenderer/index.ts
  24. 1 1
      packages/tres/src/core/useScene/component.ts
  25. 1 1
      packages/tres/src/types/index.d.ts

+ 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',

+ 1 - 1
docs/api/composables.md

@@ -13,7 +13,7 @@ The `useRenderLoop` composable is the core of **TresJS** animations. It allows y
 ```ts
 const { onLoop, resume } = useRenderLoop()
 
-onLoop(({ delta, elapsed }) => {
+onLoop(({ delta, elapsed, clock, dt }) => {
   // I will run at every frame ~ 60FPS (depending of your monitor)
 })
 ```

+ 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 }

二进制
packages/tres/public/models/ugly-naked-bunny/Face_29.png


二进制
packages/tres/public/models/ugly-naked-bunny/assets/Face_1.png


二进制
packages/tres/public/models/ugly-naked-bunny/assets/Face_2.png


二进制
packages/tres/public/models/ugly-naked-bunny/assets/Face_3.png


二进制
packages/tres/public/models/ugly-naked-bunny/assets/Face_4.png


二进制
packages/tres/public/models/ugly-naked-bunny/assets/Face_5.png


文件差异内容过多而无法显示
+ 3350 - 0
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny-animated.gltf


文件差异内容过多而无法显示
+ 371 - 0
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny-join.gltf


二进制
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.bin


二进制
packages/tres/public/models/ugly-naked-bunny/ugly-naked-bunny.glb


文件差异内容过多而无法显示
+ 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 Shapes from '/@/components/Shapes.vue'
+import AnimatedModel from '/@/components/AnimatedModel.vue'
 
 useTweakPane()
 </script>
 
 <template>
   <Suspense>
-    <Shapes />
+    <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>

+ 60 - 0
packages/tres/src/components/TheBasic.vue

@@ -0,0 +1,60 @@
+<script setup lang="ts">
+import {
+  sRGBEncoding,
+  LinearEncoding,
+  BasicShadowMap,
+  PCFShadowMap,
+  PCFSoftShadowMap,
+  VSMShadowMap,
+  NoToneMapping,
+  LinearToneMapping,
+  ReinhardToneMapping,
+  CineonToneMapping,
+  ACESFilmicToneMapping,
+  CustomToneMapping,
+} from 'three'
+import { reactive, ref } from 'vue'
+
+import { OrbitControls, TransformControls } from '@tresjs/cientos'
+import { useRenderLoop } from '..'
+/* import { OrbitControls, GLTFModel } from '@tresjs/cientos' */
+
+const state = reactive({
+  clearColor: '#201919',
+  shadows: true,
+  alpha: false,
+  physicallyCorrectLights: true,
+  shadowMapType: BasicShadowMap,
+  outputEncoding: sRGBEncoding,
+  toneMapping: NoToneMapping,
+})
+
+const sphereRef = ref()
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ delta, elapsed, state }) => {
+  sphereRef.value.position.y += Math.sin(elapsed * 0.01) * 0.1
+})
+</script>
+<template>
+  <TresCanvas v-bind="state">
+    <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
+    <OrbitControls make-default />
+    <TresScene>
+      <TresAmbientLight :intensity="0.5" />
+      <TransformControls mode="scale" :object="sphereRef" />
+
+      <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
+        <TresSphereGeometry />
+        <TresMeshToonMaterial color="#FBB03B" />
+      </TresMesh>
+      <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.7" cast-shadow />
+      <TresMesh :rotation="[-Math.PI / 2, 0, 0]" receive-shadow>
+        <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+        <TresMeshToonMaterial />
+      </TresMesh>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="1" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 2 - 2
packages/tres/src/core/useInstanceCreator/index.ts

@@ -86,8 +86,8 @@ export function useInstanceCreator(prefix: string) {
       // check if args prop is defined on the vnode
       let internalInstance
       if (catalogue) {
-        if (vnode.children?.default) {
-          const internal = vnode.children
+        if ((vnode.children as unknown as { default: any })?.default) {
+          const internal = (vnode.children as unknown as { default: any })
             .default()
             .map((child: TresVNode) => createInstanceFromVNode(child)) as TresInstance[]
 

+ 12 - 7
packages/tres/src/core/useRenderLoop/index.ts

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

+ 2 - 1
packages/tres/src/core/useRenderer/index.ts

@@ -16,6 +16,7 @@ import {
   WebGLRenderer,
   ShadowMapType,
   PCFShadowMap,
+  Clock,
 } from 'three'
 import type { TextureEncoding, ToneMapping } from 'three'
 import { useRenderLoop, useTres } from '/@/core/'
@@ -188,7 +189,7 @@ export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef,
 
     const { setState } = useTres()
     setState('renderer', renderer.value)
-
+    setState('clock', new Clock())
     updateRendererOptions()
     updateRendererSize()
     resume()

+ 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)
       }

+ 1 - 1
packages/tres/src/types/index.d.ts

@@ -9,7 +9,7 @@ export type TresVNodeType = VNodeTypes & {
   __name?: string
   setup?: (props: Readonly<any>) => void
 }
-export type TresVNode = VNode & { children?: Array<VNode>; type: TresVNodeType }
+export type TresVNode = VNode & { children?: Array<VNode | { default: any }>; type: TresVNodeType }
 export type TresAttributes = Record<string, any> & { args?: number[] }
 
 export type TresColor = string | number | Color | number[]

部分文件因为文件数量过多而无法显示