Explorar o código

Merge branch 'main' into bugfix/invalidate-on-use-loader-and-use-texture

Alvaro Saburido hai 1 mes
pai
achega
21fc189634
Modificáronse 59 ficheiros con 2946 adicións e 1113 borrados
  1. 8 0
      CHANGELOG.md
  2. 0 2
      docs/.vitepress/config/de.ts
  3. 0 2
      docs/.vitepress/config/es.ts
  4. 0 2
      docs/.vitepress/config/fr.ts
  5. 0 2
      docs/.vitepress/config/nl.ts
  6. 0 2
      docs/.vitepress/config/zh.ts
  7. 1 1
      docs/cookbook/tweakpane.md
  8. 0 63
      docs/de/directives/v-always-look-at.md
  9. 2 1
      docs/de/directives/v-light-helper.md
  10. 0 65
      docs/directives/v-always-look-at.md
  11. 0 84
      docs/directives/v-rotate.md
  12. 0 61
      docs/es/directives/v-always-look-at.md
  13. 0 61
      docs/fr/directives/v-always-look-at.md
  14. 0 61
      docs/nl/directives/v-always-look-at.md
  15. 3 3
      docs/package.json
  16. 0 61
      docs/zh/directives/v-always-look-at.md
  17. 2 1
      netlify.toml
  18. 26 26
      package.json
  19. 1 0
      playground/vue/components.d.ts
  20. 5 5
      playground/vue/package.json
  21. BIN=BIN
      playground/vue/public/models/Artificer.glb
  22. 11 0
      playground/vue/public/models/cyber_samurai/license.txt
  23. BIN=BIN
      playground/vue/public/models/cyber_samurai/scene.bin
  24. 2121 0
      playground/vue/public/models/cyber_samurai/scene.gltf
  25. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet1_baseColor.png
  26. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet1_emissive.png
  27. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet1_metallicRoughness.png
  28. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet1_normal.png
  29. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet2_baseColor.png
  30. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet2_emissive.png
  31. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet2_metallicRoughness.png
  32. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet2_normal.png
  33. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet3_baseColor.png
  34. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet3_metallicRoughness.png
  35. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/LowSet3_normal.png
  36. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe2_baseColor.jpeg
  37. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe2_metallicRoughness.png
  38. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe2_normal.jpeg
  39. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe_baseColor.png
  40. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe_metallicRoughness.png
  41. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe_normal.png
  42. BIN=BIN
      playground/vue/public/models/cyber_samurai/textures/Robe_transmission.png
  43. 133 41
      playground/vue/src/pages/advanced/MemoryTresObjects.vue
  44. 0 1
      playground/vue/src/pages/basic/Lights.vue
  45. 27 0
      playground/vue/src/pages/events/complex-model/Character.vue
  46. 25 0
      playground/vue/src/pages/events/complex-model/ComplexModel.vue
  47. 53 0
      playground/vue/src/pages/events/complex-model/index.vue
  48. 54 0
      playground/vue/src/pages/events/groups/index.vue
  49. 10 0
      playground/vue/src/router/routes/events.ts
  50. 297 243
      pnpm-lock.yaml
  51. 0 7
      src/components/TresCanvas.vue
  52. 1 0
      src/composables/useTresReady/createReadyEventHook/index.ts
  53. 22 16
      src/devtools/plugin.ts
  54. 3 1
      src/directives/vDistanceTo.ts
  55. 3 1
      src/directives/vLightHelper.ts
  56. 9 12
      src/utils/index.ts
  57. 119 278
      src/utils/is.test.ts
  58. 9 9
      src/utils/is.ts
  59. 1 1
      src/utils/primitive/createRetargetingProxy.ts

+ 8 - 0
CHANGELOG.md

@@ -1,4 +1,12 @@
+# Changelog
 
