Pārlūkot izejas kodu

fix: localstate for objects with events and manual register/unregister from nodeOps using ctx (#767)

* fix: localstate for objects with events and manual register/unregister from nodeOps using ctx

* chore: remove unused imports

* feat: use shallowRef for objects with events

* chore: remove unused import
Alvaro Saburido 1 gadu atpakaļ
vecāks
revīzija
9a53e60bfb

+ 19 - 19
package.json

@@ -68,24 +68,24 @@
   },
   "dependencies": {
     "@alvarosabu/utils": "^3.2.0",
-    "@vue/devtools-api": "^6.6.2",
-    "@vueuse/core": "^10.10.0"
+    "@vue/devtools-api": "^6.6.3",
+    "@vueuse/core": "^10.11.0"
   },
   "devDependencies": {
     "@release-it/conventional-changelog": "^8.0.1",
-    "@stackblitz/sdk": "^1.10.0",
+    "@stackblitz/sdk": "^1.11.0",
     "@tresjs/cientos": "3.9.0",
     "@tresjs/eslint-config": "^1.1.0",
-    "@types/three": "^0.165.0",
-    "@typescript-eslint/eslint-plugin": "^7.11.0",
-    "@typescript-eslint/parser": "^7.11.0",
+    "@types/three": "^0.166.0",
+    "@typescript-eslint/eslint-plugin": "^7.16.0",
+    "@typescript-eslint/parser": "^7.16.0",
     "@vitejs/plugin-vue": "^5.0.5",
     "@vitest/coverage-c8": "^0.33.0",
-    "@vitest/coverage-v8": "^1.6.0",
-    "@vitest/ui": "^1.6.0",
+    "@vitest/coverage-v8": "^2.0.1",
+    "@vitest/ui": "^2.0.1",
     "@vue/test-utils": "^2.4.6",
-    "eslint": "^9.4.0",
-    "eslint-plugin-vue": "^9.26.0",
+    "eslint": "^9.6.0",
+    "eslint-plugin-vue": "^9.27.0",
     "esno": "^4.7.0",
     "gsap": "^3.12.5",
     "husky": "^9.0.11",
@@ -93,24 +93,24 @@
     "kolorist": "^1.8.0",
     "ohmyfetch": "^0.4.21",
     "pathe": "^1.1.2",
-    "release-it": "^17.3.0",
+    "release-it": "^17.5.0",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-copy": "^3.5.0",
     "rollup-plugin-visualizer": "^5.12.0",
     "sponsorkit": "^0.14.6",
-    "three": "^0.165.0",
-    "unocss": "^0.60.4",
-    "unplugin": "^1.10.1",
-    "unplugin-vue-components": "^0.27.0",
-    "vite": "^5.2.12",
+    "three": "^0.166.1",
+    "unocss": "^0.61.3",
+    "unplugin": "^1.11.0",
+    "unplugin-vue-components": "^0.27.2",
+    "vite": "^5.3.3",
     "vite-plugin-banner": "^0.7.1",
     "vite-plugin-dts": "3.9.1",
     "vite-plugin-inspect": "^0.8.4",
     "vite-plugin-require-transform": "^1.0.21",
     "vite-svg-loader": "^5.1.0",
-    "vitepress": "1.2.2",
-    "vitest": "^1.6.0",
-    "vue": "^3.4.27",
+    "vitepress": "1.3.0",
+    "vitest": "^2.0.1",
+    "vue": "^3.4.31",
     "vue-demi": "^0.14.8"
   }
 }

+ 61 - 0
playground/src/pages/events/DynamicObjects.vue

