Explorar el Código

feat: event handling registration from localState of nodes

alvarosabu hace 1 año
padre
commit
a839320224

+ 1 - 0
playground/src/components/TheExperience.vue

@@ -70,6 +70,7 @@ watchEffect(() => {
       :rotation="[-Math.PI / 2, 0, Math.PI / 2]"
       name="floor"
       receive-shadow
+      @click="wireframe = !wireframe"
     >
       <TresPlaneGeometry :args="[20, 20, 20]" />
       <TresMeshToonMaterial

+ 1 - 1
src/components/TresCanvas.vue

@@ -139,7 +139,7 @@ onMounted(() => {
     emit,
   })
 
-  usePointerEventHandler({ scene: scene.value, contextParts: context.value })
+  usePointerEventHandler(context.value)
 
   const { registerCamera, camera, cameras, deregisterCamera } = context.value
 

+ 10 - 14
src/composables/usePointerEventHandler/index.ts

@@ -1,5 +1,4 @@
 import type { Intersection, Object3D, Object3DEventMap } from 'three'
-import type { TresScene } from 'src/types'
 import { computed, reactive, ref } from 'vue'
 import { uniqueBy } from '../../utils'
 import { useRaycaster } from '../useRaycaster'
@@ -17,11 +16,7 @@ export interface EventProps {
 }
 
 export const usePointerEventHandler = (
-  { scene, contextParts }:
-  {
-    scene: TresScene
-    contextParts: Pick<TresContext, 'renderer' | 'camera' | 'raycaster'>
-  },
+  ctx: TresContext,
 ) => {
   const objectsWithEventListeners = reactive({
     click: new Map<Object3D<Object3DEventMap>, CallbackFn>(),
@@ -54,13 +49,6 @@ export const usePointerEventHandler = (
     if (onPointerLeave) objectsWithEventListeners.pointerLeave.set(object, onPointerLeave)
   }
 
-  // to make the registerObject available in the custom renderer (nodeOps), it is attached to the scene
-  scene.userData.tres__registerAtPointerEventHandler = registerObject
-  scene.userData.tres__deregisterAtPointerEventHandler = deregisterObject
-
-  scene.userData.tres__registerBlockingObjectAtPointerEventHandler = registerBlockingObject
-  scene.userData.tres__deregisterBlockingObjectAtPointerEventHandler = deregisterBlockingObject
-
   const objectsToWatch = computed(() =>
     uniqueBy(
       [
@@ -73,7 +61,13 @@ export const usePointerEventHandler = (
     ),
   )
 
-  const { onClick, onPointerMove } = useRaycaster(objectsToWatch, contextParts)
+  // Temporaly add the methods to the context, this should be handled later by the EventManager state on the context https://github.com/Tresjs/tres/issues/515
+  ctx.registerObjectAtPointerEventHandler = registerObject
+  ctx.deregisterObjectAtPointerEventHandler = deregisterObject
+  ctx.registerBlockingObjectAtPointerEventHandler = registerBlockingObject
+  ctx.deregisterBlockingObjectAtPointerEventHandler = deregisterBlockingObject
+
+  const { onClick, onPointerMove } = useRaycaster(objectsToWatch, ctx)
 
   onClick(({ intersects, event }) => {
     if (intersects.length) objectsWithEventListeners.click.get(intersects[0].object)?.(intersects[0], event)
@@ -101,5 +95,7 @@ export const usePointerEventHandler = (
   return {
     registerObject,
     deregisterObject,
+    registerBlockingObject,
+    deregisterBlockingObject,
   }
 }

+ 5 - 5
src/composables/useRaycaster/index.ts

@@ -20,10 +20,10 @@ interface PointerClickEventPayload {
 
 export const useRaycaster = (
   objects: Ref<THREE.Object3D[]>,
-  { renderer, camera, raycaster }: Pick<TresContext, 'renderer' | 'camera' | 'raycaster'>,
+  ctx: TresContext,
 ) => {
   // having a separate computed makes useElementBounding work
-  const canvas = computed(() => renderer.value.domElement as HTMLCanvasElement)
+  const canvas = computed(() => ctx.renderer.value.domElement as HTMLCanvasElement)
 
   const { x, y } = usePointer({ target: canvas })
 
@@ -39,11 +39,11 @@ export const useRaycaster = (
   }
 
   const getIntersectsByRelativePointerPosition = ({ x, y }: { x: number; y: number }) => {
-    if (!camera.value) return
+    if (!ctx.camera.value) return
 
-    raycaster.value.setFromCamera(new Vector2(x, y), camera.value)
+    ctx.raycaster.value.setFromCamera(new Vector2(x, y), ctx.camera.value)
 
-    return raycaster.value.intersectObjects(objects.value, false)
+    return ctx.raycaster.value.intersectObjects(objects.value, false)
   }
 
   const getIntersects = (event?: PointerEvent | MouseEvent) => {

+ 10 - 1
src/composables/useTresContextProvider/index.ts

@@ -1,6 +1,6 @@
 import { toValue, useElementSize, useFps, useMemory, useRafFn, useWindowSize, refDebounced } from '@vueuse/core'
 import { inject, provide, readonly, shallowRef, computed, ref, onUnmounted, watchEffect } from 'vue'
-import type { Camera, EventDispatcher, WebGLRenderer } from 'three'
+import type { Camera, EventDispatcher, Object3D, WebGLRenderer } from 'three'
 import { Raycaster } from 'three'
 import type { ComputedRef, DeepReadonly, MaybeRef, MaybeRefOrGetter, Ref, ShallowRef } from 'vue'
 import { calculateMemoryUsage } from '../../utils/perf'
@@ -10,6 +10,7 @@ import { useRenderer } from '../useRenderer'
 import { extend } from '../../core/catalogue'
 import { useLogger } from '../useLogger'
 import type { TresScene } from '../../types'
+import type { EventProps } from '../usePointerEventHandler'
 
 export interface InternalState {
   priority: Ref<number>
@@ -62,9 +63,17 @@ export interface TresContext {
      * Advance one frame when renderMode === 'manual'
      */
   advance: () => void
+  // Camera
   registerCamera: (camera: Camera) => void
   setCameraActive: (cameraOrUuid: Camera | string) => void
   deregisterCamera: (camera: Camera) => void
+  // Events
+  // Temporaly add the methods to the context, this should be handled later by the EventManager state on the context https://github.com/Tresjs/tres/issues/515
+  // When thats done maybe we can short the names of the methods since the parent will give the context.
+  registerObjectAtPointerEventHandler: (object: Object3D & EventProps) => void
+  deregisterObjectAtPointerEventHandler: (object: Object3D) => void
+  registerBlockingObjectAtPointerEventHandler: (object: Object3D) => void
+  deregisterBlockingObjectAtPointerEventHandler: (object: Object3D) => void
 }
 
 export function useTresContextProvider({

+ 31 - 35
src/core/nodeOps.ts

@@ -5,7 +5,6 @@ import type { Object3D, Camera } from 'three'
 import type { TresContext } from '../composables'
 import { useLogger } from '../composables'
 import { deepArrayEqual, isHTMLTag, kebabToCamel } from '../utils'
-
 import type { TresObject, TresObject3D, TresScene } from '../types'
 import { catalogue } from './catalogue'
 
@@ -95,28 +94,27 @@ export const nodeOps: RendererOptions<TresObject, TresObject | null> = {
     return instance as TresObject
   },
   insert(child, parent) {
+    if (!child) return
+    
     if (parent && parent.isScene) {
       scene = parent as unknown as TresScene
-      if (child) {
-        child.__tres.root = scene.__tres.root as TresContext
-      }
     }
 
-    const parentObject = parent || scene
+    if (scene) {
+      child.__tres.root = scene.__tres.root as TresContext
+    }
 
+    const parentObject = parent || scene
+    
     if (child?.isObject3D) {
-
+      const { registerCamera, registerObjectAtPointerEventHandler } = child.__tres.root
       if (child?.isCamera) {
-        child.__tres.root.registerCamera?.(child as unknown as Camera)
+        registerCamera(child as unknown as Camera)
       }
-
       if (
         child && supportedPointerEvents.some(eventName => child[eventName])
       ) {
-        if (!scene?.userData.tres__registerAtPointerEventHandler)
-          throw 'could not find tres__registerAtPointerEventHandler on scene\'s userData'
-
-        scene?.userData.tres__registerAtPointerEventHandler?.(child as Object3D)
+        registerObjectAtPointerEventHandler(child as Object3D)
       }
     }
 
@@ -137,6 +135,10 @@ export const nodeOps: RendererOptions<TresObject, TresObject | null> = {
   remove(node) {
     if (!node) return
     // remove is only called on the node being removed and not on child nodes.
+    const { 
+      deregisterObjectAtPointerEventHandler,
+      deregisterBlockingObjectAtPointerEventHandler, 
+    } = node.__tres.root
 
     if (node.isObject3D) {
       const object3D = node as unknown as Object3D
@@ -155,37 +157,23 @@ export const nodeOps: RendererOptions<TresObject, TresObject | null> = {
         }
       }
 
-      const deregisterAtPointerEventHandler = scene?.userData.tres__deregisterAtPointerEventHandler
-      const deregisterBlockingObjectAtPointerEventHandler
-        = scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler
-
       const deregisterAtPointerEventHandlerIfRequired = (object: TresObject) => {
-
-        if (!deregisterBlockingObjectAtPointerEventHandler)
-          throw 'could not find tres__deregisterBlockingObjectAtPointerEventHandler on scene\'s userData'
-
-        scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(object as Object3D)
-
-        if (!deregisterAtPointerEventHandler)
-          throw 'could not find tres__deregisterAtPointerEventHandler on scene\'s userData'
-
+        deregisterBlockingObjectAtPointerEventHandler(object as Object3D)
         if (
           object && supportedPointerEvents.some(eventName => object[eventName])
         )
-          deregisterAtPointerEventHandler?.(object as Object3D)
+          deregisterObjectAtPointerEventHandler?.(object as Object3D)
       }
 
       const deregisterCameraIfRequired = (object: Object3D) => {
         const deregisterCamera = node.__tres.root.deregisterCamera
 
-        if (!deregisterCamera)
-          throw 'could not find tres__deregisterCamera on scene\'s userData'
-
         if ((object as Camera).isCamera)
           deregisterCamera?.(object as Camera)
       }
 
       node.removeFromParent?.()
+
       object3D.traverse((child: Object3D) => {
         disposeMaterialsAndGeometries(child)
         deregisterCameraIfRequired(child)
@@ -204,13 +192,21 @@ export const nodeOps: RendererOptions<TresObject, TresObject | null> = {
     if (node) {
       let root = node
       let key = prop
-      if (node.isObject3D && key === 'blocks-pointer-events') {
-        if (nextValue || nextValue === '')
-          scene?.userData.tres__registerBlockingObjectAtPointerEventHandler?.(node as Object3D)
-        else
-          scene?.userData.tres__deregisterBlockingObjectAtPointerEventHandler?.(node as Object3D)
 
-        return
+      if (node.__tres.root) {
+        const { 
+          registerBlockingObjectAtPointerEventHandler,
+          deregisterBlockingObjectAtPointerEventHandler, 
+        } = node.__tres.root
+  
+        if (node.isObject3D && key === 'blocks-pointer-events') {
+          if (nextValue || nextValue === '')
+            registerBlockingObjectAtPointerEventHandler(node as Object3D)
+          else
+            deregisterBlockingObjectAtPointerEventHandler(node as Object3D)
+  
+          return
+        }
       }
 
       let finalKey = kebabToCamel(key)