浏览代码

fix: move raycaster logic from nodeOps to TresCanvas

alvarosabu 2 年之前
父节点
当前提交
d2200aee41

+ 11 - 6
playground/src/components/TheBasic.vue

@@ -1,8 +1,8 @@
 <script setup lang="ts">
 <script setup lang="ts">
-import { sRGBEncoding, BasicShadowMap, NoToneMapping  } from 'three'
+import { sRGBEncoding, BasicShadowMap, NoToneMapping } from 'three'
 import { reactive, ref } from 'vue'
 import { reactive, ref } from 'vue'
 import { TresCanvas } from '/@/components/TresCanvas'
 import { TresCanvas } from '/@/components/TresCanvas'
-import { OrbitControls, TransformControls  } from '@tresjs/cientos'
+import { OrbitControls, TransformControls } from '@tresjs/cientos'
 import { useRenderLoop } from '/@/'
 import { useRenderLoop } from '/@/'
 
 
 const state = reactive({
 const state = reactive({
@@ -23,16 +23,21 @@ onLoop(({ elapsed }) => {
   if (!sphereRef.value) return
   if (!sphereRef.value) return
   sphereRef.value.position.y += Math.sin(elapsed) * 0.01
   sphereRef.value.position.y += Math.sin(elapsed) * 0.01
 })
 })
+
+function onPointerEnter(ev) {
+  if (ev) {
+    ev.object.material.color.set('#DFFF45')
+  }
+}
 </script>
 </script>
 <template>
 <template>
   <TresCanvas v-bind="state">
   <TresCanvas v-bind="state">
-    <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000"
-    :look-at="[0,0,0]" />
+    <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000" :look-at="[0, 0, 0]" />
     <OrbitControls />
     <OrbitControls />
     <TresAmbientLight :intensity="0.5" />
     <TresAmbientLight :intensity="0.5" />
 
 
-    <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
-      <TresSphereGeometry :args="[2,32,32]"/>
+    <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow @pointer-enter="onPointerEnter">
+      <TresSphereGeometry :args="[2, 32, 32]" />
       <TresMeshToonMaterial color="cyan" />
       <TresMeshToonMaterial color="cyan" />
     </TresMesh>
     </TresMesh>
 
 

+ 1 - 1
playground/src/components/TheEvents.vue

@@ -37,7 +37,6 @@ function onPointerLeave(ev) {
     <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
     <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
 
 
     <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.2" cast-shadow />
     <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.2" cast-shadow />
-    <OrbitControls />
     <template v-for="x in [-2.5, 0, 2.5]">
     <template v-for="x in [-2.5, 0, 2.5]">
       <template v-for="y in [-2.5, 0, 2.5]">
       <template v-for="y in [-2.5, 0, 2.5]">
         <TresMesh
         <TresMesh
@@ -53,6 +52,7 @@ function onPointerLeave(ev) {
         </TresMesh>
         </TresMesh>
       </template>
       </template>
     </template>
     </template>
+    <OrbitControls />
     <TresAmbientLight :intensity="0.5" />
     <TresAmbientLight :intensity="0.5" />
   </TresCanvas>
   </TresCanvas>
 </template>
 </template>

+ 1 - 1
playground/src/pages/index.vue

@@ -1,6 +1,6 @@
 <script setup lang="ts"></script>
 <script setup lang="ts"></script>
 <template>
 <template>
   <Suspense>
   <Suspense>
-    <MultipleCanvas />
+    <TheEvents />
   </Suspense>
   </Suspense>
 </template>
 </template>

+ 27 - 0
src/components/TresScene.ts

@@ -7,6 +7,7 @@ import { useCamera, useRenderer, useRenderLoop, useRaycaster, useTres } from '/@
 import { extend } from '/@/core/catalogue'
 import { extend } from '/@/core/catalogue'
 import { RendererPresetsType } from '/@/composables/useRenderer/const'
 import { RendererPresetsType } from '/@/composables/useRenderer/const'
 import { TresObject } from '../types'
 import { TresObject } from '../types'
+import { useEventListener } from '@vueuse/core'
 
 
 export interface TresSceneProps {
 export interface TresSceneProps {
   shadows?: boolean
   shadows?: boolean
@@ -80,12 +81,38 @@ export const TresScene = defineComponent<TresSceneProps>({
 
 
       const { raycaster, pointer } = useRaycaster()
       const { raycaster, pointer } = useRaycaster()
 
 
+      let prevInstance: TresEvent | null = null
+      let currentInstance: TresEvent | null = null
+
       watchEffect(() => {
       watchEffect(() => {
         if (activeCamera.value) raycaster.value.setFromCamera(pointer.value, activeCamera.value)
         if (activeCamera.value) raycaster.value.setFromCamera(pointer.value, activeCamera.value)
       })
       })
 
 
       onLoop(() => {
       onLoop(() => {
         if (activeCamera.value) renderer.value?.render(scene, activeCamera.value)
         if (activeCamera.value) renderer.value?.render(scene, activeCamera.value)
+
+        if (raycaster.value) {
+          const intersects = raycaster.value.intersectObjects(scene.children)
+
+          if (intersects.length > 0) {
+            currentInstance = intersects[0]
+            if (prevInstance === null) {
+              currentInstance.object.events.onPointerEnter?.(currentInstance)
+            }
+          } else {
+            if (prevInstance !== null) {
+              currentInstance.object.events.onPointerLeave?.(prevInstance)
+              currentInstance = null
+            }
+          }
+
+          prevInstance = currentInstance
+        }
+      })
+
+      useEventListener(window, 'click', () => {
+        if (currentInstance === null) return
+        currentInstance.object.events.onClick?.(currentInstance)
       })
       })
     }
     }
 
 

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

@@ -2,10 +2,6 @@ import { useTres } from '/@/composables'
 import { Raycaster, Vector2 } from 'three'
 import { Raycaster, Vector2 } from 'three'
 import { Ref, ref, ShallowRef, shallowRef } from 'vue'
 import { Ref, ref, ShallowRef, shallowRef } from 'vue'
 
 
-const raycaster = shallowRef(new Raycaster())
-const pointer = ref(new Vector2())
-const currentInstance = ref(null)
-
 /**
 /**
  * Raycaster composable return type
  * Raycaster composable return type
  *
  *
@@ -37,10 +33,14 @@ export interface UseRaycasterReturn {
  * @return {*} {UseRaycasterReturn}
  * @return {*} {UseRaycasterReturn}
  */
  */
 export function useRaycaster(): UseRaycasterReturn {
 export function useRaycaster(): UseRaycasterReturn {
-  const { setState } = useTres()
-  setState('raycaster', raycaster.value)
-  setState('pointer', pointer)
-  setState('currentInstance', currentInstance)
+  const raycaster = shallowRef(new Raycaster())
+  const pointer = ref(new Vector2())
+  const currentInstance = ref(null)
+  const tres = useTres()
+  console.log({ tres })
+  tres.setState('raycaster', raycaster.value)
+  tres.setState('pointer', pointer)
+  tres.setState('currentInstance', currentInstance)
 
 
   function onPointerMove(event: MouseEvent) {
   function onPointerMove(event: MouseEvent) {
     pointer.value.x = (event.clientX / window.innerWidth) * 2 - 1
     pointer.value.x = (event.clientX / window.innerWidth) * 2 - 1

+ 33 - 44
src/core/nodeOps.ts

@@ -1,5 +1,5 @@
 import { BufferAttribute, Mesh } from 'three'
 import { BufferAttribute, Mesh } from 'three'
-import { useCamera, useRaycaster, useRenderLoop, useLogger } from '/@/composables'
+import { useCamera, useRaycaster, useRenderLoop, useLogger, useTres } from '/@/composables'
 import { RendererOptions } from 'vue'
 import { RendererOptions } from 'vue'
 import { catalogue } from './catalogue'
 import { catalogue } from './catalogue'
 import { isFunction, useEventListener } from '@vueuse/core'
 import { isFunction, useEventListener } from '@vueuse/core'
@@ -8,13 +8,34 @@ import { isHTMLTag, kebabToCamel } from '../utils'
 
 
 const { logWarning } = useLogger()
 const { logWarning } = useLogger()
 
 
-function hasEvents(obj: any) {
-  for (const prop in obj) {
-    if (prop.indexOf('on') === 0) {
-      return true
+const onRE = /^on[^a-z]/
+export const isOn = (key: string) => onRE.test(key)
+
+export function patchEvent(
+  el: Element & { _vei?: Record<string, Invoker | undefined> },
+  rawName: string,
+  prevValue: EventValue | null,
+  nextValue: EventValue | null,
+  instance: ComponentInternalInstance | null = null,
+) {
+  // vei = vue event invokers
+  const invokers = el._vei || (el._vei = {})
+  const existingInvoker = invokers[rawName]
+  if (nextValue && existingInvoker) {
+    // patch
+    existingInvoker.value = nextValue
+  } else {
+    const [name, options] = parseName(rawName)
+    if (nextValue) {
+      // add
+      const invoker = (invokers[rawName] = createInvoker(nextValue, instance))
+      addEventListener(el, name, invoker, options)
+    } else if (existingInvoker) {
+      // remove
+      removeEventListener(el, name, existingInvoker, options)
+      invokers[rawName] = undefined
     }
     }
   }
   }
-  return false
 }
 }
 
 
 function noop(fn: string): any {
 function noop(fn: string): any {
@@ -54,6 +75,8 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
       else if (instance.isBufferGeometry) instance.attach = 'geometry'
       else if (instance.isBufferGeometry) instance.attach = 'geometry'
     }
     }
 
 
+    instance.events = {}
+
     return instance
     return instance
   },
   },
   insert(child, parent, anchor) {
   insert(child, parent, anchor) {
@@ -72,43 +95,6 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
         parent[child.attach] = child
         parent[child.attach] = child
       }
       }
     }
     }
-
-    const { onLoop } = useRenderLoop()
-
-    // RayCasting
-    let prevInstance: TresEvent | null = null
-    let currentInstance: TresEvent | null = null
-
-    if (child && child instanceof Mesh && hasEvents(child)) {
-      const { raycaster } = useRaycaster()
-      onLoop(() => {
-        if (parent?.children && child && raycaster) {
-          const intersects = raycaster.value.intersectObjects(parent.children)
-
-          if (intersects.length > 0 && intersects[0].object.uuid === child.uuid) {
-            currentInstance = intersects[0]
-
-            if (prevInstance === null || prevInstance.object.uuid !== currentInstance?.object.uuid) {
-              child.onPointerEnter?.(currentInstance)
-            }
-
-            child.onPointerMove?.(currentInstance)
-          } else {
-            currentInstance = null
-            if (prevInstance !== null) {
-              child.onPointerLeave?.(prevInstance)
-            }
-          }
-
-          prevInstance = currentInstance
-        }
-      })
-
-      useEventListener(window, 'click', () => {
-        if (currentInstance === null) return
-        child.onClick?.(currentInstance)
-      })
-    }
   },
   },
   remove(node) {
   remove(node) {
     if (!node) return
     if (!node) return
@@ -117,7 +103,7 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
       parent.removeChild(node)
       parent.removeChild(node)
     }
     }
   },
   },
-  patchProp(node, prop, _prevValue, nextValue) {
+  patchProp(node, prop, _prevValue, nextValue, _isSVG = false, prevChildren, parentComponent) {
     if (node) {
     if (node) {
       let root = node
       let root = node
       let key = prop
       let key = prop
@@ -144,6 +130,9 @@ export const nodeOps: RendererOptions<TresObject, TresObject> = {
 
 
         if (!target?.set) root = chain.reduce((acc, key) => acc[kebabToCamel(key)], root)
         if (!target?.set) root = chain.reduce((acc, key) => acc[kebabToCamel(key)], root)
       }
       }
+      if (isOn(key)) {
+        node.events[key] = nextValue
+      }
       let value = nextValue
       let value = nextValue
       if (value === '') value = true
       if (value === '') value = true
       // Set prop, prefer atomic methods if applicable
       // Set prop, prefer atomic methods if applicable