@@ -0,0 +1,61 @@
+<script setup lang="ts">
+import { reactive } from 'vue'
+import { TresCanvas } from '@tresjs/core'
+import { Box, OrbitControls, Sphere, StatsGl } from '@tresjs/cientos'
+
+const hotspots = reactive([
+  {
+    position: [-2, 0, -2],
+  },
+  {
+    position: [0, 0, -2],
+  },
+  {
+    position: [2, 0, -2],
+  },
+])
+
+const addHotspot = () => {
+  const newHotspot = reactive({
+    position: [-2, 0, 0],
+  })
+  hotspots.push(newHotspot)
+}
+
+const removeHotspot = () => {
+  hotspots.pop()
+}
+
+const grow = (event) => {
+  event.object.scale.set(1.5, 1.5, 1.5)
+}
+
+const shrink = (event) => {
+  event.object.scale.set(1, 1, 1)
+}
+</script>
+
+<template>
+  <TresCanvas>
+    <Suspense>
+      <StatsGl />
+    </Suspense>
+    <OrbitControls />
+    <TresPerspectiveCamera />
+    <TresAmbientLight :args="['white', 0.5]" />
+    <Box :position="[0, 0, 0]" :scale="[1, 1, 1]" @click="addHotspot" @context-menu="removeHotspot">
+      <TresMeshNormalMaterial />
+    </Box>
+    <Sphere
+      v-for="(hotspot, index) in hotspots"
+      :key="index"
+      :args="[0.5, 16, 16]"
+      :position="hotspot.position"
+      @click="console.log('click', index)"
+      @pointer-enter="grow"
+      @pointer-leave="shrink"
+    >
+      <TresMeshNormalMaterial />
+    </Sphere>
+  </TresCanvas>
+</template>

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

@@ -4,7 +4,7 @@ import type { ThreeEvent } from '@tresjs/core'
 import { TresCanvas } from '@tresjs/core'
 import { BasicShadowMap, NoToneMapping, SRGBColorSpace } from 'three'
 import { TresLeches, useControls } from '@tresjs/leches'
-import { OrbitControls } from '@tresjs/cientos'
+import { OrbitControls, StatsGl } from '@tresjs/cientos'
 import '@tresjs/leches/styles'
 
 const gl = {
@@ -64,6 +64,9 @@ function onPointerMissed(ev: ThreeEvent<MouseEvent>) {
     window-size
     v-bind="gl"
   >
+    <Suspense>
+      <StatsGl />
+    </Suspense>
     <TresPerspectiveCamera
       :position="[11, 11, 11]"
       :look-at="[0, 0, 0]"

+ 5 - 0
playground/src/router/routes/events.ts

@@ -9,4 +9,9 @@ export const eventsRoutes = [
     name: 'FSP Drops Reproduction',
     component: () => import('../../pages/events/FpsDropsReproduction.vue'),
   },
+  {
+    path: '/events/dynamic-objects',
+    name: 'Dynamic Objects',
+    component: () => import('../../pages/events/DynamicObjects.vue'),
+  },
 ]

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 258 - 223
pnpm-lock.yaml


+ 0 - 3
src/components/TresCanvas.vue

@@ -29,7 +29,6 @@ import {
   type TresContext,
   useLogger,
   useTresContextProvider,
-  useTresEventManager,
 } from '../composables'
 import { extend } from '../core/catalogue'
 import { nodeOps } from '../core/nodeOps'
@@ -162,8 +161,6 @@ onMounted(() => {
     emit,
   })
 
-  useTresEventManager(scene.value, context.value, emit)
-
   const { registerCamera, camera, cameras, deregisterCamera } = context.value
 
   mountCustomRenderer(context.value)

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

@@ -11,7 +11,7 @@ import { extend } from '../../core/catalogue'
 import { useLogger } from '../useLogger'
 import type { EmitEventFn, TresObject, TresScene } from '../../types'
 import type { EventProps } from '../usePointerEventHandler'
-import type { TresEventManager } from '../useTresEventManager'
+import { type TresEventManager, useTresEventManager } from '../useTresEventManager'
 import useSizes, { type SizesType } from '../useSizes'
 import type { RendererLoop } from '../../core/loop'
 import { createRenderLoop } from '../../core/loop'
@@ -214,9 +214,11 @@ export function useTresContextProvider({
 
   ctx.loop.setReady(false)
   ctx.loop.start()
+
   onTresReady(() => {
     emit('ready', ctx)
     ctx.loop.setReady(true)
+    useTresEventManager(scene, ctx, emit)
   })
 
   onUnmounted(() => {

+ 31 - 6
src/composables/useTresEventManager/index.ts

@@ -1,6 +1,6 @@
-import { computed, shallowRef } from 'vue'
+import { shallowRef } from 'vue'
 import type { Object3D, Object3DEventMap, Scene } from 'three'
-import type { EmitEventFn, EmitEventName, Intersection, TresEvent, TresObject } from 'src/types'
+import type { EmitEventFn, EmitEventName, Intersection, TresEvent, TresInstance, TresObject } from 'src/types'
 import type { TresContext } from '../useTresContextProvider'
 import { useRaycaster } from '../useRaycaster'
 import { hyphenate } from '../../utils'
@@ -16,6 +16,8 @@ export interface TresEventManager {
    * So we need to track them separately
    * Note: These are used in nodeOps
    */
+  registerObject: (object: unknown) => void
+  deregisterObject: (object: unknown) => void
   registerPointerMissedObject: (object: unknown) => void
   deregisterPointerMissedObject: (object: unknown) => void
 }
@@ -31,10 +33,10 @@ export function useTresEventManager(
   if (scene) { _scene.value = scene }
   if (context) { _context.value = context }
 
-  const hasEvents = object => object.__tres?.eventCount > 0
-  const hasChildrenWithEvents = object => object.children?.some(child => hasChildrenWithEvents(child)) || hasEvents(object)
+  const hasEvents = (object: TresInstance) => object.__tres?.eventCount > 0
+  const hasChildrenWithEvents = (object: TresInstance) => object.children?.some((child: TresInstance) => hasChildrenWithEvents(child)) || hasEvents(object)
   // TODO: Optimize to not hit test on the whole scene
-  const objectsWithEvents = computed(() => _scene.value?.children?.filter(hasChildrenWithEvents) || [])
+  const objectsWithEvents = shallowRef((_scene.value?.children as TresInstance[]).filter(hasChildrenWithEvents) || [])
 
   function executeEventListeners(
     listeners: Function | Function[],
@@ -180,6 +182,21 @@ export function useTresEventManager(
     emit('pointer-missed', { event })
   })
 
+  function registerObject(maybeTresObject: unknown) {
+    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+      objectsWithEvents.value.push(maybeTresObject)
+    }
+  }
+
+  function deregisterObject(maybeTresObject: unknown) {
+    if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject)) {
+      const index = objectsWithEvents.value.indexOf(maybeTresObject)
+      if (index > -1) {
+        objectsWithEvents.value.splice(index, 1)
+      }
+    }
+  }
+
   function registerPointerMissedObject(maybeTresObject: unknown) {
     if (is.tresObject(maybeTresObject) && is.object3D(maybeTresObject) && maybeTresObject.onPointerMissed) {
       pointerMissedObjects.push(maybeTresObject)
@@ -198,9 +215,17 @@ export function useTresEventManager(
   // Attach methods to tres context
   context.eventManager = {
     forceUpdate,
+    registerObject,
+    deregisterObject,
     registerPointerMissedObject,
     deregisterPointerMissedObject,
   }
 
-  return { forceUpdate, registerPointerMissedObject, deregisterPointerMissedObject }
+  return {
+    forceUpdate,
+    registerObject,
+    deregisterObject,
+    registerPointerMissedObject,
+    deregisterPointerMissedObject,
+  }
 }

+ 9 - 0
src/core/nodeOps.ts

@@ -112,6 +112,10 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
     child = unboxTresPrimitive(childInstance)
     parent = unboxTresPrimitive(parentInstance)
 
+    if (child.__tres && child.__tres?.eventCount > 0) {
+      context.eventManager?.registerObject(child)
+    }
+
     context.registerCamera(child)
     // NOTE: Track onPointerMissed objects separate from the scene
     context.eventManager?.registerPointerMissedObject(child)
@@ -144,6 +148,11 @@ export const nodeOps: (context: TresContext) => RendererOptions<TresObject, Tres
 
     if (!node) { return }
 
+    // Remove from event manager if necessary
+    if (node?.__tres && node.__tres?.eventCount > 0) {
+      context.eventManager?.deregisterObject(node)
+    }
+
     // NOTE: Derive `dispose` value for this `remove` call and
     // recursive remove calls.
     dispose = is.und(dispose) ? 'default' : dispose

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels