Kaynağa Gözat

Merge branch 'main' into feature/60-shapes-abstraction

Alvaro 2 yıl önce
ebeveyn
işleme
24e5c20a2f
72 değiştirilmiş dosya ile 2339 ekleme ve 634 silme
  1. 13 0
      .vitepress/cache/deps_temp/_metadata.json
  2. 1 0
      .vitepress/cache/deps_temp/package.json
  3. 88 0
      .vitepress/cache/deps_temp/vue.js
  4. 3 0
      .vitepress/cache/deps_temp/vue.js.map
  5. 7 1
      .vscode/settings.json
  6. 61 2
      apps/playground/astro.config.mjs
  7. 9 1
      apps/playground/package.json
  8. BIN
      apps/playground/public/animations.png
  9. BIN
      apps/playground/public/gltf-model.png
  10. 5 0
      apps/playground/public/logo-dark.svg
  11. 5 0
      apps/playground/public/logo.svg
  12. BIN
      apps/playground/public/materials.png
  13. BIN
      apps/playground/public/portal-journey.png
  14. BIN
      apps/playground/public/textures.png
  15. BIN
      apps/playground/public/transform-controls.png
  16. BIN
      apps/playground/public/tres-basic.png
  17. BIN
      apps/playground/public/tres-donut.png
  18. 38 0
      apps/playground/src/components/BasicAnimations.vue
  19. 0 62
      apps/playground/src/components/Card.astro
  20. 3 0
      apps/playground/src/components/ExperimentInfo.vue
  21. 28 0
      apps/playground/src/components/Header.astro
  22. 126 0
      apps/playground/src/components/Materials.vue
  23. 13 0
      apps/playground/src/components/SuspenseWrapper.vue
  24. 50 0
      apps/playground/src/components/Textures.vue
  25. 21 0
      apps/playground/src/components/TheInfo.astro
  26. 2 4
      apps/playground/src/components/TresDonut.vue
  27. 20 0
      apps/playground/src/components/portal-journey/TheExperience.vue
  28. 43 0
      apps/playground/src/components/portal-journey/TheFireFlies.vue
  29. 83 0
      apps/playground/src/components/portal-journey/ThePortal.vue
  30. 7 0
      apps/playground/src/components/portal-journey/shaders/fireflies/fragment.glsl
  31. 16 0
      apps/playground/src/components/portal-journey/shaders/fireflies/vertex.glsl
  32. 101 0
      apps/playground/src/components/portal-journey/shaders/portal/fragment.glsl
  33. 11 0
      apps/playground/src/components/portal-journey/shaders/portal/vertex.glsl
  34. 8 0
      apps/playground/src/components/textures/Textures.vue
  35. 62 0
      apps/playground/src/components/textures/TheExperience.vue
  36. 75 0
      apps/playground/src/layouts/ExperimentLayout.astro
  37. 78 29
      apps/playground/src/layouts/Layout.astro
  38. 34 7
      apps/playground/src/pages/index.astro
  39. 61 0
      apps/playground/src/pages/vue/animations.mdx
  40. 59 0
      apps/playground/src/pages/vue/basic.mdx
  41. 0 11
      apps/playground/src/pages/vue/gltf-model.astro
  42. 40 0
      apps/playground/src/pages/vue/gltf-model.mdx
  43. 147 0
      apps/playground/src/pages/vue/materials.mdx
  44. 334 0
      apps/playground/src/pages/vue/portal-journey.mdx
  45. 85 0
      apps/playground/src/pages/vue/textures.mdx
  46. 0 11
      apps/playground/src/pages/vue/transform-controls.astro
  47. 111 0
      apps/playground/src/pages/vue/transform-controls.mdx
  48. 0 11
      apps/playground/src/pages/vue/tres-basic.astro
  49. 0 11
      apps/playground/src/pages/vue/tres-donut.astro
  50. 41 0
      apps/playground/src/pages/vue/tres-donut.mdx
  51. 5 1
      docs/.vitepress/config.ts
  52. 27 0
      docs/api/events.md
  53. 23 0
      docs/cientos/loaders/gltf-model.md
  54. 1 1
      docs/cientos/shapes/plane.md
  55. 1 1
      docs/index.md
  56. BIN
      docs/public/cientos-banner.png
  57. 3 3
      package.json
  58. 8 0
      packages/cientos/CHANGELOG.md
  59. 6 6
      packages/cientos/package.json
  60. 8 2
      packages/cientos/src/core/useGLTF/component.ts
  61. 7 0
      packages/tres/CHANGELOG.md
  62. 5 5
      packages/tres/package.json
  63. 59 0
      packages/tres/src/components/TheEvents.vue
  64. 24 10
      packages/tres/src/components/TheExperience.vue
  65. 1 2
      packages/tres/src/core/useCatalogue/index.ts
  66. 48 28
      packages/tres/src/core/useInstanceCreator/index.ts
  67. 27 0
      packages/tres/src/core/useRaycaster/index.ts
  68. 5 1
      packages/tres/src/core/useScene/component.ts
  69. 2 2
      packages/tres/src/core/useTres/index.ts
  70. 2 1
      packages/tres/src/index.ts
  71. 10 0
      packages/tres/src/types/index.d.ts
  72. 178 421
      pnpm-lock.yaml

+ 13 - 0
.vitepress/cache/deps_temp/_metadata.json

@@ -0,0 +1,13 @@
+{
+  "hash": "70fc7654",
+  "browserHash": "018a6510",
+  "optimized": {
+    "vue": {
+      "src": "../../../node_modules/.pnpm/vue@3.2.45/node_modules/vue/dist/vue.runtime.esm-bundler.js",
+      "file": "vue.js",
+      "fileHash": "3e4efe08",
+      "needsInterop": false
+    }
+  },
+  "chunks": {}
+}

+ 1 - 0
.vitepress/cache/deps_temp/package.json

@@ -0,0 +1 @@
+{"type":"module"}

Dosya farkı çok büyük olduğundan ihmal edildi
+ 88 - 0
.vitepress/cache/deps_temp/vue.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 0
.vitepress/cache/deps_temp/vue.js.map


+ 7 - 1
.vscode/settings.json

@@ -16,5 +16,11 @@
   "scss.lint.unknownAtRules": "ignore",
   "css.lint.unknownAtRules": "ignore",
   "eslint.workingDirectories": ["apps/", "packages/"],
-  "testing.automaticallyOpenPeekView": "never"
+  "testing.automaticallyOpenPeekView": "never",
+  "files.associations": {
+    "*.mdx": "markdown"
+  },
+  "[astro]": {
+    "editor.defaultFormatter": "astro-build.astro-vscode"
+  }
 }

+ 61 - 2
apps/playground/astro.config.mjs

@@ -1,9 +1,68 @@
 import { defineConfig } from 'astro/config'
-
+import glsl from 'vite-plugin-glsl'
+import UnoCSS from 'unocss/astro'
+import {
+  presetAttributify,
+  presetIcons,
+  presetTypography,
+  presetUno,
+  presetWebFonts,
+  transformerDirectives,
+  transformerVariantGroup,
+} from 'unocss'
+import presetDaisy from 'unocss-preset-daisy'
 // https://astro.build/config
 import vue from '@astrojs/vue'
 
+// https://astro.build/config
+import mdx from '@astrojs/mdx'
+
 // https://astro.build/config
 export default defineConfig({
-  integrations: [vue({ appEntrypoint: '/src/pages/_app' })],
+  vite: {
+    ssr: {
+      noExternal: ['@kidonng/daisyui'],
+    },
+    plugins: [glsl()],
+  },
+  integrations: [
+    vue({
+      appEntrypoint: '/src/pages/_app',
+    }),
+    UnoCSS({
+      presets: [
+        presetUno(),
+        presetAttributify(),
+        presetIcons({
+          scale: 1.2,
+          warn: true,
+          extraProperties: {
+            display: 'inline-block',
+            'vertical-align': 'middle',
+            // ...
+          },
+        }),
+        presetTypography({
+          cssExtend: {
+            blockquote: {
+              padding: '1rem',
+              'border-left': `8px solid #888 !important`,
+              background: '#e8e8e8',
+            },
+          },
+        }),
+        presetWebFonts({
+          fonts: {
+            sans: 'DM Sans',
+            serif: 'DM Serif Display',
+            mono: 'DM Mono',
+          },
+        }),
+        presetDaisy(),
+      ],
+      transformers: [transformerDirectives(), transformerVariantGroup()],
+      safelist: 'prose prose-sm m-auto text-left'.split(' '),
+    }),
+    mdx(),
+  ],
 })

+ 9 - 1
apps/playground/package.json

@@ -20,13 +20,21 @@
     "astro": "astro"
   },
   "dependencies": {
+    "@astrojs/mdx": "^0.14.0",
     "@astrojs/vue": "^1.2.2",
     "astro": "^1.9.2",
+    "astro-seo": "^0.7.0",
     "vue": "^3.2.45"
   },
   "devDependencies": {
+    "@iconify-json/carbon": "^1.1.13",
+    "@iconify-json/logos": "^1.1.22",
+    "@kidonng/daisyui": "^2.31.0",
     "@tresjs/cientos": "workspace:^1.4.0",
     "@tresjs/core": "workspace:^1.5.0",
-    "three": "^0.148.0"
+    "three": "^0.148.0",
+    "unocss": "^0.48.0",
+    "unocss-preset-daisy": "^1.2.0",
+    "vite-plugin-glsl": "^1.0.1"
   }
 }

BIN
apps/playground/public/animations.png


BIN
apps/playground/public/gltf-model.png


+ 5 - 0
apps/playground/public/logo-dark.svg

@@ -0,0 +1,5 @@
+<svg width="44" height="10" viewBox="0 0 44 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.14255 1.42916C5.53095 0.781817 6.46913 0.781816 6.85753 1.42915L11.0913 8.4855C11.4913 9.15203 11.0111 10 10.2338 10H1.76623C0.988935 10 0.508822 9.15203 0.908736 8.4855L5.14255 1.42916Z" fill="#82DBC5"/>
+<rect x="19" y="1" width="9" height="9" rx="1" fill="#f2f2f2"/>
+<circle cx="39.5" cy="5.5" r="4.5" fill="#EFAC35"/>
+</svg>

+ 5 - 0
apps/playground/public/logo.svg

@@ -0,0 +1,5 @@
+<svg width="44" height="10" viewBox="0 0 44 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.14255 1.42916C5.53095 0.781817 6.46913 0.781816 6.85753 1.42915L11.0913 8.4855C11.4913 9.15203 11.0111 10 10.2338 10H1.76623C0.988935 10 0.508822 9.15203 0.908736 8.4855L5.14255 1.42916Z" fill="#82DBC5"/>
+<rect x="19" y="1" width="9" height="9" rx="1" fill="#4F4F4F"/>
+<circle cx="39.5" cy="5.5" r="4.5" fill="#EFAC35"/>
+</svg>

BIN
apps/playground/public/materials.png


BIN
apps/playground/public/portal-journey.png


BIN
apps/playground/public/textures.png


BIN
apps/playground/public/transform-controls.png


BIN
apps/playground/public/tres-basic.png


BIN
apps/playground/public/tres-donut.png


+ 38 - 0
apps/playground/src/components/BasicAnimations.vue

@@ -0,0 +1,38 @@
+<script setup lang="ts">
+import { ShallowRef, shallowRef } from 'vue'
+
+import { useRenderLoop, TresInstance } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+
+const boxRef: ShallowRef<TresInstance | null> = shallowRef(null)
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ delta, elapsed }) => {
+  if (boxRef.value) {
+    boxRef.value.rotation.y += delta
+    boxRef.value.rotation.z = elapsed * 0.2
+  }
+})
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#82DBC5"
+    shadows
+    alpha
+    window-size
+    power-preference="high-performance"
+    physically-correct-lights
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[1, 2, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh ref="boxRef" :scale="1" cast-shadow>
+        <TresBoxGeometry :args="[1, 1, 1]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="2" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 0 - 62
apps/playground/src/components/Card.astro

@@ -1,62 +0,0 @@
----
-export interface Props {
-	title: string;
-	body: string;
-	href: string;
-}
-
-const { href, title, body } = Astro.props;
----
-
-<li class="link-card">
-	<a href={href}>
-		<h2>
-			{title}
-			<span>&rarr;</span>
-		</h2>
-		<p>
-			{body}
-		</p>
-	</a>
-</li>
-<style>
-	.link-card {
-		list-style: none;
-		display: flex;
-		padding: 0.15rem;
-		background-color: white;
-		background-image: var(--accent-gradient);
-		background-size: 400%;
-		border-radius: 0.5rem;
-		background-position: 100%;
-		transition: background-position 0.6s cubic-bezier(0.22, 1, 0.36, 1);
-		box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
-	}
-
-	.link-card > a {
-		width: 100%;
-		text-decoration: none;
-		line-height: 1.4;
-		padding: 1rem 1.3rem;
-		border-radius: 0.35rem;
-		color: #111;
-		background-color: white;
-		opacity: 0.8;
-	}
-	h2 {
-		margin: 0;
-		font-size: 1.25rem;
-		transition: color 0.6s cubic-bezier(0.22, 1, 0.36, 1);
-	}
-	p {
-		margin-top: 0.5rem;
-		margin-bottom: 0;
-		color: #444;
-	}
-	.link-card:is(:hover, :focus-within) {
-		background-position: 0;
-	}
-	.link-card:is(:hover, :focus-within) h2 {
-		color: rgb(var(--accent));
-	}
-</style>

+ 3 - 0
apps/playground/src/components/ExperimentInfo.vue

@@ -0,0 +1,3 @@
+<script setup lang="ts"></script>
+
+<template>Awiwi</template>

+ 28 - 0
apps/playground/src/components/Header.astro

@@ -0,0 +1,28 @@
+---
+import Logo from '/logo.svg'
+
+export interface Props {
+  title: string
+  visible: boolean
+}
+
+const { visible = false, title } = Astro.props
+---
+
+<header class={`fixed top-0 z-10 w-full bg-white bg-opacity-60 py-4 ${visible ? 'visible' : 'hidden'}`}>
+  <div class="px-4 sm:px-0 container mx-auto flex justify-between">
+    <div class="flex">
+      <img src={Logo} class="mr-8" />
+      <a class="font-bold" href="/" title="Home">{title}</a>
+    </div>
+    <ul class="flex">
+      <a href="https://tresjs.org/" target="_blank" class="i-carbon-document"></a>
+    </ul>
+  </div>
+</header>
+
+<style scoped>
+  header {
+    backdrop-filter: blur(10px);
+  }
+</style>

+ 126 - 0
apps/playground/src/components/Materials.vue

@@ -0,0 +1,126 @@
+<script setup lang="ts">
+import { OrbitControls, Plane, useTweakPane } from '@tresjs/cientos'
+import {
+  BasicShadowMap,
+  Color,
+  CubeTextureLoader,
+  MeshBasicMaterial,
+  MeshLambertMaterial,
+  MeshMatcapMaterial,
+  MeshNormalMaterial,
+  MeshPhongMaterial,
+  MeshPhysicalMaterial,
+  MeshStandardMaterial,
+  MeshToonMaterial,
+  NoToneMapping,
+  sRGBEncoding,
+} from 'three'
+import { shallowReactive, shallowRef } from 'vue'
+
+const { pane } = useTweakPane()
+const state = shallowReactive({
+  clearColor: '#4f4f4f',
+  shadows: true,
+  alpha: false,
+  physicallyCorrectLights: true,
+  shadowMapType: BasicShadowMap,
+  outputEncoding: sRGBEncoding,
+  toneMapping: NoToneMapping,
+})
+
+const cubeTextureLoader = new CubeTextureLoader()
+
+const environmentMap = cubeTextureLoader.load([
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/px.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/nx.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/py.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/ny.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/pz.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/nz.jpg',
+])
+
+const materialState = shallowReactive({
+  color: '#008080',
+  metalness: 0.5,
+  wireframe: false,
+})
+
+const sphereRef = shallowRef()
+pane
+  .addInput(materialState, 'color', {
+    label: 'Color',
+  })
+  .on('change', (ev: any) => {
+    sphereRef.value.material.color = new Color(ev.value)
+  })
+
+pane
+  .addInput(materialState, 'wireframe', {
+    label: 'Wireframe',
+  })
+  .on('change', (ev: any) => {
+    sphereRef.value.material.wireframe = ev.value
+  })
+
+const materialProps = ['metalness', 'roughness']
+
+materialProps.forEach(element => {
+  pane
+    .addBlade({
+      view: 'slider',
+      label: element,
+      min: 0,
+      max: 1,
+      value: 0.5,
+    })
+    .on('change', (ev: any) => {
+      materialState[element] = ev.value
+      sphereRef.value.material[element] = ev.value
+    })
+})
+
+pane
+  .addBlade({
+    view: 'list',
+    label: 'Materials',
+    options: [
+      { text: 'MeshBasicMaterial', value: MeshBasicMaterial },
+      { text: 'MeshToonMaterial', value: MeshToonMaterial },
+      /*    { text: 'MeshDepthMaterial', value: MeshDepthMaterial },
+      { text: 'MeshDistanceMaterial', value: MeshDistanceMaterial }, */
+      { text: 'MeshLambertMaterial', value: MeshLambertMaterial },
+      { text: 'MeshMatcapMaterial', value: MeshMatcapMaterial },
+      { text: 'MeshNormalMaterial', value: MeshNormalMaterial },
+      { text: 'MeshPhongMaterial', value: MeshPhongMaterial },
+      { text: 'MeshPhysicalMaterial', value: MeshPhysicalMaterial },
+      { text: 'MeshStandardMaterial', value: MeshStandardMaterial },
+    ],
+    value: MeshToonMaterial,
+  })
+  .on('change', ev => {
+    sphereRef.value.material = new ev.value(materialState)
+
+    if (ev.value === MeshStandardMaterial || ev.value === MeshPhysicalMaterial) {
+      sphereRef.value.material.envMap = environmentMap
+    }
+  })
+</script>
+<template>
+  <TresCanvas v-bind="state">
+    <TresPerspectiveCamera :position="[3, 3, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <OrbitControls />
+    <TresScene>
+      <TresMesh ref="sphereRef" :position="[0, 1, 0]" cast-shadow recieve-shadow>
+        <TresSphereGeometry :args="[1, 32, 32]" />
+        <TresMeshToonMaterial :color="materialState.color" />
+      </TresMesh>
+      <Plane :args="[10, 10]" recieve-shadow>
+        <TresMeshToonMaterial color="#222" />
+      </Plane>
+      <TresDirectionalLight :position="[-3, 4, 4]" :intensity="2" cast-shadow />
+      <TresDirectionalLight :position="[3, 4, 0]" :intensity="0.5" cast-shadow />
+
+      <TresAmbientLight :intensity="1" />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 13 - 0
apps/playground/src/components/SuspenseWrapper.vue

@@ -0,0 +1,13 @@
+<script setup lang="ts">
+defineProps<{
+  component: any
+}>()
+
+/* import Textures from './Textures.vue' */
+</script>
+<template>
+  <pre>{{ component }}.</pre>
+  <Suspense>
+    <component :is="component.__name" />
+  </Suspense>
+</template>

+ 50 - 0
apps/playground/src/components/Textures.vue

@@ -0,0 +1,50 @@
+<script setup lang="ts">
+import { Ref, ref } from 'vue'
+
+import { useTexture, useRenderLoop } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+import { TresInstance } from '@tresjs/core/dist/types'
+
+const pbrTexture = await useTexture({
+  map: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  displacementMap:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  roughnessMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Roughness.jpg',
+  normalMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_NormalGL.jpg',
+  ambientOcclusion:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_AmbientOcclusion.jpg',
+})
+
+const sphereRef: Ref<TresInstance | null> = ref(null)
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ delta }) => {
+  // I will run at every frame ~ 60FPS (depending of your monitor)
+  if (sphereRef.value) {
+    sphereRef.value.rotation.y = delta
+  }
+})
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#82DBC5"
+    shadows
+    alpha
+    window-size
+    power-preference="high-performance"
+    preserve-drawing-buffer
+    physically-correct-lights
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[1, 2, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh ref="sphereRef" :scale="1" cast-shadow>
+        <TresSphereGeometry :args="[1, 100, 100]" />
+        <TresMeshStandardMaterial v-bind="pbrTexture" displacement-scale="0.2" />
+      </TresMesh>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="2" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 21 - 0
apps/playground/src/components/TheInfo.astro

@@ -0,0 +1,21 @@
+---
+/* const { path } = Astro.params
+
+console.log(Astro) */
+---
+
+<div class="fixed bottom-0 right-0">
+  <div class="dropdown dropdown-top dropdown-end p-4 w-full flex justify-end z-60">
+    <label
+      tabindex={0}
+      class="btn btn-circle important:(bg-white border-white text-gray-600 dark:text-light-200 hover:bg-gray-100) cursor-pointer"
+      ><i class="i-carbon-information"></i></label
+    >
+    <div
+      tabindex={0}
+      class="mb-8 dropdown-content px-4 shadow bg-base-100 rounded-box pb-8 mr-4 z-20 overflow-scroll max-h-screen-md"
+    >
+      <div class="prose"><slot /></div>
+    </div>
+  </div>
+</div>

+ 2 - 4
apps/playground/src/components/TresDonut.vue

@@ -1,18 +1,16 @@
 <script setup lang="ts">
 import { reactive } from 'vue'
 
-import { OrbitControls } from '@tresjs/cientos'
-
 const state = reactive({
   shadows: true,
   alpha: false,
+  clearColor: '#4f4f4f',
 })
 </script>
 
 <template>
   <TresCanvas v-bind="state">
-    <OrbitControls />
-    <TresPerspectiveCamera :position="[8, 8, 8]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresPerspectiveCamera :position="[0, 0, 6]" :fov="75" :aspect="1" :near="0.1" :far="1000" />
     <TresScene>
       <TresMesh>
         <TresTorusGeometry :args="[1, 0.5, 16, 32]" />

+ 20 - 0
apps/playground/src/components/portal-journey/TheExperience.vue

@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import ThePortal from './ThePortal.vue'
+import { OrbitControls } from '@tresjs/cientos'
+import TheFireFlies from './TheFireFlies.vue'
+</script>
+
+<template>
+  <Suspense>
+    <TresCanvas clear-color="#201919" shadows alpha window-size power-preference="high-performance">
+      <OrbitControls />
+      <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+      <TresScene>
+        <TresFog :args="['#201919', 0.1, 75]" />
+        <ThePortal />
+        <TheFireFlies />
+        <TresAmbientLight :position="[10, 10, 10]" :intensity="1.5" color="#00ff00" />
+      </TresScene>
+    </TresCanvas>
+  </Suspense>
+</template>

+ 43 - 0
apps/playground/src/components/portal-journey/TheFireFlies.vue

@@ -0,0 +1,43 @@
+<script setup lang="ts">
+import { useRenderLoop } from '@tresjs/core'
+import { AdditiveBlending } from 'three'
+import FirefliesVertex from './shaders/fireflies/vertex.glsl'
+import FirefliesFragment from './shaders/fireflies/fragment.glsl'
+
+const shader = {
+  transparent: true,
+  blending: AdditiveBlending,
+  depthWrite: false,
+
+  vertexShader: FirefliesVertex,
+  fragmentShader: FirefliesFragment,
+  uniforms: {
+    uSize: { value: 100 },
+    uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
+    uTime: { value: 0 },
+  },
+}
+
+const firefliesCount = 60
+const positionArray = new Float32Array(firefliesCount * 3)
+const scaleArray = new Float32Array(firefliesCount)
+
+for (let i = 0; i < firefliesCount; i++) {
+  positionArray[i * 3 + 0] = (Math.random() - 0.5) * 4
+  positionArray[i * 3 + 1] = Math.random() * 4
+  positionArray[i * 3 + 2] = (Math.random() - 0.5) * 4
+  scaleArray[i] = Math.random()
+}
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ elapsed }) => {
+  shader.uniforms.uTime.value = elapsed
+})
+</script>
+<template>
+  <TresPoints>
+    <TresBufferGeometry :position="[positionArray, 3]" :a-scale="[scaleArray, 1]" />
+    <TresShaderMaterial v-bind="shader" />
+  </TresPoints>
+</template>

+ 83 - 0
apps/playground/src/components/portal-journey/ThePortal.vue

@@ -0,0 +1,83 @@
+<script setup lang="ts">
+import { sRGBEncoding, DoubleSide, MeshBasicMaterial, ShaderMaterial, Color, Mesh } from 'three'
+import { useRenderLoop, useTexture } from '@tresjs/core'
+import { useGLTF, useTweakPane } from '@tresjs/cientos'
+
+import PortalVertex from './shaders/portal/vertex.glsl'
+import PortalFragment from './shaders/portal/fragment.glsl'
+
+const experiment = {
+  portalColorStart: '#7030eb',
+  portalColorEnd: '#ddc0ff',
+}
+
+const { pane } = useTweakPane()
+
+const portalCtrls = pane.addFolder({ title: 'Portal' })
+portalCtrls
+  .addInput(experiment, 'portalColorStart', {
+    label: 'color start',
+    min: 0,
+    max: 1,
+    step: 0.01,
+  })
+  .on('change', ({ value }) => {
+    portalLightMaterial.uniforms.uColorStart.value.set(value)
+  })
+portalCtrls
+  .addInput(experiment, 'portalColorEnd', {
+    label: 'color end',
+    min: 0,
+    max: 1,
+    step: 0.01,
+  })
+  .on('change', ({ value }) => {
+    portalLightMaterial.uniforms.uColorEnd.value.set(value)
+  })
+
+const { scene: portal } = await useGLTF(
+  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/portal/portal.glb',
+  {
+    draco: true,
+  },
+)
+
+const bakedTexture = await useTexture([
+  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/portal/baked.jpg',
+])
+
+bakedTexture.flipY = false
+bakedTexture.encoding = sRGBEncoding
+
+// Baked material
+const bakedMaterial = new MeshBasicMaterial({
+  map: bakedTexture,
+  side: DoubleSide,
+})
+
+const portalLightMaterial = new ShaderMaterial({
+  uniforms: {
+    uTime: { value: 0 },
+    uColorStart: { value: new Color(experiment.portalColorStart) },
+    uColorEnd: { value: new Color(experiment.portalColorEnd) },
+  },
+  vertexShader: PortalVertex,
+  fragmentShader: PortalFragment,
+  side: DoubleSide,
+})
+
+const portalObj = portal
+const bakedMesh = portalObj.children.find(child => child.name === 'baked')
+;(bakedMesh as Mesh).material = bakedMaterial
+const portalCircle = portalObj.children.find(child => child.name === 'portalCircle')
+;(portalCircle as Mesh).material = portalLightMaterial
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ _delta, elapsed }) => {
+  portalLightMaterial.uniforms.uTime.value = elapsed
+})
+</script>
+<template>
+  <TresMesh v-bind="portal" />
+</template>

+ 7 - 0
apps/playground/src/components/portal-journey/shaders/fireflies/fragment.glsl

@@ -0,0 +1,7 @@
+void main()
+{
+  float distanceToCenter = distance(gl_PointCoord, vec2(0.5));
+  float strength = 0.05 / distanceToCenter - 0.1;
+
+  gl_FragColor = vec4(1.0, 1.0, 1.0, strength);
+}

+ 16 - 0
apps/playground/src/components/portal-journey/shaders/fireflies/vertex.glsl

@@ -0,0 +1,16 @@
+uniform float uPixelRatio;
+uniform float uSize;
+uniform float uTime;
+attribute float aScale;
+
+void main()
+{
+    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+    modelPosition.y += sin(uTime + modelPosition.x * 100.0) * aScale * 0.2;
+    vec4 viewPosition = viewMatrix * modelPosition;
+    vec4 projectionPosition = projectionMatrix * viewPosition;
+
+    gl_Position = projectionPosition;
+    gl_PointSize = aScale * uSize * uPixelRatio;
+    gl_PointSize *= (1.0 / - viewPosition.z);
+}

+ 101 - 0
apps/playground/src/components/portal-journey/shaders/portal/fragment.glsl

@@ -0,0 +1,101 @@
+varying vec2 vUv;
+uniform float uTime;
+uniform vec3 uColorStart;
+uniform vec3 uColorEnd;
+
+//	Classic Perlin 3D Noise 
+//	by Stefan Gustavson
+//
+vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
+vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
+vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}
+
+float cnoise(vec3 P){
+  vec3 Pi0 = floor(P); // Integer part for indexing
+  vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
+  Pi0 = mod(Pi0, 289.0);
+  Pi1 = mod(Pi1, 289.0);
+  vec3 Pf0 = fract(P); // Fractional part for interpolation
+  vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
+  vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
+  vec4 iy = vec4(Pi0.yy, Pi1.yy);
+  vec4 iz0 = Pi0.zzzz;
+  vec4 iz1 = Pi1.zzzz;
+
+  vec4 ixy = permute(permute(ix) + iy);
+  vec4 ixy0 = permute(ixy + iz0);
+  vec4 ixy1 = permute(ixy + iz1);
+
+  vec4 gx0 = ixy0 / 7.0;
+  vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
+  gx0 = fract(gx0);
+  vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
+  vec4 sz0 = step(gz0, vec4(0.0));
+  gx0 -= sz0 * (step(0.0, gx0) - 0.5);
+  gy0 -= sz0 * (step(0.0, gy0) - 0.5);
+
+  vec4 gx1 = ixy1 / 7.0;
+  vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
+  gx1 = fract(gx1);
+  vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
+  vec4 sz1 = step(gz1, vec4(0.0));
+  gx1 -= sz1 * (step(0.0, gx1) - 0.5);
+  gy1 -= sz1 * (step(0.0, gy1) - 0.5);
+
+  vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
+  vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
+  vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
+  vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
+  vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
+  vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
+  vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
+  vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
+
+  vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
+  g000 *= norm0.x;
+  g010 *= norm0.y;
+  g100 *= norm0.z;
+  g110 *= norm0.w;
+  vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
+  g001 *= norm1.x;
+  g011 *= norm1.y;
+  g101 *= norm1.z;
+  g111 *= norm1.w;
+
+  float n000 = dot(g000, Pf0);
+  float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
+  float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
+  float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
+  float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
+  float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
+  float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
+  float n111 = dot(g111, Pf1);
+
+  vec3 fade_xyz = fade(Pf0);
+  vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
+  vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
+  float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 
+  return 2.2 * n_xyz;
+}
+
+void main() 
+{
+    float speed = 0.1;
+    // Displace the UV coordinates by a noise value
+    vec2 displacedUv = vUv + cnoise(vec3(vUv* 5.0, uTime * speed));
+    // Get the color from the texture
+    float strength = cnoise(vec3(displacedUv * 5.0, uTime * speed * 2.0));
+
+    // Outer Glow
+
+    float outerGlow = distance(vUv, vec2(0.5)) * 5.0 - 1.4;
+    strength += outerGlow;
+
+    strength += step(- 0.2, strength) * 0.8;
+
+    strength = clamp(strength, 0.0, 1.0);
+
+    vec3 color = mix(uColorStart, uColorEnd, strength);
+
+    gl_FragColor = vec4(color, 1.0);
+}

+ 11 - 0
apps/playground/src/components/portal-journey/shaders/portal/vertex.glsl

@@ -0,0 +1,11 @@
+varying vec2 vUv;
+
+void main() 
+{
+    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+    vec4 viewPosition = viewMatrix * modelPosition;
+    vec4 projectionPosition = projectionMatrix * viewPosition;
+
+    gl_Position = projectionPosition;
+    vUv = uv;
+}

+ 8 - 0
apps/playground/src/components/textures/Textures.vue

@@ -0,0 +1,8 @@
+<script setup lang="ts">
+import TheExperience from './TheExperience.vue'
+</script>
+<template>
+  <Suspense>
+    <TheExperience />
+  </Suspense>
+</template>

+ 62 - 0
apps/playground/src/components/textures/TheExperience.vue

@@ -0,0 +1,62 @@
+<script setup lang="ts">
+import { Ref, ref } from 'vue'
+
+import { useTexture, useRenderLoop } from '@tresjs/core'
+import { OrbitControls, Plane } from '@tresjs/cientos'
+import { TresInstance } from '@tresjs/core/dist/types'
+
+const pbrTexture = await useTexture({
+  map: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  displacementMap:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  roughnessMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Roughness.jpg',
+  normalMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_NormalGL.jpg',
+  ambientOcclusion:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_AmbientOcclusion.jpg',
+})
+
+const blackRock = await useTexture({
+  map: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Color.jpg',
+  displacementMap:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  roughnessMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Roughness.jpg',
+  normalMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_NormalGL.jpg',
+  ambientOcclusion:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_AmbientOcclusion.jpg',
+})
+
+const sphereRef: Ref<TresInstance | null> = ref(null)
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ delta }) => {
+  // I will run at every frame ~ 60FPS (depending of your monitor)
+  if (sphereRef.value) {
+    sphereRef.value.rotation.y += delta
+  }
+})
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#82DBC5"
+    shadows
+    alpha
+    window-size
+    power-preference="high-performance"
+    preserve-drawing-buffer
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh ref="sphereRef" :position="[0, 4, 0]" :scale="1" cast-shadow>
+        <TresSphereGeometry :args="[1, 500, 500]" />
+        <TresMeshStandardMaterial v-bind="blackRock" displacement-scale="0.2" />
+      </TresMesh>
+      <Plane :args="[10, 10, 500, 500]">
+        <TresMeshStandardMaterial v-bind="pbrTexture" displacement-scale="0.6" />
+      </Plane>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="1" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 75 - 0
apps/playground/src/layouts/ExperimentLayout.astro

@@ -0,0 +1,75 @@
+---
+import { SEO } from 'astro-seo'
+import '@kidonng/daisyui/index.css'
+
+const { frontmatter } = Astro.props
+---
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width" />
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+    <meta name="generator" content={Astro.generator} />
+    <SEO
+      title={frontmatter.title}
+      description={frontmatter.description}
+      openGraph={{
+        basic: {
+          title: frontmatter.title,
+          type: 'website',
+          image: frontmatter.thumbnail,
+        },
+        image: {
+          alt: 'TresJS',
+        },
+      }}
+      twitter={{
+        creator: '@alvarosabu',
+      }}
+      extend={{
+        // extending the default link tags
+        link: [{ rel: 'icon', href: '/favicon.ico' }],
+        // extending the default meta tags
+        meta: [
+          {
+            name: 'twitter:image',
+            content: frontmatter.thumbnail,
+          },
+          { name: 'twitter:title', content: frontmatter.title },
+          { name: 'twitter:description', content: frontmatter.description },
+          { name: 'twitter:card', content: 'summary_large_image' },
+          { name: 'twitter:site', content: '@alvarosabu' },
+          { name: 'twitter:creator', content: '@alvarosabu' },
+          { name: 'og:title', content: frontmatter.title },
+          { name: 'og:description', content: frontmatter.description },
+          { name: 'og:image', content: frontmatter.thumbnail },
+          { name: 'og:url', content: frontmatter.url },
+          { name: 'og:site_name', content: 'TresJS' },
+        ],
+      }}
+    />
+    <title>{frontmatter.title}</title>
+  </head>
+  <body>
+    <slot />
+    <style is:global>
+      :root {
+        --accent: 124, 58, 237;
+        --accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
+      }
+      html {
+        font-family: system-ui, sans-serif;
+        background-color: #f6f6f6;
+      }
+      body {
+        margin: 0;
+      }
+      code {
+        font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono,
+          Courier New, monospace;
+      }
+    </style>
+  </body>
+</html>

+ 78 - 29
apps/playground/src/layouts/Layout.astro

@@ -1,38 +1,87 @@
 ---
+import { SEO } from 'astro-seo'
+
+import Header from '../components/Header.astro'
+
 export interface Props {
-	title: string;
+  title: string
 }
 
-const { title } = Astro.props;
+const { title } = Astro.props
+
+const meta = {
+  description: 'Playground for TresJS experiments for Vue',
+  thumbnail: 'https://pbs.twimg.com/media/FjtkgAlXwAIF7a_?format=png&name=4096x4096',
+  url: 'https://playground.tresjs.org/',
+}
 ---
 
 <!DOCTYPE html>
 <html lang="en">
-	<head>
-		<meta charset="UTF-8" />
-		<meta name="viewport" content="width=device-width" />
-		<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
-		<meta name="generator" content={Astro.generator} />
-		<title>{title}</title>
-	</head>
-	<body>
-		<slot />
-	</body>
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width" />
+    <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
+    <meta name="generator" content={Astro.generator} />
+    <title>{title}</title>
+    <SEO
+      title={title}
+      description={meta.description}
+      openGraph={{
+        basic: {
+          title: title,
+          type: 'website',
+          image: meta.thumbnail,
+        },
+        image: {
+          alt: 'TresJS',
+        },
+      }}
+      twitter={{
+        creator: '@alvarosabu',
+      }}
+      extend={{
+        // extending the default link tags
+        link: [{ rel: 'icon', href: '/favicon.ico' }],
+        // extending the default meta tags
+        meta: [
+          {
+            name: 'twitter:image',
+            content: meta.thumbnail,
+          },
+          { name: 'twitter:title', content: title },
+          { name: 'twitter:description', content: meta.description },
+          { name: 'twitter:card', content: 'summary_large_image' },
+          { name: 'twitter:site', content: '@alvarosabu' },
+          { name: 'twitter:creator', content: '@alvarosabu' },
+          { name: 'og:title', content: title },
+          { name: 'og:description', content: meta.description },
+          { name: 'og:image', content: meta.thumbnail },
+          { name: 'og:url', content: meta.url },
+          { name: 'og:site_name', content: 'TresJS' },
+        ],
+      }}
+    />
+  </head>
+  <body>
+    <Header title={title} visible={Astro.url.pathname === '/'} />
+    <slot />
+    <style is:global>
+      :root {
+        --accent: 124, 58, 237;
+        --accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
+      }
+      html {
+        font-family: system-ui, sans-serif;
+        background-color: #f6f6f6;
+      }
+      body {
+        margin: 0;
+      }
+      code {
+        font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono,
+          Courier New, monospace;
+      }
+    </style>
+  </body>
 </html>
-<style is:global>
-	:root {
-		--accent: 124, 58, 237;
-		--accent-gradient: linear-gradient(45deg, rgb(var(--accent)), #da62c4 30%, white 60%);
-	}
-	html {
-		font-family: system-ui, sans-serif;
-		background-color: #F6F6F6;
-	}
-	body {
-		margin: 0;
-	}
-	code {
-		font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
-			Bitstream Vera Sans Mono, Courier New, monospace;
-	}
-</style>

+ 34 - 7
apps/playground/src/pages/index.astro

@@ -1,11 +1,38 @@
 ---
-import TheExperience from '../components/TheExperience.vue';
-import Layout from '../layouts/Layout.astro';
+import Layout from '../layouts/Layout.astro'
+
+const vueExperiments = await Astro.glob('./vue/**/*.mdx')
+const experiments = [...vueExperiments].map(experiment => ({
+  framework: 'vue',
+  ...experiment,
+}))
 ---
 
-<Layout title="Welcome to Astro.">
-	<main>
-		<TheExperience client:only />
-	</main>
+<Layout title="Playground">
+  <main
+    class="container px-4 py-20 sm:px-0 mx-auto grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-8 sm:gap-12 md:gap-16"
+  >
+    {
+      experiments.map(experiment => (
+        <a href={experiment.url} class="card bg-base-100  shadow-xl rounded-lg overflow-hidden relative">
+          <figure>
+            <img src={experiment.frontmatter.thumbnail} alt={experiment.frontmatter.title} />
+          </figure>
+          <div class="card-body p-4">
+            <h2 class="card-title font-bold mb-8 text-lg">{experiment.frontmatter.title}</h2>
+            <p class="mb-4">{experiment.frontmatter.description}</p>
+            <footer class="card-actions justify-end">
+              <ul class="flex flex-wrap">
+                {experiment.frontmatter.tags.map(tag => (
+                  <li>
+                    <span class="text-xs text-gray-700 font-semibold bg-lime-200 py-1 px-2 rounded mr-2">{tag}</span>
+                  </li>
+                ))}
+              </ul>
+            </footer>
+          </div>
+        </a>
+      ))
+    }
+  </main>
 </Layout>
-

+ 61 - 0
apps/playground/src/pages/vue/animations.mdx

@@ -0,0 +1,61 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /animations.png
+title: Animations
+author: Alvarosabu
+description: A basic example of how to animate a geometry using useRendererLoop composable
+tags: ['basic', 'animations', 'useRendererLoop']
+---
+
+import BasicAnimations from '/@/components/BasicAnimations.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<BasicAnimations client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+Tutorial [here](https://tresjs.org/examples/basic-animations.html)
+
+```vue
+<script setup lang="ts">
+import { ShallowRef, shallowRef } from 'vue'
+
+import { useRenderLoop, TresInstance } from '@tresjs/core'
+import { OrbitControls } from '@tresjs/cientos'
+
+const boxRef: ShallowRef<TresInstance | null> = shallowRef(null)
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ delta, elapsed }) => {
+  if (boxRef.value) {
+    boxRef.value.rotation.y += delta
+    boxRef.value.rotation.z = elapsed * 0.2
+  }
+})
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#82DBC5"
+    shadows
+    alpha
+    window-size
+    power-preference="high-performance"
+    physically-correct-lights
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[1, 2, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh ref="boxRef" :scale="1" cast-shadow>
+        <TresBoxGeometry :args="[1, 1, 1]" />
+        <TresMeshNormalMaterial />
+      </TresMesh>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="2" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+</TheInfo>

+ 59 - 0
apps/playground/src/pages/vue/basic.mdx

@@ -0,0 +1,59 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /tres-basic.png
+title: Tres Basic
+author: Alvarosabu
+description: A basic example of how to create a scene with TresJS
+tags: ['basic', 'orbit-controls']
+---
+
+import TresBasic from '/@/components/TresBasic.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<TresBasic client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+```vue
+<script setup lang="ts">
+import { reactive } from 'vue'
+import { BasicShadowMap, sRGBEncoding, NoToneMapping } from 'three'
+
+import { OrbitControls } from '@tresjs/cientos'
+
+const state = reactive({
+  clearColor: '#82DBC5',
+  shadows: true,
+  alpha: false,
+  physicallyCorrectLights: true,
+  shadowMapType: BasicShadowMap,
+  outputEncoding: sRGBEncoding,
+  toneMapping: NoToneMapping,
+})
+</script>
+
+<template>
+  <TresCanvas v-bind="state">
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh :position="[-2, 2, 0]" :rotation="[0, Math.PI, 0]" cast-shadow>
+        <TresConeGeometry :args="[1, 1.5, 3]" />
+        <TresMeshToonMaterial color="#82DBC5" />
+      </TresMesh>
+      <TresMesh :position="[0, 0, 0]" cast-shadow>
+        <TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
+        <TresMeshToonMaterial color="#4F4F4F" />
+      </TresMesh>
+      <TresMesh :position="[2, -2, 0]" cast-shadow>
+        <TresSphereGeometry />
+        <TresMeshToonMaterial color="#FBB03B" />
+      </TresMesh>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="2" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+</TheInfo>

+ 0 - 11
apps/playground/src/pages/vue/gltf-model.astro

@@ -1,11 +0,0 @@
----
-import GLTFModel from '/@/components/GLTFModel.vue';
-import Layout from '/@/layouts/Layout.astro';
----
-
-<Layout title="Welcome to Astro.">
-	<main>
-		<GLTFModel client:only />
-	</main>
-</Layout>
-

+ 40 - 0
apps/playground/src/pages/vue/gltf-model.mdx

@@ -0,0 +1,40 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /gltf-model.png
+title: GLTF Model
+author: Alvarosabu
+description: How to add a Gltf Model to using GLTFModel component on TresJS
+tags: ['basic', 'cientos', 'gltf-model']
+---
+
+import GLTFModel from '/@/components/GLTFModel.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<GLTFModel client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+```vue
+<script setup lang="ts">
+import { sRGBEncoding } from 'three'
+
+import { OrbitControls, GLTFModel } from '@tresjs/cientos'
+</script>
+
+<template>
+  <Suspense>
+    <TresCanvas clear-color="#82DBC5" shadows alpha window-size :output-encoding="sRGBEncoding">
+      <OrbitControls />
+      <TresPerspectiveCamera :position="[5, 5, 5]" :fov="75" :near="0.1" :far="1000" />
+      <TresScene :fog="'#82DBC5'">
+        <TresAmbientLight :color="0xffffff" :intensity="0.25" />
+        <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.7" cast-shadow />
+        <GLTFModel path="https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/aku-aku/AkuAku.gltf" draco />
+      </TresScene>
+    </TresCanvas>
+  </Suspense>
+</template>
+```
+
+</TheInfo>

+ 147 - 0
apps/playground/src/pages/vue/materials.mdx

@@ -0,0 +1,147 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /materials.png
+title: Materials
+author: Alvarosabu
+description: A basic example of how to create a scene with TresJS
+tags: ['basic', 'materials', 'useTweakPane']
+---
+
+import Materials from '/@/components/Materials.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<Materials client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+```vue
+<script setup lang="ts">
+import { OrbitControls, Plane, useTweakPane } from '@tresjs/cientos'
+import {
+  BasicShadowMap,
+  Color,
+  CubeTextureLoader,
+  MeshBasicMaterial,
+  MeshLambertMaterial,
+  MeshMatcapMaterial,
+  MeshNormalMaterial,
+  MeshPhongMaterial,
+  MeshPhysicalMaterial,
+  MeshStandardMaterial,
+  MeshToonMaterial,
+  NoToneMapping,
+  sRGBEncoding,
+} from 'three'
+import { shallowReactive, shallowRef } from 'vue'
+
+const { pane } = useTweakPane()
+const state = shallowReactive({
+  clearColor: '#4f4f4f',
+  shadows: true,
+  alpha: false,
+  physicallyCorrectLights: true,
+  shadowMapType: BasicShadowMap,
+  outputEncoding: sRGBEncoding,
+  toneMapping: NoToneMapping,
+})
+
+const cubeTextureLoader = new CubeTextureLoader()
+
+const environmentMap = cubeTextureLoader.load([
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/px.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/nx.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/py.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/ny.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/pz.jpg',
+  'https://raw.githubusercontent.com/Tresjs/assets/main/textures/environmentMap/nz.jpg',
+])
+
+const materialState = shallowReactive({
+  color: '#008080',
+  metalness: 0.5,
+  wireframe: false,
+})
+
+const sphereRef = shallowRef()
+pane
+  .addInput(materialState, 'color', {
+    label: 'Color',
+  })
+  .on('change', (ev: any) => {
+    sphereRef.value.material.color = new Color(ev.value)
+  })
+
+pane
+  .addInput(materialState, 'wireframe', {
+    label: 'Wireframe',
+  })
+  .on('change', (ev: any) => {
+    sphereRef.value.material.wireframe = ev.value
+  })
+
+const materialProps = ['metalness', 'roughness']
+
+materialProps.forEach(element => {
+  pane
+    .addBlade({
+      view: 'slider',
+      label: element,
+      min: 0,
+      max: 1,
+      value: 0.5,
+    })
+    .on('change', (ev: any) => {
+      materialState[element] = ev.value
+      sphereRef.value.material[element] = ev.value
+    })
+})
+
+pane
+  .addBlade({
+    view: 'list',
+    label: 'Materials',
+    options: [
+      { text: 'MeshBasicMaterial', value: MeshBasicMaterial },
+      { text: 'MeshToonMaterial', value: MeshToonMaterial },
+      /*    { text: 'MeshDepthMaterial', value: MeshDepthMaterial },
+      { text: 'MeshDistanceMaterial', value: MeshDistanceMaterial }, */
+      { text: 'MeshLambertMaterial', value: MeshLambertMaterial },
+      { text: 'MeshMatcapMaterial', value: MeshMatcapMaterial },
+      { text: 'MeshNormalMaterial', value: MeshNormalMaterial },
+      { text: 'MeshPhongMaterial', value: MeshPhongMaterial },
+      { text: 'MeshPhysicalMaterial', value: MeshPhysicalMaterial },
+      { text: 'MeshStandardMaterial', value: MeshStandardMaterial },
+    ],
+    value: MeshToonMaterial,
+  })
+  .on('change', ev => {
+    sphereRef.value.material = new ev.value(materialState)
+
+    if (ev.value === MeshStandardMaterial || ev.value === MeshPhysicalMaterial) {
+      sphereRef.value.material.envMap = environmentMap
+    }
+  })
+</script>
+<template>
+  <TresCanvas v-bind="state">
+    <TresPerspectiveCamera :position="[3, 3, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <OrbitControls />
+    <TresScene>
+      <TresMesh ref="sphereRef" :position="[0, 1, 0]" cast-shadow recieve-shadow>
+        <TresSphereGeometry :args="[1, 32, 32]" />
+        <TresMeshToonMaterial :color="materialState.color" />
+      </TresMesh>
+      <Plane :args="[10, 10]" recieve-shadow>
+        <TresMeshToonMaterial color="#222" />
+      </Plane>
+      <TresDirectionalLight :position="[-3, 4, 4]" :intensity="2" cast-shadow />
+      <TresDirectionalLight :position="[3, 4, 0]" :intensity="0.5" cast-shadow />
+
+      <TresAmbientLight :intensity="1" />
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+</TheInfo>

+ 334 - 0
apps/playground/src/pages/vue/portal-journey.mdx

@@ -0,0 +1,334 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /portal-journey.png
+title: Portal Journey
+author: Alvarosabu
+description: A basic example of how to create a scene with TresJS
+tags: ['gltf', 'shaders', 'useTexture', 'useGLTF', 'useTweakPane', 'baked']
+---
+
+import TheExperience from '/@/components/portal-journey/TheExperience.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<TheExperience client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+Famous portal scene from the amazing [Three.js Journey](https://threejs-journey.com/) course by [Bruno Simon](https://bruno-simon.com/)
+
+```vue
+<script setup lang="ts">
+import { OrbitControls } from '@tresjs/cientos'
+import ThePortal from './ThePortal.vue'
+import TheFireFlies from './TheFireFlies.vue'
+</script>
+
+<template>
+  <Suspense>
+    <TresCanvas clear-color="#201919" shadows alpha window-size power-preference="high-performance">
+      <OrbitControls />
+      <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+      <TresScene>
+        <TresFog :args="['#201919', 0.1, 75]" />
+        <ThePortal />
+        <TheFireFlies />
+        <TresAmbientLight :position="[10, 10, 10]" :intensity="1.5" color="#00ff00" />
+      </TresScene>
+    </TresCanvas>
+  </Suspense>
+</template>
+```
+
+## ThePortal.vue
+
+```vue
+<script setup lang="ts">
+import { sRGBEncoding, DoubleSide, MeshBasicMaterial, ShaderMaterial, Color, Mesh } from 'three'
+import { useRenderLoop, useTexture } from '@tresjs/core'
+import { useGLTF, useTweakPane } from '@tresjs/cientos'
+
+import PortalVertex from './shaders/portal/vertex.glsl'
+import PortalFragment from './shaders/portal/fragment.glsl'
+
+const experiment = {
+  portalColorStart: '#7030eb',
+  portalColorEnd: '#ddc0ff',
+}
+
+const { pane } = useTweakPane()
+
+const portalCtrls = pane.addFolder({ title: 'Portal' })
+portalCtrls
+  .addInput(experiment, 'portalColorStart', {
+    label: 'color start',
+    min: 0,
+    max: 1,
+    step: 0.01,
+  })
+  .on('change', ({ value }) => {
+    portalLightMaterial.uniforms.uColorStart.value.set(value)
+  })
+portalCtrls
+  .addInput(experiment, 'portalColorEnd', {
+    label: 'color end',
+    min: 0,
+    max: 1,
+    step: 0.01,
+  })
+  .on('change', ({ value }) => {
+    portalLightMaterial.uniforms.uColorEnd.value.set(value)
+  })
+
+const { scene: portal } = await useGLTF(
+  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/portal/portal.glb',
+  {
+    draco: true,
+  },
+)
+
+const bakedTexture = await useTexture([
+  'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/portal/baked.jpg',
+])
+
+bakedTexture.flipY = false
+bakedTexture.encoding = sRGBEncoding
+
+// Baked material
+const bakedMaterial = new MeshBasicMaterial({
+  map: bakedTexture,
+  side: DoubleSide,
+})
+
+const portalLightMaterial = new ShaderMaterial({
+  uniforms: {
+    uTime: { value: 0 },
+    uColorStart: { value: new Color(experiment.portalColorStart) },
+    uColorEnd: { value: new Color(experiment.portalColorEnd) },
+  },
+  vertexShader: PortalVertex,
+  fragmentShader: PortalFragment,
+  side: DoubleSide,
+})
+
+const portalObj = portal
+const bakedMesh = portalObj.children.find(child => child.name === 'baked')
+;(bakedMesh as Mesh).material = bakedMaterial
+const portalCircle = portalObj.children.find(child => child.name === 'portalCircle')
+;(portalCircle as Mesh).material = portalLightMaterial
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ _delta, elapsed }) => {
+  portalLightMaterial.uniforms.uTime.value = elapsed
+})
+</script>
+<template>
+  <TresMesh v-bind="portal" />
+</template>
+```
+
+### Shaders
+
+```glsl
+// portal/vertex.glsl
+varying vec2 vUv;
+
+void main()
+{
+    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+    vec4 viewPosition = viewMatrix * modelPosition;
+    vec4 projectionPosition = projectionMatrix * viewPosition;
+
+    gl_Position = projectionPosition;
+    vUv = uv;
+}
+```
+
+```glsl
+// portal/fragment.glsl
+varying vec2 vUv;
+uniform float uTime;
+uniform vec3 uColorStart;
+uniform vec3 uColorEnd;
+
+//	Classic Perlin 3D Noise
+//	by Stefan Gustavson
+//
+vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
+vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
+vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}
+
+float cnoise(vec3 P){
+vec3 Pi0 = floor(P); // Integer part for indexing
+vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
+Pi0 = mod(Pi0, 289.0);
+Pi1 = mod(Pi1, 289.0);
+vec3 Pf0 = fract(P); // Fractional part for interpolation
+vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
+vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
+vec4 iy = vec4(Pi0.yy, Pi1.yy);
+vec4 iz0 = Pi0.zzzz;
+vec4 iz1 = Pi1.zzzz;
+
+vec4 ixy = permute(permute(ix) + iy);
+vec4 ixy0 = permute(ixy + iz0);
+vec4 ixy1 = permute(ixy + iz1);
+
+vec4 gx0 = ixy0 / 7.0;
+vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
+gx0 = fract(gx0);
+vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
+vec4 sz0 = step(gz0, vec4(0.0));
+gx0 -= sz0 * (step(0.0, gx0) - 0.5);
+gy0 -= sz0 * (step(0.0, gy0) - 0.5);
+
+vec4 gx1 = ixy1 / 7.0;
+vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
+gx1 = fract(gx1);
+vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
+vec4 sz1 = step(gz1, vec4(0.0));
+gx1 -= sz1 * (step(0.0, gx1) - 0.5);
+gy1 -= sz1 * (step(0.0, gy1) - 0.5);
+
+vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
+vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
+vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
+vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
+vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
+vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
+vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
+vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
+
+vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
+g000 *= norm0.x;
+g010 *= norm0.y;
+g100 *= norm0.z;
+g110 *= norm0.w;
+vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
+g001 *= norm1.x;
+g011 *= norm1.y;
+g101 *= norm1.z;
+g111 *= norm1.w;
+
+float n000 = dot(g000, Pf0);
+float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
+float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
+float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
+float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
+float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
+float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
+float n111 = dot(g111, Pf1);
+
+vec3 fade_xyz = fade(Pf0);
+vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
+vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
+float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x);
+return 2.2 * n_xyz;
+}
+
+void main()
+{
+  float speed = 0.1;
+  // Displace the UV coordinates by a noise value
+  vec2 displacedUv = vUv + cnoise(vec3(vUv* 5.0, uTime * speed));
+  // Get the color from the texture
+  float strength = cnoise(vec3(displacedUv * 5.0, uTime * speed * 2.0));
+
+  // Outer Glow
+
+  float outerGlow = distance(vUv, vec2(0.5)) * 5.0 - 1.4;
+  strength += outerGlow;
+
+  strength += step(- 0.2, strength) * 0.8;
+
+  strength = clamp(strength, 0.0, 1.0);
+
+  vec3 color = mix(uColorStart, uColorEnd, strength);
+
+  gl_FragColor = vec4(color, 1.0);
+}
+```
+
+## TheFireFlies.vue
+
+```vue
+<script setup lang="ts">
+import { useRenderLoop } from '@tresjs/core'
+import { AdditiveBlending } from 'three'
+import FirefliesVertex from './shaders/fireflies/vertex.glsl'
+import FirefliesFragment from './shaders/fireflies/fragment.glsl'
+
+const shader = {
+  transparent: true,
+  blending: AdditiveBlending,
+  depthWrite: false,
+
+  vertexShader: FirefliesVertex,
+  fragmentShader: FirefliesFragment,
+  uniforms: {
+    uSize: { value: 100 },
+    uPixelRatio: { value: Math.min(window.devicePixelRatio, 2) },
+    uTime: { value: 0 },
+  },
+}
+
+const firefliesCount = 60
+const positionArray = new Float32Array(firefliesCount * 3)
+const scaleArray = new Float32Array(firefliesCount)
+
+for (let i = 0; i < firefliesCount; i++) {
+  positionArray[i * 3 + 0] = (Math.random() - 0.5) * 4
+  positionArray[i * 3 + 1] = Math.random() * 4
+  positionArray[i * 3 + 2] = (Math.random() - 0.5) * 4
+  scaleArray[i] = Math.random()
+}
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ elapsed }) => {
+  shader.uniforms.uTime.value = elapsed
+})
+</script>
+<template>
+  <TresPoints>
+    <TresBufferGeometry :position="[positionArray, 3]" :a-scale="[scaleArray, 1]" />
+    <TresShaderMaterial v-bind="shader" />
+  </TresPoints>
+</template>
+```
+
+### Shaders
+
+```glsl
+// fireflies/vertex.glsl
+uniform float uPixelRatio;
+uniform float uSize;
+uniform float uTime;
+attribute float aScale;
+
+void main()
+{
+    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
+    modelPosition.y += sin(uTime + modelPosition.x * 100.0) * aScale * 0.2;
+    vec4 viewPosition = viewMatrix * modelPosition;
+    vec4 projectionPosition = projectionMatrix * viewPosition;
+
+    gl_Position = projectionPosition;
+    gl_PointSize = aScale * uSize * uPixelRatio;
+    gl_PointSize *= (1.0 / - viewPosition.z);
+}
+```
+
+```glsl
+// fireflies/fragment.glsl
+void main()
+{
+  float distanceToCenter = distance(gl_PointCoord, vec2(0.5));
+  float strength = 0.05 / distanceToCenter - 0.1;
+
+  gl_FragColor = vec4(1.0, 1.0, 1.0, strength);
+}
+```
+
+</TheInfo>

+ 85 - 0
apps/playground/src/pages/vue/textures.mdx

@@ -0,0 +1,85 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /textures.png
+title: Textures
+author: Alvarosabu
+description: A basic example of how to load textures using the useTexture composables and suspense
+tags: ['basic', 'useTexture', 'suspense']
+---
+
+import Textures from '/@/components/textures/Textures.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<Textures client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+Tutorial available [here](https://tresjs.org/examples/load-textures.html)
+
+```vue
+<script setup lang="ts">
+import { Ref, ref } from 'vue'
+
+import { useTexture, useRenderLoop } from '@tresjs/core'
+import { OrbitControls, Plane } from '@tresjs/cientos'
+import { TresInstance } from '@tresjs/core/dist/types'
+
+const pbrTexture = await useTexture({
+  map: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  displacementMap:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  roughnessMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Roughness.jpg',
+  normalMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_NormalGL.jpg',
+  ambientOcclusion:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_AmbientOcclusion.jpg',
+})
+
+const blackRock = await useTexture({
+  map: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Color.jpg',
+  displacementMap:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Displacement.jpg',
+  roughnessMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_Roughness.jpg',
+  normalMap: 'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_NormalGL.jpg',
+  ambientOcclusion:
+    'https://raw.githubusercontent.com/Tresjs/assets/main/textures/black-rock/Rock035_2K_AmbientOcclusion.jpg',
+})
+
+const sphereRef: Ref<TresInstance | null> = ref(null)
+
+const { onLoop } = useRenderLoop()
+
+onLoop(({ delta }) => {
+  // I will run at every frame ~ 60FPS (depending of your monitor)
+  if (sphereRef.value) {
+    sphereRef.value.rotation.y += delta
+  }
+})
+</script>
+
+<template>
+  <TresCanvas
+    clear-color="#82DBC5"
+    shadows
+    alpha
+    window-size
+    power-preference="high-performance"
+    preserve-drawing-buffer
+  >
+    <OrbitControls />
+    <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh ref="sphereRef" :position="[0, 4, 0]" :scale="1" cast-shadow>
+        <TresSphereGeometry :args="[1, 500, 500]" />
+        <TresMeshStandardMaterial v-bind="blackRock" displacement-scale="0.2" />
+      </TresMesh>
+      <Plane :args="[10, 10, 500, 500]">
+        <TresMeshStandardMaterial v-bind="pbrTexture" displacement-scale="0.6" />
+      </Plane>
+      <TresDirectionalLight :position="[0, 2, 4]" :intensity="1" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+</TheInfo>

+ 0 - 11
apps/playground/src/pages/vue/transform-controls.astro

@@ -1,11 +0,0 @@
----
-import TransformControls from '/@/components/TransformControls.vue';
-import Layout from '/@/layouts/Layout.astro';
----
-
-<Layout title="Transform Controls.">
-	<main>
-		<TransformControls client:only />
-	</main>
-</Layout>
-

+ 111 - 0
apps/playground/src/pages/vue/transform-controls.mdx

@@ -0,0 +1,111 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /transform-controls.png
+title: GLTF Model
+author: Alvarosabu
+description: Example of how to use Transform controls from cientos package
+tags: ['basic', 'cientos', 'use-tweak-pane', 'transform-controls']
+---
+
+import TransformControls from '/@/components/TransformControls.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<TransformControls client:only />
+
+<TheInfo >
+# { frontmatter.title }
+
+```vue
+<script setup lang="ts">
+import { shallowRef, shallowReactive } from 'vue'
+import { BasicShadowMap, sRGBEncoding, NoToneMapping } from 'three'
+
+import { OrbitControls, useTweakPane, TransformControls } from '@tresjs/cientos'
+
+const state = shallowReactive({
+  clearColor: '#201919',
+  shadows: true,
+  alpha: false,
+  physicallyCorrectLights: true,
+  shadowMapType: BasicShadowMap,
+  outputEncoding: sRGBEncoding,
+  toneMapping: NoToneMapping,
+})
+
+const transformState = shallowReactive({
+  mode: 'translate',
+  size: 1,
+  axis: 'XY',
+  showX: true,
+  showY: true,
+  showZ: true,
+})
+
+const boxRef = shallowRef()
+
+const { pane } = useTweakPane()
+
+pane
+  .addBlade({
+    view: 'list',
+    label: 'outputEncoding',
+    options: [
+      { text: 'Translate', value: 'translate' },
+      { text: 'Rotate', value: 'rotate' },
+      { text: 'Scale', value: 'scale' },
+    ],
+    value: transformState.mode,
+  })
+  .on('change', ev => {
+    transformState.mode = ev.value
+  })
+
+pane.addInput(transformState, 'size')
+
+const axisFolder = pane.addFolder({ title: 'Axis' })
+
+axisFolder
+  .addBlade({
+    view: 'list',
+    label: 'axis',
+    options: [
+      { text: 'X', value: 'X' },
+      { text: 'Y', value: 'Y' },
+      { text: 'XY', value: 'XY' },
+      { text: 'YZ', value: 'YZ' },
+      { text: 'XZ', value: 'XZ' },
+      { text: 'XYZ', value: 'XYZ' },
+    ],
+    value: transformState.axis,
+  })
+  .on('change', ev => {
+    transformState.axis = ev.value
+  })
+axisFolder.addInput(transformState, 'showX')
+axisFolder.addInput(transformState, 'showY')
+axisFolder.addInput(transformState, 'showZ')
+</script>
+
+<template>
+  <TresCanvas v-bind="state">
+    <OrbitControls make-default />
+    <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
+
+    <TresScene>
+      <TransformControls :object="boxRef" v-bind="transformState" />
+      <TresMesh ref="boxRef" :position="[0, 4, 0]" cast-shadow>
+        <TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
+        <TresMeshToonMaterial color="#FBB03B" />
+      </TresMesh>
+      <TresMesh :rotation="[-Math.PI / 2, 0, 0]" receive-shadow>
+        <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+        <TresMeshToonMaterial />
+      </TresMesh>
+      <TresAmbientLight :intensity="0.5" />
+      <TresDirectionalLight :position="[0, 8, 4]" :intensity="1.5" cast-shadow />
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+</TheInfo>

+ 0 - 11
apps/playground/src/pages/vue/tres-basic.astro

@@ -1,11 +0,0 @@
----
-import TresBasic from '/@/components/TresBasic.vue';
-import Layout from '/@/layouts/Layout.astro';
----
-
-<Layout title="Tres Basic">
-	<main>
-		<TresBasic client:only />
-	</main>
-</Layout>
-

+ 0 - 11
apps/playground/src/pages/vue/tres-donut.astro

@@ -1,11 +0,0 @@
----
-import TresDonut from '/@/components/TresDonut.vue';
-import Layout from '/@/layouts/Layout.astro';
----
-
-<Layout title="Tres Donut 🍩">
-	<main>
-		<TresDonut client:only />
-	</main>
-</Layout>
-

+ 41 - 0
apps/playground/src/pages/vue/tres-donut.mdx

@@ -0,0 +1,41 @@
+---
+layout: /@/layouts/ExperimentLayout.astro
+thumbnail: /tres-donut.png
+title: Tres Donut 🍩
+author: Alvarosabu
+description: Plain donut with TresJS
+tags: ['basic']
+---
+
+import TresDonut from '/@/components/TresDonut.vue'
+import TheInfo from '/@/components/TheInfo.astro'
+
+<TresDonut client:only />
+
+<TheInfo>
+# { frontmatter.title }
+
+```vue
+<script setup lang="ts">
+import { reactive } from 'vue'
+
+const state = reactive({
+  shadows: true,
+  alpha: false,
+})
+</script>
+
+<template>
+  <TresCanvas v-bind="state">
+    <TresPerspectiveCamera :position="[0, 0, 6]" :fov="75" :aspect="1" :near="0.1" :far="1000" />
+    <TresScene>
+      <TresMesh>
+        <TresTorusGeometry :args="[1, 0.5, 16, 32]" />
+        <TresMeshBasicMaterial color="orange" />
+      </TresMesh>
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+</TheInfo>

+ 5 - 1
docs/.vitepress/config.ts

@@ -32,6 +32,10 @@ export default defineConfig({
             text: 'Composables',
             link: '/api/composables',
           },
+          {
+            text: 'Events',
+            link: '/api/events',
+          },
         ],
       },
       {
@@ -57,7 +61,7 @@ export default defineConfig({
         ],
       },
       {
-        text: 'Cientos ⚡️',
+        text: 'Cientos 💛',
         collapsible: true,
         items: [
           { text: 'Introduction', link: '/cientos/' },

+ 27 - 0
docs/api/events.md

@@ -0,0 +1,27 @@
+# Events <Badge type="warning" text="^1.6.0" />
+
+**TresJS** Mesh objects emit pointer events when they are interacted with using `raycaster` and `pointer` objects under the hood.
+
+```html
+<TresMesh
+  @click="(ev) => console.log('click', ev)"
+  @pointer-move="(ev) => console.log('click', ev)"
+  @pointer-enter="(ev) => console.log('click', ev)"
+  @pointer-leave="(ev) => console.log('click', ev)"
+/>
+```
+
+## Event Data
+
+The event data is a `TresEvent` object that contains the following properties:
+
+```ts
+;({
+  object: Object3D, // The mesh object that emitted the event
+  distance: number, // The distance between the camera and the mesh
+  point: Vector3, // The intersection point between the ray and the mesh
+  uv: Vector2, // The uv coordinates of the intersection point
+  face: Face3, // The face of the mesh that was intersected
+  faceIndex: number, // The index of the face that was intersected
+})
+```

+ 23 - 0
docs/cientos/loaders/gltf-model.md

@@ -20,6 +20,29 @@ import { OrbitControls, GLTFModel } from '@tresjs/cientos'
 </template>
 ```
 
+## Model reference
+
+You can access the model reference by pasing a `ref` to the `model` prop and then using the method `getModel()` to get the object.
+
+```vue{3,6}
+<script setup lang="ts">
+import { OrbitControls, GLTFModel } from '@tresjs/cientos'
+
+const modelRef = shallowRef<THREE.Object3D>()
+
+watch(modelRef, ({getModel}) => {
+  const model = getModel()
+
+  model.traverse((child) => {
+    if (child.isMesh) {
+      child.castShadow = true
+      child.receiveShadow = true
+    }
+  })
+})
+</script>
+```
+
 ## Props
 
 | Prop          | Description                                                                                                           | Default     |

+ 1 - 1
docs/cientos/shapes/plane.md

@@ -1,4 +1,4 @@
-# Plane
+# Plane <Badge type="warning" text="^1.5.0" />
 
 ![](/cientos/plane.png)
 

+ 1 - 1
docs/index.md

@@ -6,7 +6,7 @@ titleTemplate: The solution for 3D on VueJS
 
 hero:
   name: TresJS
-  text: Bring ThreeJS to VueJS ecosystem
+  text: Bring Three to the Vue ecosystem
   tagline: Create awesome 3D experiences with the framework you love.
   image:
     src: /hero.png

BIN
docs/public/cientos-banner.png


+ 3 - 3
package.json

@@ -51,8 +51,8 @@
     "@changesets/changelog-github": "^0.4.7",
     "@changesets/cli": "^2.25.2",
     "@stackblitz/sdk": "^1.8.1",
-    "@tresjs/cientos": "workspace:^1.4.0",
-    "@tresjs/core": "workspace:^1.5.0",
+    "@tresjs/cientos": "workspace:^1.5.0",
+    "@tresjs/core": "workspace:^1.5.1",
     "@typescript-eslint/eslint-plugin": "^5.42.0",
     "@typescript-eslint/parser": "^5.42.0",
     "conventional-changelog-cli": "^2.2.2",
@@ -62,7 +62,7 @@
     "prettier": "^2.7.1",
     "unocss": "^0.48.0",
     "vite-svg-loader": "^4.0.0",
-    "vitepress": "1.0.0-alpha.35",
+    "vitepress": "1.0.0-alpha.38",
     "vue": "^3.2.45",
     "vue-eslint-parser": "^9.1.0"
   },

