Ver código fonte

feat: shallowRef with manually track in useCamera

fix: HMR with TresCanvas
enpitsulin 2 anos atrás
pai
commit
177f00e7d8

+ 36 - 31
src/components/TresCanvas.vue

@@ -7,7 +7,7 @@ import {
     type ShadowMapType,
     type ToneMapping,
 } from 'three'
-import { Fragment, computed, defineComponent, h, onMounted, provide, ref, shallowRef, watch, watchEffect } from 'vue'
+import { Fragment, computed, defineComponent, h, onMounted, provide, ref, shallowRef, watch } from 'vue'
 import { useTresContextProvider } from '../composables'
 import { render } from '../core/renderer'
 
@@ -17,6 +17,7 @@ import {
     useRenderLoop,
 } from '../composables'
 
+import { triggerRef } from 'vue'
 import type { RendererPresetsType } from '../composables/useRenderer/const'
 import type { TresCamera, TresObject } from '../types/'
 
@@ -47,7 +48,6 @@ const props = withDefaults(defineProps<TresCanvasProps>(), {
 })
 
 const { logWarning } = useLogger()
-
 const canvas = ref<HTMLCanvasElement>()
 
 /*
@@ -68,7 +68,7 @@ const slots = defineSlots<{
 const disableRender = computed(() => props.disableRender)
 
 const context = useTresContextProvider({
-    scene: scene.value,
+    scene,
     canvas,
     windowSize: computed(() => props.windowSize),
     disableRender,
@@ -77,14 +77,16 @@ const context = useTresContextProvider({
 
 usePointerEventHandler({ scene: scene.value, contextParts: context })
 
+
+const internalFnComponent = defineComponent({
+    setup() {
+        provide('useTres', context);
+        return () => h(Fragment, null, slots && slots.default ? slots.default() : [])
+    }
+})
+
 const renderScene = () => {
     const container = scene.value as unknown as TresObject
-    const internalFnComponent = defineComponent({
-        setup() {
-            provide('useTres', context);
-            return () => h(Fragment, null, slots && slots.default ? slots.default() : [])
-        }
-    })
     render(h(internalFnComponent), container)
 }
 
@@ -92,27 +94,10 @@ const renderScene = () => {
 
 onMounted(() => {
 
-    const { addCamera, camera, cameras, removeCamera } = context
+    const { addCamera, removeCamera } = context
 
     renderScene()
 
-    const addDefaultCamera = () => {
-        const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
-        camera.position.set(3, 3, 3)
-        camera.lookAt(0, 0, 0)
-        addCamera(camera)
-
-        const unwatch = watchEffect(
-            () => {
-                if (cameras.value.length >= 2) {
-                    camera.removeFromParent()
-                    removeCamera(camera)
-                    unwatch?.()
-                }
-            },
-        )
-    }
-
     watch(() => props.camera, (newCamera, oldCamera) => {
         if (newCamera)
             addCamera(newCamera)
@@ -124,20 +109,40 @@ onMounted(() => {
         immediate: true
     })
 
-    if (!camera.value) {
+})
+
+const addDefaultCamera = () => {
+    const camera = new PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000)
+    camera.position.set(3, 3, 3)
+    camera.lookAt(0, 0, 0)
+    context.addCamera(camera)
+
+    if (context.cameras.value.length >= 2) {
+        camera.removeFromParent()
+        context.removeCamera(camera)
+    }
+}
+
+watch(canvas, val => {
+    if (!val) return
+    triggerRef(scene)
+})
+
+watch(scene, () => {
+    if (!context.camera.value) {
         logWarning(
             'No camera found. Creating a default perspective camera. ' +
-            'To have full control over a camera, please add one to the scene.'
+            'To have full control over a camera, please add one to the scene.',
         )
         addDefaultCamera()
     }
 })
 
 
+
 if (import.meta.hot)
     import.meta.hot.on('vite:afterUpdate', () => {
-        const container = scene.value as unknown as TresObject
-        render(h(Fragment, null, slots && slots.default ? slots.default() : []), container)
+        renderScene()
         scene.value.updateMatrix()
         resume()
     })

+ 13 - 16
src/composables/useCamera/index.ts

@@ -1,17 +1,18 @@
-import { computed, watchEffect, onUnmounted, ref } from 'vue'
 import { Camera, OrthographicCamera, PerspectiveCamera } from 'three'
+import { computed, triggerRef, watch, watchEffect } from 'vue'
 
-import type { TresScene } from '../../types'
 import type { TresContext } from '../useTresContextProvider'
 
 
-export const useCamera = ({ sizes, scene }: Pick<TresContext, 'sizes'> & { scene: TresScene }) => {
-
-  // the computed does not trigger, when for example the camera postion changes
-  const cameras = ref<Camera[]>([])
+export const useCamera = ({ sizes, scene }: Pick<TresContext, 'sizes' | 'scene'>) => {
+  const cameras = computed<Camera[]>(() => scene.value.children.filter((i): i is Camera => (i as any).isCamera))
   const camera = computed<Camera | undefined>(
-    () => cameras.value[0]
+    () => cameras.value[0],
   )
+  // don't known why need manually trigger here
+  watch(camera, () => {
+    triggerRef(cameras)
+  }, { deep: true })
 
   const addCamera = (newCamera: Camera, active = false) => {
     if (cameras.value.some(({ uuid }) => uuid === newCamera.uuid))
@@ -21,11 +22,10 @@ export const useCamera = ({ sizes, scene }: Pick<TresContext, 'sizes'> & { scene
       setCameraActive(newCamera)
     else
       cameras.value.push(newCamera)
-
   }
 
   const removeCamera = (camera: Camera) => {
-    cameras.value = cameras.value.filter(({ uuid }) => uuid !== camera.uuid)
+    scene.value.remove(camera)
   }
 
   const setCameraActive = (cameraOrUuid: string | Camera) => {
@@ -36,7 +36,7 @@ export const useCamera = ({ sizes, scene }: Pick<TresContext, 'sizes'> & { scene
     if (!camera) return
 
     const otherCameras = cameras.value.filter(({ uuid }) => uuid !== camera.uuid)
-    cameras.value = [camera, ...otherCameras]
+    scene.value.add(...otherCameras)
   }
 
   watchEffect(() => {
@@ -51,12 +51,9 @@ export const useCamera = ({ sizes, scene }: Pick<TresContext, 'sizes'> & { scene
     }
   })
 
-  scene.userData.tres__registerCamera = addCamera
-  scene.userData.tres__deregisterCamera = removeCamera
+  scene.value.userData.tres__registerCamera = addCamera
+  scene.value.userData.tres__deregisterCamera = removeCamera
 
-  onUnmounted(() => {
-    cameras.value = []
-  })
 
   return {
     camera,
@@ -65,4 +62,4 @@ export const useCamera = ({ sizes, scene }: Pick<TresContext, 'sizes'> & { scene
     removeCamera,
     setCameraActive,
   }
-}
+}

+ 3 - 3
src/composables/useRenderer/index.ts

@@ -1,6 +1,6 @@
 import { Color, WebGLRenderer } from 'three'
 import { rendererPresets, RendererPresetsType } from './const'
-import { shallowRef, watchEffect, onUnmounted, type MaybeRef, computed, watch } from 'vue'
+import { shallowRef, watchEffect, onUnmounted, type MaybeRef, computed, watch, ShallowRef } from 'vue'
 import {
   toValue,
   unrefElement,
@@ -110,7 +110,7 @@ export function useRenderer(
   }:
     {
       canvas: MaybeRef<HTMLCanvasElement | undefined>
-      scene: Scene
+      scene: ShallowRef<Scene>
       options: UseRendererOptions
       contextParts: Pick<TresContext, 'sizes' | 'camera'>
       disableRender: MaybeRefOrGetter<boolean>
@@ -235,7 +235,7 @@ export function useRenderer(
 
   onLoop(() => {
     if (camera.value && !toValue(disableRender))
-      renderer.value.render(scene, camera.value)
+      renderer.value.render(scene.value, camera.value)
   })
 
   resume()

+ 3 - 3
src/composables/useTresContextProvider/index.ts

@@ -29,7 +29,7 @@ export function useTresContextProvider({
   disableRender,
   rendererOptions
 }: {
-  scene: Scene,
+  scene: ShallowRef<Scene>,
   canvas: MaybeRef<HTMLCanvasElement | undefined>
   windowSize: MaybeRefOrGetter<boolean>
   disableRender: MaybeRefOrGetter<boolean>
@@ -53,7 +53,7 @@ export function useTresContextProvider({
     width,
     aspectRatio
   }
-  const localScene = shallowRef<Scene>(scene);
+
   const {
     camera,
     cameras,
@@ -73,7 +73,7 @@ export function useTresContextProvider({
 
   const toProvide: TresContext = {
     sizes,
-    scene: localScene,
+    scene,
     camera,
     cameras: readonly(cameras),
     renderer,