+## [4.3.3](https://github.com/Tresjs/tres/compare/4.3.2...4.3.3) (2025-02-06)
+
+### Bug Fixes
+
+* remove camera warning log on default camera creation ([#916](https://github.com/Tresjs/tres/issues/916)) ([5d490b4](https://github.com/Tresjs/tres/commit/5d490b4989ed68ff81569398d5b1fe8f09cddea0))
+* safely remove helpers in vDistanceTo and vLightHelper directives ([#919](https://github.com/Tresjs/tres/issues/919)) ([f512b1a](https://github.com/Tresjs/tres/commit/f512b1a129f3740337c813f3a9cd79355356e043))
+* typescript build issues utils ([#924](https://github.com/Tresjs/tres/issues/924)) ([e9b7bf9](https://github.com/Tresjs/tres/commit/e9b7bf97f7561aaa7dba9eae46954278a04ab017))
 
 ## [4.3.2](https://github.com/Tresjs/tres/compare/4.3.1...4.3.2) (2025-01-03)
 

+ 0 - 2
docs/.vitepress/config/de.ts

@@ -76,9 +76,7 @@ export const deConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
         items: [
           { text: 'v-log', link: '/de/directives/v-log' },
           { text: 'v-light-helper', link: '/de/directives/v-light-helper' },
-          { text: 'v-always-look-at', link: '/de/directives/v-always-look-at' },
           { text: 'v-distance-to', link: '/de/directives/v-distance-to' },
-          { text: 'v-rotate', link: '/directives/v-rotate' },
         ],
       },
       {

+ 0 - 2
docs/.vitepress/config/es.ts

@@ -77,9 +77,7 @@ export const esConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
         items: [
           { text: 'v-log', link: '/es/directives/v-log' },
           { text: 'v-light-helper', link: '/es/directives/v-light-helper' },
-          { text: 'v-always-look-at', link: '/es/directives/v-always-look-at' },
           { text: 'v-distance-to', link: '/es/directives/v-distance-to' },
-          { text: 'v-rotate', link: '/directives/v-rotate' },
         ],
       },
       {

+ 0 - 2
docs/.vitepress/config/fr.ts

@@ -77,9 +77,7 @@ export const frConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
         items: [
           { text: 'v-log', link: '/fr/directives/v-log' },
           { text: 'v-light-helper', link: '/fr/directives/v-light-helper' },
-          { text: 'v-always-look-at', link: '/fr/directives/v-always-look-at' },
           { text: 'v-distance-to', link: '/fr/directives/v-distance-to' },
-          { text: 'v-rotate', link: '/directives/v-rotate' },
         ],
       },
       {

+ 0 - 2
docs/.vitepress/config/nl.ts

@@ -76,9 +76,7 @@ export const nlConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
         items: [
           { text: 'v-log', link: '/nl/directives/v-log' },
           { text: 'v-light-helper', link: '/nl/directives/v-light-helper' },
-          { text: 'v-always-look-at', link: '/nl/directives/v-always-look-at' },
           { text: 'v-distance-to', link: '/nl/directives/v-distance-to' },
-          { text: 'v-rotate', link: '/directives/v-rotate' },
         ],
       },
       {

+ 0 - 2
docs/.vitepress/config/zh.ts

@@ -75,9 +75,7 @@ export const zhConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
         items: [
           { text: 'v-log', link: '/zh/directives/v-log' },
           { text: 'v-light-helper', link: '/zh/directives/v-light-helper' },
-          { text: 'v-always-look-at', link: '/zh/directives/v-always-look-at' },
           { text: 'v-distance-to', link: '/zh/directives/v-distance-to' },
-          { text: 'v-rotate', link: '/directives/v-rotate' },
         ],
       },
       {

+ 1 - 1
docs/cookbook/tweakpane.md

@@ -8,7 +8,7 @@ difficulty: 0
 
 # Tweakpane
 
-To make it easier to control the parameters of your scene, you can use [Tweakpane](https://cocopon.github.io/tweakpane/). In this guide, we will show you how to use Tweakpane to control the parameters of your scene.
+To make it easier to control the parameters of your scene, you can use [Tweakpane](https://tweakpane.github.io/docs/). In this guide, we will show you how to use Tweakpane to control the parameters of your scene.
 
 <StackBlitzEmbed project-id="tweakpane" />
 

+ 0 - 63
docs/de/directives/v-always-look-at.md

@@ -1,63 +0,0 @@
-# v-always-look-at 👀
-
-Mit der neuen Direktive `v-always-look-at`, die von **TresJS** bereitgestellt wird, kannst du ein [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) anweisen, immer eine spezifische Position anzuschauen. Diese kann als Vector3 oder Array übergeben werden.
-
-## Benutzung
-
-```vue{3,9}
-<script setup lang="ts">
-import { TresCanvas } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]" />
-      <Box
-        v-always-look-at="new Vector3(0, 0, 0)"
-      />
-  </TresCanvas>
-</template>
-```
-
-Egal, wohin sich die Box bewegt, sie wird immer auf die Position [0,0,0] ausgerichtet sein.
-
-### Warum nicht die eingebaute Methode look-at verwenden?
-
-Eine berechtigte Frage wäre, warum man nicht die `:look-at`-Methode direkt in der Komponente verwenden sollte.
-Die Antwort ist, dass mit der Methode `:look-at` angegeben wird, dass die Position nur einmal beim Mounten der Instanz angeschaut wird. Wenn sich das Objekt ändert, wird dies nicht aktualisiert.
-
-### Du kannst auch andere Instanzen anschauen!
-
-Ein weiterer Vorteil ist, dass du mit der Kamera auch nicht-stationäre Objekte beobachten kannst:
-
-Zum Beispiel:
-
-```vue{4,6,20,23}
-<script setup lang="ts">
-import { shallowRef } from 'vue'
-import { TresCanvas, useRenderLoop } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-
-const sphereRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-// Die Position der Kugel wird verändert, aber die Kamera folgt ihr.
-onLoop(({ elapsed }) => {
-  if (sphereRef.value) {
-    sphereRef.value.value.position.y = Math.sin(elapsed) * 1.5
-  }
-})
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]"
-        v-always-look-at="sphereRef"
-      />
-      <Sphere
-        ref="sphereRef"
-        :scale="0.5"
-      />
-  </TresCanvas>
-</template>
-```

+ 2 - 1
docs/de/directives/v-light-helper.md

@@ -12,7 +12,8 @@ Die folgenden Lichter werden unterstützt:
 
 ```vue{2,8,11,14,17}
 <script setup lang="ts">
-import { OrbitControls, Sphere, vLightHelper } from '@tresjs/cientos'
+import { vLightHelper } from '@tresjs/core'
+import { OrbitControls, Sphere } from '@tresjs/cientos'
 </script>
 <template>
   <TresCanvas >

+ 0 - 65
docs/directives/v-always-look-at.md

@@ -1,65 +0,0 @@
-# v-always-look-at 👀 <Badge type="warning" text="deprecated since v4" />
-
-::: warning
-This directive has been removed on the `v4` due incompatibility with the new renderer loop.
-:::
-
-With the new directive v-always-look-at provided by **TresJS**, you can add easily command an [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) to always look at a specific position, this could be passed as a Vector3 or an Array.
-
-## Usage
-
-```vue{3,9}
-<script setup lang="ts">
-import { TresCanvas, vAlwaysLookAt } from '@tresjs/core'
-import { Box } from '@tresjs/cientos'
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]" />
-      <Box
-        v-always-look-at="new Vector3(0, 0, 0)"
-      />
-  </TresCanvas>
-</template>
-```
-No matter where the `Box` move will always look-at the position [0,0,0]
-
-### Why not use the in built method look-at?
-
-You could ask, this is fine but I can use the `:look-at` method directly in the component, why should I need this?
-
-The answers is that with the method `:look-at` you will indicate to look at that position just once, when the instance is mounted, then if the object changes this will not get updated.
-
-### You can look at other instance too!
-
-Another advantage is that you can look at an instance in movement, for example with the camera, like so:
-
-```vue{4,6,20,23}
-<script setup lang="ts">
-import { shallowRef } from 'vue'
-import { TresCanvas, useRenderLoop, vAlwaysLookAt } from '@tresjs/core'
-import { Box } from '@tresjs/cientos'
-
-const sphereRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-// here we update the position of the sphere and the camera will always follow the object
-onLoop(({ elapsed }) => {
-  if (sphereRef.value) {
-    sphereRef.value.value.position.y = Math.sin(elapsed) * 1.5
-  }
-})
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]"
-        v-always-look-at="sphereRef"
-      />
-      <Sphere
-        ref="sphereRef"
-        :scale="0.5"
-      />
-  </TresCanvas>
-</template>
-```

+ 0 - 84
docs/directives/v-rotate.md

@@ -1,84 +0,0 @@
-# v-rotate  <Badge type="warning" text="deprecated since v4" />
-
-::: warning
-This directive has been removed on the `v4` due incompatibility with the new renderer loop.
-:::
-
-## Problem
-
-When you want to simply add rotation to your mesh, you have to use the template reference, [useRenderLoop](/api/composables#userenderloop) and then assign the axis and the speed, but before check if you mesh is already available:
-
-```vue
-<script setup lang="ts">
-import { useRenderLoop } from '@tresjs/core'
-import { shallowRef, watch } from 'vue'
-
-const boxRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-onLoop(({ elapsed }) => {
-  if (boxRef.value) {
-    boxRef.value.rotation.x = elapsed
-  }
-})
-</script>
-
-<template>
-  <TresCanvas>
-    <TresPerspectiveCamera :position="[0, 2, 5]" />
-    <TresMesh
-      ref="boxRef"
-      :scale="0.5"
-    >
-      <TresBoxGeometry />
-      <TresMesh>
-        <OrbitControls />
-      </TresMesh>
-    </TresMesh>
-  </TresCanvas>
-</template>
-```
-
-And is A LOT of code just for a simple rotation right? Normally we need something fast to see if something is working
-
-## Usage
-
-With the new directive v-rotate provided by **TresJS**, you can do this by just adding `v-rotate` to the instance.
-
-```vue{2,8}
-<script setup lang="ts">
-import { vRotate } from '@tresjs/core'
-</script>
-<template>
-    <TresCanvas >
-    <TresPerspectiveCamera :position="[0, 2, 5]" />
-    <TresMesh
-      v-rotate // 😍
-    >
-      <TresBoxGeometry />
-    </TresMesh>
-  </TresCanvas>
-</template>
-```
-By default `v-rotate` uses [Quaternions](https://threejs.org/docs/index.html?q=quater#api/en/math/Quaternion) so you don't have to worry by [Gimbal Lock](https://en.wikipedia.org/wiki/Gimbal_lock), or check if you mesh is available in the first frames.
-
-## Modifiers
-
-You can control the axis and the rotation speed by adding modifiers
-
-```vue{2,8}
-<script setup lang="ts">
-import { vRotate } from '@tresjs/core'
-</script>
-<template>
-    <TresCanvas >
-    <TresPerspectiveCamera :position="[0, 2, 5]" />
-    <TresMesh
-      v-rotate:x.y="0.1" // the axis will be x and y with a speed of 0.1
-    >
-      <TresBoxGeometry />
-    </TresMesh>
-  </TresCanvas>
-</template>
-```

+ 0 - 61
docs/es/directives/v-always-look-at.md

@@ -1,61 +0,0 @@
-# v-always-look-at 👀
-
-Con la nueva directiva v-always-look-at proporcionada por **TresJS**, puedes agregar fácilmente un comando [Object3D](https://tresjs.org/docs/index.html?q=object#api/en /core/Object3D) para mirar siempre una posición específica, esto podría pasarse como Vector3 o Array.
-
-## Uso
-
-```vue{3,9}
-<script setup lang="ts">
-import { TresCanvas } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]" />
-      <Box
-        v-always-look-at="new Vector3(0, 0, 0)"
-      />
-  </TresCanvas>
-</template>
-```
-No importa dónde se mueva la caja, siempre se observará la posición [0,0,0]
-
-### ¿Por qué no utilizar el método integrado de revisión?
-
-Podrías preguntar, esto está bien, pero puedo usar el método `:look-at` directamente en el componente, ¿por qué debería necesitar esto?
-
-La respuesta es que con el método `:look-at` se te indicará mirar esa posición solo una vez, cuando la instancia esté montada, luego si el objeto cambia esto no se actualizará.
-
-### ¡Puedes observar otra instancia también!
-
-Otra ventaja es que puedes observar una instancia en movimiento, por ejemplo con la cámara, así:
-
-```vue{4,6,20,23}
-<script setup lang="ts">
-import { shallowRef } from 'vue'
-import { TresCanvas, useRenderLoop } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-
-const sphereRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-// here we update the position of the sphere and the camera will always follow the object
-onLoop(({ elapsed }) => {
-  if (sphereRef.value) {
-    sphereRef.value.value.position.y = Math.sin(elapsed) * 1.5
-  }
-})
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]"
-        v-always-look-at="sphereRef"
-      />
-      <Sphere
-        ref="sphereRef"
-        :scale="0.5"
-      />
-  </TresCanvas>
-</template>
-```

+ 0 - 61
docs/fr/directives/v-always-look-at.md

@@ -1,61 +0,0 @@
-# v-always-look-at 👀
-
- Avec la nouvelle directive v-always-look-at fournie par **TresJS**, vous pouvez facilement ajouter une commande à un [Objet 3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) pour toujours regarder une position spécifique, cela pourrait être passé en tant que Vector3 ou Array..
-
-## Usage
-
-```vue{3,9}
-<script setup lang="ts">
-import { TresCanvas } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]" />
-      <Box
-        v-always-look-at="new Vector3(0, 0, 0)"
-      />
-  </TresCanvas>
-</template>
-```
-Peu importe où la boîte se déplace, elle regardera toujours la position [0,0,0]
-
-### Pourquoi ne pas utiliser la méthode intégrée look-at?
-
-Vous pourriez demander, c'est bien mais je peux utiliser la méthode `:look-at` directement dans le composant, pourquoi aurais-je besoin de ça?
-
-La réponse est qu'avec la méthode `:look-at`, vous indiquerez de regarder cette position une seule fois, lorsque l'instance est montée, puis si l'objet change, il ne sera pas mis à jour.
-
-### Vous pouvez également consulter une autre instance!
-
-Un autre avantage est que vous pouvez regarder une instance en mouvement, par exemple avec la caméra, comme ceci:
-
-```vue{4,6,20,23}
-<script setup lang="ts">
-import { shallowRef } from 'vue'
-import { TresCanvas, useRenderLoop } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-
-const sphereRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-// ici nous mettons à jour la position de la sphère et la caméra suivra toujours l'objet
-onLoop(({ elapsed }) => {
-  if (sphereRef.value) {
-    sphereRef.value.value.position.y = Math.sin(elapsed) * 1.5
-  }
-})
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]"
-        v-always-look-at="sphereRef"
-      />
-      <Sphere
-        ref="sphereRef"
-        :scale="0.5"
-      />
-  </TresCanvas>
-</template>
-```

+ 0 - 61
docs/nl/directives/v-always-look-at.md

@@ -1,61 +0,0 @@
-# v-always-look-at 👀
-
-Met de nieuwe directive v-always-look-at aangeboden door **TresJS**, kunt u eenvoudig een opdracht [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) toevoegen om altijd naar een specifieke positie te kijken, dit kan worden doorgegeven als een Vector3 of een Array.
-
-## Gebruik
-
-```vue{3,9}
-<script setup lang="ts">
-import { TresCanvas } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]" />
-      <Box
-        v-always-look-at="new Vector3(0, 0, 0)"
-      />
-  </TresCanvas>
-</template>
-```
-Het maakt niet uit waar de 'Box'-beweging naartoe gaat, er wordt altijd naar de positie [0,0,0] gekeken
-
-### Waarom zou u niet de ingebouwde methode 'look-at' gebruiken?
-
-Je zou kunnen vragen: dit is prima, maar ik kan de `:look-at`-methode rechtstreeks in de component gebruiken, waarom zou ik dit nodig hebben?
-
-Het antwoord is dat je met de methode `:look-at` aangeeft dat je slechts één keer naar die positie wilt kijken, wanneer de instantie is aangekoppeld. Als het object verandert, wordt dit niet bijgewerkt.
-
-### Je kunt ook naar een andere instantie kijken!
-
-Een ander voordeel is dat je bijvoorbeeld met de camera een bewegende instantie als volgt kunt bekijken:
-
-```vue{4,6,20,23}
-<script setup lang="ts">
-import { shallowRef } from 'vue'
-import { TresCanvas, useRenderLoop } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-
-const sphereRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-// hier werken we de positie van de bol bij en zal de camera het object altijd volgen
-onLoop(({ elapsed }) => {
-  if (sphereRef.value) {
-    sphereRef.value.value.position.y = Math.sin(elapsed) * 1.5
-  }
-})
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]"
-        v-always-look-at="sphereRef"
-      />
-      <Sphere
-        ref="sphereRef"
-        :scale="0.5"
-      />
-  </TresCanvas>
-</template>
-```

+ 3 - 3
docs/package.json

@@ -14,9 +14,9 @@
   },
   "devDependencies": {
     "@iconify-json/logos": "^1.2.4",
-    "@iconify-json/mdi": "^1.2.2",
-    "unocss": "^0.65.2",
+    "@iconify-json/mdi": "^1.2.3",
+    "unocss": "^65.4.3",
     "vite-svg-loader": "^5.1.0",
-    "vitepress-plugin-group-icons": "^1.3.2"
+    "vitepress-plugin-group-icons": "^1.3.5"
   }
 }

+ 0 - 61
docs/zh/directives/v-always-look-at.md

@@ -1,61 +0,0 @@
-# v-always-look-at 👀
-
-使用 **TresJS** 提供的新指令 `v-always-look-at`,您可以轻松地使 [Object3D](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D) 始终朝向特定位置,可以传入 Vector3 对象或数组。
-
-## 推荐使用
-
-```vue{3,9}
-<script setup lang="ts">
-import { TresCanvas } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]" />
-      <Box
-        v-always-look-at="new Vector3(0, 0, 0)"
-      />
-  </TresCanvas>
-</template>
-```
-无论 Box 移动到何处,它都将始终朝向位置 [0,0,0]。
-
-### 为什么不使用内置的 look-at 方法呢?
-
-您可能会问,我可以直接在组件中使用 `:look-at` 方法,为什么我需要这个呢?
-
-答案是使用 `:look-at` 方法时,您只会在实例挂载时指示其一次性朝向该位置,然后如果对象更改,它将不会更新。
-
-### 您还可以查看其他实例!
-
-另一个优势是您可以查看一个在移动中的实例,例如使用相机,如下所示:
-
-```vue{4,6,20,23}
-<script setup lang="ts">
-import { shallowRef } from 'vue'
-import { TresCanvas, useRenderLoop } from '@tresjs/core'
-import { Box, vAlwaysLookAt } from '@tresjs/cientos'
-
-const sphereRef = shallowRef()
-
-const { onLoop } = useRenderLoop()
-
-// 在这里,我们更新了球体的位置,相机将始终跟随该对象
-onLoop(({ elapsed }) => {
-  if (sphereRef.value) {
-    sphereRef.value.value.position.y = Math.sin(elapsed) * 1.5
-  }
-})
-</script>
-<template>
-    <TresCanvas >
-      <TresPerspectiveCamera :position="[0, 2, 5]"
-        v-always-look-at="sphereRef"
-      />
-      <Sphere
-        ref="sphereRef"
-        :scale="0.5"
-      />
-  </TresCanvas>
-</template>
-```

+ 2 - 1
netlify.toml

@@ -3,4 +3,5 @@ publish = "docs/.vitepress/dist"
 command = "pnpm run build && pnpm docs:build"
 
 [build.environment]
-NODE_VERSION = "18"
+COREPACK_INTEGRITY_KEYS = "0"
+NODE_VERSION = "20"

+ 26 - 26
package.json

@@ -1,8 +1,8 @@
 {
   "name": "@tresjs/core",
   "type": "module",
-  "version": "4.3.2",
-  "packageManager": "pnpm@9.15.1",
+  "version": "4.3.3",
+  "packageManager": "pnpm@10.6.3",
   "description": "Declarative ThreeJS using Vue Components",
   "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
   "license": "MIT",
@@ -72,46 +72,46 @@
   "dependencies": {
     "@alvarosabu/utils": "^3.2.0",
     "@vue/devtools-api": "^6.6.3",
-    "@vueuse/core": "^12.0.0"
+    "@vueuse/core": "^12.5.0"
   },
   "devDependencies": {
-    "@release-it/conventional-changelog": "^9.0.3",
+    "@release-it/conventional-changelog": "^10.0.0",
     "@stackblitz/sdk": "^1.11.0",
-    "@tresjs/cientos": "4.0.3",
+    "@tresjs/cientos": "4.1.0",
     "@tresjs/eslint-config": "^1.4.0",
-    "@types/three": "^0.171.0",
-    "@typescript-eslint/eslint-plugin": "^8.18.1",
-    "@typescript-eslint/parser": "^8.18.1",
+    "@types/three": "^0.173.0",
+    "@typescript-eslint/eslint-plugin": "^8.23.0",
+    "@typescript-eslint/parser": "^8.23.0",
     "@vitejs/plugin-vue": "^5.2.1",
     "@vitest/coverage-c8": "^0.33.0",
-    "@vitest/coverage-v8": "^2.1.8",
-    "@vitest/ui": "^2.1.8",
+    "@vitest/coverage-v8": "^3.0.5",
+    "@vitest/ui": "^3.0.5",
     "@vue/test-utils": "^2.4.6",
-    "eslint": "^9.17.0",
+    "eslint": "^9.19.0",
     "eslint-plugin-vue": "^9.32.0",
     "esno": "^4.8.0",
-    "gsap": "^3.12.5",
-    "jsdom": "^25.0.1",
+    "gsap": "^3.12.7",
+    "jsdom": "^26.0.0",
     "kolorist": "^1.8.0",
     "ohmyfetch": "^0.4.21",
-    "pathe": "^1.1.2",
-    "release-it": "^17.10.0",
+    "pathe": "^2.0.2",
+    "release-it": "^18.1.2",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-copy": "^3.5.0",
-    "rollup-plugin-visualizer": "^5.12.0",
-    "sponsorkit": "^0.16.2",
-    "three": "^0.171.0",
-    "unocss": "^0.65.2",
-    "unplugin": "^2.1.0",
-    "unplugin-vue-components": "^0.28.0",
-    "vite": "^6.0.5",
+    "rollup-plugin-visualizer": "^5.14.0",
+    "sponsorkit": "^16.3.0",
+    "three": "^0.173.0",
+    "unocss": "^65.4.3",
+    "unplugin": "^2.1.2",
+    "unplugin-vue-components": "^28.0.0",
+    "vite": "^6.1.0",
     "vite-plugin-banner": "^0.8.0",
-    "vite-plugin-dts": "4.4.0",
-    "vite-plugin-inspect": "^0.10.4",
+    "vite-plugin-dts": "4.5.0",
+    "vite-plugin-inspect": "^10.1.0",
     "vite-plugin-require-transform": "^1.0.21",
     "vite-svg-loader": "^5.1.0",
-    "vitepress": "1.5.0",
-    "vitest": "2.1.8",
+    "vitepress": "1.6.3",
+    "vitest": "3.0.5",
     "vue": "3.5.13",
     "vue-demi": "^0.14.10"
   }

+ 1 - 0
playground/vue/components.d.ts

@@ -2,6 +2,7 @@
 // @ts-nocheck
 // Generated by unplugin-vue-components
 // Read more: https://github.com/vuejs/core/pull/3399
+// biome-ignore lint: disable
 export {}
 
 /* prettier-ignore */

+ 5 - 5
playground/vue/package.json

@@ -9,17 +9,17 @@
     "preview": "vite preview"
   },
   "dependencies": {
-    "@tresjs/cientos": "4.0.3",
+    "@tresjs/cientos": "4.1.0",
     "@tresjs/core": "workspace:^",
     "@tresjs/leches": "https://pkg.pr.new/@tresjs/leches@9ad0cd3",
     "vue-router": "^4.5.0"
   },
   "devDependencies": {
     "@tweakpane/plugin-essentials": "^0.2.0",
-    "unplugin-auto-import": "^0.19.0",
+    "unplugin-auto-import": "^19.0.0",
     "vite-plugin-glsl": "^1.3.1",
-    "vite-plugin-qrcode": "^0.2.3",
-    "vite-plugin-vue-devtools": "7.6.8",
-    "vue-tsc": "^2.1.10"
+    "vite-plugin-qrcode": "^0.2.4",
+    "vite-plugin-vue-devtools": "7.7.1",
+    "vue-tsc": "^2.2.0"
   }
 }

BIN=BIN
playground/vue/public/models/Artificer.glb


+ 11 - 0
playground/vue/public/models/cyber_samurai/license.txt

@@ -0,0 +1,11 @@
+Model Information:
+* title:	Cyber Samurai
+* source:	https://sketchfab.com/3d-models/cyber-samurai-26ccafaddb2745ceb56ae5cfc65bfed5
+* author:	KhoaMinh (https://sketchfab.com/duongminhkhoa231)
+
+Model License:
+* license type:	CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
+* requirements:	Author must be credited. Commercial use is allowed.
+
+If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
+This work is based on "Cyber Samurai" (https://sketchfab.com/3d-models/cyber-samurai-26ccafaddb2745ceb56ae5cfc65bfed5) by KhoaMinh (https://sketchfab.com/duongminhkhoa231) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)

BIN=BIN
playground/vue/public/models/cyber_samurai/scene.bin


+ 2121 - 0
playground/vue/public/models/cyber_samurai/scene.gltf

@@ -0,0 +1,2121 @@
+{
+  "accessors": [
+    {
+      "bufferView": 2,
+      "componentType": 5126,
+      "count": 22852,
+      "max": [
+        0.3888629972934723,
+        0.34200799465179443,
+        1.180968999862671
+      ],
+      "min": [
+        -0.41683098673820496,
+        -0.17232699692249298,
+        -0.15904900431632996
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 274224,
+      "componentType": 5126,
+      "count": 22852,
+      "max": [
+        0.9999995231628418,
+        0.9999427795410156,
+        0.9611836075782776
+      ],
+      "min": [
+        -0.9999992847442627,
+        -0.999980092048645,
+        -0.9355192184448242
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "componentType": 5126,
+      "count": 22852,
+      "max": [
+        0.9999946355819702,
+        0.9999123215675354,
+        0.9999966025352478,
+        1.0
+      ],
+      "min": [
+        -0.9999880790710449,
+        -0.9999754428863525,
+        -0.9999997019767761,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "componentType": 5126,
+      "count": 22852,
+      "max": [
+        0.9558839797973633,
+        0.9822540283203125
+      ],
+      "min": [
+        0.0028860000893473625,
+        0.0
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "componentType": 5125,
+      "count": 129852,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 548448,
+      "componentType": 5126,
+      "count": 26319,
+      "max": [
+        0.38885998725891113,
+        0.3572610020637512,
+        1.7076330184936523
+      ],
+      "min": [
+        -0.5131080150604248,
+        -0.21989400684833527,
+        0.933476984500885
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 864276,
+      "componentType": 5126,
+      "count": 26319,
+      "max": [
+        0.9999306201934814,
+        0.9999575614929199,
+        0.9994837641716003
+      ],
+      "min": [
+        -0.9999827146530151,
+        -0.9999853372573853,
+        -0.9996336102485657
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 365632,
+      "componentType": 5126,
+      "count": 26319,
+      "max": [
+        0.9999440908432007,
+        0.9998992085456848,
+        0.9999984502792358,
+        1.0
+      ],
+      "min": [
+        -0.9999955296516418,
+        -0.9996469020843506,
+        -0.9996435642242432,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 182816,
+      "componentType": 5126,
+      "count": 26319,
+      "max": [
+        0.989749014377594,
+        0.9927729964256287
+      ],
+      "min": [
+        0.004393000155687332,
+        0.0014639999717473984
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 519408,
+      "componentType": 5125,
+      "count": 150336,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1180104,
+      "componentType": 5126,
+      "count": 4,
+      "max": [
+        0.6982300281524658,
+        -0.0,
+        0.15845899283885956
+      ],
+      "min": [
+        0.630731999874115,
+        -0.0,
+        0.06281299889087677
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 393368,
+      "componentType": 5126,
+      "count": 4,
+      "max": [
+        0.0,
+        0.0
+      ],
+      "min": [
+        0.0,
+        0.0
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1120752,
+      "componentType": 5125,
+      "count": 4,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1180152,
+      "componentType": 5126,
+      "count": 13092,
+      "max": [
+        0.26241999864578247,
+        0.25091901421546936,
+        1.8171850442886353
+      ],
+      "min": [
+        -0.2654770016670227,
+        -0.13356000185012817,
+        0.9097139835357666
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1337256,
+      "componentType": 5126,
+      "count": 13092,
+      "max": [
+        0.9999979734420776,
+        0.9999581575393677,
+        0.9999839067459106
+      ],
+      "min": [
+        -1.0,
+        -0.9999948740005493,
+        -0.999850869178772
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 786736,
+      "componentType": 5126,
+      "count": 13092,
+      "max": [
+        1.0,
+        0.9996975660324097,
+        0.999649703502655,
+        1.0
+      ],
+      "min": [
+        -1.0,
+        -0.9997356534004211,
+        -0.9996322989463806,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 393400,
+      "componentType": 5126,
+      "count": 13092,
+      "max": [
+        1.9983700513839722,
+        0.9982240200042725
+      ],
+      "min": [
+        0.0009769999887794256,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1120768,
+      "componentType": 5125,
+      "count": 42786,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1494360,
+      "componentType": 5126,
+      "count": 7076,
+      "max": [
+        0.09949500113725662,
+        0.2341039925813675,
+        1.8253309726715088
+      ],
+      "min": [
+        -0.06973399966955185,
+        -0.02545199915766716,
+        1.10629403591156
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1579272,
+      "componentType": 5126,
+      "count": 7076,
+      "max": [
+        0.9961228966712952,
+        0.9999498128890991,
+        0.9998998641967773
+      ],
+      "min": [
+        -0.9814677238464355,
+        -0.9999959468841553,
+        -0.999752938747406
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 996208,
+      "componentType": 5126,
+      "count": 7076,
+      "max": [
+        0.9999997019767761,
+        0.9998977184295654,
+        0.9999046325683594,
+        1.0
+      ],
+      "min": [
+        -0.9999997615814209,
+        -0.9968668222427368,
+        -0.9996189475059509,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 498136,
+      "componentType": 5126,
+      "count": 7076,
+      "max": [
+        0.9921860098838806,
+        0.993399977684021
+      ],
+      "min": [
+        0.18115200102329254,
+        0.10693400353193283
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1291912,
+      "componentType": 5125,
+      "count": 18960,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1664184,
+      "componentType": 5126,
+      "count": 8928,
+      "max": [
+        0.1618030071258545,
+        0.15253399312496185,
+        1.9074920415878296
+      ],
+      "min": [
+        -0.1618030071258545,
+        -0.09909799695014954,
+        1.0424449443817139
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1771320,
+      "componentType": 5126,
+      "count": 8928,
+      "max": [
+        0.9995060563087463,
+        0.9998518228530884,
+        0.9992355108261108
+      ],
+      "min": [
+        -0.9999985694885254,
+        -0.9999579191207886,
+        -0.9999877214431763
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1109424,
+      "componentType": 5126,
+      "count": 8928,
+      "max": [
+        0.9997061491012573,
+        0.9892580509185791,
+        0.9998816251754761,
+        1.0
+      ],
+      "min": [
+        -0.9995588660240173,
+        -0.9990516901016235,
+        -0.9983786940574646,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 554744,
+      "componentType": 5126,
+      "count": 8928,
+      "max": [
+        1.9982550144195557,
+        0.9981909990310669
+      ],
+      "min": [
+        0.010254000313580036,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1367752,
+      "componentType": 5125,
+      "count": 32832,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1878456,
+      "componentType": 5126,
+      "count": 2128,
+      "max": [
+        0.018611999228596687,
+        0.2228350043296814,
+        1.4702080488204956
+      ],
+      "min": [
+        -0.024013999849557877,
+        0.059216998517513275,
+        1.046955943107605
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1903992,
+      "componentType": 5126,
+      "count": 2128,
+      "max": [
+        1.0,
+        0.9999452829360962,
+        0.9939849972724915
+      ],
+      "min": [
+        -1.0,
+        -0.9999489784240723,
+        -0.9931654334068298
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1252272,
+      "componentType": 5126,
+      "count": 2128,
+      "max": [
+        1.0,
+        0.9999667406082153,
+        0.9953843951225281,
+        1.0
+      ],
+      "min": [
+        -1.0,
+        -0.9999905824661255,
+        -0.9955764412879944,
+        1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 626168,
+      "componentType": 5126,
+      "count": 2128,
+      "max": [
+        0.984798014163971,
+        0.9962170124053955
+      ],
+      "min": [
+        0.09521500021219254,
+        0.07910200208425522
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1499080,
+      "componentType": 5125,
+      "count": 3192,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 1929528,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.30268600583076477,
+        -0.007915000431239605,
+        1.4412590265274048
+      ],
+      "min": [
+        -0.5269920229911804,
+        -0.35247498750686646,
+        1.0795739889144897
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2002032,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9997707009315491,
+        0.9990962147712708,
+        0.9982661008834839
+      ],
+      "min": [
+        -0.9995562434196472,
+        -0.9987220764160156,
+        -0.9997043013572693
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1286320,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9967876076698303,
+        0.9997400641441345,
+        0.9977863430976868,
+        1.0
+      ],
+      "min": [
+        -0.9973857998847961,
+        -0.9984351396560669,
+        -0.9997639060020447,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 643192,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9982320070266724,
+        0.998179018497467
+      ],
+      "min": [
+        0.0014649999793618917,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1511848,
+      "componentType": 5125,
+      "count": 17484,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2074536,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.28752100467681885,
+        0.0642239972949028,
+        1.3922029733657837
+      ],
+      "min": [
+        -0.445266991853714,
+        -0.32754701375961304,
+        1.0523790121078491
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2147040,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9996461272239685,
+        0.9975607991218567,
+        0.9970384836196899
+      ],
+      "min": [
+        -0.999562680721283,
+        -0.9978818297386169,
+        -0.9994223117828369
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1382992,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9994586110115051,
+        0.9999904632568359,
+        0.996799647808075,
+        1.0
+      ],
+      "min": [
+        -0.999954879283905,
+        -0.997148871421814,
+        -0.9996189475059509,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 691528,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9982320070266724,
+        0.998179018497467
+      ],
+      "min": [
+        0.0014649999793618917,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1581784,
+      "componentType": 5125,
+      "count": 17484,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2219544,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.2938440144062042,
+        0.04327499866485596,
+        1.414965033531189
+      ],
+      "min": [
+        -0.47231000661849976,
+        -0.33869001269340515,
+        1.0511349439620972
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2292048,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9990829825401306,
+        0.9997231364250183,
+        0.9974861145019531
+      ],
+      "min": [
+        -0.9992952346801758,
+        -0.9983346462249756,
+        -0.9986277222633362
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1479664,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9997884631156921,
+        0.9967545866966248,
+        0.9866943359375,
+        1.0
+      ],
+      "min": [
+        -0.9995779991149902,
+        -0.9990196824073792,
+        -0.9991007447242737,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 739864,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9982320070266724,
+        0.998179018497467
+      ],
+      "min": [
+        0.0014649999793618917,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1651720,
+      "componentType": 5125,
+      "count": 17484,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2364552,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.29919201135635376,
+        0.019221000373363495,
+        1.4373409748077393
+      ],
+      "min": [
+        -0.4995200037956238,
+        -0.3435629904270172,
+        1.0649479627609253
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2437056,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.995328962802887,
+        0.9921239018440247,
+        0.999584972858429
+      ],
+      "min": [
+        -0.9992684125900269,
+        -0.9914990663528442,
+        -0.9976168870925903
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1576336,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9981541633605957,
+        0.9996695518493652,
+        0.9938307404518127,
+        1.0
+      ],
+      "min": [
+        -0.9994898438453674,
+        -0.9974603652954102,
+        -0.9981280565261841,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 788200,
+      "componentType": 5126,
+      "count": 6042,
+      "max": [
+        0.9982320070266724,
+        0.998179018497467
+      ],
+      "min": [
+        0.0014649999793618917,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1721656,
+      "componentType": 5125,
+      "count": 17484,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2509560,
+      "componentType": 5126,
+      "count": 8984,
+      "max": [
+        0.49312299489974976,
+        0.34123799204826355,
+        1.631456971168518
+      ],
+      "min": [
+        -0.5320050120353699,
+        -0.31412801146507263,
+        -0.24451300501823425
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2617368,
+      "componentType": 5126,
+      "count": 8984,
+      "max": [
+        0.9997312426567078,
+        0.9994485378265381,
+        0.9996271729469299
+      ],
+      "min": [
+        -0.9995750784873962,
+        -0.999030351638794,
+        -1.0
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1673008,
+      "componentType": 5126,
+      "count": 8984,
+      "max": [
+        0.9993167519569397,
+        0.9995604157447815,
+        1.0,
+        1.0
+      ],
+      "min": [
+        -0.9998977780342102,
+        -0.9995617866516113,
+        -1.0,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 836536,
+      "componentType": 5126,
+      "count": 8984,
+      "max": [
+        0.9983440041542053,
+        0.9982870221138
+      ],
+      "min": [
+        0.0009769999887794256,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1791592,
+      "componentType": 5125,
+      "count": 25944,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2725176,
+      "componentType": 5126,
+      "count": 7138,
+      "max": [
+        0.35905298590660095,
+        0.33345600962638855,
+        1.4553409814834595
+      ],
+      "min": [
+        -0.5486680269241333,
+        -0.2577190101146698,
+        0.3281809985637665
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2810832,
+      "componentType": 5126,
+      "count": 7138,
+      "max": [
+        0.9999141693115234,
+        0.9964073896408081,
+        0.9999107122421265
+      ],
+      "min": [
+        -0.9997358918190002,
+        -0.9964073896408081,
+        -0.9999078512191772
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1816752,
+      "componentType": 5126,
+      "count": 7138,
+      "max": [
+        0.99983149766922,
+        0.9990725517272949,
+        0.9995837807655334,
+        1.0
+      ],
+      "min": [
+        -0.9990630745887756,
+        -0.9991554617881775,
+        -0.9991077184677124,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 908408,
+      "componentType": 5126,
+      "count": 7138,
+      "max": [
+        0.9983530044555664,
+        0.9982309937477112
+      ],
+      "min": [
+        0.0009769999887794256,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1895368,
+      "componentType": 5125,
+      "count": 21600,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 2896488,
+      "componentType": 5126,
+      "count": 9090,
+      "max": [
+        0.37779000401496887,
+        0.298568993806839,
+        1.3408260345458984
+      ],
+      "min": [
+        -0.5247780084609985,
+        -0.30593299865722656,
+        -0.11074600368738174
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3005568,
+      "componentType": 5126,
+      "count": 9090,
+      "max": [
+        0.99993896484375,
+        0.9989990592002869,
+        0.9993376731872559
+      ],
+      "min": [
+        -0.9992146492004395,
+        -0.998407244682312,
+        -0.9969444870948792
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 1930960,
+      "componentType": 5126,
+      "count": 9090,
+      "max": [
+        0.9985038042068481,
+        0.999447762966156,
+        0.9958119988441467,
+        1.0
+      ],
+      "min": [
+        -0.9968814253807068,
+        -0.9998263120651245,
+        -0.9997501969337463,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 965512,
+      "componentType": 5126,
+      "count": 9090,
+      "max": [
+        0.9984520077705383,
+        0.9982370138168335
+      ],
+      "min": [
+        0.0009769999887794256,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 1981768,
+      "componentType": 5125,
+      "count": 21276,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3114648,
+      "componentType": 5126,
+      "count": 9332,
+      "max": [
+        0.37846899032592773,
+        0.31691300868988037,
+        1.5855679512023926
+      ],
+      "min": [
+        -0.5316749811172485,
+        -0.3012399971485138,
+        -0.23478500545024872
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3226632,
+      "componentType": 5126,
+      "count": 9332,
+      "max": [
+        0.9998252987861633,
+        0.9990951418876648,
+        0.9990085959434509
+      ],
+      "min": [
+        -0.9996034502983093,
+        -0.9982666969299316,
+        -0.9981836676597595
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 2076400,
+      "componentType": 5126,
+      "count": 9332,
+      "max": [
+        0.9999507069587708,
+        0.9999927878379822,
+        0.998931884765625,
+        1.0
+      ],
+      "min": [
+        -0.9997187256813049,
+        -0.9999405145645142,
+        -0.9975491762161255,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 1038232,
+      "componentType": 5126,
+      "count": 9332,
+      "max": [
+        0.9983540177345276,
+        0.9984530210494995
+      ],
+      "min": [
+        0.0009769999887794256,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 2066872,
+      "componentType": 5125,
+      "count": 23220,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3338616,
+      "componentType": 5126,
+      "count": 8091,
+      "max": [
+        0.2066829949617386,
+        0.3269540071487427,
+        2.1839160919189453
+      ],
+      "min": [
+        -0.2960129976272583,
+        -0.08548299968242645,
+        1.6644059419631958
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3435708,
+      "componentType": 5126,
+      "count": 8091,
+      "max": [
+        0.9997463822364807,
+        0.9999589920043945,
+        0.9999997615814209
+      ],
+      "min": [
+        -0.9997219443321228,
+        -0.9999253749847412,
+        -0.9997945427894592
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 2225712,
+      "componentType": 5126,
+      "count": 8091,
+      "max": [
+        0.9999913573265076,
+        0.9998190999031067,
+        0.9999240040779114,
+        1.0
+      ],
+      "min": [
+        -0.999964714050293,
+        -0.9983249306678772,
+        -0.9993649125099182,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 1112888,
+      "componentType": 5126,
+      "count": 8091,
+      "max": [
+        0.9984790086746216,
+        0.9982749819755554
+      ],
+      "min": [
+        0.05224600061774254,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 2159752,
+      "componentType": 5125,
+      "count": 31398,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3532800,
+      "componentType": 5126,
+      "count": 4308,
+      "max": [
+        0.4125030040740967,
+        0.3480769991874695,
+        1.7954050302505493
+      ],
+      "min": [
+        -0.10604099929332733,
+        -0.06983699649572372,
+        1.4445760250091553
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3584496,
+      "componentType": 5126,
+      "count": 4308,
+      "max": [
+        0.9996963143348694,
+        0.996893584728241,
+        0.9998301863670349
+      ],
+      "min": [
+        -0.9998646974563599,
+        -0.9969135522842407,
+        -0.996651291847229
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 2355168,
+      "componentType": 5126,
+      "count": 4308,
+      "max": [
+        0.9989230036735535,
+        0.996043860912323,
+        0.9984588623046875,
+        1.0
+      ],
+      "min": [
+        -0.9997791051864624,
+        -0.9991548657417297,
+        -0.9961976408958435,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 1177616,
+      "componentType": 5126,
+      "count": 4308,
+      "max": [
+        1.9984560012817383,
+        0.9769369959831238
+      ],
+      "min": [
+        0.05224600061774254,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 2285344,
+      "componentType": 5125,
+      "count": 18072,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3636192,
+      "componentType": 5126,
+      "count": 9878,
+      "max": [
+        0.3486259877681732,
+        0.717494010925293,
+        1.4615429639816284
+      ],
+      "min": [
+        0.11367899924516678,
+        -0.42945998907089233,
+        0.5426409840583801
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3754728,
+      "componentType": 5126,
+      "count": 9878,
+      "max": [
+        0.9967538714408875,
+        0.9964601993560791,
+        0.9988740086555481
+      ],
+      "min": [
+        -0.9990900158882141,
+        -0.9978090524673462,
+        -0.998744547367096
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 2424096,
+      "componentType": 5126,
+      "count": 9878,
+      "max": [
+        0.9972242712974548,
+        0.998240053653717,
+        0.9998434782028198,
+        1.0
+      ],
+      "min": [
+        -0.9981921911239624,
+        -0.9988219738006592,
+        -0.9996985197067261,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 1212080,
+      "componentType": 5126,
+      "count": 9878,
+      "max": [
+        1.7312480211257935,
+        0.9940770268440247
+      ],
+      "min": [
+        0.0009769999887794256,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 2357632,
+      "componentType": 5125,
+      "count": 29796,
+      "type": "SCALAR"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3873264,
+      "componentType": 5126,
+      "count": 2812,
+      "max": [
+        0.34592100977897644,
+        0.10950300097465515,
+        1.4425300359725952
+      ],
+      "min": [
+        0.19639000296592712,
+        -0.4161979854106903,
+        1.0648119449615479
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 2,
+      "byteOffset": 3907008,
+      "componentType": 5126,
+      "count": 2812,
+      "max": [
+        0.9999678134918213,
+        0.9962161779403687,
+        0.9893038868904114
+      ],
+      "min": [
+        -0.9999678134918213,
+        -0.9987887740135193,
+        -0.9988492727279663
+      ],
+      "type": "VEC3"
+    },
+    {
+      "bufferView": 3,
+      "byteOffset": 2582144,
+      "componentType": 5126,
+      "count": 2812,
+      "max": [
+        0.9959115982055664,
+        0.9754102826118469,
+        0.9942792057991028,
+        1.0
+      ],
+      "min": [
+        -0.9936113953590393,
+        -0.995703935623169,
+        -0.9983628392219543,
+        -1.0
+      ],
+      "type": "VEC4"
+    },
+    {
+      "bufferView": 1,
+      "byteOffset": 1291104,
+      "componentType": 5126,
+      "count": 2812,
+      "max": [
+        1.7232309579849243,
+        0.8930680155754089
+      ],
+      "min": [
+        0.05224600061774254,
+        0.0009769999887794256
+      ],
+      "type": "VEC2"
+    },
+    {
+      "bufferView": 0,
+      "byteOffset": 2476816,
+      "componentType": 5125,
+      "count": 7632,
+      "type": "SCALAR"
+    }
+  ],
+  "asset": {
+    "extras": {
+      "author": "KhoaMinh (https://sketchfab.com/duongminhkhoa231)",
+      "license": "CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)",
+      "source": "https://sketchfab.com/3d-models/cyber-samurai-26ccafaddb2745ceb56ae5cfc65bfed5",
+      "title": "Cyber Samurai"
+    },
+    "generator": "Sketchfab-12.67.0",
+    "version": "2.0"
+  },
+  "bufferViews": [
+    {
+      "buffer": 0,
+      "byteLength": 2507344,
+      "name": "floatBufferViews",
+      "target": 34963
+    },
+    {
+      "buffer": 0,
+      "byteLength": 1313600,
+      "byteOffset": 2507344,
+      "byteStride": 8,
+      "name": "floatBufferViews",
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteLength": 3940752,
+      "byteOffset": 3820944,
+      "byteStride": 12,
+      "name": "floatBufferViews",
+      "target": 34962
+    },
+    {
+      "buffer": 0,
+      "byteLength": 2627136,
+      "byteOffset": 7761696,
+      "byteStride": 16,
+      "name": "floatBufferViews",
+      "target": 34962
+    }
+  ],
+  "buffers": [
+    {
+      "byteLength": 10388832,
+      "uri": "scene.bin"
+    }
+  ],
+  "extensionsUsed": [
+    "KHR_materials_transmission"
+  ],
+  "images": [
+    {
+      "uri": "textures/LowSet1_baseColor.png"
+    },
+    {
+      "uri": "textures/LowSet1_metallicRoughness.png"
+    },
+    {
+      "uri": "textures/LowSet1_emissive.png"
+    },
+    {
+      "uri": "textures/LowSet1_normal.png"
+    },
+    {
+      "uri": "textures/LowSet2_baseColor.png"
+    },
+    {
+      "uri": "textures/LowSet2_metallicRoughness.png"
+    },
+    {
+      "uri": "textures/LowSet2_emissive.png"
+    },
+    {
+      "uri": "textures/LowSet2_normal.png"
+    },
+    {
+      "uri": "textures/LowSet3_baseColor.png"
+    },
+    {
+      "uri": "textures/LowSet3_metallicRoughness.png"
+    },
+    {
+      "uri": "textures/LowSet3_normal.png"
+    },
+    {
+      "uri": "textures/Robe_baseColor.png"
+    },
+    {
+      "uri": "textures/Robe_metallicRoughness.png"
+    },
+    {
+      "uri": "textures/Robe_normal.png"
+    },
+    {
+      "uri": "textures/Robe_transmission.png"
+    },
+    {
+      "uri": "textures/Robe2_baseColor.jpeg"
+    },
+    {
+      "uri": "textures/Robe2_metallicRoughness.png"
+    },
+    {
+      "uri": "textures/Robe2_normal.jpeg"
+    }
+  ],
+  "materials": [
+    {
+      "doubleSided": true,
+      "emissiveFactor": [
+        1.0,
+        1.0,
+        1.0
+      ],
+      "emissiveTexture": {
+        "index": 2
+      },
+      "name": "LowSet1",
+      "normalTexture": {
+        "index": 3
+      },
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 0
+        },
+        "metallicRoughnessTexture": {
+          "index": 1
+        }
+      }
+    },
+    {
+      "doubleSided": true,
+      "emissiveFactor": [
+        1.0,
+        1.0,
+        1.0
+      ],
+      "emissiveTexture": {
+        "index": 6
+      },
+      "name": "LowSet2",
+      "normalTexture": {
+        "index": 7
+      },
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 4
+        },
+        "metallicRoughnessTexture": {
+          "index": 5
+        }
+      }
+    },
+    {
+      "doubleSided": true,
+      "name": "LowSet3",
+      "normalTexture": {
+        "index": 10
+      },
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 8
+        },
+        "metallicRoughnessTexture": {
+          "index": 9
+        }
+      }
+    },
+    {
+      "alphaMode": "BLEND",
+      "doubleSided": true,
+      "extensions": {
+        "KHR_materials_transmission": {
+          "transmissionFactor": 1.0,
+          "transmissionTexture": {
+            "index": 14
+          }
+        }
+      },
+      "name": "Robe",
+      "normalTexture": {
+        "index": 13
+      },
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 11
+        },
+        "metallicFactor": 0.0,
+        "metallicRoughnessTexture": {
+          "index": 12
+        }
+      }
+    },
+    {
+      "doubleSided": true,
+      "name": "Robe2",
+      "normalTexture": {
+        "index": 17
+      },
+      "pbrMetallicRoughness": {
+        "baseColorTexture": {
+          "index": 15
+        },
+        "metallicFactor": 0.0,
+        "metallicRoughnessTexture": {
+          "index": 16
+        }
+      }
+    }
+  ],
+  "meshes": [
+    {
+      "name": "Object_0",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 1,
+            "POSITION": 0,
+            "TANGENT": 2,
+            "TEXCOORD_0": 3
+          },
+          "indices": 4,
+          "material": 4,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_1",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 6,
+            "POSITION": 5,
+            "TANGENT": 7,
+            "TEXCOORD_0": 8
+          },
+          "indices": 9,
+          "material": 3,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_2",
+      "primitives": [
+        {
+          "attributes": {
+            "POSITION": 10,
+            "TEXCOORD_0": 11
+          },
+          "indices": 12,
+          "material": 2,
+          "mode": 1
+        }
+      ]
+    },
+    {
+      "name": "Object_3",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 14,
+            "POSITION": 13,
+            "TANGENT": 15,
+            "TEXCOORD_0": 16
+          },
+          "indices": 17,
+          "material": 0,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_4",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 19,
+            "POSITION": 18,
+            "TANGENT": 20,
+            "TEXCOORD_0": 21
+          },
+          "indices": 22,
+          "material": 0,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_5",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 24,
+            "POSITION": 23,
+            "TANGENT": 25,
+            "TEXCOORD_0": 26
+          },
+          "indices": 27,
+          "material": 0,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_6",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 29,
+            "POSITION": 28,
+            "TANGENT": 30,
+            "TEXCOORD_0": 31
+          },
+          "indices": 32,
+          "material": 0,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_7",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 34,
+            "POSITION": 33,
+            "TANGENT": 35,
+            "TEXCOORD_0": 36
+          },
+          "indices": 37,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_8",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 39,
+            "POSITION": 38,
+            "TANGENT": 40,
+            "TEXCOORD_0": 41
+          },
+          "indices": 42,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_9",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 44,
+            "POSITION": 43,
+            "TANGENT": 45,
+            "TEXCOORD_0": 46
+          },
+          "indices": 47,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_10",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 49,
+            "POSITION": 48,
+            "TANGENT": 50,
+            "TEXCOORD_0": 51
+          },
+          "indices": 52,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_11",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 54,
+            "POSITION": 53,
+            "TANGENT": 55,
+            "TEXCOORD_0": 56
+          },
+          "indices": 57,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_12",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 59,
+            "POSITION": 58,
+            "TANGENT": 60,
+            "TEXCOORD_0": 61
+          },
+          "indices": 62,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_13",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 64,
+            "POSITION": 63,
+            "TANGENT": 65,
+            "TEXCOORD_0": 66
+          },
+          "indices": 67,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_14",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 69,
+            "POSITION": 68,
+            "TANGENT": 70,
+            "TEXCOORD_0": 71
+          },
+          "indices": 72,
+          "material": 1,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_15",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 74,
+            "POSITION": 73,
+            "TANGENT": 75,
+            "TEXCOORD_0": 76
+          },
+          "indices": 77,
+          "material": 2,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_16",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 79,
+            "POSITION": 78,
+            "TANGENT": 80,
+            "TEXCOORD_0": 81
+          },
+          "indices": 82,
+          "material": 2,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_17",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 84,
+            "POSITION": 83,
+            "TANGENT": 85,
+            "TEXCOORD_0": 86
+          },
+          "indices": 87,
+          "material": 2,
+          "mode": 4
+        }
+      ]
+    },
+    {
+      "name": "Object_18",
+      "primitives": [
+        {
+          "attributes": {
+            "NORMAL": 89,
+            "POSITION": 88,
+            "TANGENT": 90,
+            "TEXCOORD_0": 91
+          },
+          "indices": 92,
+          "material": 2,
+          "mode": 4
+        }
+      ]
+    }
+  ],
+  "nodes": [
+    {
+      "children": [
+        1
+      ],
+      "matrix": [
+        1.0,
+        0.0,
+        0.0,
+        0.0,
+        0.0,
+        2.220446049250313e-16,
+        -1.0,
+        0.0,
+        0.0,
+        1.0,
+        2.220446049250313e-16,
+        0.0,
+        0.0,
+        0.0,
+        0.0,
+        1.0
+      ],
+      "name": "Sketchfab_model"
+    },
+    {
+      "children": [
+        2,
+        3,
+        4,
+        5,
+        6,
+        7,
+        8,
+        9,
+        10,
+        11,
+        12,
+        13,
+        14,
+        15,
+        16,
+        17,
+        18,
+        19,
+        20
+      ],
+      "name": "RoninFinalS.obj.cleaner.materialmerger.gles"
+    },
+    {
+      "mesh": 0,
+      "name": "Object_2"
+    },
+    {
+      "mesh": 1,
+      "name": "Object_3"
+    },
+    {
+      "mesh": 2,
+      "name": "Object_4"
+    },
+    {
+      "mesh": 3,
+      "name": "Object_5"
+    },
+    {
+      "mesh": 4,
+      "name": "Object_6"
+    },
+    {
+      "mesh": 5,
+      "name": "Object_7"
+    },
+    {
+      "mesh": 6,
+      "name": "Object_8"
+    },
+    {
+      "mesh": 7,
+      "name": "Object_9"
+    },
+    {
+      "mesh": 8,
+      "name": "Object_10"
+    },
+    {
+      "mesh": 9,
+      "name": "Object_11"
+    },
+    {
+      "mesh": 10,
+      "name": "Object_12"
+    },
+    {
+      "mesh": 11,
+      "name": "Object_13"
+    },
+    {
+      "mesh": 12,
+      "name": "Object_14"
+    },
+    {
+      "mesh": 13,
+      "name": "Object_15"
+    },
+    {
+      "mesh": 14,
+      "name": "Object_16"
+    },
+    {
+      "mesh": 15,
+      "name": "Object_17"
+    },
+    {
+      "mesh": 16,
+      "name": "Object_18"
+    },
+    {
+      "mesh": 17,
+      "name": "Object_19"
+    },
+    {
+      "mesh": 18,
+      "name": "Object_20"
+    }
+  ],
+  "samplers": [
+    {
+      "magFilter": 9729,
+      "minFilter": 9987,
+      "wrapS": 10497,
+      "wrapT": 10497
+    }
+  ],
+  "scene": 0,
+  "scenes": [
+    {
+      "name": "Sketchfab_Scene",
+      "nodes": [
+        0
+      ]
+    }
+  ],
+  "textures": [
+    {
+      "sampler": 0,
+      "source": 0
+    },
+    {
+      "sampler": 0,
+      "source": 1
+    },
+    {
+      "sampler": 0,
+      "source": 2
+    },
+    {
+      "sampler": 0,
+      "source": 3
+    },
+    {
+      "sampler": 0,
+      "source": 4
+    },
+    {
+      "sampler": 0,
+      "source": 5
+    },
+    {
+      "sampler": 0,
+      "source": 6
+    },
+    {
+      "sampler": 0,
+      "source": 7
+    },
+    {
+      "sampler": 0,
+      "source": 8
+    },
+    {
+      "sampler": 0,
+      "source": 9
+    },
+    {
+      "sampler": 0,
+      "source": 10
+    },
+    {
+      "sampler": 0,
+      "source": 11
+    },
+    {
+      "sampler": 0,
+      "source": 12
+    },
+    {
+      "sampler": 0,
+      "source": 13
+    },
+    {
+      "sampler": 0,
+      "source": 14
+    },
+    {
+      "sampler": 0,
+      "source": 15
+    },
+    {
+      "sampler": 0,
+      "source": 16
+    },
+    {
+      "sampler": 0,
+      "source": 17
+    }
+  ]
+}

BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet1_baseColor.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet1_emissive.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet1_metallicRoughness.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet1_normal.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet2_baseColor.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet2_emissive.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet2_metallicRoughness.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet2_normal.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet3_baseColor.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet3_metallicRoughness.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/LowSet3_normal.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe2_baseColor.jpeg


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe2_metallicRoughness.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe2_normal.jpeg


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe_baseColor.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe_metallicRoughness.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe_normal.png


BIN=BIN
playground/vue/public/models/cyber_samurai/textures/Robe_transmission.png


+ 133 - 41
playground/vue/src/pages/advanced/MemoryTresObjects.vue

@@ -1,72 +1,164 @@
 <script setup lang="ts">
 import { TresCanvas } from '@tresjs/core'
+import type { Group, Material } from 'three'
+import { BoxGeometry, Color, Mesh, MeshBasicMaterial, PerspectiveCamera, Scene, Vector3, WebGLRenderer } from 'three'
+import { onUnmounted, ref } from 'vue'
 
-const toggleMax = 1000
+const toggleMax = 400
 const numObjectsMax = 2000
-const startTimeMS = Date.now()
-
+const startTimeMS = ref(0)
+const width = 900
+const height = 600
 const toggleCount = ref(0)
-const show = ref(false)
-const msg = ref('Test is running.')
+const showTres = ref(false)
+const showVueThree = ref(false)
+const msg = ref('Click Start Test to begin.')
 const r = ref(null)
-const isPaused = ref(true)
 
 let intervalId: ReturnType<typeof setInterval>
-
-const startInterval = () => {
-  intervalId = setInterval(() => {
+const testVueThree = (() => {
+  let renderer: WebGLRenderer | null = null
+  let scene: Scene | null = null
+  let camera: PerspectiveCamera | null = null
+  let frameCount = 0
+  function testVueThree() {
     if (toggleCount.value < toggleMax) {
-      // NOTE: Make sure that objects are mounted by
-      // checking `!!r.value`.
       if (r.value) {
-        show.value = false
-        toggleCount.value++
+        if (renderer) {
+          if (frameCount < 2) {
+            frameCount++
+            renderer.render(scene!, camera!)
+          }
+          else {
+            camera?.removeFromParent()
+            scene!.children.forEach((m) => { ((m as Mesh).material as Material).dispose(); (m as Mesh).geometry.dispose() })
+            renderer.dispose()
+            frameCount = 0
+            camera = null
+            scene = null
+            renderer = null
+            showVueThree.value = false
+            toggleCount.value++
+          }
+        }
+        else {
+          renderer = new WebGLRenderer({ canvas: r.value })
+          renderer.setSize(width, height)
+          renderer.setClearColor(new Color('#EEE'))
+          scene = new Scene()
+          camera = new PerspectiveCamera()
+          camera.position.x = 10
+          camera.position.y = 10
+          camera.lookAt(new Vector3(0, 0, 0))
+          for (let i = 0; i < numObjectsMax; i++) {
+            scene.add(new Mesh(new BoxGeometry(), new MeshBasicMaterial()))
+          }
+          scene.add(camera)
+          renderer.render(scene, camera)
+        }
       }
-      else {
-        show.value = true
+      else if (!showVueThree.value) {
+        showVueThree.value = true
       }
     }
     else {
+      const elapsedSec = (Date.now() - startTimeMS.value) / 1000
+      msg.value = `Plain Vue/THREE test completed in ${elapsedSec} seconds.`
       clearInterval(intervalId)
-      const elapsedSec = (Date.now() - startTimeMS) / 1000
-      msg.value = `Test completed in ${elapsedSec} seconds.`
     }
-  }, 1000 / 120)
-}
-
-const togglePause = () => {
-  isPaused.value = !isPaused.value
-  if (!isPaused.value) {
-    startInterval()
   }
-  else {
-    clearInterval(intervalId)
+  return testVueThree
+})()
+const testTres = (() => {
+  let frameCount = 0
+  return () => {
+    if (toggleCount.value < toggleMax) {
+      if (r.value && frameCount < 2) {
+        // NOTE: Wait until Tres has actually rendered before
+        // removing the canvas.
+        ((r.value as Group).children[0] as Mesh).onAfterRender = () => { frameCount++ }
+      }
+      else {
+        if (frameCount < 1) {
+          showTres.value = true
+        }
+        else {
+          toggleCount.value++
+          showTres.value = false
+          frameCount = 0
+        }
+      }
+    }
+    else {
+      const elapsedSec = (Date.now() - startTimeMS.value) / 1000
+      msg.value = `Tres test completed in ${elapsedSec} seconds.`
+      clearInterval(intervalId)
+    }
   }
+})()
+const isStarted = ref(false)
+const startTestTres = () => {
+  isStarted.value = true
+  startTimeMS.value = Date.now()
+  // NOTE: Using `setInterval`; it typically will keep
+  // running in situations were `requestAnimationFrame` will pause.
+  intervalId = setInterval(testTres, 1000 / 60)
+  msg.value = 'Test is running...'
 }
-
-onUnmounted(() => clearInterval(intervalId))
+const startTestVueThree = () => {
+  isStarted.value = true
+  startTimeMS.value = Date.now()
+  // NOTE: Using `setInterval`; it typically will keep
+  // running in situations were `requestAnimationFrame` will pause.
+  intervalId = setInterval(testVueThree, 1000 / 60)
+  msg.value = 'Test is running...'
+}
+onUnmounted(() => {
+  clearInterval(intervalId)
+})
 </script>
 
 <template>
   <OverlayInfo>
-    <h1>Memory test: Tres Objects</h1>
+    <h1>Memory test: Canvases with objects – Tres vs Plain Vue/THREE</h1>
+    <p><span style="color: red">IMPORTANT</span> Epileptic warning: the tests run on this page cause the screen to flash rapidly.</p>
     <h2>Setup</h2>
-    <p>This page will successively create and remove a TresCanvas containing a number of objects.</p>
+    <p>This test will create and remove {{ toggleMax }} canvas instances with {{ numObjectsMax }} objects/materials/geometries each.</p>
+    <h2>Note</h2>
+    <ul>
+      <li>These tests are intended to help spot memory leaks.</li>
+      <li>Faster/slower test duration does not indicate a problem.</li>
+    </ul>
     <h2>Status</h2>
     <p>{{ msg }}</p>
-    <p>Number of TresCanvases created: {{ toggleCount }} / {{ toggleMax }}</p>
-    <p>Number of Objects per TresCanvas: {{ numObjectsMax }}</p>
-    <button style="padding: 8px 16px; margin-top: 10px;" @click="togglePause">
-      {{ isPaused ? 'Start Test' : 'Pause Test' }}
+    <p>Number of canvases created: {{ toggleCount }} / {{ toggleMax }}</p>
+    <button
+      v-if="!isStarted"
+      style="padding: 8px 16px; margin-top: 10px;"
+      @click="startTestTres"
+    >
+      Start Tres test
+    </button>
+
+    <button
+      v-if="!isStarted"
+      style="padding: 8px 16px; margin-top: 10px;"
+      @click="startTestVueThree"
+    >
+      Start plain Vue/THREE test
     </button>
   </OverlayInfo>
-  <div v-if="show" style="width: 90%; height: 90%; border: 1px solid #F00">
-    <TresCanvas clear-color="black">
-      <TresGroup ref="r" />
-      <TresMesh v-for="_, i of Array.from({ length: numObjectsMax })" :key="i">
-        <TresMeshBasicMaterial />
-        <TresBoxGeometry />
-      </TresMesh>
+  <div v-if="showTres" :style="{ width: `${width}px`, height: `${height}px` }">
+    <TresCanvas clear-color="#EEE">
+      <TresGroup ref="r">
+        <TresMesh v-for="_, i of Array.from({ length: numObjectsMax })" :key="i">
+          <TresMeshBasicMaterial />
+          <TresBoxGeometry />
+        </TresMesh>
+      </TresGroup>
     </TresCanvas>
   </div>
+  <div v-if="showVueThree">
+    <canvas ref="r" clear-color="black"></canvas>
+  </div>
 </template>

+ 0 - 1
playground/vue/src/pages/basic/Lights.vue

@@ -45,7 +45,6 @@ const planeRef: Ref<TresObject | null> = ref(null)
       <TresMeshToonMaterial />
     </TresMesh>
     <TresMesh
-      v-rotate.x="0.005"
       :position="[-2, 2, 0]"
     >
       <TresBoxGeometry :args="[1, 1, 1]" />

+ 27 - 0
playground/vue/src/pages/events/complex-model/Character.vue

@@ -0,0 +1,27 @@
+<script setup lang="ts">
+/* eslint-disable no-console */
+import { useGLTF } from '@tresjs/cientos'
+
+const { nodes } = await useGLTF('/models/Artificer.glb', { draco: true })
+console.log(nodes)
+const model = nodes.Engineer_Rig
+
+model.position.set(-4, 0, 0)
+
+const handleClick = (e: PointerEvent) => {
+  console.log('clicked', e)
+}
+
+const handlePointerEnter = (e: PointerEvent) => {
+  console.log('pointer-enter', e)
+}
+
+const handlePointerLeave = (e: PointerEvent) => {
+  console.log('pointer-leave', e)
+}
+/* eslint-enable no-console */
+</script>
+
+<template>
+  <primitive :object="model" @click="handleClick" @pointer-enter="handlePointerEnter" @pointer-leave="handlePointerLeave" />
+</template>

+ 25 - 0
playground/vue/src/pages/events/complex-model/ComplexModel.vue

@@ -0,0 +1,25 @@
+<script setup lang="ts">
+/* eslint-disable no-console */
+import { useGLTF } from '@tresjs/cientos'
+
+const { nodes } = await useGLTF('/models/cyber_samurai/scene.gltf', { draco: true })
+console.log(nodes)
+const model = nodes.Sketchfab_model
+
+const handleClick = (e: PointerEvent) => {
+  console.log('clicked', e)
+}
+
+const handlePointerEnter = (e: PointerEvent) => {
+  console.log('pointer-enter', e)
+}
+
+const handlePointerLeave = (e: PointerEvent) => {
+  console.log('pointer-leave', e)
+}
+/* eslint-enable no-console */
+</script>
+
+<template>
+  <primitive :object="model" @click="handleClick" @pointer-enter="handlePointerEnter" @pointer-leave="handlePointerLeave" />
+</template>

+ 53 - 0
playground/vue/src/pages/events/complex-model/index.vue

@@ -0,0 +1,53 @@
+<script setup lang="ts">
+/* eslint-disable no-console */
+import { OrbitControls } from '@tresjs/cientos'
+import {
+  TresCanvas,
+} from '@tresjs/core'
+import { BoxGeometry, Mesh, MeshNormalMaterial } from 'three'
+import { TresLeches, useControls } from '@tresjs/leches'
+import '@tresjs/leches/styles'
+import Character from './Character.vue'
+import ComplexModel from './ComplexModel.vue'
+
+const box = new Mesh(new BoxGeometry(1, 1, 1), new MeshNormalMaterial())
+box.position.set(4, 0, 0)
+useControls('fpsgraph')
+
+const handleClick = (e: PointerEvent) => {
+  console.log('clicked', e)
+}
+
+const handlePointerEnter = (e: PointerEvent) => {
+  console.log('pointer-enter', e)
+}
+
+const handlePointerLeave = (e: PointerEvent) => {
+  console.log('pointer-leave', e)
+}
+/* eslint-enable no-console */
+</script>
+
+<template>
+  <TresLeches />
+  <TresCanvas clear-color="#202020">
+    <TresPerspectiveCamera
+      :position="[6, 6, 6]"
+      :look-at="[0, 0, 0]"
+    />
+    <OrbitControls />
+    <Suspense>
+      <Character />
+    </Suspense>
+    <Suspense>
+      <ComplexModel />
+    </Suspense>
+    <primitive
+      :object="box"
+      @click="handleClick"
+      @pointer-enter="handlePointerEnter"
+      @pointer-leave="handlePointerLeave"
+    />
+    <TresAmbientLight :intensity="1" />
+  </TresCanvas>
+</template>

+ 54 - 0
playground/vue/src/pages/events/groups/index.vue

@@ -0,0 +1,54 @@
+<script setup lang="ts">
+/* eslint-disable no-console */
+import { OrbitControls } from '@tresjs/cientos'
+import { TresCanvas } from '@tresjs/core'
+
+const handleClick = (e: PointerEvent) => {
+  console.log('clicked', e)
+}
+
+const handlePointerEnter = (e: PointerEvent) => {
+  console.log('pointer-enter', e)
+}
+
+const handlePointerLeave = (e: PointerEvent) => {
+  console.log('pointer-leave', e)
+}
+/* eslint-enable no-console */
+</script>
+
+<template>
+  <TresCanvas clear-color="#202020" shadows>
+    <!-- Camera setup -->
+    <TresPerspectiveCamera
+      :position="[5, 5, 5]"
+      :look-at="[0, 0, 0]"
+    />
+    <OrbitControls />
+
+    <!-- Lights -->
+    <TresDirectionalLight :position="[5, 5, 5]" :intensity="1" />
+    <TresAmbientLight :intensity="0.5" />
+
+    <!-- Group of geometric shapes -->
+    <TresGroup @click="handleClick" @pointer-enter="handlePointerEnter" @pointer-leave="handlePointerLeave">
+      <!-- Box -->
+      <TresMesh :position="[-2, 0, 0]">
+        <TresBoxGeometry :args="[1, 1, 1]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+
+      <!-- Sphere -->
+      <TresMesh :position="[0, 0, 0]">
+        <TresSphereGeometry :args="[0.7, 32, 32]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+
+      <!-- Cone -->
+      <TresMesh :position="[2, 0, 0]">
+        <TresConeGeometry :args="[0.7, 1.5, 32]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+    </TresGroup>
+  </TresCanvas>
+</template>

+ 10 - 0
playground/vue/src/router/routes/events.ts

@@ -14,4 +14,14 @@ export const eventsRoutes = [
     name: 'Dynamic Objects',
     component: () => import('../../pages/events/DynamicObjects.vue'),
   },
+  {
+    path: '/events/complex-model',
+    name: 'Complex Model',
+    component: () => import('../../pages/events/complex-model/index.vue'),
+  },
+  {
+    path: '/events/groups',
+    name: 'Groups',
+    component: () => import('../../pages/events/groups/index.vue'),
+  },
 ]

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 297 - 243
pnpm-lock.yaml


+ 0 - 7
src/components/TresCanvas.vue

@@ -28,7 +28,6 @@ import {
 import pkg from '../../package.json'
 import {
   type TresContext,
-  useLogger,
   useTresContextProvider,
 } from '../composables'
 import { extend } from '../core/catalogue'
@@ -97,8 +96,6 @@ const slots = defineSlots<{
   default: () => any
 }>()
 
-const { logWarning } = useLogger()
-
 const canvas = ref<HTMLCanvasElement>()
 
 /*
@@ -234,10 +231,6 @@ onMounted(() => {
   )
 
   if (!camera.value) {
-    logWarning(
-      'No camera found. Creating a default perspective camera. '
-      + 'To have full control over a camera, please add one to the scene.',
-    )
     addDefaultCamera()
   }
 

+ 1 - 0
src/composables/useTresReady/createReadyEventHook/index.ts

@@ -81,6 +81,7 @@ export function createReadyEventHook<T>(
     on: onOrCall,
     off: hook.off,
     trigger: hook.trigger,
+    clear: hook.clear,
     cancel,
   }
 }

+ 22 - 16
src/devtools/plugin.ts

@@ -187,23 +187,29 @@ export function registerTresDevtools(app: DevtoolsApp, tres: TresContext) {
           }
 
           if (instance.isScene) {
-            payload.state.info = {
-              objects: instance.children.length,
-              memory: calculateMemoryUsage(instance),
-              calls: tres.renderer.value.info.render.calls,
-              triangles: tres.renderer.value.info.render.triangles,
-              points: tres.renderer.value.info.render.points,
-              lines: tres.renderer.value.info.render.lines,
+            payload.state = {
+              ...payload.state,
+              state: [
+                {
+                  key: 'Scene Info',
+                  value: {
+                    objects: instance.children.length,
+                    memory: calculateMemoryUsage(instance),
+                    calls: tres.renderer.value.info.render.calls,
+                    triangles: tres.renderer.value.info.render.triangles,
+                    points: tres.renderer.value.info.render.points,
+                    lines: tres.renderer.value.info.render.lines,
+                  },
+                },
+                {
+                  key: 'Programs',
+                  value: tres.renderer.value.info.programs?.map(program => ({
+                    ...program,
+                    programName: program.name,
+                  })) || [],
+                },
+              ],
             }
-            payload.state.programs = tres.renderer.value.info.programs?.map(program => ({
-              key: program.name,
-              value: {
-                ...program,
-                vertexShader: program.vertexShader,
-                attributes: program.getAttributes(),
-                uniforms: program.getUniforms(),
-              },
-            })) || []
           }
         }
       })

+ 3 - 1
src/directives/vDistanceTo.ts

@@ -33,6 +33,8 @@ export const vDistanceTo = {
   },
   unmounted: (el: TresObject) => {
     arrowHelper?.dispose()
-    el.parent.remove(arrowHelper)
+    if (el.parent) {
+      el.parent.remove(arrowHelper)
+    }
   },
 }

+ 3 - 1
src/directives/vLightHelper.ts

@@ -56,6 +56,8 @@ export const vLightHelper = {
     if (currentInstance && currentInstance.dispose) {
       currentInstance.dispose()
     }
-    el.parent.remove(currentInstance)
+    if (el.parent) {
+      el.parent.remove(currentInstance)
+    }
   },
 }

+ 9 - 12
src/utils/index.ts

@@ -25,14 +25,14 @@ export const merge = (target: any, source: any) => {
 
 const HTML_TAGS
   = 'html,body,base,head,link,meta,style,title,address,article,aside,footer,'
-  + 'header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,'
-  + 'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,'
-  + 'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,'
-  + 'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,'
-  + 'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,'
-  + 'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,'
-  + 'option,output,progress,select,textarea,details,dialog,menu,'
-  + 'summary,template,blockquote,iframe,tfoot'
+    + 'header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,'
+    + 'figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,'
+    + 'data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,'
+    + 'time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,'
+    + 'canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,'
+    + 'th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,'
+    + 'option,output,progress,select,textarea,details,dialog,menu,'
+    + 'summary,template,blockquote,iframe,tfoot'
 
 export const isHTMLTag = /* #__PURE__ */ makeMap(HTML_TAGS)
 
@@ -294,7 +294,7 @@ export function disposeObject3D(object: TresObject): void {
   children.forEach(child => disposeObject3D(child))
 
   if (object instanceof Scene) {
-    // Optionally handle Scene-specific cleanup
+    // TODO: Handle Scene-specific cleanup
   }
   else {
     const mesh = object as unknown as Partial<Mesh>
@@ -303,16 +303,13 @@ export function disposeObject3D(object: TresObject): void {
     }
     if (mesh.geometry) {
       mesh.geometry.dispose()
-      delete mesh.geometry
     }
 
     if (Array.isArray(mesh.material)) {
       mesh.material.forEach(material => disposeMaterial(material))
-      delete mesh.material
     }
     else if (mesh.material) {
       disposeMaterial(mesh.material)
-      delete mesh.material
     }
   }
 }

+ 119 - 278
src/utils/is.test.ts

@@ -1,285 +1,126 @@
-import { BufferGeometry, Fog, MeshBasicMaterial, MeshNormalMaterial, Object3D, PerspectiveCamera } from 'three'
+import type { Camera, Light, Material, Object3D } from 'three'
+import { AmbientLight, BufferGeometry, DirectionalLight, Fog, Group, Mesh, MeshBasicMaterial, MeshNormalMaterial, OrthographicCamera, PerspectiveCamera, PointLight, Scene } from 'three'
 import * as is from './is'
 
-describe('is', () => {
-  describe('is.num(a: any)', () => {
-    describe('true', () => {
-      it('number', () => {
-        assert(is.num(0))
-        assert(is.num(-1))
-        assert(is.num(Math.PI))
-        assert(is.num(Number.POSITIVE_INFINITY))
-        assert(is.num(Number.NEGATIVE_INFINITY))
-        assert(is.num(42))
-        assert(is.num(0b1111))
-        assert(is.num(0o17))
-        assert(is.num(0xF))
-      })
-    })
-    describe('false', () => {
-      it('null', () => {
-        assert(!is.num(null))
-      })
-      it('undefined', () => {
-        assert(!is.num(undefined))
-      })
-      it('string', () => {
-        assert(!is.num(''))
-        assert(!is.num('1'))
-      })
-      it('function', () => {
-        assert(!is.num(() => {}))
-        assert(!is.num(() => 1))
-      })
-      it('array', () => {
-        assert(!is.num([]))
-        assert(!is.num([1]))
-      })
-    })
-  })
-  describe('is.und(a: any)', () => {
-    describe('true', () => {
-      it('undefined', () => {
-        assert(is.und(undefined))
-      })
-    })
-    describe('false', () => {
-      it('null', () => {
-        assert(!is.und(null))
-      })
-      it('number', () => {
-        assert(!is.und(0))
-        assert(!is.und(-1))
-        assert(!is.und(Math.PI))
-        assert(!is.und(Number.POSITIVE_INFINITY))
-        assert(!is.und(Number.NEGATIVE_INFINITY))
-        assert(!is.und(42))
-      })
-      it('string', () => {
-        assert(!is.und(''))
-        assert(!is.und('tresObject'))
-      })
-      it('function', () => {
-        assert(!is.und(() => {}))
-      })
-      it('array', () => {
-        assert(!is.und([]))
-      })
-    })
-  })
-  describe('is.tresObject(a: any)', () => {
-    describe('true', () => {
-      it('object3D', () => {
-        assert(is.tresObject(new Object3D()))
-      })
-      it('bufferGeometry', () => {
-        assert(is.tresObject(new BufferGeometry()))
-      })
-      it('material', () => {
-        assert(is.tresObject(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(is.tresObject(new Fog('red')))
-      })
-      it('camera', () => {
-        assert(is.tresObject(new PerspectiveCamera()))
-      })
-    })
-    describe('false', () => {
-      it('undefined', () => {
-        assert(!is.tresObject(undefined))
-      })
-      it('null', () => {
-        assert(!is.tresObject(null))
-      })
-      it('number', () => {
-        assert(!is.tresObject(0))
-        assert(!is.tresObject(Math.PI))
-        assert(!is.tresObject(Number.POSITIVE_INFINITY))
-        assert(!is.tresObject(Number.NEGATIVE_INFINITY))
-        assert(!is.tresObject(42))
-      })
-      it('string', () => {
-        assert(!is.tresObject(''))
-        assert(!is.tresObject('tresObject'))
-      })
-      it('function', () => {
-        assert(!is.tresObject(() => {}))
-        assert(!is.tresObject(() => {}))
-      })
-    })
-  })
+const NUMBERS: Record<string, number> = {
+  '0': 0,
+  '1': 1,
+  '-1': -1,
+  '42': 42,
+  'Math.PI': Math.PI,
+  '-inf': Number.NEGATIVE_INFINITY,
+  '+inf': Number.POSITIVE_INFINITY,
+  '0b1111': 0b1111,
+  '0o17': 0o17,
+  '0xF': 0xF,
+}
 
-  describe('is.bufferGeometry(a: any)', () => {
-    describe('true', () => {
-      it('bufferGeometry', () => {
-        assert(is.bufferGeometry(new BufferGeometry()))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.bufferGeometry(new Object3D()))
-      })
-      it('material', () => {
-        assert(!is.bufferGeometry(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(!is.bufferGeometry(new Fog('red')))
-      })
-      it('camera', () => {
-        assert(!is.bufferGeometry(new PerspectiveCamera()))
-      })
-      it('undefined', () => {
-        assert(!is.bufferGeometry(undefined))
-      })
-      it('null', () => {
-        assert(!is.bufferGeometry(null))
-      })
-      it('number', () => {
-        assert(!is.bufferGeometry(0))
-        assert(!is.bufferGeometry(Math.PI))
-        assert(!is.bufferGeometry(Number.POSITIVE_INFINITY))
-        assert(!is.bufferGeometry(Number.NEGATIVE_INFINITY))
-        assert(!is.bufferGeometry(42))
-      })
-      it('string', () => {
-        assert(!is.bufferGeometry(''))
-        assert(!is.bufferGeometry('bufferGeometry'))
-      })
-      it('function', () => {
-        assert(!is.bufferGeometry(() => {}))
-        assert(!is.bufferGeometry(() => {}))
-      })
-    })
-  })
+const BOOLEANS: Record<string, boolean> = {
+  true: true,
+  false: false,
+}
 
-  describe('is.material(a: any)', () => {
-    describe('true', () => {
-      it('material', () => {
-        assert(is.material(new MeshNormalMaterial()))
-        assert(is.material(new MeshBasicMaterial()))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.bufferGeometry(new Object3D()))
-      })
-      it('bufferGeometry', () => {
-        assert(!is.bufferGeometry(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(!is.bufferGeometry(new Fog('red')))
-      })
-      it('camera', () => {
-        assert(!is.bufferGeometry(new PerspectiveCamera()))
-      })
-      it('undefined', () => {
-        assert(!is.bufferGeometry(undefined))
-      })
-      it('null', () => {
-        assert(!is.bufferGeometry(null))
-      })
-      it('number', () => {
-        assert(!is.bufferGeometry(0))
-        assert(!is.bufferGeometry(Math.PI))
-        assert(!is.bufferGeometry(Number.POSITIVE_INFINITY))
-        assert(!is.bufferGeometry(Number.NEGATIVE_INFINITY))
-        assert(!is.bufferGeometry(42))
-      })
-      it('string', () => {
-        assert(!is.bufferGeometry(''))
-        assert(!is.bufferGeometry('bufferGeometry'))
-      })
-      it('function', () => {
-        assert(!is.bufferGeometry(() => {}))
-        assert(!is.bufferGeometry(() => {}))
-      })
-    })
-  })
+const STRINGS: Record<string, string> = {
+  a: 'a',
+  aa: 'aa',
+  stringEmpty: '',
+  stringNumber: '0',
+}
 
-  describe('is.camera(a: any)', () => {
-    describe('true', () => {
-      it('camera', () => {
-        assert(is.camera(new PerspectiveCamera()))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.camera(new Object3D()))
-      })
-      it('bufferGeometry', () => {
-        assert(!is.camera(new BufferGeometry()))
-      })
-      it('material', () => {
-        assert(!is.camera(new MeshNormalMaterial()))
-      })
-      it('fog', () => {
-        assert(!is.camera(new Fog('red')))
-      })
-      it('undefined', () => {
-        assert(!is.camera(undefined))
-      })
-      it('null', () => {
-        assert(!is.camera(null))
-      })
-      it('number', () => {
-        assert(!is.camera(0))
-        assert(!is.camera(Math.PI))
-        assert(!is.camera(Number.POSITIVE_INFINITY))
-        assert(!is.camera(Number.NEGATIVE_INFINITY))
-        assert(!is.camera(42))
-      })
-      it('string', () => {
-        assert(!is.camera(''))
-        assert(!is.camera('camera'))
-      })
-      it('function', () => {
-        assert(!is.camera(() => {}))
-        assert(!is.camera(() => {}))
-      })
-    })
-  })
+const NULL: Record<string, null> = {
+  null: null,
+}
 
-  describe('is.fog(a: any)', () => {
-    describe('true', () => {
-      it('fog', () => {
-        assert(is.fog(new Fog('red')))
-      })
-    })
-    describe('false', () => {
-      it('object3D', () => {
-        assert(!is.fog(new Object3D()))
-      })
-      it('camera', () => {
-        assert(!is.fog(new PerspectiveCamera()))
-      })
-      it('bufferGeometry', () => {
-        assert(!is.fog(new BufferGeometry()))
-      })
-      it('material', () => {
-        assert(!is.fog(new MeshNormalMaterial()))
-      })
-      it('undefined', () => {
-        assert(!is.fog(undefined))
-      })
-      it('null', () => {
-        assert(!is.fog(null))
-      })
-      it('number', () => {
-        assert(!is.fog(0))
-        assert(!is.fog(Math.PI))
-        assert(!is.fog(Number.POSITIVE_INFINITY))
-        assert(!is.fog(Number.NEGATIVE_INFINITY))
-        assert(!is.fog(42))
-      })
-      it('string', () => {
-        assert(!is.fog(''))
-        assert(!is.fog('camera'))
-      })
-      it('function', () => {
-        assert(!is.fog(() => {}))
-        assert(!is.fog(() => {}))
-      })
-    })
-  })
+const UNDEFINED: Record<string, undefined> = {
+  undefined,
+}
+
+const ARRAYS: Record<string, any[]> = {
+  arrayEmpty: [],
+  arrayZero: [0],
+  arrayString: ['a'],
+  arrayMixed: ['', 0, undefined],
+}
+
+const FUNCTIONS: Record<string, (...a: any) => any> = {
+  functionVoid: () => {},
+  functionUndefined: () => undefined,
+  functionNull: () => null,
+  functionString: () => 'a',
+  functionZero: () => 0,
+}
+
+const FOGS: Record<string, Fog> = {
+  fogRed: new Fog('red'),
+  fogBlue: new Fog('blue'),
+}
+
+const MATERIALS: Record<string, Material> = {
+  meshNormalMaterial: new MeshNormalMaterial(),
+  meshBasicMaterial: new MeshBasicMaterial(),
+}
+
+const CAMERAS: Record<string, Camera> = {
+  perspectiveCamera: new PerspectiveCamera(),
+  orthographicCamera: new OrthographicCamera(),
+}
+
+const SCENES: Record<string, Scene> = {
+  scene: new Scene(),
+}
+
+const LIGHTS: Record<string, Light> = {
+  pointLight: new PointLight(),
+  directionalLight: new DirectionalLight(),
+  ambientLight: new AmbientLight(),
+}
+
+const OBJECT3DS: Record<string, Object3D> = {
+  mesh: new Mesh(),
+  group: new Group(),
+  ...SCENES,
+  ...LIGHTS,
+  ...CAMERAS,
+}
+
+const BUFFER_GEOMETRIES: Record<string, BufferGeometry> = {
+  bufferGeometry: new BufferGeometry(),
+}
+
+const OBJECTS = Object.assign({}, { '{}': {}, '{ a: "a" }': { a: 'a' } }, FOGS, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES)
+
+// NOTE: See definition in is.ts
+const TRES_OBJECTS = Object.assign({}, MATERIALS, OBJECT3DS, BUFFER_GEOMETRIES, FOGS)
+
+const ALL = Object.assign({}, NUMBERS, BOOLEANS, STRINGS, NULL, UNDEFINED, ARRAYS, FUNCTIONS, OBJECTS)
+
+describe('is', () => {
+  describe('is.und(a: any)', () => { test(is.und, UNDEFINED) })
+  describe('is.arr(a: any)', () => { test(is.arr, ARRAYS) })
+  describe('is.num(a: any)', () => { test(is.num, NUMBERS) })
+  describe('is.str(a: any)', () => { test(is.str, STRINGS) })
+  describe('is.bool(a: any)', () => { test(is.bool, BOOLEANS) })
+  describe('is.fun(a: any)', () => { test(is.fun, FUNCTIONS) })
+  describe('is.obj(a: any)', () => { test(is.obj, OBJECTS) })
+  describe('is.object3D(a: any)', () => { test(is.object3D, OBJECT3DS) })
+  describe('is.camera(a: any)', () => { test(is.camera, CAMERAS) })
+  describe('is.bufferGeometry(a: any)', () => { test(is.bufferGeometry, BUFFER_GEOMETRIES) })
+  describe('is.material(a: any)', () => { test(is.material, MATERIALS) })
+  describe('is.light(a: any)', () => { test(is.light, LIGHTS) })
+  describe('is.fog(a: any)', () => { test(is.fog, FOGS) })
+  describe('is.scene(a: any)', () => { test(is.scene, SCENES) })
+  describe('is.tresObject(a: any)', () => { test(is.tresObject, TRES_OBJECTS) })
 })
+
+/**
+ * Test all values in `ALL`.
+ * `fn` should return `true` for all values in `truth` else `false`.
+ */
+function test(fn: (a: any) => boolean, truth: Record<string, any>) {
+  const trueKeys = new Set(Object.keys(truth))
+  for (const [key, value] of Object.entries(ALL)) {
+    it(`returns ${trueKeys.has(key)} for ${key}`, () => {
+      expect(fn(value)).toBe(trueKeys.has(key))
+    })
+  }
+}

+ 9 - 9
src/utils/is.ts

@@ -1,11 +1,11 @@
 import type { TresObject, TresPrimitive } from 'src/types'
 import type { BufferGeometry, Camera, Fog, Light, Material, Object3D, Scene } from 'three'
 
-export function und(u: unknown) {
+export function und(u: unknown): u is undefined {
   return typeof u === 'undefined'
 }
 
-export function arr(u: unknown) {
+export function arr(u: unknown): u is Array<unknown> {
   return Array.isArray(u)
 }
 
@@ -30,31 +30,31 @@ export function obj(u: unknown): u is Record<string | number | symbol, unknown>
 }
 
 export function object3D(u: unknown): u is Object3D {
-  return obj(u) && ('isObject3D' in u) && !!(u.isObject3D)
+  return obj(u) && !!(u.isObject3D)
 }
 
 export function camera(u: unknown): u is Camera {
-  return obj(u) && 'isCamera' in u && !!(u.isCamera)
+  return obj(u) && !!(u.isCamera)
 }
 
 export function bufferGeometry(u: unknown): u is BufferGeometry {
-  return obj(u) && 'isBufferGeometry' in u && !!(u.isBufferGeometry)
+  return obj(u) && !!(u.isBufferGeometry)
 }
 
 export function material(u: unknown): u is Material {
-  return obj(u) && 'isMaterial' in u && !!(u.isMaterial)
+  return obj(u) && !!(u.isMaterial)
 }
 
 export function light(u: unknown): u is Light {
-  return obj(u) && 'isLight' in u && !!(u.isLight)
+  return obj(u) && !!(u.isLight)
 }
 
 export function fog(u: unknown): u is Fog {
-  return obj(u) && 'isFog' in u && !!(u.isFog)
+  return obj(u) && !!(u.isFog)
 }
 
 export function scene(u: unknown): u is Scene {
-  return obj(u) && 'isScene' in u && !!(u.isScene)
+  return obj(u) && !!(u.isScene)
 }
 
 export function tresObject(u: unknown): u is TresObject {

+ 1 - 1
src/utils/primitive/createRetargetingProxy.ts

@@ -1,7 +1,7 @@
 export function createRetargetingProxy<T extends Record<string | number | symbol, any>, K extends keyof T & string & symbol>(
   target: T,
   getters = {} as Record<string | number | symbol, (t: T) => unknown>,
-  setters = {} as Partial<Record<K, (val: T[K], t: T, proxy: T, setTarget: (newTarget: T) => void) => boolean>>,
+  setters = {} as Record<K, (val: T[K], t: T, proxy: T, setTarget: (newTarget: T) => void) => boolean>,
 ) {
   let _target = target
 

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio