浏览代码

wip: stash

alvarosabu 1 年之前
父节点
当前提交
54bd86ebda
共有 3 个文件被更改,包括 92 次插入24 次删除
  1. 29 9
      playground/src/pages/perf/index.vue
  2. 27 12
      src/components/TresCanvas.vue
  3. 36 3
      src/utils/index.ts

+ 29 - 9
playground/src/pages/perf/index.vue

@@ -1,8 +1,9 @@
 <script setup lang="ts">
 import { TresCanvas } from '@tresjs/core'
 import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
-import { TresLeches, Perf, useControls } from '@tresjs/leches'
-import '@tresjs/leches/styles'
+
+/* import { TresLeches, Perf, useControls } from '@tresjs/leches'
+import '@tresjs/leches/styles' */
 import { OrbitControls } from '@tresjs/cientos'
 import { useRouter } from 'vue-router'
 import AkuAku from './AkuAku.vue'
@@ -18,9 +19,9 @@ const gl = {
 
 const router = useRouter()
 
-const { isShown } = useControls({
+/* const { isShown } = useControls({
   isShown: true,
-})
+}) */
 
 const ctx = ref(null)
 
@@ -29,6 +30,8 @@ watchEffect(() => {
   console.log('ctx', ctx.value)
 })
 
+const isShown = ref(true)
+/* 
 useControls({
   button: {
     label: 'Render dispose',
@@ -37,23 +40,40 @@ useControls({
       ctx.value.dispose()
     },
   },
+}) */
 
-})
+// Route change example
+
+/* useControls({
+  button: {
+    label: 'Go other page',
+    type: 'button',
+    onClick() {
+      router.push('/empty')
+    },
+  },
+}) */
 </script>
 
 <template>
-  <TresLeches />
+  <RouterLink to="empty">
+    Go
+  </RouterLink>
+  <button @click="isShown = !isShown">
+    Toggle
+  </button>
+  <!--   <TresLeches /> -->
   <TresCanvas
     v-bind="gl"
     ref="ctx"
   >
-    <Perf />
+    <!--    <Perf /> -->
     <TresPerspectiveCamera :position="[3, 3, 3]" />
     <OrbitControls />
     <Suspense> 
       <AkuAku v-if="isShown" />
     </Suspense>
-    <!--     <TresGroup v-if="isShown">
+    <TresGroup v-if="isShown">
       <TresMesh
         :position="[0, 0, 0]"
       >
@@ -67,7 +87,7 @@ useControls({
         <TresSphereGeometry />
         <TresMeshToonMaterial color="pink" />
       </TresMesh>
-    </TresGroup> -->
+    </TresGroup>
     <TresAmbientLight :intensity="1" />
   </TresCanvas>
 </template>

+ 27 - 12
src/components/TresCanvas.vue

@@ -20,7 +20,7 @@ import {
   defineComponent,
   h, 
   getCurrentInstance,
-} from 'vue'
+  onUnmounted } from 'vue'
 import {
   useTresContextProvider,
   useLogger,
@@ -29,10 +29,11 @@ import {
   type TresContext,
 } from '../composables'
 import { extend } from '../core/catalogue'
-import { render } from '../core/renderer'
+import { render, disposeCustomRenderer } from '../core/renderer'
 
 import type { RendererPresetsType } from '../composables/useRenderer/const'
 import type { TresCamera, TresObject } from '../types/'
+import disposeObject3D, { dispose } from '../utils'
 
 export interface TresCanvasProps
   extends Omit<WebGLRendererParameters, 'canvas'> {
@@ -84,7 +85,7 @@ const slots = defineSlots<{
 }>()
 
 const instance = getCurrentInstance()?.appContext.app
-
+let InternalComponent = null
 const createInternalComponent = (context: TresContext) =>
   defineComponent({
     setup() {
@@ -97,17 +98,26 @@ const createInternalComponent = (context: TresContext) =>
   })
 
 const mountCustomRenderer = (context: TresContext) => {
-  const InternalComponent = createInternalComponent(context)
+  InternalComponent = createInternalComponent(context)
   render(h(InternalComponent), scene.value as unknown as TresObject)
 }
 
-const dispose = (context: TresContext, force = false) => {
+const unmountCustomRenderer = () => {
+  InternalComponent = null
+}
+
+const hardDispose = (context: TresContext) => {
+  dispose(scene.value)
+  scene.value = null as unknown as Scene
+  render(null, scene.value as unknown as TresObject)
+  unmountCustomRenderer()
+  /* context.renderer.value.forceContextLoss()
+  context.renderer.value.dispose()
+  context.renderer.value.renderLists.dispose() */
+}
+
+const softDispose = (context: TresContext) => {
   scene.value.children = []
-  if (force) {
-    context.renderer.value.dispose()
-    context.renderer.value.renderLists.dispose()
-    context.renderer.value.forceContextLoss()
-  }
   mountCustomRenderer(context)
   resume()
 }
@@ -116,7 +126,7 @@ const disableRender = computed(() => props.disableRender)
 
 const context = shallowRef<TresContext | null>(null)
 
-defineExpose({ context, dispose: () => dispose(context.value as TresContext, true) })
+defineExpose({ context, dispose: () => hardDispose(context.value as TresContext) })
 
 onMounted(() => {
   const existingCanvas = canvas as Ref<HTMLCanvasElement>
@@ -177,8 +187,13 @@ onMounted(() => {
     addDefaultCamera()
   }
 
+  // If there is a change detected on HMR, we do a soft disposal.
   if (import.meta.hot && context.value)
-    import.meta.hot.on('vite:afterUpdate', () => dispose(context.value as TresContext))
+    import.meta.hot.on('vite:afterUpdate', () => softDispose(context.value as TresContext))
+})
+
+onUnmounted(() => {
+  if (context.value) hardDispose(context.value)
 })
 </script>
 

+ 36 - 3
src/utils/index.ts

@@ -1,3 +1,6 @@
+import { Object3D } from 'three'
+import { c } from 'vitest/dist/reporters-5f784f42'
+
 export function toSetMethodName(key: string) {
   return `set${key[0].toUpperCase()}${key.slice(1)}`
 }
@@ -130,9 +133,39 @@ export const isArray = Array.isArray as (a: any) => a is any[] | readonly any[]
 
 // Disposes an object and all its properties
 export function dispose<TresObj extends { dispose?: () => void; type?: string; [key: string]: any }>(obj: TresObj) {
-  if (obj.dispose && obj.type !== 'Scene') obj.dispose()
+  if (obj.children) obj.children.forEach((child: any) => dispose(child))
   for (const p in obj) {
-    ;(p as any).dispose?.()
+    ;(obj[p] as any)?.dispose?.()
     delete obj[p]
   }
-}
+
+  if (obj.dispose && obj.type !== 'Scene') obj.dispose()
+}
+
+export function disposeObject3D(object: Object3D): void {
+  // If object has a parent and both are Object3D, remove from parent
+  if (object.parent && object instanceof Object3D && object.parent instanceof Object3D) {
+    object.parent.remove(object)
+  }
+
+  // If object has children, recursively call this function for each child
+  object.children.forEach((child: Object3D) => disposeObject3D(child))
+
+  // Check which properties have a dispose method and dispose them
+  for (const property in object) {
+    if (Object.prototype.hasOwnProperty.call(object, property)) {
+      const value = (object as any)[property]
+      if (value && typeof value.dispose === 'function') {
+        value.dispose()
+        delete (object as any)[property]
+      }
+    }
+  }
+
+  // If the object has a dispose method and is not a Scene, also dispose it
+  if (typeof (object as any).dispose === 'function' && object.type !== 'Scene') {
+    (object as any).dispose()
+  }
+}
+
+export default disposeObject3D