+ 8 - 0
packages/cientos/CHANGELOG.md

@@ -1,3 +1,11 @@
+# 1.5.0 (2023-01-19)
+
+### Features
+
+- **cientos:** plane abstraction ([3f27400](https://github.com/Tresjs/tres/commit/3f274007d3177bb35bcd74a705ab7c74ca3ce1cf))
+- **cientos:** updated onLoop delta usage for useAnimations ([9e7fdbd](https://github.com/Tresjs/tres/commit/9e7fdbd9d1184b8405fdc252c2ba19e53b1bf91b))
+- **cientos:** useAnimations ([2704288](https://github.com/Tresjs/tres/commit/2704288fd8d814ef9091001f3630fbdb97f13884))
+
 # 1.4.0 (2023-01-10)
 
 ### Features

+ 6 - 6
packages/cientos/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@tresjs/cientos",
   "description": "Collection of useful helpers and fully functional, ready-made abstractions for Tres",
-  "version": "1.4.0",
+  "version": "1.5.0",
   "type": "module",
   "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
   "files": [
@@ -42,20 +42,20 @@
     "vue": "^3.2.45"
   },
   "devDependencies": {
-    "@tweakpane/plugin-essentials": "^0.1.5",
+    "@tweakpane/plugin-essentials": "^0.1.7",
     "@vitejs/plugin-vue": "^4.0.0",
     "kolorist": "^1.6.0",
     "pathe": "^1.0.0",
     "rollup-plugin-analyzer": "^4.0.0",
-    "rollup-plugin-visualizer": "^5.8.3",
-    "tweakpane": "^3.1.1",
+    "rollup-plugin-visualizer": "^5.9.0",
+    "tweakpane": "^3.1.2",
     "typescript": "^4.9.4",
-    "vite": "^4.0.3",
+    "vite": "^4.0.4",
     "vite-plugin-banner": "^0.7.0",
     "vite-plugin-dts": "^1.7.1"
   },
   "dependencies": {
-    "@tresjs/core": "workspace:^1.5.0",
+    "@tresjs/core": "workspace:^1.5.1",
     "three-stdlib": "^2.21.5"
   }
 }

+ 8 - 2
packages/cientos/src/core/useGLTF/component.ts

@@ -10,13 +10,19 @@ export const GLTFModel = defineComponent({
     decoderPath: String,
   },
 
-  async setup(props) {
+  async setup(props, { expose }) {
     const scene = inject<Ref<Scene>>('local-scene')
 
+    function getModel() {
+      return model
+    }
+    expose({ getModel })
     const { scene: model } = await useGLTF(props.path as string, { draco: props.draco, decoderPath: props.decoderPath })
     if (scene?.value) {
       scene.value.add(model)
     }
-    return () => null
+    return () => {
+      model
+    }
   },
 })

+ 7 - 0
packages/tres/CHANGELOG.md

@@ -1,3 +1,10 @@
+# 1.5.1 (2023-01-19)
+
+### Bug Fixes
+
+- **core:** corret use of clock.getDelta() ([728b43e](https://github.com/Tresjs/tres/commit/728b43ee5e95549c02c98941de4091af5681fa66))
+- **core:** remove histoire ([14ab727](https://github.com/Tresjs/tres/commit/14ab727685bcab4fa78addd620f1652700ca5613))
+
 # 1.5.0 (2023-01-10)
 
 ### Features

+ 5 - 5
packages/tres/package.json

@@ -1,7 +1,7 @@
 {
   "name": "@tresjs/core",
   "description": "Declarative ThreeJS using Vue Components",
-  "version": "1.5.0",
+  "version": "1.5.1",
   "type": "module",
   "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
   "files": [
@@ -49,12 +49,12 @@
   },
   "devDependencies": {
     "@alvarosabu/utils": "^2.2.0",
-    "@tresjs/cientos": "workspace:^1.4.0",
+    "@tresjs/cientos": "workspace:^1.5.0",
     "@types/three": "^0.148.0",
     "@vitejs/plugin-vue": "^4.0.0",
     "@vitest/coverage-c8": "^0.25.8",
     "@vitest/ui": "^0.25.8",
-    "@vueuse/core": "^9.7.0",
+    "@vueuse/core": "^9.11.0",
     "gl": "6.0.1",
     "happy-dom": "^8.1.0",
     "kolorist": "^1.6.0",
@@ -63,10 +63,10 @@
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-visualizer": "^5.8.3",
     "unplugin-vue-components": "^0.22.12",
-    "vite": "^4.0.3",
+    "vite": "^4.0.4",
     "vite-plugin-banner": "^0.7.0",
     "vite-plugin-dts": "^1.7.1",
-    "vite-plugin-glsl": "^1.0.1",
+    "vite-plugin-glsl": "^1.1.0",
     "vite-plugin-require-transform": "^1.0.4",
     "vitest": "^0.25.8",
     "vue-demi": "^0.13.11"

+ 59 - 0
packages/tres/src/components/TheEvents.vue

@@ -0,0 +1,59 @@
+<script setup lang="ts">
+import { BasicShadowMap, NoToneMapping, sRGBEncoding } from 'three'
+import { reactive } from 'vue'
+import { OrbitControls } from '@tresjs/cientos'
+
+const state = reactive({
+  clearColor: '#201919',
+  shadows: true,
+  alpha: false,
+  shadowMapType: BasicShadowMap,
+  outputEncoding: sRGBEncoding,
+  toneMapping: NoToneMapping,
+})
+
+function onClick(ev) {
+  if (ev) {
+    ev.object.material.color.set('#00ffff')
+  }
+}
+
+function onPointerEnter(ev) {
+  console.log(ev)
+  if (ev) {
+    ev.object.material.color.set('#ff0000')
+  }
+}
+
+function onPointerLeave(ev) {
+  if (ev) {
+    /*  ev.object.material.color.set('#efefef') */
+  }
+}
+</script>
+
+<template>
+  <TresCanvas v-bind="state">
+    <TresPerspectiveCamera :position="[11, 11, 11]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
+    <OrbitControls />
+    <TresScene>
+      <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.2" cast-shadow />
+      <template v-for="x in [-2.5, 0, 2.5]">
+        <template v-for="y in [-2.5, 0, 2.5]">
+          <TresMesh
+            v-for="z in [-2.5, 0, 2.5]"
+            :key="[x, y, z]"
+            :position="[x, y, z]"
+            @click="onClick"
+            @pointer-enter="onPointerEnter"
+            @pointer-leave="onPointerLeave"
+          >
+            <TresBoxGeometry :args="[1, 1, 1]" />
+            <TresMeshToonMaterial color="#efefef" />
+          </TresMesh>
+        </template>
+      </template>
+      <TresAmbientLight :intensity="0.5" />
+    </TresScene>
+  </TresCanvas>
+</template>

+ 24 - 10
packages/tres/src/components/TheExperience.vue

@@ -91,6 +91,21 @@ pane
     console.log(ev.value)
     state.toneMapping = ev.value
   })
+
+function onPointerEnter(ev) {
+  console.log('onPointerEnter', ev)
+}
+
+function onPointerMove(ev) {
+  console.log('onPointerMove', ev)
+}
+
+function onPointerLeave(ev) {
+  console.log('onPointerLeave', ev)
+}
+function onClick(ev) {
+  console.log('click', ev)
+}
 </script>
 <template>
   <TresCanvas v-bind="state">
@@ -98,18 +113,17 @@ pane
     <OrbitControls make-default />
     <TresScene>
       <TresAmbientLight :intensity="0.5" />
-      <!--  <TresMesh :position="[-2, 6, 0]" :rotation="[0, Math.PI, 0]" cast-shadow>
-        <TresConeGeometry :args="[1, 1.5, 3]" />
-        <TresMeshToonMaterial color="#82DBC5" />
-      </TresMesh> -->
-      <!-- <TransformControls :object="boxRef" mode="rotate" />
-      <TresMesh ref="boxRef" :position="[0, 4, 0]" cast-shadow>
-        <TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
-        <TresMeshToonMaterial color="#4F4F4F" />
-      </TresMesh> -->
       <TransformControls mode="scale" :object="sphereRef" />
 
-      <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
+      <TresMesh
+        ref="sphereRef"
+        :position="[0, 4, 0]"
+        cast-shadow
+        @pointer-enter="onPointerEnter"
+        @pointer-move="onPointerMove"
+        @pointer-leave="onPointerLeave"
+        @click="onClick"
+      >
         <TresSphereGeometry />
         <TresMeshToonMaterial color="#FBB03B" />
       </TresMesh>

+ 1 - 2
packages/tres/src/core/useCatalogue/index.ts

@@ -10,7 +10,7 @@ delete catalogue.value.Scene
 
 let localApp: App
 export function useCatalogue(app?: App, prefix = 'Tres') {
-  const { logMessage, logError } = useLogger()
+  const { logError } = useLogger()
   if (!localApp && app) {
     localApp = app
   }
@@ -23,7 +23,6 @@ export function useCatalogue(app?: App, prefix = 'Tres') {
     }
     catalogue.value = Object.assign(catalogue.value, objects)
     const components = createComponentInstances(ref(objects))
-    logMessage('Adding objects to catalogue', { objects, catalogue: catalogue.value.uuid })
 
     if (localApp) {
       components.forEach(([key, cmp]) => {

+ 48 - 28
packages/tres/src/core/useInstanceCreator/index.ts

@@ -1,17 +1,19 @@
 /* eslint-disable new-cap */
 /* eslint-disable @typescript-eslint/no-empty-function */
-import { BufferAttribute, FogBase, OrthographicCamera, PerspectiveCamera, Scene } from 'three'
-import { defineComponent, inject, Ref } from 'vue'
+import { BufferAttribute, FogBase, OrthographicCamera, PerspectiveCamera, Raycaster, Scene } from 'three'
+import { defineComponent, inject, onUnmounted, Ref } from 'vue'
+import { useEventListener } from '@vueuse/core'
+
 import { isArray, isDefined, isFunction } from '@alvarosabu/utils'
 import { normalizeVectorFlexibleParam } from '/@/utils/normalize'
-import { useCamera, useCatalogue, useScene } from '/@/core/'
+import { useCamera, useCatalogue, useRenderLoop, useScene } from '/@/core/'
 import { useLogger } from '/@/composables'
-import { TresAttributes, TresCatalogue, TresInstance, TresVNode, TresVNodeType } from '/@/types'
+import { TresAttributes, TresCatalogue, TresInstance, TresVNode, TresVNodeType, TresEvent } from '/@/types'
 
 const VECTOR3_PROPS = ['rotation', 'scale', 'position']
 
 export function useInstanceCreator(prefix: string) {
-  const { logMessage, logError } = useLogger()
+  const { /* logMessage, */ logError } = useLogger()
 
   function processSetAttributes(props: Record<string, any>, instance: TresInstance) {
     if (!isDefined(props)) return
@@ -114,7 +116,6 @@ export function useInstanceCreator(prefix: string) {
           processProps(vnode.props, internalInstance)
         }
       }
-      logMessage(`Created ${vNodeType} instance`, internalInstance)
       return internalInstance
     }
   }
@@ -157,10 +158,13 @@ export function useInstanceCreator(prefix: string) {
           const name = `${prefix}${key}`
           const cmp = defineComponent({
             name,
-            setup(props, { slots, attrs, ...ctx }) {
+            setup(_props, { slots, attrs, ...ctx }) {
               const { scene: fallback } = useScene()
+              const { onLoop } = useRenderLoop()
               const scene = inject<Ref<Scene>>('local-scene') || fallback
-              const catalogue = inject<Ref<TresCatalogue>>('catalogue')
+              /* const { raycaster } = useRaycaster() */
+              const raycaster = inject<Ref<Raycaster>>('raycaster') /* 
+              const currentInstance = inject<Ref>('currentInstance') */
               const { pushCamera } = useCamera()
 
               let instance = createInstance(threeObj, attrs, slots)
@@ -175,6 +179,41 @@ export function useInstanceCreator(prefix: string) {
                 scene?.value.add(instance)
               }
 
+              let prevInstance: TresEvent | null = null
+              let currentInstance: TresEvent | null = null
+              if (instance.isMesh) {
+                onLoop(() => {
+                  if (instance && raycaster?.value) {
+                    const intersects = raycaster?.value.intersectObjects(scene.value.children)
+
+                    if (intersects.length > 0) {
+                      currentInstance = intersects[0]
+
+                      if (prevInstance === null || prevInstance.object.uuid !== currentInstance?.object.uuid) {
+                        ctx.emit('pointer-enter', currentInstance)
+                      }
+
+                      ctx.emit('pointer-move', currentInstance)
+                    } else {
+                      currentInstance = null
+                      if (prevInstance !== null) {
+                        ctx.emit('pointer-leave', prevInstance)
+                      }
+                    }
+
+                    prevInstance = currentInstance
+                  }
+                })
+
+                const clickEventListener = useEventListener(window, 'click', () => {
+                  ctx.emit('click', prevInstance)
+                })
+
+                onUnmounted(() => {
+                  clickEventListener()
+                })
+              }
+
               if (scene?.value && instance.isFog) {
                 scene.value.fog = instance as unknown as FogBase
               }
@@ -191,30 +230,11 @@ export function useInstanceCreator(prefix: string) {
                   if (instance.isObject3D) {
                     scene?.value.add(instance)
                   }
-
-                  logMessage(name, {
-                    instance,
-                    sceneuuid: scene?.value.uuid,
-                    catalogue: catalogue?.value.uuid,
-                    props,
-                    slots: slots.default ? slots.default() : undefined,
-                    attrs,
-                    ctx,
-                    scene,
-                  })
                 })
               }
 
               ctx.expose(instance)
-              logMessage(name, {
-                sceneuuid: scene?.value.uuid,
-                catalogue: catalogue?.value.uuid,
-                props,
-                slots: slots.default ? slots.default() : undefined,
-                attrs,
-                ctx,
-                scene,
-              })
+
               return () => {}
             },
           })

+ 27 - 0
packages/tres/src/core/useRaycaster/index.ts

@@ -0,0 +1,27 @@
+import { Raycaster, Vector2 } from 'three'
+import { onUnmounted, provide, ref, shallowRef } from 'vue'
+
+const raycaster = shallowRef(new Raycaster())
+const pointer = ref(new Vector2())
+const currentInstance = ref(null)
+
+export function useRaycaster() {
+  provide('raycaster', raycaster)
+  provide('pointer', pointer)
+  provide('currentInstance', currentInstance)
+
+  function onPointerMove(event: MouseEvent) {
+    pointer.value.x = (event.clientX / window.innerWidth) * 2 - 1
+    pointer.value.y = -(event.clientY / window.innerHeight) * 2 + 1
+  }
+
+  window.addEventListener('pointermove', onPointerMove)
+
+  onUnmounted(() => {
+    window.removeEventListener('pointermove', onPointerMove)
+  })
+  return {
+    raycaster,
+    pointer,
+  }
+}

+ 5 - 1
packages/tres/src/core/useScene/component.ts

@@ -3,6 +3,7 @@ import type { Renderer } from 'three'
 import { defineComponent, inject, provide, Ref } from 'vue'
 import { useRenderLoop } from '../useRenderLoop'
 import { useScene } from './'
+import { useRaycaster } from '../useRaycaster'
 
 /**
  * Vue component for rendering a Tres component.
@@ -13,11 +14,14 @@ export const Scene = defineComponent({
     const { scene } = useScene()
     const renderer = inject<Ref<Renderer>>('renderer')
     const { activeCamera } = useCamera()
+    const { raycaster, pointer } = useRaycaster()
     const { onLoop } = useRenderLoop()
 
     provide('local-scene', scene)
 
-    onLoop(({ clock }) => {
+    onLoop(() => {
+      raycaster.value.setFromCamera(pointer.value, activeCamera.value)
+
       if (renderer?.value && activeCamera?.value && scene?.value) {
         renderer.value.render(scene?.value, activeCamera.value)
       }

+ 2 - 2
packages/tres/src/core/useTres/index.ts

@@ -1,5 +1,5 @@
 import { WebGLRenderer } from 'three'
-import { reactive, toRefs } from 'vue'
+import { shallowReactive, toRefs } from 'vue'
 import { Camera } from '/@/core'
 
 export interface TresState {
@@ -8,7 +8,7 @@ export interface TresState {
   [key: string]: any
 }
 
-const state: TresState = reactive({})
+const state: TresState = shallowReactive({})
 
 export function useTres() {
   function getState(key: string) {

+ 2 - 1
packages/tres/src/index.ts

@@ -1,7 +1,7 @@
 import { App, Component } from 'vue'
 import { TresCanvas } from '/@/core/useRenderer/component'
 import { Scene } from '/@/core/useScene/component'
-import { useCatalogue, useInstanceCreator } from '/@/core'
+import { useCatalogue, useInstanceCreator, useTres } from '/@/core'
 export * from '/@/core'
 export * from './keys'
 import { version } from '../package.json'
@@ -22,6 +22,7 @@ const plugin: TresPlugin = {
     const { catalogue, extend } = useCatalogue(app, prefix)
     app.provide('catalogue', catalogue)
     app.provide('extend', extend)
+    app.provide('useTres', useTres())
     const { createComponentInstances } = useInstanceCreator(prefix)
     const components = createComponentInstances(catalogue)
     /*  const components = createComponentInstances(

+ 10 - 0
packages/tres/src/types/index.d.ts

@@ -13,6 +13,16 @@ export type TresVNode = VNode & { children?: Array<VNode | { default: any }>; ty
 export type TresAttributes = Record<string, any> & { args?: number[] }
 
 export type TresColor = string | number | Color | number[]
+
+export interface TresEvent extends Intersection<Object3D<Event>> {
+  object: Object3D
+  distance: number
+  face?: Face3
+  faceIndex?: number | undefined
+  point: Vector3
+  uv?: Vector2
+}
+
 declare global {
   // Define the window interface, with type annotations for the properties and methods of the window object
   interface Window {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 178 - 421
pnpm-lock.yaml


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor