浏览代码

fix: recursive disposal if nodes are not primitives

alvarosabu 1 年之前
父节点
当前提交
7e4c509b8e
共有 1 个文件被更改,包括 64 次插入45 次删除
  1. 64 45
      src/core/nodeOps.ts

+ 64 - 45
src/core/nodeOps.ts

@@ -115,71 +115,90 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
   },
   },
   remove(node) {
   remove(node) {
     if (!node) return
     if (!node) return
-    // remove is only called on the node being removed and not on child nodes.
 
 
-    if (node.isObject3D) {
-      const object3D = node as unknown as Object3D
+    const disposeMaterialsAndGeometries = (object3D: Object3D) => {
+      const tresObject3D = object3D as TresObject3D
 
 
-      const disposeMaterialsAndGeometries = (object3D: Object3D) => {
-        const tresObject3D = object3D as TresObject3D
-
-        if (!object3D.userData.tres__materialViaProp) {
-          tresObject3D.material?.dispose()
-          tresObject3D.material = undefined
-        }
-          
-        if (!object3D.userData.tres__geometryViaProp) {
-          tresObject3D.geometry?.dispose()
-          tresObject3D.geometry = undefined
-        }
+      if (!object3D.userData.tres__materialViaProp) {
+        tresObject3D.material?.dispose()
+        delete tresObject3D.material
       }
       }
+        
+      if (!object3D.userData.tres__geometryViaProp) {
+        tresObject3D.geometry?.dispose()
+        delete tresObject3D.geometry
+      }
+    }
 
 
-      const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
-      const deregisterBlockingObjectAtPointerEventHandler
+    const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
+    const deregisterBlockingObjectAtPointerEventHandler
         = scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler
         = scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler
 
 
-      const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => {
+    const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => {
 
 
-        if (!deregisterBlockingObjectAtPointerEventHandler)
-          throw 'could not find tres__deregisterBlockingObjectAtPointerEventHandler on scene\'s userData'
+      if (!deregisterBlockingObjectAtPointerEventHandler)
+        throw 'could not find tres__deregisterBlockingObjectAtPointerEventHandler on scene\'s userData'
 
 
-        scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(object as Object3D)
+      scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(object as Object3D)
 
 
-        if (!deregisterAtPointerEventHandler)
-          throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'
+      if (!deregisterAtPointerEventHandler)
+        throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'
 
 
-        if (
-          object?.onClick
+      if (
+        object?.onClick
           || object?.onPointerMove
           || object?.onPointerMove
           || object?.onPointerEnter
           || object?.onPointerEnter
           || object?.onPointerLeave
           || object?.onPointerLeave
-        )
-          deregisterAtPointerEventHandler?.(object as Object3D)
-      }
+      )
+        deregisterAtPointerEventHandler?.(object as Object3D)
+    }
 
 
-      const deregisterCameraIfRequired = (object: Object3D) => {
-        const deregisterCamera = scene?.userData.tres__deregisterCamera
+    const deregisterCameraIfRequired = (object: Object3D) => {
+      const deregisterCamera = scene?.userData.tres__deregisterCamera
 
 
-        if (!deregisterCamera)
-          throw 'could not find tres__deregisterCamera on scene\'s userData'
+      if (!deregisterCamera)
+        throw 'could not find tres__deregisterCamera on scene\'s userData'
 
 
-        if ((object as Camera).isCamera)
-          deregisterCamera?.(object as Camera)
-      }
+      if ((object as Camera).isCamera)
+        deregisterCamera?.(object as Camera)
+    }
 
 
+    // Since remove is only called on the node being removed and not on child nodes.
+    // We need to recursively remove objects.
+    // In order for an object to be able to dispose it has to have
+    //   - a dispose method,
+    //   - it cannot be a <primitive object={...} />
+    //   - it cannot be a THREE.Scene, because three has broken it's own api
+    //
+    const isPrimitive = node?.primitive
+    const shouldDispose = !isPrimitive && (node.dispose === undefined ? node.dispose !== null : false)
+
+    // Add code to detach 
+    if (node?.isObject3D && node.parent?.isObject3D) {
+      node.parent.remove(node)
       node.removeFromParent?.()
       node.removeFromParent?.()
-      object3D.traverse((child: Object3D) => {
-        disposeMaterialsAndGeometries(child)
-        deregisterCameraIfRequired(child)
-        deregisterAtPointerEventHandlerIfRequired?.(child as TresObject)
-      })
-
-      disposeMaterialsAndGeometries(object3D)
-      deregisterCameraIfRequired(object3D)
-      deregisterAtPointerEventHandlerIfRequired?.(object3D as TresObject)
     }
     }
 
 
-    node.dispose?.()
+    // Only dispose if it's not a primitive and it has a dispose method,
+    //  if its a primitive, disposal needs to be handled by the user.
+    if (!isPrimitive && node?.isObject3D) {
+      node.children.forEach((child: TresObject) => nodeOps.remove(child))
+      disposeMaterialsAndGeometries(node as Object3D)
+      deregisterCameraIfRequired(node as Object3D)
+      deregisterAtPointerEventHandlerIfRequired?.(node as TresObject)
+      for (const p in node) {
+        if (p !== 'geometry' && p !== 'material') {
+          ;(p as any).dispose?.()
+          delete node[p]
+        }
+      }
+    }
+    
+    if (shouldDispose && node.dispose && node.type !== 'Scene') {
+      node.dispose()
+    }
+
+    // Add invalidation code similar to https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/core/utils.ts#L436
   },
   },
   patchProp(node, prop, _prevValue, nextValue) {
   patchProp(node, prop, _prevValue, nextValue) {
     if (node) {
     if (node) {