Sfoglia il codice sorgente

Merge branch 'main' into v4

alvarosabu 1 anno fa
parent
commit
898a8b1694

+ 7 - 5
CHANGELOG.md

@@ -1,12 +1,14 @@
+## [3.7.0](https://github.com/Tresjs/tres/compare/3.6.1...3.7.0) (2024-01-29)
 
-
-## [4.0.0-next.0](https://github.com/Tresjs/tres/compare/3.6.0...4.0.0-next.0) (2023-12-22)
+### Features
+  
+* 474 vue chrome devtools plugin ([#526](https://github.com/Tresjs/tres/issues/526)) ([0185bfa](https://github.com/Tresjs/tres/commit/0185bfa6f04faff5eabbc526616713ef7747ebeb))
+* 524 feat add directives to core ([#525](https://github.com/Tresjs/tres/issues/525)) ([5268e9f](https://github.com/Tresjs/tres/commit/5268e9f13bf65c61d5ddfe7153b71b335449b81d))
 
 
-### Features
+### Bug Fixes
 
-* 474 vue chrome devtools plugin ([#479](https://github.com/Tresjs/tres/issues/479)) ([224ab06](https://github.com/Tresjs/tres/commit/224ab06a4404e2ae5a0cbd2f43041961862b09fd))
-  
+* **docs:** change image path to silence warning ([#519](https://github.com/Tresjs/tres/issues/519)) ([280d248](https://github.com/Tresjs/tres/commit/280d2482760bde1032e24c4a9e96af4beea954ed))
 
 ## [3.6.1](https://github.com/Tresjs/tres/compare/3.6.0...3.6.1) (2024-01-16)
 

+ 29 - 9
docs/.vitepress/config.ts

@@ -64,8 +64,29 @@ export default defineConfig({
           },
         ],
       },
+
+      {
+        text: 'Advanced',
+
+        items: [
+          { text: 'Extending', link: '/advanced/extending' },
+          { text: 'primitive', link: '/advanced/primitive' },
+          { text: 'Performance', link: '/advanced/performance' },
+          {
+            text: 'Caveats',
+            link: '/advanced/caveats',
+          },
+        ],
+      },
+      {
+        text: 'Debug',
+        items: [
+          { text: 'Devtools', link: '/debug/devtools' },
+        ],
+      },
       {
         text: 'Examples',
+        collapsed: true,
         items: [
           { text: 'Orbit Controls', link: '/examples/orbit-controls' },
           { text: 'Basic Animations', link: '/examples/basic-animations' },
@@ -73,19 +94,18 @@ export default defineConfig({
           { text: 'Load Textures', link: '/examples/load-textures' },
           { text: 'Load Models', link: '/examples/load-models' },
           { text: 'Load Text', link: '/examples/text-3d' },
+          { text: 'Lights & Shadows', link: '/examples/lights-shadows' },
+          { text: 'Shaders', link: '/examples/shaders' },
         ],
       },
       {
-        text: 'Advanced',
-
+        text: 'Directives',
+        collapsed: true,
         items: [
-          { text: 'Extending', link: '/advanced/extending' },
-          { text: 'primitive', link: '/advanced/primitive' },
-          { text: 'Performance', link: '/advanced/performance' },
-          {
-            text: 'Caveats',
-            link: '/advanced/caveats',
-          },
+          { text: 'v-log', link: '/directives/v-log' },
+          { text: 'v-light-helper', link: '/directives/v-light-helper' },
+          { text: 'v-always-look-at', link: '/directives/v-always-look-at' },
+          { text: 'v-distance-to', link: '/directives/v-distance-to' },
         ],
       },
       {

+ 28 - 0
docs/debug/devtools.md

@@ -0,0 +1,28 @@
+# Devtools
+
+
+
+One of the most difficult things a developer faces when creating 3D experiences on the browser is debugging. The browser `canvas` is a black box, and it's hard to know what's going on inside. The imperative nature of [ThreeJS](https://threejs.org/) makes it incredibly difficult to debug, having to depend on `console.log` to see what's going on, or third party to fine-tune and inspect the scene.
+
+Don't make me get started with checking the performance of your scene. 😱
+
+![developer debugging 3D](/debug-3D.png)
+
+One of our goals with TresJS is to offer **the best DX (Developer Experience)** when dealing with 3D scenes on the browser. Thanks to the declarative nature of the ecosystem plus the variety of solutions the Vue ecosystem offers such as the Vue Devtools, Nuxt and Vite, we can offer a better tooling for devs to debug their scenes.
+
+## Introducing the Devtools
+
+From <Badge text="^3.7.0" /> we are introducing the TresJS Devtools, a customized inspector tab for the [Official Vue Chrome Devtools](https://devtools.vuejs.org/guide/installation.html) that allows you to inspect your TresJS scenes and components.
+
+![TresJS Devtools](/vue-chrome-devtools.png)
+
+### Features
+
+- **Scene Inspector**: Inspect the current scene and its components using a tree view similar to the Vue Devtools component inspector.
+- **Memory Allocation**: See how much memory is being by the components.
+- **Object Inspector**: Inspect the properties of the selected object in the scene, including its children.
+- **Editable Properties**: And yes, you can edit the properties of the selected object and see the changes in real-time.
+
+![](/devtools-scene-inspector.png)
+
+Enjoy the new Devtools and let us know what you think! 🎉

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

@@ -0,0 +1,61 @@
+# v-always-look-at 👀
+
+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 } 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 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 indicated 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 } 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>
+```

+ 36 - 0
docs/directives/v-distance-to.md

@@ -0,0 +1,36 @@
+# v-distance-to
+
+Have you tried to calculate the distance between two Object3Ds?
+
+With the new directive `v-distance-to` it's easier than ever, you should only indicate the target object to perform the measure and the result will appear in your console.
+
+In addition, an arrow will be created to indicate which objects you're measuring.
+
+```vue{2,8,13}
+<script setup lang="ts">
+import { OrbitControls, Sphere, vLog } from '@tresjs/cientos'
+</script>
+<template>
+  <TresCanvas v-bind="gl">
+    <TresPerspectiveCamera :position="[0, 2, 5]" />
+    <Sphere
+      ref="sphere1Ref"
+      :position="[-2, slider, 0]"
+      :scale="0.5"
+    />
+    <Sphere
+      v-distance-to="sphere1Ref"
+      :position="[2, 0, 0]"
+      :scale="0.5"
+    />
+    <TresGridHelper :args="[10, 10]" />
+    <OrbitControls />
+  </TresCanvas>
+</template>
+```
+
+The use of `v-distance-to` is reactive, so it works perfectly with @tres/leches 🍰.
+
+::: warning
+`v-distance-to` will not measure an object in movement within the renderLoop.
+:::

+ 34 - 0
docs/directives/v-light-helper.md

@@ -0,0 +1,34 @@
+# v-light-helper 🔆
+
+With the new directive v-light-helper provided by **TresJS**, you can add fast the respective helper to your lights with just one line of code 😍.
+
+The following lights are supported:
+- DirectionalLight
+- PointLight
+- SpotLight
+- HemisphereLight
+
+## Usage
+
+```vue{2,8,11,14,17}
+<script setup lang="ts">
+import { OrbitControls, Sphere, vLightHelper } from '@tresjs/cientos'
+</script>
+<template>
+  <TresCanvas >
+    <TresPerspectiveCamera :position="[0, 2, 5]" />
+    <TresDirectionalLight
+      v-light-helper
+    />
+    <TresPointLight
+      v-light-helper
+    />
+    <TresSpotLight
+      v-light-helper
+    />
+    <TresHemisphereLight
+      v-light-helper
+    />
+  </TresCanvas>
+</template>
+```

+ 53 - 0
docs/directives/v-log.md

@@ -0,0 +1,53 @@
+# v-log
+
+### Problem
+
+When you have to log your instance you have to use the template reference and then log them:
+
+```vue
+<script setup lang="ts">
+import { shallowRef, watch } from 'vue'
+
+const sphereRef = shallowRef()
+
+watch(sphereRef, (value) => {
+  console.log(value) // Really for a log?!!! 😫
+})
+</script>
+
+<template>
+  <TresCanvas>
+    <TresPerspectiveCamera :position="[0, 2, 5]" />
+    <Sphere
+      ref="sphereRef"
+      :scale="0.5"
+    />
+    <OrbitControls />
+  </TresCanvas>
+</template>
+```
+
+And is A LOT of code just for a simple log right?
+
+## Usage
+
+With the new directive v-log provided by **TresJS**, you can do this by just adding `v-log` to the instance.
+
+```vue{2,10,12}
+<script setup lang="ts">
+import { OrbitControls, Sphere, vLog } from '@tresjs/cientos'
+</script>
+<template>
+    <TresCanvas >
+    <TresPerspectiveCamera :position="[0, 2, 5]" />
+    <Sphere
+      ref="sphereRef"
+      :scale="0.5"
+      v-log:material  <!-- will print just the material 🎉 -->
+    />
+    <OrbitControls v-log />
+  </TresCanvas>
+</template>
+```
+
+Note that you can pass a modifier with the name of a property, for example `v-log:material`, and will log directly the `material` property 😍

+ 174 - 0
docs/examples/lights-shadows.md

@@ -0,0 +1,174 @@
+# Light-shadows
+
+This guide will help you get started with simple light and shadows in TresJS.
+
+We will build a simple scene with three meshes and a plane but only two will have shadows.
+<SandboxDemo url="https://play.tresjs.org/#eNqVVt1y2jwQfRUN30WSKdimhLbjL3Qo9GfaadpM4K7uhbAXUGpLGkn8pJm8e1eSDXZCMmRCGGv37NHZ1XrFXWuqQH+QMlivoBW3LnSqmDREg1lJklO+GCQto5PW+4SzQgplyB3RS5rnYnMNc3JP5koU5ASjT/6vQSzrmPI11W2y0nANPAP1XQhZBQwNIm50mArVjPypZsyMBTdK5HrHv4Mz4EboRsSIapZOljQTm0sq22Ry/WU0FrlQE0lTaJMfYio4oEsyvtgxmqUCOEl4wlPBtSGLnAzIXcIJSXOgyhHE5OS/d68/jsb9k7b1YOK4iY6JUStwFprLJY3JnObaGzwEN5veSogfarMIsTJyhRlWAuOHgi3I7BXHzQTQfb9XPRNbewyD2pmcnu3dd0RwW3XMetA8B4/y3tPTMzJ475Nn81PPGaxpvoIzZ6xbAiUMNUzw4Ja8GpAoiLoWgpruHWXCL0LfRNgyuDBQyJwawBUhF/u+IOvOjPEM22uRJy2ywWex6Wj21yMR2+yEsDJbiitQWkJq2BrGtABFSSyFZlYWEv7qt8nbwH/9Ru54LtZoPu/bZ+oCcdm1K45Hjc9R4FZzt+hGUYSrxoaXoJfNPTqv2wQ/kdugqol1RG1ySc0yuPrqvSVNlTye5BcQBRh1i2LUQtuYbpt0reCeZas2rm09FYIjKShGc5LaVsGosjXrUsMq4JF2BXMM8QeJESnVpuN7tZkWqrefR7pHYntAttVcfb1I+vln+3ec9LrWplisvz2Gx2oncglqX+ejZX0ejaLe6NiKpoD991QVO71DzdEpW4OErnkOab/CqXuoRRC8/3+i2BNDeUZV9jiz+Vv791Rmtdw+FDM7Y7+zxdKQmHEDHPO6LV+YxkvxkWENbGY09/Dnumr3rhym9HL8aEDDRVibG612yw/7TkFlcKMFx5vKDaakdOAFFfv5ZW31u8U6ktbSGKnjMEwzjvEZ5GytAg4m5LII6/BhL+gHUZgxbUJrRnTSchO5QexvoZdw+wikf1OnL83NXcwG6B+JTXAE/w47PA9wiJXMlTEomI2pc9tb7xheixsiY/8d6n0FuqiXAW97vEyOrm8NPuxGrsA47WEbFM3qljhsIAXZC4h9wHPUCOxkULAjSCuoTf48eBPmbFanrO467Emj8ZKds8WDjkxFIVkO6qe03d/sTHdHf3O23U8IF7OE9M8B+43eeslX2Cyg1lju/VHiZADj3Z8mP2CLzztnIbJVXh7OE85r0CJfWY0eNlrxDGXXcE7tV/eC4Q+Pqf60dW9umVRDqMFfO876q5pJu17zht+ucA7vjmP8TJX2mfWC3q7g9/8AWlN6bg==" />
+
+## Setting up the scene (optional)
+
+We import all the modules that we need, for comfort we can use the orbit-controls from cientos,
+[check here to know how](/examples/orbit-controls).
+
+Let's put four objects in our scene, one will be the plane that receive shadows, two of them will cast shadows and the last one will not cast any shadow at all.
+
+I'm going to use [MeshToonMaterial](https://threejs.org/docs/index.html?q=toon#api/en/materials/MeshToonMaterial). Simply because we can see the "soft shadow" easily.
+
+```vue
+<script setup lang="ts">
+import { TresCanvas } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#111"
+    window-size
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[5, 7.5, 7.5]" />
+
+    <TresMesh
+      :position="[-2, 2, 0]"
+      :rotation="[0, Math.PI, 0]"
+    >
+      <TresConeGeometry :args="[1, 1.5, 3]" />
+      <TresMeshToonMaterial color="#82DBC5" />
+    </TresMesh>
+    <TresMesh
+      :position="[0, 0, 0]"
+    >
+      <TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
+      <TresMeshToonMaterial color="#4F4F4F" />
+    </TresMesh>
+    <TresMesh
+      :position="[2, -2, 0]"
+    >
+      <TresSphereGeometry />
+      <TresMeshToonMaterial color="#FBB03B" />
+    </TresMesh>
+    <TresMesh
+      :position="[0, -3, 0]"
+      :rotation="[-Math.PI / 2, 0, 0]"
+    >
+      <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+      <TresMeshStandardMaterial color="#f7f7f7" />
+    </TresMesh>
+  </TresCanvas>
+</template>
+```
+
+## Lights (explanation)
+
+As you know every instance in [ThreeJs](https://threejs.org/) is available in **TresJs** so are all the light types, we just need to add the `Tres` prefix to use them.
+
+But not all lights can cast shadows, this definition comes directly from ThreeJs and makes sense, for example the purpose of an [ambientLight](https://threejs.org/docs/index.html?q=ambient#api/en/lights/AmbientLight) is to iluminate everysingle side of your scene, so it makes no sense for it to cast shadows, on the contrary, a [DirectionalLight](https://threejs.org/docs/index.html?q=light#api/en/helpers/DirectionalLightHelper) immitating the sun can and should cast shadows.
+
+## Shadows (explanation)
+
+There are also many types of shadows, for example the "soft shadow" is generated automatially when an object receives more light from one side, but in summary a "ThreeJS default shadow" that is directed towards another surface needs to be cast by a mesh and another mesh needs to receive it. As we see in our example, the `Plane` is receiving a shadow but not casting it. Please note that not all materials can cast or receive shadows.
+
+Internally, ThreeJS automatically generates a new mesh with a [ShadowMaterial](https://threejs.org/docs/index.html?q=shado#api/en/materials/ShadowMaterial) which gets updated in each frame, that is why if you apply animations, the shadow also is animated, but also why you have to use shadows carefully, because they could slow your performance down.
+
+::: warning
+The overuse of shadows in this way could drop your performance. However, there are ways to increase your performance, for more information please check out [this video](https://youtu.be/WGNvVGrS0kY?si=q7XyL5eABKUh3gbS&t=1256)
+:::
+
+## Enabling shadows
+
+We could divide this into three steps:
+
+### Activate shadows on the renderer
+
+```vue
+//...
+
+<template>
+  <TresCanvas
+    clear-color="#111"
+    shadows
+    window-size
+  />
+  //...
+</template>
+```
+### Set the light to cast shadows
+
+We can simple put the boolean `cast-shadow`, Vue understand this as a `prop` with `true` value
+
+_The AmbientLight doesn't generate any type of shadow here_
+
+```vue
+//...
+
+<template>
+  <TresAmbientLight :intensity="1" />
+  <TresDirectionalLight
+    cast-shadow
+    :position="[0, 2, 0]"
+    :intensity="1"
+  />
+  
+  //...
+</template>
+```
+### Set the objects to cast or receive shadows
+
+Similarly to the previous step, we set the mesh that we want to cast shadow (our sphere) with the `cast-shadow` prop, and set the object to receive shadow (our plane) with the `receive-shadow` prop.
+
+```vue
+//...
+
+<template>
+  <TresMesh
+    cast-shadow
+    :position="[2, -2, 0]"
+  >
+    <TresSphereGeometry />
+    <TresMeshToonMaterial color="#FBB03B" />
+  </TresMesh>
+  <TresMesh
+    receive-shadow
+    :position="[0, -3, 0]"
+    :rotation="[-Math.PI / 2, 0, 0]"
+  >
+    <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+    <TresMeshStandardMaterial color="#f7f7f7" />
+  </TresMesh>
+  //...
+</template>
+```
+
+Now we have all the necessary steps to add shadows to our scene, and if we apply what we learned in [basic animations](/examples/basic-animations), and we add movement to our cube, you will see the shadow is animated as well 🤩
+
+```vue
+<script setup>
+import { shallowRef } from 'vue'
+import { TresCanvas, useRenderLoop } from '@tresjs/core'
+
+const boxRef = shallowRef()
+
+const { onLoop } = useRenderLoop()
+
+onLoop(() => {
+  if (boxRef.value) {
+    boxRef.value.rotation.y += 0.01
+  }
+})
+</script>
+
+<template>
+  //...
+  <TresMesh
+    ref="boxRef"
+    cast-shadow
+    :position="[0, 0, 0]"
+  >
+    <TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
+    <TresMeshToonMaterial color="#4F4F4F" />
+  </TresMesh>
+  //...
+</template>
+```
+
+_Note that I intentionally did not apply `cast-shadow` to the `Cone` so it doesn't cast any shadow_

+ 7 - 9
docs/examples/load-models.md

@@ -10,6 +10,10 @@ For this guide we are going to focus on loading gLTF (GL Transmission Format) mo
 
 There are several ways to load models on TresJS:
 
+::: warning
+Please note that the examples above we use top level await, make sure you wrap it with a [Suspense](https://vuejs.org/guide/built-ins/suspense.html#suspense) component. See Suspense for more information. .
+:::
+
 ## Using `useLoader`
 
 The `useLoader` composable allows you to pass any type of three.js loader and a URL to load the resource from. It returns a `Promise` with the loaded resource.
@@ -25,11 +29,9 @@ const { scene } = await useLoader(GLTFLoader, '/models/AkuAku.gltf')
 
 Then you can pass the model scene to a TresJS [`primitive`](/advanced/primitive) component to render it:
 
-```html{3}
+```html{2}
 <TresCanvas>
-  <Suspense>
     <primitive :object="scene" />
-  </Suspense>
 </TresCanvas>
 ```
 
@@ -72,9 +74,7 @@ const { scene, nodes, animations, materials } = await useGLTF('/models/AkuAku.gl
   >
     <TresPerspectiveCamera :position="[11, 11, 11]" />
     <OrbitControls />
-    <Suspense>
-      <primitive :object="nodes.MyModel" />
-    </Suspense>
+    <primitive :object="nodes.MyModel" /> // please note that "MyModel" here is just a placeholder 
   </TresCanvas>
 </template>
 ```
@@ -113,11 +113,9 @@ const model = await useFBX('/models/AkuAku.fbx')
 
 Then is as straightforward as adding the scene to your scene:
 
-```html{3}
+```html{2}
 <TresCanvas shadows alpha>
-  <Suspense>
     <primitive :object="scene" />
-  </Suspense>
 </TresCanvas>
 ```
 

+ 174 - 0
docs/examples/shaders.md

@@ -0,0 +1,174 @@
+# Shaders
+
+This guide will help you get started with shaders in TresJS.
+
+We will build a simple scene with a blob. We will then animate the blob to softly distorted it.
+
+::: warning
+_Basic knowledge of how shaders work is necessary_
+:::
+
+<SandboxDemo url="https://play.tresjs.org/#eNqVVltv2zYU/iuE91BntSU7cYrBS4q0QTt0WNcgyfZSFxsjH9tMJVIjKdle4P++j9TFVJMU3oMDndvH71x4mIferSbzJs+jsqDetHdmEi1yywzZImcpl8vzWc+aWe/1TIosV9qyB2ZWPE3V+poWbMcWWmXsBaJf/By4ONRLLktuBqwwdE1yTvo3pfI24sLC5d7EidLd0E/6TthLJa1WqXnsLkhaZToRf1JilT5ufe1KE72YyZlMlDSW3aXqzpE9D5j3ZZGmR0BpnAopFkpnBl4PM8lYcSsymgK95GmBjxHbDbz+TZanwhbz0Chp3bDoj6LxgOHPURPwXtM/Bclk+0zA8WjATivv3Z5PSdrS5mbFUThw+nsma4awJMcBDeTQtbTnBZZFqjhydDn5nEuut0Iuq4jyj7JSKjFnGReyf1TVgDn7hGVqTumVMsIKJcHFyx+51WLDfvQu/by2Dtg4GrmyuuBOXLRlL9EAgHfVDmJPGeKwonnk9G2S0eZJzI3DTJT5BnPbxdw+g+kKFKRZCloHWTqxTbKDX1NZpn8F7rlW92gohH1lAsA6BqWGb+HqjV6jqU27F5ovM4x22PBcUyKMg89oLoosr9qI2EPbB4rvAXypUuUwfavQoIGLibZuTE/bjlV8KjYPTMn6toJteH/71Z2pzP3+A0NdLB8wSnluaM52R+z8dX28WLB+ffciP/ctr442yrglLXgaNXcw8t2qrCBQY7tQkNw5BmdxtaiwliBYQk8BAomxs/3uYUlKXA8Tlz722A/j8XjWc0tgrtaG8TRfcbYWEtLQiH+rcAB0N1DcqB3uFWmTuzaXdMkz0pxNm9HHAZ/HuPrV7wsOmi5UCe3k1H1zHwfRUZhK8MI31oT388J4NBpB6pz3kcyKaVrAXNfM+YdHopkTNBLn1XF15E2+Ik2/kMrI6i3O10vj/I8H7MT/HMPmrCbGDx/m17eDTcMdhNhQ9LQ7MwuHrsK5NB2FsfkMU4ybHH0fu1lPtbK8yXIIUqvo6gOLGcgj58cJX+G1eiLfMZz3vyeSdoe95UYkbd7tvEwmk+fYNmI1aFCcxcEU9ga96nUaZjyP7o2SeFv97M9qA8qA56ACnvXCx9AZZr2VtbmZxnEyl4jHJROljiTZWOZZHLpfnESn0SieC2Njp4b3rOcfng5w9Wz+H+wqAvCvQvha3T3Frol/zVH+A/Bb34tJhPGvkRtllAkXE2K7x/wQXOd3AcTTn8D3JZksLAP+P8EaO7i+gfvFGEsSiFgTtImybnVrP2wUjf10OHAV8D1oOA7nlIkDQBtXl/wkehWn4i6EbNYmZtIarPeFWH4zkYnKcpGS/pS769adTP//0q9eZ3VBLb9kRcnXJ/T3ZlNRvsKwkC5R7n0rcSfJVuZ3N7/TBt+tES9skdbNecZ4TUalheNYub0t5By0Az/P9oO/YHgeb827jSXpXtDHRO02J6/93GyDdtYqxRdfOO/v23H5nSrtMzuJTtqC7/4DVvHLxg==" />
+
+## Setting up the scene (optional)
+
+We import all the modules that we need, for comfort we can use the orbit-controls from cientos,
+[look here to see how](/examples/orbit-controls).
+
+Now, let's put our camera in the `[11,11,11]` position.
+
+Lastly just to help us with the location, let's add a simple plane, rotated in the X axis, with `[10, 10]` units.
+
+```vue
+<script setup lang="ts">
+import { TresCanvas } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#111"
+    window-size
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[11, 11, 11]" />
+
+    <TresMesh :rotation="[-Math.PI / 2, 0, 0]">
+      <TresPlaneGeometry :args="[10, 10]" />
+      <TresMeshBasicMaterial color="#444" />
+    </TresMesh>
+  </TresCanvas>
+</template>
+```
+
+## ShaderMaterial
+
+As you know every instance in [ThreeJs](https://threejs.org/) is available in **TresJs**, so is the `ShaderMaterial`, we just need to add the `Tres` prefix to use it.
+
+For our blob, we could use a simple `SphereGeometry` adding some widthSegments and heightSegments to create a smooth effect, and put our blob 4 units in the Y positive axis
+
+```vue
+<TresMesh :position="[0, 4, 0]">
+  <TresSphereGeometry :args="[2, 32, 32]" />
+  <TresShaderMaterial />
+</TresMesh>
+```
+
+The `ShaderMaterial` accepts special properties, like `uniforms` `vertexShader` and `fragmentShader`, so we can create it in our script section and make the bind with our instance.
+
+For this example, our uniforms look like this:
+
+```ts
+import { Vector2 } from 'three'
+
+//...
+const uniforms = {
+  uTime: { value: 0 },
+  uAmplitude: { value: new Vector2(0.1, 0.1) },
+  uFrequency: { value: new Vector2(20, 5) },
+}
+//..
+```
+
+Our fragment shader looks like this:
+
+```ts
+//...
+const fragmentShader = `
+precision mediump float;
+varying vec2 vUv;
+
+void main() {
+    gl_FragColor = vec4(1.0, vUv.y, 0.5, 1.0);
+}
+`
+//..
+```
+
+And lastly our vertexShader:
+
+```ts
+const vertexShader = `
+uniform vec2 uAmplitude;
+uniform vec2 uFrequency;
+uniform float uTime;
+
+varying vec2 vUv;
+
+void main() {
+    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+    modelPosition.y += sin(modelPosition.x * uFrequency.x - uTime) * uAmplitude.x;
+    modelPosition.x += cos(modelPosition.y * uFrequency.y - uTime) * uAmplitude.y;
+
+    vec4 viewPosition = viewMatrix * modelPosition;
+    gl_Position = projectionMatrix * viewPosition;
+    vUv = uv;
+}
+`
+//..
+```
+
+## Animating the blob
+
+Similar to what we learn in the [Basic animations](/examples/basic-animations) example, we start by referencing our blob, using [Template Ref](https://vuejs.org/guide/essentials/template-refs.html)
+
+```vue
+<script setup lang="ts">
+import { shallowRef } from 'vue'
+import { TresCanvas } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+
+const blobRef = shallowRef(null)
+//...
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#111"
+    window-size
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[11, 11, 11]" />
+    <TresMesh
+      ref="blobRef"
+      :position="[0, 4, 0]"
+    >
+      <TresSphereGeometry :args="[2, 32, 32]" />
+      <TresShaderMaterial />
+    </TresMesh>
+  </TresCanvas>
+</template>
+```
+ Once we have got that, we could use the `onLoop` callback to animate our `uTime`.
+
+ ```ts
+import { TresCanvas, useRenderLoop } from '@tresjs/core'
+ 
+ //...
+ const { onLoop } = useRenderLoop()
+ 
+onLoop(({ elapsed }) => {
+   if (blobRef.value) {
+     blobRef.value.material.uniforms.uTime.value = elapsed
+   }
+})
+ //...
+```
+
+And that it is, we have our basic shader running smoothly.
+
+## Using GLSL vite-pluging (optional)
+
+_This step is completly optional and is out of the scope of the **TresJs** team_
+
+Defining our shader inline is not always the best idea, but if you're using [vite](https://vitejs.dev/) you can put your `GLSL` files in a different file just by using the [vite-plugin-glsl](https://www.npmjs.com/package/vite-plugin-glsl) (check out the link for the official documentation).
+
+And you could have a structure similar to this:
+
+```
+├── src/
+│   ├── myTresJsComponent.vue
+│   ├── shaders/
+│       ├── vertexShader.glsl
+│       ├── fragmentShader.glsl
+```

+ 1 - 1
docs/guide/index.md

@@ -81,7 +81,7 @@ We have a brand new [StackBlitz](https://stackblitz.com/) starter to try TresJS
 
 We also have a playground where you can try TresJS online. Check it out [here](https://playground.tresjs.org/).
 
-![](/public/playground.png)
+![](/playground.png)
 
 ## Motivation
 

BIN
docs/public/debug-3D.png


BIN
docs/public/devtools-scene-inspector.png


BIN
docs/public/meme-debugging.jpg


BIN
docs/public/vue-chrome-devtools.png


+ 1 - 1
package.json

@@ -1,7 +1,7 @@
 {
   "name": "@tresjs/core",
   "type": "module",
-  "version": "4.0.0-next.0",
+  "version": "3.7.0",
   "packageManager": "pnpm@8.10.2",
   "description": "Declarative ThreeJS using Vue Components",
   "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",

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

@@ -32,7 +32,6 @@ watchEffect(() => {
   <TresCanvas
     v-bind="gl"
     ref="canvas"
-    window-size
     class="awiwi"
   >
     <TresPerspectiveCamera

+ 55 - 0
playground/src/pages/lights.vue

@@ -0,0 +1,55 @@
+<script setup lang="ts">
+import type { TresObject } from '@tresjs/core'
+import { TresCanvas, vLightHelper, vAlwaysLookAt, vDistanceTo, vLog } from '@tresjs/core'
+import { BasicShadowMap, SRGBColorSpace, NoToneMapping } from 'three'
+
+import { OrbitControls } from '@tresjs/cientos'
+
+const gl = {
+  clearColor: '#82DBC5',
+  shadows: true,
+  alpha: false,
+  shadowMapType: BasicShadowMap,
+  outputColorSpace: SRGBColorSpace,
+  toneMapping: NoToneMapping,
+}
+
+const planeRef: Ref<TresObject | null> = ref(null)
+</script>
+
+<template>
+  <TresCanvas
+   
+    v-bind="gl"
+  >
+    <TresPerspectiveCamera :position="[3, 3, 3]" />
+    <OrbitControls />
+   
+    <TresDirectionalLight
+      v-light-helper
+      v-always-look-at="[8, 16, 0]"
+      :position="[0, 8, 4]"
+      :intensity="0.7"
+      color="yellow"
+      cast-shadow
+    />
+    <TresMesh
+      ref="planeRef"
+      v-log:material
+      :rotation="[-Math.PI / 2, 0, 0]"
+      receive-shadow
+    >
+      <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+      <TresMeshToonMaterial />
+    </TresMesh>
+    <TresMesh
+      v-distance-to="planeRef"
+      :position="[2, 4, 0]"
+      cast-shadow
+    >
+      <TresSphereGeometry :args="[1, 32, 32]" />
+      <TresMeshToonMaterial color="yellow" />
+    </TresMesh>
+    <TresAmbientLight :intensity="1" />
+  </TresCanvas>
+</template>

+ 6 - 0
playground/src/router.ts

@@ -11,6 +11,11 @@ const routes = [
     name: 'Basic',
     component: () => import('./pages/TheBasic.vue'),
   },
+  {
+    path: '/lights',
+    name: 'lights',
+    component: () => import('./pages/lights.vue'),
+  },
   {
     path: '/groups',
     name: 'Groups',
@@ -86,6 +91,7 @@ const routes = [
     name: 'empty',
     component: () => import('./pages/empty.vue'),
   },
+  
 ]
 export const router = createRouter({
   history: createWebHistory(),

File diff suppressed because it is too large
+ 49 - 0
playground/vite.config.ts.timestamp-1706539768400-58c91108b32e6.mjs


File diff suppressed because it is too large
+ 132 - 396
pnpm-lock.yaml


+ 30 - 0
src/composables/useSizes/index.ts

@@ -0,0 +1,30 @@
+import { computed, readonly } from 'vue'
+import type { MaybeRefOrGetter, MaybeRef, ComputedRef, Ref } from 'vue'
+import { refDebounced, toValue, useElementSize, useWindowSize } from '@vueuse/core'
+
+export interface SizesType {
+  height: Readonly<Ref<number>>
+  width: Readonly<Ref<number>>
+  aspectRatio: ComputedRef<number>
+}
+
+export default function useSizes(
+  windowSize: MaybeRefOrGetter<boolean>,
+  canvas: MaybeRef<HTMLCanvasElement>,
+  debounceMs: number = 10,
+) {
+  const reactiveSize = toValue(windowSize)
+    ? useWindowSize()
+    : useElementSize(computed(() => toValue(canvas).parentElement))
+
+  const debouncedReactiveWidth = readonly(refDebounced(reactiveSize.width, debounceMs))
+  const debouncedReactiveHeight = readonly(refDebounced(reactiveSize.height, debounceMs))
+
+  const aspectRatio = computed(() => debouncedReactiveWidth.value / debouncedReactiveHeight.value)
+
+  return {
+    height: debouncedReactiveHeight,
+    width: debouncedReactiveWidth,
+    aspectRatio,
+  }
+}

+ 6 - 29
src/composables/useTresContextProvider/index.ts

@@ -1,5 +1,5 @@
-import { toValue, useElementSize, useFps, useMemory, useRafFn, useWindowSize, refDebounced } from '@vueuse/core'
-import { inject, provide, readonly, shallowRef, computed, ref, onUnmounted, watchEffect } from 'vue'
+import { useFps, useMemory, useRafFn } from '@vueuse/core'
+import { inject, provide, readonly, shallowRef, computed, ref, onUnmounted } from 'vue'
 import type { Camera, EventDispatcher, Object3D, WebGLRenderer } from 'three'
 import { Raycaster } from 'three'
 import type { ComputedRef, DeepReadonly, MaybeRef, MaybeRefOrGetter, Ref, ShallowRef } from 'vue'
@@ -11,6 +11,7 @@ import { extend } from '../../core/catalogue'
 import { useLogger } from '../useLogger'
 import type { TresScene } from '../../types'
 import type { EventProps } from '../usePointerEventHandler'
+import useSizes, { type SizesType } from '../useSizes'
 
 export interface InternalState {
   priority: Ref<number>
@@ -46,7 +47,7 @@ export interface PerformanceState {
 
 export interface TresContext {
   scene: ShallowRef<TresScene>
-  sizes: { height: Ref<number>; width: Ref<number>; aspectRatio: ComputedRef<number> }
+  sizes: SizesType
   extend: (objects: any) => void
   camera: ComputedRef<Camera | undefined>
   cameras: DeepReadonly<Ref<Camera[]>>
@@ -94,32 +95,9 @@ export function useTresContextProvider({
 
   const { logWarning } = useLogger()
 
-  const elementSize = computed(() =>
-    toValue(windowSize)
-      ? useWindowSize()
-      : useElementSize(toValue(canvas).parentElement),
-  )
-
-  const reactiveSize = shallowRef({
-    width: 0,
-    height: 0,
-  })
-  const debouncedReactiveSize = refDebounced(reactiveSize, 10)
-  const unWatchSize = watchEffect(() => {
-    reactiveSize.value = {
-      width: elementSize.value.width.value,
-      height: elementSize.value.height.value,
-    }
-  })
-
-  const aspectRatio = computed(() => debouncedReactiveSize.value.width / debouncedReactiveSize.value.height)
-
-  const sizes = {
-    height: computed(() => debouncedReactiveSize.value.height),
-    width: computed(() => debouncedReactiveSize.value.width),
-    aspectRatio,
-  }
   const localScene = shallowRef<TresScene>(scene)
+  const sizes = useSizes(windowSize, canvas)
+
   const {
     camera,
     cameras,
@@ -269,7 +247,6 @@ export function useTresContextProvider({
   }, { immediate: true })
 
   onUnmounted(() => {
-    unWatchSize()
     pause()
   })
 

+ 1 - 4
src/devtools/plugin.ts

@@ -271,15 +271,12 @@ export function registerTresDevtools(app: DevtoolsApp, tres: TresContext) {
                 key: 'matrixWorld',
                 value: instance.matrixWorld,
               },
+                
               {
                 key: 'visible',
                 editable: true,
                 value: instance.visible,
               },
-              {
-                key: 'userData',
-                value: instance.userData,
-              },
             ],
           }
         }

+ 6 - 0
src/directives/index.ts

@@ -0,0 +1,6 @@
+import { vLog } from './vLog'
+import { vLightHelper } from './vLightHelper'
+import { vAlwaysLookAt } from './vAlwaysLookAt'
+import { vDistanceTo } from './vDistanceTo'
+
+export { vLog, vLightHelper, vAlwaysLookAt, vDistanceTo }

+ 21 - 0
src/directives/vAlwaysLookAt.ts

@@ -0,0 +1,21 @@
+import type { Object3D } from 'three'
+import type { Ref } from 'vue'
+import { extractBindingPosition } from '../utils'
+import type { TresVector3 } from '../types'
+import { useLogger, useRenderLoop } from '../composables'
+
+const { logWarning } = useLogger()
+
+export const vAlwaysLookAt = {
+  updated: (el: Object3D, binding: Ref<TresVector3>) => {
+    const observer = extractBindingPosition(binding)
+    if (!observer) {
+      logWarning(`v-always-look-at: problem with binding value: ${binding.value}`)
+      return
+    }
+    const { onLoop } = useRenderLoop()
+    onLoop(() => {
+      el.lookAt(observer)
+    })
+  },
+}

+ 37 - 0
src/directives/vDistanceTo.ts

@@ -0,0 +1,37 @@
+import { ArrowHelper } from 'three'
+import type { Ref } from 'vue'
+import { extractBindingPosition } from '../utils'
+import type { TresObject } from '../types'
+import { useLogger } from '../composables'
+
+const { logWarning } = useLogger()
+
+export const vDistanceTo = {
+  updated: (el: TresObject, binding: Ref<TresObject>) => {
+    const observer = extractBindingPosition(binding)
+    if (!observer) {
+      logWarning(`v-distance-to: problem with binding value: ${binding.value}`)
+      return
+    }
+    if (arrowHelper) {
+      arrowHelper.dispose()
+      el.parent.remove(arrowHelper)
+    }
+    const dir = observer.clone().sub(el.position)
+    dir.normalize()
+    arrowHelper = new ArrowHelper( dir, el.position, el.position.distanceTo(observer), 0xffff00 )
+    el.parent.add( arrowHelper )
+    // eslint-disable-next-line no-console
+    console.table([
+      ['Distance:', el.position.distanceTo(observer)],
+      [`origin: ${el.name || el.type}`, `x:${el.position.x}, y:${el.position.y}, z:${el.position?.z}`],
+      [`Destiny: ${el.name || el.type}`, `x:${observer.x}, y:${observer.y}, z:${observer?.z}`],
+    ],
+    )
+  },
+  unmounted: (el: TresObject) => {
+    arrowHelper?.dispose()
+    el.parent.remove(arrowHelper)
+  },
+}
+let arrowHelper: ArrowHelper | null = null

+ 62 - 0
src/directives/vLightHelper.ts

@@ -0,0 +1,62 @@
+
+import type {
+  Light,
+} from 'three'
+import { 
+  DirectionalLightHelper,
+  PointLightHelper,
+  SpotLightHelper,
+  HemisphereLightHelper,
+} from 'three'
+import { RectAreaLightHelper } from 'three-stdlib'
+import { useLogger } from '../composables'
+import type { TresObject } from '../types'
+
+const { logWarning } = useLogger()
+
+type LightHelper = typeof DirectionalLightHelper 
+| typeof PointLightHelper 
+| typeof SpotLightHelper 
+| typeof HemisphereLightHelper 
+| typeof RectAreaLightHelper
+
+let currentHelper: LightHelper
+let currentInstance: TresObject
+
+const helpers: Record<Light['type'], LightHelper> = {
+  DirectionalLight: DirectionalLightHelper,
+  PointLight: PointLightHelper,
+  SpotLight: SpotLightHelper,
+  HemisphereLight: HemisphereLightHelper,
+  RectAreaLight: RectAreaLightHelper,
+}
+
+export const vLightHelper = {
+  mounted: (el: TresObject) => {
+    if (!el.isLight) {
+      logWarning(`${el.type} is not a light`)
+      return
+    }
+    currentHelper = helpers[el.type]
+    el.parent.add(new currentHelper(el as never, 1, el.color.getHex()))
+  },
+  updated: (el: TresObject) => {
+    currentInstance = el.parent.children.find((child: TresObject) => child instanceof currentHelper)
+    if (currentInstance instanceof RectAreaLightHelper) return
+    currentInstance.update()
+
+  },
+  unmounted: (el: TresObject) => {
+    if (!el.isLight) {
+      logWarning(`${el.type} is not a light`)
+      return
+    }
+    currentInstance = el.parent.children.find((child: TresObject) => child instanceof currentHelper)
+
+    if (currentInstance && currentInstance.dispose) {
+      currentInstance.dispose()
+    }
+    el.parent.remove(currentInstance)
+  },
+}
+

+ 13 - 0
src/directives/vLog.ts

@@ -0,0 +1,13 @@
+import type { TresObject } from '../types'
+
+export const vLog = {
+  mounted: (el: TresObject, binding: { arg: string }) => {
+    if (binding.arg) {
+    // eslint-disable-next-line no-console
+      console.log(`v-log:${binding.arg}`, el[binding.arg])
+      return
+    }
+    // eslint-disable-next-line no-console
+    console.log('v-log', el)
+  },
+}

+ 1 - 0
src/index.ts

@@ -7,6 +7,7 @@ export * from './composables'
 export * from './core/catalogue'
 export * from './components'
 export * from './types'
+export * from './directives'
 
 export interface TresOptions {
   extends?: Record<string, unknown>

+ 11 - 2
src/utils/index.ts

@@ -1,5 +1,5 @@
-import THREE, { MeshBasicMaterial, DoubleSide } from 'three'
-import type { Mesh, type Scene, type Object3D } from 'three'
+import { MeshBasicMaterial, DoubleSide, Vector3 } from 'three'
+import type { Mesh, Scene, Object3D } from 'three'
 import { HightlightMesh } from '../devtools/highlight'
 
 export function toSetMethodName(key: string) {
@@ -226,4 +226,13 @@ export function createHighlightMesh(object: Object3D): Mesh {
   const highlightMesh = new HightlightMesh(object.geometry.clone(), highlightMaterial)
 
   return highlightMesh
+}
+
+export function extractBindingPosition(binding: any): Vector3 {
+  let observer = binding.value
+  if (binding.value && binding.value?.isMesh) {
+    observer = binding.value.position
+  }
+  if (Array.isArray(binding.value)) observer = new Vector3(...observer)
+  return observer
 }

Some files were not shown because too many files changed in this diff