Browse Source

Hola mundo 🍩🪐

Alvaro 2 năm trước cách đây
mục cha
commit
286ac4ccea
100 tập tin đã thay đổi với 3997 bổ sung3 xóa
  1. 6 0
      .eslintignore
  2. 41 0
      .eslintrc.js
  3. 2 0
      .github/FUNDING.yml
  4. 27 0
      .github/workflows/actions/pnpm/action.yml
  5. 41 0
      .github/workflows/test.yml
  6. 8 0
      .gitignore
  7. 1 0
      .npmrc
  8. 4 0
      .prettierrc.js
  9. 17 0
      .vscode/launch.json
  10. 20 0
      .vscode/settings.json
  11. 1 1
      LICENSE
  12. 39 2
      README.md
  13. 25 0
      docs/.vitepress/config.ts
  14. 25 0
      docs/.vitepress/theme/components/FirstScene.vue
  15. 26 0
      docs/.vitepress/theme/components/FirstSceneLightToon.vue
  16. 44 0
      docs/.vitepress/theme/config.css
  17. 15 0
      docs/.vitepress/theme/index.ts
  18. 29 0
      docs/guide/getting-started.md
  19. 46 0
      docs/guide/index.md
  20. 94 0
      docs/guide/your-first-scene.md
  21. 35 0
      docs/index.md
  22. 6 0
      docs/public/favicon-dark.svg
  23. 5 0
      docs/public/favicon.svg
  24. BIN
      docs/public/hero.png
  25. 5 0
      docs/public/logo.svg
  26. 62 0
      package.json
  27. 24 0
      packages/cientos/.gitignore
  28. 54 0
      packages/cientos/CHANGELOG.md
  29. 60 0
      packages/cientos/package.json
  30. 1 0
      packages/cientos/public/vite.svg
  31. 31 0
      packages/cientos/src/core/OrbitControls.vue
  32. 21 0
      packages/cientos/src/core/useGLTF/component.ts
  33. 41 0
      packages/cientos/src/core/useGLTF/index.ts
  34. 38 0
      packages/cientos/src/core/useTweakPane/index.ts
  35. 8 0
      packages/cientos/src/env.d.ts
  36. 5 0
      packages/cientos/src/index.ts
  37. 146 0
      packages/cientos/stats.html
  38. 27 0
      packages/cientos/tsconfig.json
  39. 9 0
      packages/cientos/tsconfig.node.json
  40. 70 0
      packages/cientos/vite.config.ts
  41. 25 0
      packages/tres/.gitignore
  42. 3 0
      packages/tres/.vscode/extensions.json
  43. 179 0
      packages/tres/CHANGELOG.md
  44. 40 0
      packages/tres/README.md
  45. 15 0
      packages/tres/components.d.ts
  46. 20 0
      packages/tres/histoire.config.ts
  47. 9 0
      packages/tres/histoire.setup.ts
  48. 13 0
      packages/tres/index.html
  49. 77 0
      packages/tres/package.json
  50. BIN
      packages/tres/public/favicon-16x16.png
  51. BIN
      packages/tres/public/favicon-32x32.png
  52. 6 0
      packages/tres/public/favicon-dark.svg
  53. BIN
      packages/tres/public/favicon.ico
  54. 5 0
      packages/tres/public/favicon.svg
  55. 5 0
      packages/tres/public/logo-dark.svg
  56. 5 0
      packages/tres/public/logo.svg
  57. 1173 0
      packages/tres/public/models/AkuAku.gltf
  58. BIN
      packages/tres/public/pwa-192x192.png
  59. BIN
      packages/tres/public/pwa-512x512.png
  60. 5 0
      packages/tres/public/safari-pinned-tab.svg
  61. BIN
      packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_ambientOcclusion.jpg
  62. BIN
      packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_basecolor.jpg
  63. BIN
      packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_height.png
  64. BIN
      packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_normal.jpg
  65. BIN
      packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_roughness.jpg
  66. 1 0
      packages/tres/public/vite.svg
  67. 30 0
      packages/tres/src/App.vue
  68. 1 0
      packages/tres/src/assets/vue.svg
  69. 30 0
      packages/tres/src/components/TestSphere.vue
  70. 1 0
      packages/tres/src/composables/index.ts
  71. 32 0
      packages/tres/src/composables/useLogger.ts
  72. 9 0
      packages/tres/src/core/index.ts
  73. 109 0
      packages/tres/src/core/useCamera/index.ts
  74. 18 0
      packages/tres/src/core/useCatalogue/index.ts
  75. 19 0
      packages/tres/src/core/useCatalogue/useCatalogue.test.ts
  76. 115 0
      packages/tres/src/core/useInstanceCreator/index.ts
  77. 75 0
      packages/tres/src/core/useLoader/index.ts
  78. 46 0
      packages/tres/src/core/useRenderLoop/index.ts
  79. 79 0
      packages/tres/src/core/useRenderer/component.ts
  80. 233 0
      packages/tres/src/core/useRenderer/index.ts
  81. 32 0
      packages/tres/src/core/useScene/component.ts
  82. 10 0
      packages/tres/src/core/useScene/index.ts
  83. 10 0
      packages/tres/src/core/useScene/useScene.test.ts
  84. 72 0
      packages/tres/src/core/useTexture/index.ts
  85. 28 0
      packages/tres/src/core/useTres/index.ts
  86. 10 0
      packages/tres/src/env.d.ts
  87. 35 0
      packages/tres/src/examples/Basic.story.vue
  88. 36 0
      packages/tres/src/examples/GUI/Tweakpane.story.vue
  89. 29 0
      packages/tres/src/examples/cientos/controls/OrbitControls.story.vue
  90. 26 0
      packages/tres/src/examples/lighting/RedBlue.story.vue
  91. 8 0
      packages/tres/src/examples/models/gltf/AkuAku.vue
  92. 7 0
      packages/tres/src/examples/models/gltf/AkuAkuCientos.vue
  93. 20 0
      packages/tres/src/examples/models/gltf/Basic.story.vue
  94. 19 0
      packages/tres/src/examples/models/gltf/GLTFModel.story.vue
  95. 20 0
      packages/tres/src/examples/models/gltf/useGLTF.story.vue
  96. 46 0
      packages/tres/src/examples/shaders/blob/Blob.story.vue
  97. 6 0
      packages/tres/src/examples/shaders/blob/shaders/fragment.glsl
  98. 15 0
      packages/tres/src/examples/shaders/blob/shaders/vertex.glsl
  99. 30 0
      packages/tres/src/index.ts
  100. 1 0
      packages/tres/src/keys.ts

+ 6 - 0
.eslintignore

@@ -0,0 +1,6 @@
+packages/**/dist
+**.spec.js
+**.test.ts
+**.test.js
+**.cy.js
+**/cypress/**

+ 41 - 0
.eslintrc.js

@@ -0,0 +1,41 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true,
+    browser: true,
+    es6: true,
+  },
+  parser: 'vue-eslint-parser',
+  plugins: ['vue', '@typescript-eslint'],
+  extends: [
+    'eslint:recommended',
+    'plugin:@typescript-eslint/recommended',
+    'plugin:vue/vue3-recommended',
+    'prettier',
+  ],
+  parserOptions: {
+    tsconfigRootDir: __dirname,
+    parser: '@typescript-eslint/parser',
+    ecmaVersion: 2020,
+    sourceType: 'module',
+    allowImportExportEverywhere: true,
+  },
+  ignorePatterns: ['**/*.test.ts', 'packages/**/dist', 'package.json'],
+  rules: {
+    'arrow-parens': ['error', 'as-needed'],
+    'comma-dangle': 'off',
+    'space-before-function-paren': 'off',
+    'max-len': [1, { code: 120 }],
+    'require-jsdoc': 0,
+    'no-invalid-this': 0,
+    'import/no-absolute-path': 'off',
+    '@typescript-eslint/no-explicit-any': 'off',
+    'vue/no-deprecated-slot-attribute': 'off',
+    'vue/require-default-prop': 'off',
+    'vue/html-self-closing': 'off',
+    'vue/max-attributes-per-line': 'off',
+    'vue/multi-word-component-names': 0,
+    'vue/no-multiple-template-root': 'off',
+    'vue/first-attribute-linebreak': 'off',
+  },
+}

+ 2 - 0
.github/FUNDING.yml

@@ -0,0 +1,2 @@
+github: [alvarosabu]
+ko_fi: alvarosaburido

+ 27 - 0
.github/workflows/actions/pnpm/action.yml

@@ -0,0 +1,27 @@
+# From https://github.com/remirror/template/blob/4f8c5f5629a081217672a8cce1df085510f43913/.github/actions/pnpm/action.yml
+name: 'pnpm installation'
+description: 'Install and audit dependencies for pnpm'
+inputs:
+  cache: # id of input
+    description: 'The location of the pnpm cache'
+    required: true
+    default: '.pnpm-store'
+  version: # id of input
+    description: 'The version to use'
+    required: false
+    default: 6.10.0
+
+runs:
+  using: 'composite'
+  steps:
+    - name: install pnpm
+      run: npm install pnpm@${{ inputs.version }} -g
+      shell: bash
+
+    - name: setup pnpm config
+      run: pnpm config set store-dir ${{ inputs.cache }}
+      shell: bash
+
+    - name: install dependencies
+      run: pnpm install --shamefully-hoist
+      shell: bash

+ 41 - 0
.github/workflows/test.yml

@@ -0,0 +1,41 @@
+name: Run linters
+on: [push]
+
+env:
+  PNPM_CACHE_FOLDER: .pnpm-store
+  HUSKY: 0 # Bypass husky commit hook for CI
+
+jobs:
+  lint:
+    name: Lint all projects
+    runs-on: ubuntu-20.04
+    steps:
+      - name: Checkout
+        uses: actions/checkout@v2
+
+      - name: setup caching
+        uses: actions/cache@v2
+        with:
+          path: ${{ env.PNPM_CACHE_FOLDER }}
+          key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
+          restore-keys: |
+            ${{ runner.os }}-pnpm-
+
+      - name: setup pnpm
+        uses: pnpm/action-setup@v2.2.4
+        with:
+          version: 7
+
+      - name: setup node.js
+        uses: actions/setup-node@v2
+        with:
+          node-version: 16.x
+          cache: 'pnpm'
+
+      - name: setup pnpm config
+        run: pnpm config set store-dir $PNPM_CACHE_FOLDER
+
+      - run: pnpm install --no-frozen-lockfile --shamefully-hoist
+
+      - name: Run Lint
+        run: pnpm run lint

+ 8 - 0
.gitignore

@@ -0,0 +1,8 @@
+node_modules
+.DS_Store
+apps/.DS_Store
+packages/.DS_Store
+.pnpm-store/
+# Local Netlify folder
+.netlify
+apps/lunchbox-demo

+ 1 - 0
.npmrc

@@ -0,0 +1 @@
+shamefully-hoist=true

+ 4 - 0
.prettierrc.js

@@ -0,0 +1,4 @@
+module.exports = {
+  ...require('@alvarosabu/prettier-config'),
+  printWidth: 120,
+}

+ 17 - 0
.vscode/launch.json

@@ -0,0 +1,17 @@
+{
+  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "type": "node",
+      "request": "launch",
+      "name": "Debug Current Test File",
+      "autoAttachChildProcesses": true,
+      "skipFiles": ["<node_internals>/**", "**/node_modules/**"],
+      "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs",
+      "args": ["run", "${relativeFile}"],
+      "smartStep": true,
+      "console": "integratedTerminal"
+    }
+  ]
+}

+ 20 - 0
.vscode/settings.json

@@ -0,0 +1,20 @@
+{
+  "eslint.codeAction.showDocumentation": {
+    "enable": true
+  },
+  "editor.formatOnSave": true,
+  "editor.defaultFormatter": "esbenp.prettier-vscode",
+  "[vue]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[javascript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "[typescript]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  "scss.lint.unknownAtRules": "ignore",
+  "css.lint.unknownAtRules": "ignore",
+  "eslint.workingDirectories": ["apps/", "packages/"],
+  "testing.automaticallyOpenPeekView": "never"
+}

+ 1 - 1
LICENSE

@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2022 Tres
+Copyright (c) 2022-present, (alvarosabu) Alvaro Saburido and Tres contributors
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

+ 39 - 2
README.md

@@ -1,2 +1,39 @@
-# tres
-Declarative ThreeJS using Vue Components
+![repository-banner.png](/public/github-banner.png)
+
+# Tres ▲ ■ ●
+
+> Declarative ThreeJS using Vue Components
+
+- 💡 Build 3D scene as they were Vue components
+- ⚡️ Powered by Vite
+- 🥰 It brings all the updated features of ThreeJS right awayregardless the version
+- 🦾 Fully Typed
+
+Tres (Spanish word for "three", pronounced `/tres/` ) is a way of creating ThreeJS scenes with Vue components in a declarative fashion. Think of it as a [React-three-fiber](https://docs.pmnd.rs/react-three-fiber) or [Lunchbox](https://github.com/breakfast-studio/lunchboxjs) but without the need of a [custom Vue3 Renderer](https://vuejs.org/api/custom-renderer.html).
+
+## Packages
+
+| Package               | Version (click for changelogs)                                                                         |
+| --------------------- | :----------------------------------------------------------------------------------------------------- |
+| [Tres](packages/tres) | [![tres version](https://img.shields.io/npm/v/@tresjs/core.svg?label=%20)](packages/tres/CHANGELOG.md) |
+| [Cientos](packages/cientos) | [![tres version](https://img.shields.io/npm/v/@tresjs/cientos.svg?label=%20)](packages/cientos/CHANGELOG.md) |
+
+## Docs
+
+To check the docs
+
+```
+pnpm run docs:dev
+```
+
+## Contribution
+
+Soon
+
+## License
+
+[MIT](/LICENSE)
+
+## Sponsors
+
+Be the first to support this project [here](https://github.com/sponsors/alvarosabu) ☺️

+ 25 - 0
docs/.vitepress/config.ts

@@ -0,0 +1,25 @@
+import { defineConfig } from 'vitepress'
+
+export default defineConfig({
+  title: 'TresJS',
+  description: 'Autogenerated wrappers based on THREE instances',
+  head: [['link', { rel: 'icon', type: 'image/svg', href: '/favicon.svg' }]],
+  themeConfig: {
+    logo: '/logo.svg',
+    sidebar: [
+      {
+        text: 'Guide',
+        items: [
+          // This shows `/guide/index.md` page.
+          { text: 'Introduction', link: '/guide/' },
+          { text: 'Getting Started', link: '/guide/getting-started' },
+          { text: 'Your first Scene', link: '/guide/your-first-scene' },
+        ],
+      },
+    ],
+    socialLinks: [
+      { icon: 'github', link: 'https://github.com/alvarosabu/tres' },
+      { icon: 'twitter', link: 'https://twitter.com/alvarosabu' },
+    ],
+  },
+})

+ 25 - 0
docs/.vitepress/theme/components/FirstScene.vue

@@ -0,0 +1,25 @@
+<script setup lang="ts">
+import { OrbitControls } from '@tresjs/cientos'
+
+const styles = {
+  width: '100%',
+  height: '550px',
+  border: '1px solid #e2e2e2',
+  borderRadius: '8px',
+  overflow: 'hidden',
+}
+</script>
+<template>
+  <ClientOnly>
+    <TresCanvas shadows clear-color="#fff" :style="styles">
+      <TresPerspectiveCamera :position="[0, 2, 4]" />
+      <OrbitControls />
+      <TresScene>
+        <TresMesh :rotation="[-Math.PI / 4, -Math.PI / 4, Math.PI / 4]">
+          <TresTorusGeometry :args="[1, 0.5, 16, 32]" />
+          <TresMeshBasicMaterial color="#FBB03B" />
+        </TresMesh>
+      </TresScene>
+    </TresCanvas>
+  </ClientOnly>
+</template>

+ 26 - 0
docs/.vitepress/theme/components/FirstSceneLightToon.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import { OrbitControls } from '@tresjs/cientos'
+
+const styles = {
+  width: '100%',
+  height: '550px',
+  border: '1px solid #e2e2e2',
+  borderRadius: '8px',
+  overflow: 'hidden',
+}
+</script>
+<template>
+  <ClientOnly>
+    <TresCanvas shadows clear-color="#fff" :style="styles">
+      <TresPerspectiveCamera :position="[0, 2, 4]" />
+      <OrbitControls />
+      <TresScene>
+        <TresDirectionalLight :position="[0, 2, 4]" :intensity="2" cast-shadow />
+        <TresMesh :rotation="[-Math.PI / 4, -Math.PI / 4, Math.PI / 4]">
+          <TresTorusGeometry :args="[1, 0.5, 16, 32]" />
+          <TresMeshToonMaterial color="#FBB03B" />
+        </TresMesh>
+      </TresScene>
+    </TresCanvas>
+  </ClientOnly>
+</template>

+ 44 - 0
docs/.vitepress/theme/config.css

@@ -0,0 +1,44 @@
+:root {
+  --vp-home-hero-name-color: #82dbc5;
+  --vp-c-brand: #82dbc5;
+}
+
+/**
+ * Component: Button
+ * -------------------------------------------------------------------------- */
+
+:root {
+  --vp-button-brand-border: var(--vp-c-brand-light);
+  --vp-button-brand-text: var(--vp-c-text-dark-1);
+  --vp-button-brand-bg: var(--vp-c-brand);
+  --vp-button-brand-hover-border: var(--vp-c-brand-light);
+  --vp-button-brand-hover-text: var(--vp-c-text-dark-1);
+  --vp-button-brand-hover-bg: var(--vp-c-brand-light);
+  --vp-button-brand-active-border: var(--vp-c-brand-light);
+  --vp-button-brand-active-text: var(--vp-c-text-dark-1);
+  --vp-button-brand-active-bg: var(--vp-button-brand-bg);
+
+  --vp-button-alt-border: var(--vp-c-gray-light-3);
+  --vp-button-alt-text: var(--vp-c-text-light-1);
+  --vp-button-alt-bg: var(--vp-c-gray-light-5);
+  --vp-button-alt-hover-border: var(--vp-c-gray-light-3);
+  --vp-button-alt-hover-text: var(--vp-c-text-light-1);
+  --vp-button-alt-hover-bg: var(--vp-c-gray-light-4);
+  --vp-button-alt-active-border: var(--vp-c-gray-light-3);
+  --vp-button-alt-active-text: var(--vp-c-text-light-1);
+  --vp-button-alt-active-bg: var(--vp-c-gray-light-3);
+
+  --vp-button-sponsor-border: var(--vp-c-gray-light-3);
+  --vp-button-sponsor-text: var(--vp-c-text-light-2);
+  --vp-button-sponsor-bg: transparent;
+  --vp-button-sponsor-hover-border: var(--vp-c-sponsor);
+  --vp-button-sponsor-hover-text: var(--vp-c-sponsor);
+  --vp-button-sponsor-hover-bg: transparent;
+  --vp-button-sponsor-active-border: var(--vp-c-sponsor);
+  --vp-button-sponsor-active-text: var(--vp-c-sponsor);
+  --vp-button-sponsor-active-bg: transparent;
+}
+
+a {
+  text-decoration: dashed;
+}

+ 15 - 0
docs/.vitepress/theme/index.ts

@@ -0,0 +1,15 @@
+import Tres from '@tresjs/core'
+// .vitepress/theme/index.ts
+import DefaultTheme from 'vitepress/theme'
+import './config.css'
+import FirstScene from './components/FirstScene.vue'
+import FirstSceneLightToon from './components/FirstSceneLightToon.vue'
+
+export default {
+  ...DefaultTheme,
+  enhanceApp(ctx) {
+    ctx.app.component('FirstScene', FirstScene)
+    ctx.app.component('FirstSceneLightToon', FirstSceneLightToon)
+    ctx.app.use(Tres)
+  },
+}

+ 29 - 0
docs/guide/getting-started.md

@@ -0,0 +1,29 @@
+# Instalation
+
+Learn how to install TresJS
+
+```
+pnpm i @tresjs/core three
+```
+
+> Better use with Vue 3.x and composition API
+
+## Getting started
+
+You can install TresJS as any other Vue plugin
+
+```ts
+import { createApp } from 'vue'
+import App from './App.vue'
+
+import Tres from '@tresjs/core'
+
+export const app = createApp(App)
+
+app.use(Tres)
+app.mount('#app')
+```
+
+### Nuxt
+
+Soon.

+ 46 - 0
docs/guide/index.md

@@ -0,0 +1,46 @@
+# Introduction
+
+```
+npm install three @tresjs/core -D
+```
+
+or if you use yarn
+
+```
+yarn add three @tresjs/core -D
+```
+
+pnpm users
+
+```
+pnpm add three @tresjs/core -D
+```
+
+## Motivation
+
+[ThreeJS](https://threejs.org/) is a wonderfull library to create awesome **WebGL** 3D websites. Is also a constantly updated library that makes hard for wrapper mantainers like [TroisJS](https://troisjs.github.io/) to keep up with all the enhancements.
+
+React ecosystem has an impresive **custom render** solution called [React-three-fiber](https://docs.pmnd.rs/react-three-fiber) that allows you build your scenes declaratively with re-usable, self-contained components that react to state.
+
+In my search for something similar in the VueJS ecosystem, I found this amazing library called [Lunchbox](https://github.com/breakfast-studio/lunchboxjs) which works with the same concept that R3F, it provides a [custom Vue3 Renderer](https://vuejs.org/api/custom-renderer.html). I'm also contrubuiting to improve this library so it gets as mature and feature-rich as R3F.
+
+The only problem is, Mixing renderers in Vue 3 is something the Vue community is still working on - see [here](https://github.com/vuejs/vue-loader/pull/1645) for more information. Until there is a solution similar to [React Reconciliation](https://reactjs.org/docs/reconciliation.html) you will need to create 2 separate `Apps` which might be not ideal.
+
+```ts
+// Example Vite setup
+import { createApp } from 'vue'
+import { createApp as createLunchboxApp } from 'lunchboxjs'
+import App from './App.vue'
+import LunchboxApp from './LunchboxApp.vue'
+
+// html app
+const app = createApp(App)
+app.mount('#app')
+
+// lunchbox app
+const lunchboxApp = createLunchboxApp(LunchboxApp)
+// assuming there's an element with ID `lunchbox` in your HTML app
+lunchboxApp.mount('#lunchbox')
+```
+
+So I was inspired by both libraries to create something that wouldn't require creating a **custom renderer** but intelligent enough to generate Vue components based on the ThreeJS constructors with 0-to-none manteinance required `three:latest`. That's **TresjS**.

+ 94 - 0
docs/guide/your-first-scene.md

@@ -0,0 +1,94 @@
+# Your first scene
+
+This guide will help you to create your first Tres scene. 🍩
+
+## Setting up the experience Canvas
+
+Before we can create an Scene, we need somewhere to display it. Using plain [ThreeJS](https://threejs.org/docs/index.html#manual/en/introduction/Creating-a-scene) we would need yo create a `canvas` html element to mount the `WebglRenderer` and initialize the `scene`
+
+With **TresJS** you only need to add the default component `<TresCanvas />` to the template of your Vue component.
+
+```vue
+<template>
+  <TresCanvas> // Your scene is going to live here </TresCanvas>
+</template>
+```
+
+::: warning
+It's important that all components related to the scene live between the `<TresCanvas />` component. Otherwise, they will be not rendered.
+:::
+
+The `TresCanvas` component is going to do some setup work behind the scene:
+
+- It creates a [**WebGLRenderer**](https://threejs.org/docs/index.html?q=webglrend#api/en/renderers/WebGLRenderer) that automatically updates every frame.
+- It sets the rende rloop to be called on every frame based on the browser refresh rate.
+
+## Creating a scene
+
+We need 3 core elements to create a 3D experience:
+
+- A [**Camera**](https://threejs.org/docs/index.html?q=camera#api/en/cameras/Camera)
+- An [**Object**](https://threejs.org/docs/index.html?q=object#api/en/core/Object3D)
+- An [**Scene**](https://threejs.org/docs/index.html?q=scene#api/en/scenes/Scene) to hold the camera and the object(s) together.
+
+With **TresJS** you can create a Scene using the `<TresScene />` component.
+
+```vue
+<template>
+  <TresCanvas>
+    <TresScene>
+      <!-- Your scene goes here -->
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+Then you can add a [**PerspectiveCamera**](https://threejs.org/docs/index.html?q=perspectivecamera#api/en/cameras/PerspectiveCamera) using the `<TresPerspectiveCamera />` component.
+
+```vue
+<template>
+  <TresCanvas>
+    <TresPerspectiveCamera />
+    <TresScene>
+      <!-- Your scene goes here -->
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+## Adding a Sphere
+
+That scene looks a litle empty, let's add a basic object. If we where using plain **ThreeJS** we would need to create a [**Mesh**](https://threejs.org/docs/index.html?q=mesh#api/en/objects/Mesh) object and attach to it a [**Material**](https://threejs.org/docs/index.html?q=material#api/en/materials/Material) and a [**Geometry**](https://threejs.org/docs/index.html?q=geometry#api/en/core/BufferGeometry) like this:
+
+```ts
+const geometry = new THREE.TorusGeometry(1, 0.5, 16, 32)
+const material = new THREE.MeshBasicMaterial({ color: 'orange' })
+const donut = new THREE.Mesh(geometry, material)
+scene.add(donut)
+```
+
+A Mesh is a basic scene object in three.js, and it's used to hold the geometry and the material needed to represent a shape in 3D space.
+
+Now let's see how we can easily achieve the same with **TresJS**. To do that we are going to use `<TresMesh />` component, and between the default slots, we are going to pass a `<TresTorusGeometry />` and a `<TresMeshBasicMaterial />`.
+
+```vue
+<template>
+  <TresCanvas>
+    <TresPerspectiveCamera />
+    <TresScene>
+      <TresMesh>
+        <TresTorusGeometry />
+        <TresMeshBasicMaterial color="orange" />
+      </TresMesh>
+    </TresScene>
+  </TresCanvas>
+</template>
+```
+
+::: info
+Notice that we don't need to import anything, thats because **TresJS** automatically generate a **Vue Component based of the Three Object you want to use in CamelCase with a Tres prefix**. For example, if you want to use a `AmbientLight` you would use `<TresAmbientLight />` component.
+:::
+
+<FirstScene />
+
+From that on you can start adding more objects to your scene, and start playing with the properties of the components to see how they affect the scene.

+ 35 - 0
docs/index.md

@@ -0,0 +1,35 @@
+---
+layout: home
+
+title: TresJS
+titleTemplate: The solution for 3D on VueJS
+
+hero:
+  name: TresJS
+  text: Bring ThreeJS to VueJS ecosystem
+  tagline: Think of it as a R3F or Lunchboxjs but without the need of a custom render.
+  image:
+    src: /hero.png
+    alt: Tresjs
+  actions:
+    - theme: brand
+      text: Get Started
+      link: /guide/
+    - theme: alt
+      text: Why Vite?
+      link: /guide/why
+    - theme: alt
+      text: View on GitHub
+      link: https://github.com/alvarosabu/res
+
+features:
+  - icon: 💡
+    title: Declarative
+    details: Build 3D scene as they were Vue components
+  - icon: ⚡️
+    title: Powered by Vite
+    details: Hot Module Replacement (HMR) that stays fast regardless of app size.
+  - icon: 🥰
+    title: Keeps up to date
+    details: It brings all the updated features of ThreeJS right away.
+---

+ 6 - 0
docs/public/favicon-dark.svg

@@ -0,0 +1,6 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="32" height="32" fill="white"/>
+<path d="M11.6854 4.42916C12.0738 3.78182 13.012 3.78182 13.4004 4.42915L19.1771 14.0569C19.577 14.7235 19.0969 15.5714 18.3196 15.5714H6.76624C5.98894 15.5714 5.50883 14.7235 5.90875 14.0569L11.6854 4.42916Z" fill="#82DBC5"/>
+<path d="M15.6857 11.5429C15.6857 10.9906 16.1334 10.5429 16.6857 10.5429H26C26.5522 10.5429 27 10.9906 27 11.5429V20.8572C27 21.4094 26.5522 21.8572 26 21.8572H16.6857C16.1334 21.8572 15.6857 21.4094 15.6857 20.8572V11.5429Z" fill="#4F4F4F"/>
+<circle cx="16" cy="23" r="6" fill="#EFAC35"/>
+</svg>

+ 5 - 0
docs/public/favicon.svg

@@ -0,0 +1,5 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.6854 3.42916C12.0738 2.78182 13.012 2.78182 13.4004 3.42915L19.1771 13.0569C19.577 13.7235 19.0969 14.5714 18.3196 14.5714H6.76624C5.98894 14.5714 5.50883 13.7235 5.90875 13.0569L11.6854 3.42916Z" fill="#82DBC5"/>
+<path d="M15.6857 10.5429C15.6857 9.99059 16.1334 9.54288 16.6857 9.54288H26C26.5522 9.54288 27 9.99059 27 10.5429V19.8572C27 20.4094 26.5522 20.8572 26 20.8572H16.6857C16.1334 20.8572 15.6857 20.4094 15.6857 19.8572V10.5429Z" fill="#4F4F4F"/>
+<circle cx="16" cy="22" r="6" fill="#EFAC35"/>
+</svg>

BIN
docs/public/hero.png


+ 5 - 0
docs/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>

+ 62 - 0
package.json

@@ -0,0 +1,62 @@
+{
+  "name": "tres",
+  "description": "Declarative ThreeJS using Vue Components",
+  "version": "1.0.0",
+  "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
+  "workspaces": [
+    "apps/**",
+    "packages/*"
+  ],
+  "scripts": {
+    "preinstall": "npx only-allow pnpm",
+    "update-deps": "pnpm update -i -r --latest",
+    "build:ci": "pnpm run build:tres && pnpm run build:cientos",
+    "build:tres": "pnpm --filter @tresjs/core build",
+    "build:cientos": "pnpm --filter @tresjs/cientos build",
+    "build:all": "pnpm run build",
+    "test": "pnpm run test:tres",
+    "test:tres": "pnpm --filter @tresjs/core test:ci",
+    "docs:dev": "vitepress dev docs",
+    "docs:build": "vitepress build docs",
+    "docs:serve": "vitepress serve docs",
+    "ci:version": "pnpm changelog && pnpm changeset version && pnpm install --no-frozen-lockfile --shamefully-hoist && git add .",
+    "ci:publish": "pnpm build:ci && git status && pnpm publish:pkgs && pnpm changeset tag",
+    "changeset": "changeset",
+    "publish:pkgs": "pnpm run publish:tres && pnpm publish:cientos",
+    "publish:tres": "pnpm publish --filter @tresjs/core",
+    "publish:cientos": "pnpm publish --filter @tresjs/cientos",
+    "changeset-publish": "changeset publish",
+    "changeset-tag": "changeset tag",
+    "changelog": "pnpm run changelog:tres && pnpm run changelog:three-vue",
+    "changelog:tres": "echo 'generate @tresjs/core changelog' && conventional-changelog -p angular -i ./packages/tres/CHANGELOG.md -s  --commit-path ./packages/tres && git add ./packages/tres/CHANGELOG.md",
+    "changelog:cientos": "echo 'generate @tresjs/cientos changelog' && conventional-changelog -p angular -i ./packages/cientos/CHANGELOG.md -s  --commit-path ./packages/cientos && git add ./packages/cientos/CHANGELOG.md",
+    "changelog:three-vue": "echo 'generate three-vue changelog' && conventional-changelog -p angular -i ./apps/three-vue/CHANGELOG.md -s  --commit-path ./apps/three-vue && git add ./apps/three-vue/CHANGELOG.md",
+    "lint": "pnpm run lint:tres && pnpm run lint:cientos",
+    "lint:tres": "pnpm --filter @tresjs/core lint",
+    "lint:cientos": "pnpm --filter @tresjs/cientos lint"
+  },
+  "keywords": [
+    "vue",
+    "threejs",
+    "declarative",
+    "composables"
+  ],
+  "license": "MIT",
+  "devDependencies": {
+    "@alvarosabu/prettier-config": "^1.2.0",
+    "@changesets/changelog-github": "^0.4.7",
+    "@changesets/cli": "^2.25.2",
+    "@tresjs/cientos": "workspace:^1.0.0",
+    "@tresjs/core": "workspace:^1.0.0",
+    "@typescript-eslint/eslint-plugin": "^5.42.0",
+    "@typescript-eslint/parser": "^5.42.0",
+    "conventional-changelog-cli": "^2.2.2",
+    "eslint": "^8.26.0",
+    "eslint-config-prettier": "^8.5.0",
+    "eslint-plugin-vue": "^9.7.0",
+    "prettier": "^2.7.1",
+    "vitepress": "1.0.0-alpha.26",
+    "vue": "^3.2.41",
+    "vue-eslint-parser": "^9.1.0"
+  }
+}

+ 24 - 0
packages/cientos/.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 54 - 0
packages/cientos/CHANGELOG.md

@@ -0,0 +1,54 @@
+# @tresjs/cientos
+
+## 0.4.0
+
+### Minor Changes
+
+- 5766cd1: useLoader and cientos useGLTF
+
+### Patch Changes
+
+- Updated dependencies [5766cd1]
+  - @tresjs/core@0.5.0
+
+## 0.3.0
+
+### Minor Changes
+
+- 9a20974: Big refactor of components and composables
+
+### Patch Changes
+
+- Updated dependencies [9a20974]
+  - @tresjs/core@0.4.0
+
+## 0.2.0
+
+### Minor Changes
+
+- 818fed9: Moved TweakPane to cientos
+
+### Patch Changes
+
+- Updated dependencies [818fed9]
+  - @tresjs/core@0.3.0
+
+## 0.1.1
+
+### Patch Changes
+
+- 5592faf: Fix type issues and OrbitControls
+- Updated dependencies [5592faf]
+  - @tresjs/core@0.2.1
+
+## 0.1.0
+
+### Minor Changes
+
+- 1a052b8: Cientos package setup
+
+### Patch Changes
+
+- Updated dependencies [1a052b8]
+- Updated dependencies [d080ead]
+  - @tresjs/core@0.2.0

+ 60 - 0
packages/cientos/package.json

@@ -0,0 +1,60 @@
+{
+  "name": "@tresjs/cientos",
+  "description": "Collection of useful helpers and fully functional, ready-made abstractions for Tres",
+  "version": "1.0.0",
+  "type": "module",
+  "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
+  "files": [
+    "dist"
+  ],
+  "license": "MIT",
+  "main": "./dist/trescientos.umd.cjs",
+  "module": "./dist/trescientos.js",
+  "types": "./dist/index.d.ts",
+  "exports": {
+    ".": {
+      "import": "./dist/trescientos.js",
+      "require": "./dist/trescientos.umd.cjs"
+    },
+    "./styles": "./dist/style.css",
+    "./*": "./dist/trescientos.js"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "keywords": [
+    "vue",
+    "3d",
+    "threejs",
+    "three",
+    "threejs-vue"
+  ],
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview",
+    "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue"
+  },
+  "peerDependencies": {
+    "@tresjs/core": "workspace:^1.0.0",
+    "three": "latest",
+    "vue": "^3.2.45"
+  },
+  "devDependencies": {
+    "@tweakpane/plugin-essentials": "^0.1.5",
+    "@vitejs/plugin-vue": "^3.2.0",
+    "kolorist": "^1.6.0",
+    "pathe": "^0.3.9",
+    "rollup-plugin-analyzer": "^4.0.0",
+    "rollup-plugin-visualizer": "^5.8.3",
+    "tweakpane": "^3.1.0",
+    "typescript": "^4.8.4",
+    "vite": "^3.2.3",
+    "vite-plugin-banner": "^0.6.1",
+    "vite-plugin-dts": "^1.7.0"
+  },
+  "dependencies": {
+    "@tresjs/core": "workspace:^1.0.0",
+    "three-stdlib": "^2.17.3"
+  }
+}

+ 1 - 0
packages/cientos/public/vite.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

+ 31 - 0
packages/cientos/src/core/OrbitControls.vue

@@ -0,0 +1,31 @@
+<script setup lang="ts">
+import { useRenderLoop } from '@tresjs/core'
+import { Camera, WebGLRenderer } from 'three'
+import { OrbitControls as OrbitControlsImp } from 'three-stdlib'
+import { inject, type Ref, unref, watch } from 'vue'
+
+let controls: OrbitControlsImp
+
+const camera = inject<Ref<Camera>>('camera')
+const renderer = inject<Ref<WebGLRenderer>>('renderer')
+watch(
+  [camera, renderer],
+  () => {
+    if (camera?.value && renderer?.value) {
+      controls = new OrbitControlsImp(camera.value, unref(renderer).domElement)
+      controls.enableDamping = true
+
+      const { onLoop } = useRenderLoop()
+
+      onLoop(() => {
+        if (controls) {
+          controls.update()
+        }
+      })
+    }
+  },
+  {
+    deep: true,
+  },
+)
+</script>

+ 21 - 0
packages/cientos/src/core/useGLTF/component.ts

@@ -0,0 +1,21 @@
+import { Scene } from 'three'
+import { defineComponent, inject, Ref } from 'vue'
+import { useGLTF } from '.'
+
+export const GLTFModel = defineComponent({
+  name: 'GLTFModel',
+  props: {
+    path: String,
+    draco: Boolean,
+  },
+
+  async setup(props) {
+    const scene = inject<Ref<Scene>>('local-scene')
+
+    const { scene: model } = await useGLTF(props.path as string, { draco: props.draco })
+    if (scene?.value) {
+      scene.value.add(model)
+    }
+    return () => null
+  },
+})

+ 41 - 0
packages/cientos/src/core/useGLTF/index.ts

@@ -0,0 +1,41 @@
+import { GLTFLoader, DRACOLoader } from 'three-stdlib'
+import { useLoader } from '@tresjs/core'
+import { Object3D } from 'three'
+
+export interface GLTFLoaderOptions {
+  draco?: boolean
+  decoderPath?: string
+}
+
+export interface TresObject extends Object3D {
+  geometry: THREE.BufferGeometry
+  material: THREE.Material
+}
+export interface GLTFResult {
+  nodes: Array<TresObject>
+  materials: Array<THREE.Material>
+  scene: THREE.Scene
+}
+
+let dracoLoader: DRACOLoader | null = null
+
+function setExtensions(options: GLTFLoaderOptions, extendLoader?: (loader: GLTFLoader) => void) {
+  return (loader: GLTFLoader) => {
+    if (extendLoader) {
+      extendLoader(loader as GLTFLoader)
+    }
+    if (!dracoLoader) {
+      dracoLoader = new DRACOLoader()
+    }
+    dracoLoader.setDecoderPath(options.decoderPath || 'https://www.gstatic.com/draco/versioned/decoders/1.4.3/')
+  }
+}
+export async function useGLTF(
+  path: string | string[],
+  options: GLTFLoaderOptions = {
+    draco: false,
+  },
+  extendLoader?: (loader: GLTFLoader) => void,
+): Promise<GLTFResult> {
+  return (await useLoader(GLTFLoader, path, setExtensions(options, extendLoader))) as unknown as GLTFResult
+}

+ 38 - 0
packages/cientos/src/core/useTweakPane/index.ts

@@ -0,0 +1,38 @@
+import { onUnmounted, onMounted } from 'vue'
+import { Pane } from 'tweakpane'
+import * as EssentialsPlugin from '@tweakpane/plugin-essentials'
+import { useRenderLoop } from '@tresjs/core'
+
+type TweakPane = Pane & { addBlade(blade: any): void }
+let pane: TweakPane
+let fpsGraph: any
+
+export const useTweakPane = (selector = 'tres-container') => {
+  pane = new Pane({
+    container: (document.querySelector(selector) as HTMLElement) || undefined,
+  }) as TweakPane
+  pane.registerPlugin(EssentialsPlugin)
+
+  fpsGraph = pane.addBlade({
+    view: 'fpsgraph',
+    label: 'fpsgraph',
+  })
+
+  function disposeTweakPane() {
+    if (pane) {
+      pane.dispose()
+    }
+  }
+
+  onMounted(() => {
+    const { onBeforeLoop, onAfterLoop, resume } = useRenderLoop()
+    resume()
+    onBeforeLoop(() => fpsGraph.begin())
+    onAfterLoop(() => fpsGraph.end())
+  })
+  onUnmounted(() => {
+    disposeTweakPane()
+  })
+
+  return { pane, fpsGraph, disposeTweakPane }
+}

+ 8 - 0
packages/cientos/src/env.d.ts

@@ -0,0 +1,8 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

+ 5 - 0
packages/cientos/src/index.ts

@@ -0,0 +1,5 @@
+import OrbitControls from './core/OrbitControls.vue'
+import { useTweakPane } from './core/useTweakPane'
+import { GLTFModel } from './core/useGLTF/component'
+export * from './core/useGLTF'
+export { OrbitControls, useTweakPane, GLTFModel }

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 146 - 0
packages/cientos/stats.html


+ 27 - 0
packages/cientos/tsconfig.json

@@ -0,0 +1,27 @@
+{
+  "compilerOptions": {
+    "baseUrl": ".",
+    "target": "esnext",
+    "module": "esnext",
+    "moduleResolution": "node",
+    "strict": true,
+    "jsx": "preserve",
+    "sourceMap": true,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "lib": ["esnext", "dom"],
+    "types": ["vite/client", "node"],
+    "incremental": false,
+    "skipLibCheck": true,
+    "noUnusedLocals": true,
+    "strictNullChecks": true,
+    "forceConsistentCasingInFileNames": true,
+    "declaration": true,
+    "paths": {
+      "/@/*": ["src/*"]
+    }
+  },
+  "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "histoire.setup.ts", "histoire.setup.ts"],
+  "exclude": ["dist", "node_modules", "src/**/*.cy.ts", "src/**/*.test.ts"],
+  "references": [{ "path": "./tsconfig.node.json" }, { "path": "../tres/tsconfig.json" }]
+}

+ 9 - 0
packages/cientos/tsconfig.node.json

@@ -0,0 +1,9 @@
+{
+  "compilerOptions": {
+    "composite": true,
+    "module": "ESNext",
+    "moduleResolution": "Node",
+    "allowSyntheticDefaultImports": true
+  },
+  "include": ["vite.config.ts"]
+}

+ 70 - 0
packages/cientos/vite.config.ts

@@ -0,0 +1,70 @@
+import vue from '@vitejs/plugin-vue'
+import { defineConfig } from 'vite'
+
+import banner from 'vite-plugin-banner'
+import dts from 'vite-plugin-dts'
+import analyze from 'rollup-plugin-analyzer'
+/* import { visualizer } from 'rollup-plugin-visualizer' */
+
+import { resolve } from 'pathe'
+
+import { lightGreen, yellow, gray, bold } from 'kolorist'
+
+import pkg from './package.json'
+
+// eslint-disable-next-line no-console
+console.log(`${lightGreen('▲')} ${gray('■')} ${yellow('⚡️')} ${bold('Tres/cientos')} v${pkg.version}`)
+// https://vitejs.dev/config/
+export default defineConfig({
+  resolve: {
+    alias: {
+      '/@': resolve(__dirname, './src'),
+    },
+    dedupe: ['@tresjs/core'],
+  },
+  plugins: [
+    vue({}),
+    dts({
+      insertTypesEntry: true,
+    }),
+    banner({
+      content: `/**\n * name: ${pkg.name}\n * version: v${
+        pkg.version
+      }\n * (c) ${new Date().getFullYear()}\n * description: ${pkg.description}\n * author: ${pkg.author}\n */`,
+    }),
+  ],
+  build: {
+    lib: {
+      entry: resolve(__dirname, 'src/index.ts'),
+      name: 'trescientos',
+      fileName: 'trescientos',
+    },
+    watch: {
+      include: [resolve(__dirname, 'src')],
+    },
+    rollupOptions: {
+      plugins: [
+        analyze(),
+        /*     visualizer({
+          gzipSize: true,
+          brotliSize: true,
+          open: false,
+        }), */
+      ],
+      external: ['three', 'vue', '@tresjs/core'],
+      output: {
+        exports: 'named',
+        // Provide global variables to use in the UMD build
+        // for externalized deps
+        globals: {
+          /*        '@tresjs/core': 'TresjsCore', */
+          three: 'Three',
+          vue: 'Vue',
+        },
+      },
+    },
+  },
+  optimizeDeps: {
+    exclude: ['three', 'vue', '@tresjs/core'],
+  },
+})

+ 25 - 0
packages/tres/.gitignore

@@ -0,0 +1,25 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+coverage
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
packages/tres/.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar"]
+}

+ 179 - 0
packages/tres/CHANGELOG.md

@@ -0,0 +1,179 @@
+# [0.0.0](https://github.com/alvarosabu/tres/compare/v0.1.0...v0.0.0) (2022-11-22)
+
+## 1.1.0
+
+### Minor Changes
+
+- 3eb47dd: BREAKING CHANGE: Prefixed components
+
+## 0.5.0
+
+### Minor Changes
+
+- 5766cd1: useLoader and cientos useGLTF
+
+### Bug Fixes
+
+- cientos pkg version on tres ([beff910](https://github.com/alvarosabu/tres/commit/beff910d1ccaf5ee7dec24ba0aa92dd8ad53b4cf))
+- **cientos:** changed back to string key for provides ([54f4acc](https://github.com/alvarosabu/tres/commit/54f4acc2c6527334534d03adf45d9c46516b1160))
+- **cientos:** fixed controls ([f6397d9](https://github.com/alvarosabu/tres/commit/f6397d9a70b2235db05cf6a7e6a744c9c81071ce))
+- **core:** canvas height issue and updated stories ([ea9a542](https://github.com/alvarosabu/tres/commit/ea9a54201a2962819a7d7b691833058ccfa5de62))
+- **core:** issue with props on Renderer component ([a683277](https://github.com/alvarosabu/tres/commit/a6832776cf7c4a60276d35598e9b35a90c103786))
+- **lint:** fix lint and build issues ([a3a52bc](https://github.com/alvarosabu/tres/commit/a3a52bcddf7575dbdfa8d7329c91b1a913698e94))
+- typescript issues ([1f5c528](https://github.com/alvarosabu/tres/commit/1f5c528f6307d409b79913ad844443eb3bc8b85f))
+
+### Features
+
+- **cientos:** basic cientos pkg ([efce5bb](https://github.com/alvarosabu/tres/commit/efce5bb8b31b9985caa58ab69ff83d1a5d8a194b))
+- **cientos:** cientos package nice try ([3748e65](https://github.com/alvarosabu/tres/commit/3748e65526645eead70636260981f7f6476a7829))
+- **cientos:** moved tweakpane to cientos ([96488e7](https://github.com/alvarosabu/tres/commit/96488e7205dc764bf5bdcd58eec81c3de482dc43))
+- **cientos:** provide inject keys and clean up ([e2e6f58](https://github.com/alvarosabu/tres/commit/e2e6f58cf53bd9bfcc59361bf793913339864d61))
+- **cientos:** provide inject works on orbit controls ([ff95bd0](https://github.com/alvarosabu/tres/commit/ff95bd049ca51fa7ca9dfa77713bfc551c2cbc6d))
+- **cientos:** useGLTF and GLTModel component ([55191fd](https://github.com/alvarosabu/tres/commit/55191fdf82df5d75788e478d665ebf1ab04e90ce))
+- **core:** added orbit controls from cientos to stories ([14aa6c7](https://github.com/alvarosabu/tres/commit/14aa6c7bfd0172b8cf546a26efe7e371e96d648f))
+- **core:** refactor core composables convention to index.ts ([9041ce8](https://github.com/alvarosabu/tres/commit/9041ce840b55e4c5dfe3d966a6411c0a78469577))
+- **core:** use Renderer component and scene ([b25e7e2](https://github.com/alvarosabu/tres/commit/b25e7e216c7388667156088a0664dd65bd99114f))
+- **core:** useLoader composable and story for gltf ([f566049](https://github.com/alvarosabu/tres/commit/f566049db2a1d7c5522c49a4c02e77e33e3834f0))
+- **core:** useRenderLoop with ([f160e30](https://github.com/alvarosabu/tres/commit/f160e300ea4d71d9a4da35385043021420771717))
+
+# [0.0.0](https://github.com/alvarosabu/tres/compare/v0.1.0...v0.0.0) (2022-11-17)
+
+## 0.4.0
+
+### Minor Changes
+
+- 9a20974: Big refactor of components and composables
+
+### Bug Fixes
+
+- cientos pkg version on tres ([beff910](https://github.com/alvarosabu/tres/commit/beff910d1ccaf5ee7dec24ba0aa92dd8ad53b4cf))
+- **cientos:** changed back to string key for provides ([54f4acc](https://github.com/alvarosabu/tres/commit/54f4acc2c6527334534d03adf45d9c46516b1160))
+- **cientos:** fixed controls ([f6397d9](https://github.com/alvarosabu/tres/commit/f6397d9a70b2235db05cf6a7e6a744c9c81071ce))
+- **core:** canvas height issue and updated stories ([ea9a542](https://github.com/alvarosabu/tres/commit/ea9a54201a2962819a7d7b691833058ccfa5de62))
+- **core:** issue with props on Renderer component ([a683277](https://github.com/alvarosabu/tres/commit/a6832776cf7c4a60276d35598e9b35a90c103786))
+- **lint:** fix lint and build issues ([a3a52bc](https://github.com/alvarosabu/tres/commit/a3a52bcddf7575dbdfa8d7329c91b1a913698e94))
+- typescript issues ([1f5c528](https://github.com/alvarosabu/tres/commit/1f5c528f6307d409b79913ad844443eb3bc8b85f))
+
+### Features
+
+- **cientos:** basic cientos pkg ([efce5bb](https://github.com/alvarosabu/tres/commit/efce5bb8b31b9985caa58ab69ff83d1a5d8a194b))
+- **cientos:** cientos package nice try ([3748e65](https://github.com/alvarosabu/tres/commit/3748e65526645eead70636260981f7f6476a7829))
+- **cientos:** moved tweakpane to cientos ([96488e7](https://github.com/alvarosabu/tres/commit/96488e7205dc764bf5bdcd58eec81c3de482dc43))
+- **cientos:** provide inject keys and clean up ([e2e6f58](https://github.com/alvarosabu/tres/commit/e2e6f58cf53bd9bfcc59361bf793913339864d61))
+- **cientos:** provide inject works on orbit controls ([ff95bd0](https://github.com/alvarosabu/tres/commit/ff95bd049ca51fa7ca9dfa77713bfc551c2cbc6d))
+- **core:** added orbit controls from cientos to stories ([14aa6c7](https://github.com/alvarosabu/tres/commit/14aa6c7bfd0172b8cf546a26efe7e371e96d648f))
+- **core:** refactor core composables convention to index.ts ([9041ce8](https://github.com/alvarosabu/tres/commit/9041ce840b55e4c5dfe3d966a6411c0a78469577))
+- **core:** use Renderer component and scene ([b25e7e2](https://github.com/alvarosabu/tres/commit/b25e7e216c7388667156088a0664dd65bd99114f))
+- **core:** useRenderLoop with ([f160e30](https://github.com/alvarosabu/tres/commit/f160e300ea4d71d9a4da35385043021420771717))
+
+# [0.0.0](https://github.com/alvarosabu/tres/compare/v0.1.0...v0.0.0) (2022-11-12)
+
+## 0.3.0
+
+### Minor Changes
+
+- 818fed9: Moved TweakPane to cientos
+
+### Bug Fixes
+
+- cientos pkg version on tres ([beff910](https://github.com/alvarosabu/tres/commit/beff910d1ccaf5ee7dec24ba0aa92dd8ad53b4cf))
+- **cientos:** changed back to string key for provides ([54f4acc](https://github.com/alvarosabu/tres/commit/54f4acc2c6527334534d03adf45d9c46516b1160))
+- **lint:** fix lint and build issues ([a3a52bc](https://github.com/alvarosabu/tres/commit/a3a52bcddf7575dbdfa8d7329c91b1a913698e94))
+
+### Features
+
+- **cientos:** basic cientos pkg ([efce5bb](https://github.com/alvarosabu/tres/commit/efce5bb8b31b9985caa58ab69ff83d1a5d8a194b))
+- **cientos:** cientos package nice try ([3748e65](https://github.com/alvarosabu/tres/commit/3748e65526645eead70636260981f7f6476a7829))
+- **cientos:** moved tweakpane to cientos ([96488e7](https://github.com/alvarosabu/tres/commit/96488e7205dc764bf5bdcd58eec81c3de482dc43))
+- **cientos:** provide inject keys and clean up ([e2e6f58](https://github.com/alvarosabu/tres/commit/e2e6f58cf53bd9bfcc59361bf793913339864d61))
+- **cientos:** provide inject works on orbit controls ([ff95bd0](https://github.com/alvarosabu/tres/commit/ff95bd049ca51fa7ca9dfa77713bfc551c2cbc6d))
+- **core:** added orbit controls from cientos to stories ([14aa6c7](https://github.com/alvarosabu/tres/commit/14aa6c7bfd0172b8cf546a26efe7e371e96d648f))
+
+# [0.0.0](https://github.com/alvarosabu/tres/compare/v0.1.0...v0.0.0) (2022-11-12)
+
+## 0.2.1
+
+### Patch Changes
+
+- 5592faf: Fix type issues and OrbitControls
+
+### Bug Fixes
+
+- cientos pkg version on tres ([beff910](https://github.com/alvarosabu/tres/commit/beff910d1ccaf5ee7dec24ba0aa92dd8ad53b4cf))
+- **cientos:** changed back to string key for provides ([54f4acc](https://github.com/alvarosabu/tres/commit/54f4acc2c6527334534d03adf45d9c46516b1160))
+- **lint:** fix lint and build issues ([a3a52bc](https://github.com/alvarosabu/tres/commit/a3a52bcddf7575dbdfa8d7329c91b1a913698e94))
+
+### Features
+
+- **cientos:** basic cientos pkg ([efce5bb](https://github.com/alvarosabu/tres/commit/efce5bb8b31b9985caa58ab69ff83d1a5d8a194b))
+- **cientos:** cientos package nice try ([3748e65](https://github.com/alvarosabu/tres/commit/3748e65526645eead70636260981f7f6476a7829))
+- **cientos:** provide inject keys and clean up ([e2e6f58](https://github.com/alvarosabu/tres/commit/e2e6f58cf53bd9bfcc59361bf793913339864d61))
+- **cientos:** provide inject works on orbit controls ([ff95bd0](https://github.com/alvarosabu/tres/commit/ff95bd049ca51fa7ca9dfa77713bfc551c2cbc6d))
+- **core:** added orbit controls from cientos to stories ([14aa6c7](https://github.com/alvarosabu/tres/commit/14aa6c7bfd0172b8cf546a26efe7e371e96d648f))
+
+# [0.0.0](https://github.com/alvarosabu/tres/compare/v0.1.0...v0.0.0) (2022-11-12)
+
+## 0.2.0
+
+### Minor Changes
+
+- 1a052b8: Cientos package setup
+- d080ead: Core
+
+### Features
+
+- **cientos:** basic cientos pkg ([efce5bb](https://github.com/alvarosabu/tres/commit/efce5bb8b31b9985caa58ab69ff83d1a5d8a194b))
+- **cientos:** cientos package nice try ([3748e65](https://github.com/alvarosabu/tres/commit/3748e65526645eead70636260981f7f6476a7829))
+- **cientos:** provide inject keys and clean up ([e2e6f58](https://github.com/alvarosabu/tres/commit/e2e6f58cf53bd9bfcc59361bf793913339864d61))
+- **cientos:** provide inject works on orbit controls ([ff95bd0](https://github.com/alvarosabu/tres/commit/ff95bd049ca51fa7ca9dfa77713bfc551c2cbc6d))
+
+# 0.0.0 (2022-11-07)
+
+## 0.1.0
+
+### Minor Changes
+
+- ee1548d: Forcing changeset
+- 5ed1fd1: Core
+
+### Bug Fixes
+
+- **ci:** fix lint ([859862f](https://github.com/alvarosabu/tres/commit/859862fbb2e36eb595eada333df71f45998d7051))
+- **ci:** maybe this will fix the ci? ([bf1acd2](https://github.com/alvarosabu/tres/commit/bf1acd2283e1723b1c652a877ed3c879e1629845))
+- **core:** fix typings ([e44c598](https://github.com/alvarosabu/tres/commit/e44c5987c4fdc67a5df55c9e0f8fa37356c7ee9b))
+- **core:** major clean up and typing ([8d0bf4a](https://github.com/alvarosabu/tres/commit/8d0bf4a6609b54a01f08f2e51d959fa7df123a26))
+- **core:** trying to improve HMR ([22e1aca](https://github.com/alvarosabu/tres/commit/22e1aca65651d76e6320669c95dbfcc9ad88f301))
+- expose was the key to success ([d1009c2](https://github.com/alvarosabu/tres/commit/d1009c29b119298fe8521887ddeb3a8035e8ee72))
+- **lint:** add proper linter and fix them all ([a1e933d](https://github.com/alvarosabu/tres/commit/a1e933d384eb0340e8246bd05ae6176a6dfc5457))
+- reset versions to 000 to force proper increment ([b1b0259](https://github.com/alvarosabu/tres/commit/b1b02595c2e4ba315f7d45401a6d5dd8a7d80c7b))
+- trying out with VueComponents ([003e143](https://github.com/alvarosabu/tres/commit/003e143047abef9c51439cfc44b5a19109673d6f))
+
+### Features
+
+- **core:** added background, encoding and alpha to renderer ([f0db3cc](https://github.com/alvarosabu/tres/commit/f0db3cc2bbc6f4eec78c9effae2009d6cfe36013))
+- **core:** better typing ([4d1b344](https://github.com/alvarosabu/tres/commit/4d1b344eec15d79596ec21f144133d5481bd1ae3))
+- **core:** camera options for Canvas ([87a7704](https://github.com/alvarosabu/tres/commit/87a7704f61c2e2bc07308b408ca9a1b7960bf022))
+- **core:** clean up ([95dbc60](https://github.com/alvarosabu/tres/commit/95dbc60b4b779cdd857e7f4d293f1e094f503ce2))
+- **core:** cleaned up customRenderer logic ([c0ad68d](https://github.com/alvarosabu/tres/commit/c0ad68d36ddff230bbd753ce6c15b91da2ed7043))
+- **core:** component based instantiation is working ([5f1439f](https://github.com/alvarosabu/tres/commit/5f1439ffb3baa975a1507b2caf4dd342fc37badb))
+- **core:** HMR seems to work ([9a274d9](https://github.com/alvarosabu/tres/commit/9a274d98fcfbbcffba7bf3f30677deb50e402022))
+- **core:** improved logging and removing warnings ([d25bd7a](https://github.com/alvarosabu/tres/commit/d25bd7a90e5986c656d9062c0b00e5313da974fb))
+- **core:** initial tres core ([905a4c0](https://github.com/alvarosabu/tres/commit/905a4c03365d267e1f785e81d716e3d953f12119))
+- **core:** normalized properties ([793b847](https://github.com/alvarosabu/tres/commit/793b847d713ec15a561808c96117921572b34763))
+- **core:** optimized ([a75093f](https://github.com/alvarosabu/tres/commit/a75093fc3fcb408d8635ab76960295d335b99bf5))
+- **core:** reduce amount of threejs autogenerated components to only classes ([2b29df6](https://github.com/alvarosabu/tres/commit/2b29df68da8edf37ceda0c3b44da2cdef88385fe))
+- **core:** rename main composable ([c49a4d2](https://github.com/alvarosabu/tres/commit/c49a4d2991ea2ec093684befade0fb1ad42c50da))
+- **core:** stories ([afa5498](https://github.com/alvarosabu/tres/commit/afa5498f967fd3bcfd435e8a10f1f326790dc7d8))
+- **core:** sub components is now possible ([a667bbd](https://github.com/alvarosabu/tres/commit/a667bbd706edf524670fcfe50566baa6ba605fab))
+- **core:** test for camera ([185b910](https://github.com/alvarosabu/tres/commit/185b910e318a01d80ee71ad936e08be887ab9ca1))
+- **core:** three vue component based approach ([5e7e3d6](https://github.com/alvarosabu/tres/commit/5e7e3d6504f0385bfce8cab4668d096b5a8c2d01))
+- **core:** useLoop is working ([9d20a04](https://github.com/alvarosabu/tres/commit/9d20a04785ebf6982a4e40792eb2e91a0fa29520))
+- **core:** useRenderer composable ([01d0e7f](https://github.com/alvarosabu/tres/commit/01d0e7f60def0c7ca9ccb99c91e2d270dc43dac3))
+- **core:** useTextures ([a522cc4](https://github.com/alvarosabu/tres/commit/a522cc49d5a2b0a5851d8d4bdf25b29470d509c7))
+- **core:** useTweakPane temporarely living inside core ([cb0890e](https://github.com/alvarosabu/tres/commit/cb0890e7d92ae4ef8b1ba718936d655df2767cd3))
+- **docs:** getting started page ([e4ecf78](https://github.com/alvarosabu/tres/commit/e4ecf78ec91b1f8d345e28cf9dd6f32586dbd345))
+- fix lint issues ([7236655](https://github.com/alvarosabu/tres/commit/7236655da170afa99acacf32905e7f4ce1e54936))
+- init of tres pkg ([748ff1b](https://github.com/alvarosabu/tres/commit/748ff1bbd25afe1ced1d228e52a17a53b48f9bc2))
+- **stories:** add light story ([258ebde](https://github.com/alvarosabu/tres/commit/258ebde14c30f7717e5044b07035da83a9a0d461))
+- **test:** added test for useScene ([f9bf334](https://github.com/alvarosabu/tres/commit/f9bf334a7e58a597331d35509c006baf0223d9cd))
+- **test:** useRenderer tests ([68ff3b5](https://github.com/alvarosabu/tres/commit/68ff3b5bf0bd63668cf684e8209ed383bef3863e))

+ 40 - 0
packages/tres/README.md

@@ -0,0 +1,40 @@
+# @tresjs/core ▲ ■ ●
+
+> Declarative ThreeJS using Vue Components
+
+- 💡 Build 3D scene as they were Vue components
+- ⚡️ Powered by Vite
+- 🥰 It brings all the updated features of ThreeJS right awayregardless the version
+- 🦾 Fully Typed
+
+Tres (Spanish word for "three", pronounced `/tres/` ) is a way of creating ThreeJS scenes with Vue components in a declarative fashion. Think of it as a [React-three-fiber](https://docs.pmnd.rs/react-three-fiber) or [Lunchbox](https://github.com/breakfast-studio/lunchboxjs) but without the need of a [custom Vue3 Renderer](https://vuejs.org/api/custom-renderer.html).
+
+## Setup
+
+```
+pnpm install --shamefully-hoist
+```
+
+## Dev
+
+To run the small playground without Histoire use
+
+```
+pnpm run dev
+```
+
+## Histoire
+
+All local demos will be inside [Histoire](https://histoire.dev/) stories.
+
+```
+pnpm run story:dev
+```
+
+## Build lib
+
+To build the core as library mode just use
+
+```
+pnpm run build
+```

+ 15 - 0
packages/tres/components.d.ts

@@ -0,0 +1,15 @@
+// generated by unplugin-vue-components
+// We suggest you to commit this file into source control
+// Read more: https://github.com/vuejs/core/pull/3399
+import '@vue/runtime-core'
+
+export {}
+
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    RouterLink: typeof import('vue-router')['RouterLink']
+    RouterView: typeof import('vue-router')['RouterView']
+    TresCanvas: typeof import('./src/components/TresCanvas.vue')['default']
+    TresScene: typeof import('./src/components/TresScene.vue')['default']
+  }
+}

+ 20 - 0
packages/tres/histoire.config.ts

@@ -0,0 +1,20 @@
+import { defineConfig } from 'histoire'
+import { HstVue } from '@histoire/plugin-vue'
+
+export default defineConfig({
+  theme: {
+    title: 'TresJS',
+
+    logo: {
+      light: '/logo.svg',
+      dark: '/logo-dark.svg',
+    },
+  },
+  setupFile: './histoire.setup.ts',
+  plugins: [HstVue()],
+  defaultStoryProps: {
+    iconColor: '#00c5a5',
+    responsiveDisabled: true,
+    autoPropsDisabled: true,
+  },
+})

+ 9 - 0
packages/tres/histoire.setup.ts

@@ -0,0 +1,9 @@
+import { defineSetupVue3 } from '@histoire/plugin-vue'
+
+import Tres from './src'
+
+export const setupVue3 = defineSetupVue3(({ app }) => {
+  app.use(Tres, {
+    prefix: 'Tres',
+  })
+})

+ 13 - 0
packages/tres/index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>Tres Playground</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.ts"></script>
+  </body>
+</html>

+ 77 - 0
packages/tres/package.json

@@ -0,0 +1,77 @@
+{
+  "name": "@tresjs/core",
+  "description": "Declarative ThreeJS using Vue Components",
+  "version": "1.0.0",
+  "type": "module",
+  "author": "Alvaro Saburido <hola@alvarosaburido.dev> (https://github.com/alvarosabu/)",
+  "files": [
+    "dist"
+  ],
+  "license": "MIT",
+  "main": "./dist/tres.umd.cjs",
+  "module": "./dist/tres.js",
+  "types": "./dist/index.d.ts",
+  "exports": {
+    ".": {
+      "import": "./dist/tres.js",
+      "require": "./dist/tres.umd.cjs"
+    },
+    "./styles": "./dist/style.css",
+    "./*": "./dist/tres.js"
+  },
+  "publishConfig": {
+    "access": "public"
+  },
+  "keywords": [
+    "vue",
+    "3d",
+    "threejs",
+    "three",
+    "threejs-vue"
+  ],
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview",
+    "test": "vitest",
+    "test:ci": "vitest run",
+    "test:ui": "vitest --ui",
+    "coverage": "vitest run --coverage",
+    "story:dev": "histoire dev",
+    "story:build": "histoire build",
+    "story:preview": "histoire preview",
+    "lint": "eslint . --ext .js,.jsx,.ts,.tsx,.vue"
+  },
+  "dependencies": {
+    "three": "^0.146.0",
+    "vue": "^3.2.45"
+  },
+  "peerDependencies": {
+    "three": "latest"
+  },
+  "devDependencies": {
+    "@alvarosabu/utils": "^2.2.0",
+    "@histoire/plugin-vue": "0.11.7",
+    "@tresjs/cientos": "workspace:^1.0.0",
+    "@types/three": "^0.146.0",
+    "@vitejs/plugin-vue": "^3.2.0",
+    "@vitest/coverage-c8": "^0.25.1",
+    "@vitest/ui": "^0.25.1",
+    "@vueuse/core": "^9.5.0",
+    "gl": "6.0.1",
+    "happy-dom": "^7.7.0",
+    "histoire": "0.11.7",
+    "kolorist": "^1.6.0",
+    "pathe": "^0.3.9",
+    "rollup-plugin-analyzer": "^4.0.0",
+    "rollup-plugin-visualizer": "^5.8.3",
+    "unplugin-vue-components": "^0.22.9",
+    "vite": "^3.2.3",
+    "vite-plugin-banner": "^0.6.1",
+    "vite-plugin-dts": "^1.7.0",
+    "vite-plugin-glsl": "^0.5.3",
+    "vite-plugin-require-transform": "^1.0.4",
+    "vitest": "^0.25.1",
+    "vue-demi": "^0.13.11"
+  }
+}

BIN
packages/tres/public/favicon-16x16.png


BIN
packages/tres/public/favicon-32x32.png


+ 6 - 0
packages/tres/public/favicon-dark.svg

@@ -0,0 +1,6 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="32" height="32" fill="white"/>
+<path d="M11.6854 4.42916C12.0738 3.78182 13.012 3.78182 13.4004 4.42915L19.1771 14.0569C19.577 14.7235 19.0969 15.5714 18.3196 15.5714H6.76624C5.98894 15.5714 5.50883 14.7235 5.90875 14.0569L11.6854 4.42916Z" fill="#82DBC5"/>
+<path d="M15.6857 11.5429C15.6857 10.9906 16.1334 10.5429 16.6857 10.5429H26C26.5522 10.5429 27 10.9906 27 11.5429V20.8572C27 21.4094 26.5522 21.8572 26 21.8572H16.6857C16.1334 21.8572 15.6857 21.4094 15.6857 20.8572V11.5429Z" fill="#4F4F4F"/>
+<circle cx="16" cy="23" r="6" fill="#EFAC35"/>
+</svg>

BIN
packages/tres/public/favicon.ico


+ 5 - 0
packages/tres/public/favicon.svg

@@ -0,0 +1,5 @@
+<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.6854 3.42916C12.0738 2.78182 13.012 2.78182 13.4004 3.42915L19.1771 13.0569C19.577 13.7235 19.0969 14.5714 18.3196 14.5714H6.76624C5.98894 14.5714 5.50883 13.7235 5.90875 13.0569L11.6854 3.42916Z" fill="#82DBC5"/>
+<path d="M15.6857 10.5429C15.6857 9.99059 16.1334 9.54288 16.6857 9.54288H26C26.5522 9.54288 27 9.99059 27 10.5429V19.8572C27 20.4094 26.5522 20.8572 26 20.8572H16.6857C16.1334 20.8572 15.6857 20.4094 15.6857 19.8572V10.5429Z" fill="#4F4F4F"/>
+<circle cx="16" cy="22" r="6" fill="#EFAC35"/>
+</svg>

+ 5 - 0
packages/tres/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
packages/tres/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>

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 1173 - 0
packages/tres/public/models/AkuAku.gltf


BIN
packages/tres/public/pwa-192x192.png


BIN
packages/tres/public/pwa-512x512.png


+ 5 - 0
packages/tres/public/safari-pinned-tab.svg

@@ -0,0 +1,5 @@
+<svg width="934" height="934" viewBox="0 0 934 934" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M357.84 33.2164C358.228 32.5691 359.166 32.5691 359.555 33.2164L594.818 425.321C595.217 425.988 594.737 426.836 593.96 426.836H123.434C122.657 426.836 122.177 425.988 122.577 425.321L357.84 33.2164Z" fill="#82DBC5"/>
+<path d="M457.456 269.817C457.456 269.265 457.904 268.817 458.456 268.817H812C812.552 268.817 813 269.265 813 269.817V623.361C813 623.913 812.552 624.361 812 624.361H458.456C457.904 624.361 457.456 623.913 457.456 623.361V269.817Z" fill="#4F4F4F"/>
+<circle cx="467.334" cy="660.274" r="188.546" fill="#EFAC35"/>
+</svg>

BIN
packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_ambientOcclusion.jpg


BIN
packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_basecolor.jpg


BIN
packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_height.png


BIN
packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_normal.jpg


BIN
packages/tres/public/textures/stylized-leaves-material/Stylized_Leaves_002_roughness.jpg


+ 1 - 0
packages/tres/public/vite.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

+ 30 - 0
packages/tres/src/App.vue

@@ -0,0 +1,30 @@
+<script setup lang="ts">
+import { Color } from 'three'
+import { OrbitControls, useTweakPane } from '@tresjs/cientos'
+/* import TestSphere from '/@/components/TestSphere.vue' */
+
+/* import { OrbitControls, useTweakPane } from '@tresjs/cientos' */
+/* import { OrbitControls, useTweakPane } from '../../cientos/src/'
+ */
+const colorTeal = new Color('teal')
+useTweakPane()
+
+const meshPosition = [2, 1, 0]
+</script>
+<template>
+  <Suspense>
+    <TresCanvas shadows alpha power-preference="high-performance" preserve-drawing-buffer physically-correct-lights>
+      <OrbitControls />
+      <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+      <TresScene>
+        <TresMesh :position="meshPosition" :scale="1">
+          <TresSphereGeometry />
+          <TresMeshToonMaterial :color="colorTeal" />
+        </TresMesh>
+        <TestSphere />
+        <TresAxesHelper :args="[1]" />
+        <TresDirectionalLight :position="[0, 2, 4]" :intensity="2" cast-shadow />
+      </TresScene>
+    </TresCanvas>
+  </Suspense>
+</template>

+ 1 - 0
packages/tres/src/assets/vue.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

+ 30 - 0
packages/tres/src/components/TestSphere.vue

@@ -0,0 +1,30 @@
+<script setup lang="ts">
+import { Ref, ref } from 'vue'
+import { useRenderLoop, useTexture } from '/@/core/'
+import { TresInstance } from '../types'
+
+const sphereRef: Ref<TresInstance | null> = ref(null)
+
+const { onLoop, resume } = useRenderLoop()
+resume()
+onLoop(({ _delta, elapsed }) => {
+  if (sphereRef.value) {
+    sphereRef.value.position.y = Math.sin(elapsed * 0.2) * 2.0
+  }
+})
+
+/* const texture = await useTexture(['/textures/stylized-leaves-material/Stylized_Leaves_002_basecolor.jpg']) */
+const pbrTexture = await useTexture({
+  map: '/textures/stylized-leaves-material/Stylized_Leaves_002_basecolor.jpg',
+  displacementMap: '/textures/stylized-leaves-material/Stylized_Leaves_002_height.png',
+  roughnessMap: '/textures/stylized-leaves-material/Stylized_Leaves_002_roughness.jpg',
+  normalMap: '/textures/stylized-leaves-material/Stylized_Leaves_002_normal.jpg',
+  ambientOcclusion: '/textures/stylized-leaves-material/Stylized_Leaves_002_ambientOcclusion.jpg',
+})
+</script>
+<template>
+  <TresMesh ref="sphereRef" :position="[-2, 2, 2]" :scale="1" cast-shadow>
+    <TresSphereGeometry :args="[1, 500, 500]" />
+    <TresMeshStandardMaterial v-bind="pbrTexture" />
+  </TresMesh>
+</template>

+ 1 - 0
packages/tres/src/composables/index.ts

@@ -0,0 +1 @@
+export * from './useLogger'

+ 32 - 0
packages/tres/src/composables/useLogger.ts

@@ -0,0 +1,32 @@
+/* eslint-disable no-console */
+export const isProd = import.meta.env.MODE === 'production'
+
+const logPrefix = '[TresJS ▲ ■ ●] '
+
+interface LoggerComposition {
+  logError: (message: string, error?: Error | ErrorEvent) => void
+  logWarning: (message: string) => void
+  logMessage: (name: string, value: any) => void
+}
+
+export function useLogger(): LoggerComposition {
+  function logError(message: string, error?: Error | ErrorEvent) {
+    console.error(`${logPrefix} ${message}`, error || '')
+  }
+
+  function logWarning(message: string) {
+    console.warn(`${logPrefix} ${message}`)
+  }
+
+  function logMessage(name: string, value: any) {
+    if (!isProd) {
+      console.log(`${logPrefix} - ${name}:`, value)
+    }
+  }
+
+  return {
+    logError,
+    logWarning,
+    logMessage,
+  }
+}

+ 9 - 0
packages/tres/src/core/index.ts

@@ -0,0 +1,9 @@
+export * from './useCamera'
+export * from './useCatalogue'
+export * from './useInstanceCreator'
+export * from './useRenderLoop/'
+export * from './useRenderer/'
+export * from './useScene/'
+export * from './useLoader'
+export * from './useTexture'
+export * from './useTres'

+ 109 - 0
packages/tres/src/core/useCamera/index.ts

@@ -0,0 +1,109 @@
+import { PerspectiveCamera, OrthographicCamera } from 'three'
+
+import { useWindowSize } from '@vueuse/core'
+import { computed, ComputedRef, watch, inject, Ref } from 'vue'
+
+export enum CameraType {
+  Perspective = 'Perspective',
+  Orthographic = 'Orthographic',
+}
+
+export type Camera = PerspectiveCamera | OrthographicCamera
+
+export type CameraState = {
+  cameras: Array<Camera>
+}
+
+export interface PerspectiveCameraOptions {
+  fov?: number
+  near?: number
+  far?: number
+}
+
+export interface OrthographicCameraOptions {
+  left?: number
+  right?: number
+  top?: number
+  bottom?: number
+  near?: number
+  far?: number
+}
+
+interface UseCameraReturn {
+  activeCamera: ComputedRef<Camera>
+  createCamera: (cameraType?: CameraType, options?: PerspectiveCameraOptions | OrthographicCameraOptions) => Camera
+  updateCamera: () => void
+  pushCamera: (camera: Camera) => void
+}
+
+const state: CameraState = {
+  cameras: [],
+}
+
+const VERTICAL_FIELD_OF_VIEW = 45
+let camera: Camera
+
+export function useCamera(): UseCameraReturn {
+  const { width, height } = useWindowSize()
+
+  function createCamera(
+    cameraType = CameraType.Perspective,
+    options?: PerspectiveCameraOptions | OrthographicCameraOptions,
+  ) {
+    if (cameraType === CameraType.Perspective) {
+      const { near, far, fov } = (options as PerspectiveCameraOptions) || {
+        near: 0.1,
+        far: 1000,
+        fov: VERTICAL_FIELD_OF_VIEW,
+      }
+      camera = new PerspectiveCamera(fov, aspectRatio.value, near, far)
+      state.cameras.push(camera as PerspectiveCamera)
+    } else {
+      const { left, right, top, bottom, near, far } = (options as OrthographicCameraOptions) || {
+        left: -100,
+        right: 100,
+        top: 100,
+        bottom: -100,
+        near: 0.1,
+        far: 1000,
+      }
+      camera = new OrthographicCamera(left, right, top, bottom, near, far)
+      state.cameras.push(camera as OrthographicCamera)
+    }
+
+    state.cameras.push(camera)
+    return camera
+  }
+
+  const aspectRatio = computed(() => width.value / height.value)
+
+  const activeCamera = computed(() => state.cameras[0])
+
+  function updateCamera() {
+    if (activeCamera.value instanceof PerspectiveCamera) {
+      activeCamera.value.aspect = aspectRatio.value
+    }
+    activeCamera.value.updateProjectionMatrix()
+  }
+
+  function pushCamera(camera: Camera): void {
+    const currentCamera = inject<Ref<Camera>>('camera')
+    if (camera && currentCamera) {
+      currentCamera.value = camera
+    }
+    state.cameras.push(camera)
+    if (camera instanceof PerspectiveCamera) {
+      camera.aspect = aspectRatio.value
+    }
+    camera.updateProjectionMatrix()
+  }
+
+  watch(aspectRatio, updateCamera)
+
+  return {
+    activeCamera,
+    createCamera,
+    updateCamera,
+    pushCamera,
+  }
+}

+ 18 - 0
packages/tres/src/core/useCatalogue/index.ts

@@ -0,0 +1,18 @@
+import * as THREE from 'three'
+
+let catalogue: {
+  [key: string]: any
+} = { ...THREE }
+
+delete catalogue.Scene
+
+export function useCatalogue() {
+  const extend = (objects: any) => {
+    catalogue = Object.assign(catalogue, objects)
+  }
+
+  return {
+    extend,
+    catalogue,
+  }
+}

+ 19 - 0
packages/tres/src/core/useCatalogue/useCatalogue.test.ts

@@ -0,0 +1,19 @@
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
+import { describe, test, expect } from 'vitest'
+import { useCatalogue } from './'
+
+describe('useCatalogue()', () => {
+  test('should init catalog', () => {
+    const { catalogue } = useCatalogue()
+    expect(catalogue).toHaveProperty('Mesh')
+  })
+  test('should be able to extend catalogue', () => {
+    const { catalogue, extend } = useCatalogue()
+    extend({ OrbitControls })
+    expect(catalogue).toHaveProperty('OrbitControls')
+  })
+  test('catalog objects should be instanciable', () => {
+    const { catalogue } = useCatalogue()
+    expect(new catalogue.PerspectiveCamera()).toHaveProperty('type', 'PerspectiveCamera')
+  })
+})

+ 115 - 0
packages/tres/src/core/useInstanceCreator/index.ts

@@ -0,0 +1,115 @@
+/* eslint-disable new-cap */
+/* eslint-disable @typescript-eslint/no-empty-function */
+import { PerspectiveCamera } from 'three'
+import { defineComponent } from 'vue'
+import { isArray, isDefined } from '@alvarosabu/utils'
+import { normalizeVectorFlexibleParam } from '/@/utils/normalize'
+import { useCamera, useScene } from '/@/core/'
+import { useLogger } from '/@/composables'
+import { TresInstance, TresVNodeType } from '../../types'
+
+const VECTOR3_PROPS = ['rotation', 'scale', 'position']
+
+export function useInstanceCreator(prefix: string) {
+  const { logMessage, logError } = useLogger()
+
+  function createComponentInstances(newObjects: Record<string, any>) {
+    return Object.entries(newObjects)
+      .filter(([_key, value]) => (value as { prototype: any })?.prototype?.constructor?.toString().includes('class'))
+      .map(([key, value]) => {
+        const name = `${prefix}${key}`
+        const cmp = defineComponent({
+          name,
+          setup(props, { slots, attrs, ...ctx }) {
+            logMessage(name, {
+              props,
+              slots,
+              attrs,
+              ctx,
+            })
+            let instance: TresInstance
+            const { scene } = useScene()
+            const { pushCamera } = useCamera()
+
+            function createInstance() {
+              if (slots?.default && slots?.default()) {
+                const internal = slots.default().map(vnode => {
+                  let internalInstance
+                  const vNodeType = ((vnode.type as TresVNodeType).name as string).replace(prefix, '')
+                  if (vnode?.props?.args) {
+                    internalInstance = new newObjects[vNodeType](...vnode.props.args)
+                  } else {
+                    internalInstance = new newObjects[vNodeType]()
+                  }
+                  if (vnode?.props) {
+                    processProps(vnode.props, internalInstance)
+                  }
+
+                  return internalInstance
+                })
+
+                instance = new value(...internal)
+              } else if ((attrs as { args: [] }).args) {
+                instance = new value(...(attrs as { args: [] }).args)
+              } else {
+                instance = new value()
+              }
+
+              processProps(attrs, instance)
+              if (name.includes('Camera')) {
+                pushCamera(instance as unknown as PerspectiveCamera)
+              } else {
+                scene?.value.add(instance)
+              }
+              logMessage('Instance added', scene)
+
+              ctx.expose(instance)
+            }
+
+            function processProps(attrs: any, instance: TresInstance) {
+              Object.entries(attrs).forEach(([key, value]) => {
+                const camel = key.replace(/(-\w)/g, m => m[1].toUpperCase())
+                if (camel === 'args' || value === undefined) {
+                  return
+                }
+                // Check if property is a Vector3
+                if (VECTOR3_PROPS.includes(key) && value) {
+                  value = normalizeVectorFlexibleParam(value)
+                }
+                try {
+                  if (instance[camel] && isDefined(instance[camel].set)) {
+                    if (isArray(value)) {
+                      instance[camel].set(...(value as Array<number>))
+                    } else {
+                      instance[camel].set(value)
+                    }
+                  } else {
+                    if (value === '') {
+                      value = true
+                    }
+                    instance[camel] = value
+                  }
+                } catch (error: unknown) {
+                  logError(`There was an error setting ${camel} property`, error as Error)
+                }
+              })
+
+              if (attrs.ref) {
+                attrs.ref = instance
+              }
+            }
+
+            createInstance()
+
+            return () => {}
+          },
+        })
+
+        return [name, cmp]
+      })
+  }
+
+  return {
+    createComponentInstances,
+  }
+}

+ 75 - 0
packages/tres/src/core/useLoader/index.ts

@@ -0,0 +1,75 @@
+import { isArray } from '@alvarosabu/utils'
+import { Object3D } from 'three'
+import { useLogger } from '/@/composables'
+
+export interface TresLoader<T> extends THREE.Loader {
+  load(
+    url: string,
+    onLoad?: (result: T) => void,
+    onProgress?: (event: ProgressEvent) => void,
+    onError?: (event: ErrorEvent) => void,
+  ): unknown
+}
+
+export type LoaderProto<T> = new (...args: any) => TresLoader<T extends unknown ? any : T>
+export type LoaderReturnType<T, L extends LoaderProto<T>> = T extends unknown
+  ? Awaited<ReturnType<InstanceType<L>['loadAsync']>>
+  : T
+
+export function trasverseObjects(object: Object3D) {
+  const data: { [key: string]: any } = { nodes: {}, materials: {} }
+  if (object) {
+    object.traverse((obj: any) => {
+      if (obj.name) {
+        data.nodes[obj.name] = obj
+      }
+      if (obj.material && !data.materials[obj.material.name]) {
+        data.materials[obj.material.name] = obj.material
+      }
+    })
+  }
+  return data
+}
+
+export type Extensions<T extends { prototype: LoaderProto<any> }> = (loader: T['prototype']) => void
+
+export async function useLoader<T extends LoaderProto<T>, U extends string | string[]>(
+  Loader: T,
+  url: U,
+  extensions?: Extensions<T>,
+  onProgress?: (event: ProgressEvent<EventTarget>) => void,
+  cb?: (proto: TresLoader<T>) => void,
+) {
+  const { logError } = useLogger()
+  const proto = new Loader()
+  if (cb) {
+    cb(proto)
+  }
+
+  if (extensions) {
+    extensions(Loader)
+  }
+
+  const paths = (Array.isArray(url) ? url : [url]) as string[]
+
+  const results = paths.map(
+    path =>
+      new Promise((resolve, reject) => {
+        proto.load(
+          path,
+          data => {
+            if (data.scene) {
+              Object.assign(data, trasverseObjects(data.scene))
+            }
+            resolve(data)
+          },
+          onProgress,
+          (error: ErrorEvent) => reject(logError('[useLoader] - Failed to load resource', error as unknown as Error)),
+        )
+      }),
+  )
+
+  return (isArray(url) ? await Promise.all(results) : await results[0]) as U extends any[]
+    ? LoaderReturnType<T, T>[]
+    : LoaderReturnType<T, T>
+}

+ 46 - 0
packages/tres/src/core/useRenderLoop/index.ts

@@ -0,0 +1,46 @@
+import { createEventHook, EventHookOn, Fn, useRafFn } from '@vueuse/core'
+import { Clock } from 'three'
+import { Ref } from 'vue'
+
+export interface RenderLoop {
+  delta: number
+  elapsed: number
+}
+
+export interface UseRenderLoopReturn {
+  onBeforeLoop: EventHookOn<RenderLoop>
+  onLoop: EventHookOn<RenderLoop>
+  onAfterLoop: EventHookOn<RenderLoop>
+  pause: Fn
+  resume: Fn
+  isActive: Ref<boolean>
+}
+
+const onBeforeLoop = createEventHook<RenderLoop>()
+const onLoop = createEventHook<RenderLoop>()
+const onAfterLoop = createEventHook<RenderLoop>()
+
+const clock = new Clock()
+
+const { pause, resume, isActive } = useRafFn(
+  () => {
+    const elapsed = clock.getElapsedTime()
+    const delta = clock.getDelta()
+
+    onBeforeLoop.trigger({ delta, elapsed })
+    onLoop.trigger({ delta, elapsed })
+    onAfterLoop.trigger({ delta, elapsed })
+  },
+  { immediate: false },
+)
+
+export function useRenderLoop(): UseRenderLoopReturn {
+  return {
+    onBeforeLoop: onBeforeLoop.on,
+    onLoop: onLoop.on,
+    onAfterLoop: onAfterLoop.on,
+    pause,
+    resume,
+    isActive,
+  }
+}

+ 79 - 0
packages/tres/src/core/useRenderer/component.ts

@@ -0,0 +1,79 @@
+import { ShadowMapType, TextureEncoding, ToneMapping } from 'three'
+import { h, defineComponent, ref, provide, onBeforeUnmount, shallowRef, PropType } from 'vue'
+import { useRenderer } from '.'
+import { useLogger } from '/@/composables'
+import { TresVNodeType } from '/@/types'
+
+/**
+ * Vue component for rendering a Tres component.
+ */
+
+export const TresCanvas = defineComponent({
+  name: 'TresCanvas',
+  props: {
+    shadows: Boolean,
+    shadowMapType: Object as PropType<ShadowMapType>,
+    physicallyCorrectLights: Boolean,
+    outputEncoding: Object as PropType<TextureEncoding>,
+    toneMapping: Object as PropType<ToneMapping>,
+    toneMappingExposure: Number,
+    context: Object as PropType<WebGLRenderingContext>,
+    powerPreference: Object as PropType<'high-performance' | 'low-power' | 'default'>,
+    preserveDrawingBuffer: Boolean,
+    clearColor: String,
+    windowSize: Boolean,
+  },
+  setup(props, { slots, attrs }) {
+    const { logError } = useLogger()
+
+    const canvas = ref<HTMLCanvasElement>()
+    const container = ref<HTMLElement>()
+
+    const { renderer, dispose, aspectRatio } = useRenderer(canvas, container, props)
+
+    const activeCamera = shallowRef()
+
+    provide('camera', activeCamera)
+    provide('renderer', renderer)
+    provide('aspect-ratio', aspectRatio)
+
+    if (slots.default && !slots.default().some(node => (node.type as TresVNodeType).name === 'Scene')) {
+      logError('TresCanvas must contain a Scene component.')
+    }
+    if (slots.default && !slots.default().some(node => (node.type as TresVNodeType).name?.includes('Camera'))) {
+      logError('Scene must contain a Camera component.')
+    }
+
+    if (import.meta.hot) {
+      import.meta.hot.on('vite:beforeUpdate', () => {
+        dispose()
+      })
+    }
+
+    onBeforeUnmount(() => dispose())
+
+    return () => {
+      if (slots.default) {
+        return h(
+          'div',
+          {
+            ref: container,
+            style: {
+              position: 'relative',
+              width: '100%',
+              height: '100vh',
+              ...(attrs.style as Record<string, unknown>),
+            },
+          },
+          [
+            h('canvas', {
+              ref: canvas,
+              style: { width: '100%', height: '100%', position: 'absolute', top: 0, left: 0 },
+            }),
+            slots.default(),
+          ],
+        )
+      }
+    }
+  },
+})

+ 233 - 0
packages/tres/src/core/useRenderer/index.ts

@@ -0,0 +1,233 @@
+import { watch, ref, shallowRef, computed } from 'vue'
+import {
+  MaybeComputedRef,
+  MaybeElementRef,
+  resolveUnref,
+  unrefElement,
+  useDevicePixelRatio,
+  useElementSize,
+  useWindowSize,
+} from '@vueuse/core'
+import {
+  WebGLRendererParameters,
+  NoToneMapping,
+  LinearEncoding,
+  WebGLRenderer,
+  ShadowMapType,
+  PCFShadowMap,
+} from 'three'
+import type { TextureEncoding, ToneMapping } from 'three'
+import { useRenderLoop } from '/@/core/'
+import { normalizeColor } from '/@/utils/normalize'
+
+export interface UseRendererOptions extends WebGLRendererParameters {
+  /**
+   * Enable shadows in the Renderer
+   *
+   * @default false
+   */
+  shadows?: MaybeComputedRef<boolean>
+
+  /**
+   * Set the shadow map type
+   *
+   * @default PCFSoftShadowMap
+   */
+  shadowMapType?: MaybeComputedRef<ShadowMapType>
+
+  /**
+   * Whether to use physically correct lighting mode.
+   * See the [lights / physical example](https://threejs.org/examples/#webgl_lights_physical).
+   *
+   * @default false
+   */
+  physicallyCorrectLights?: MaybeComputedRef<boolean>
+
+  /**
+   * Defines the output encoding of the renderer.
+   *
+   * @default LinearEncoding
+   */
+  outputEncoding?: MaybeComputedRef<TextureEncoding>
+
+  /**
+   * Defines the tone mapping used by the renderer.
+   *
+   * @default NoToneMapping
+   */
+  toneMapping?: MaybeComputedRef<ToneMapping>
+
+  /**
+   * Defines the tone mapping exposure used by the renderer.
+   *
+   * @default 1
+   */
+  toneMappingExposure?: MaybeComputedRef<number>
+
+  /**
+   * The context used by the renderer.
+   *
+   * @default undefined
+   */
+  context?: WebGLRenderingContext | undefined
+
+  /**
+   * Provides a hint to the user agent indicating what configuration of GPU is suitable for this WebGL context.
+   * Can be "high-performance", "low-power" or "default".
+   *
+   * @default "default"
+   */
+  powerPreference?: 'high-performance' | 'low-power' | 'default'
+
+  /**
+   * Whether to preserve the buffers until manually cleared or overwritten.
+   *
+   * @default false
+   */
+  preserveDrawingBuffer?: boolean
+
+  /**
+   * The color value to use when clearing the canvas.
+   *
+   * @default 0x000000
+   */
+  clearColor?: MaybeComputedRef<string | number>
+  windowSize?: MaybeComputedRef<boolean>
+}
+
+const renderer = shallowRef<WebGLRenderer>()
+const isReady = ref(false)
+
+/**
+ * Reactive Three.js WebGLRenderer instance
+ *
+ * @param canvas
+ * @param options
+ */
+export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef, options: UseRendererOptions) {
+  // Defaults
+  const {
+    alpha = false,
+    antialias,
+    depth,
+    logarithmicDepthBuffer,
+    failIfMajorPerformanceCaveat,
+    precision,
+    premultipliedAlpha,
+    stencil,
+    shadows = false,
+    shadowMapType = PCFShadowMap,
+    physicallyCorrectLights = false,
+    outputEncoding = LinearEncoding,
+    toneMapping = NoToneMapping,
+    toneMappingExposure = 1,
+    context = undefined,
+    powerPreference = 'default',
+    preserveDrawingBuffer = false,
+    clearColor = normalizeColor('#000000'),
+    windowSize = false,
+  } = options
+
+  const { width, height } = windowSize ? useWindowSize() : useElementSize(container)
+
+  const { pixelRatio } = useDevicePixelRatio()
+  const { pause, resume } = useRenderLoop()
+  const aspectRatio = computed(() => width.value / height.value)
+
+  const updateRendererSize = () => {
+    if (!renderer.value) {
+      return
+    }
+
+    renderer.value.setSize(width.value, height.value)
+    renderer.value.setPixelRatio(pixelRatio.value)
+  }
+
+  const updateRendererOptions = () => {
+    if (!renderer.value) {
+      return
+    }
+
+    renderer.value.shadowMap.enabled = resolveUnref(shadows)
+    renderer.value.outputEncoding = resolveUnref(outputEncoding)
+    renderer.value.shadowMap.type = resolveUnref(shadowMapType)
+    renderer.value.physicallyCorrectLights = resolveUnref(physicallyCorrectLights)
+    renderer.value.toneMapping = resolveUnref(toneMapping)
+    renderer.value.toneMappingExposure = resolveUnref(toneMappingExposure)
+    renderer.value.setClearColor(normalizeColor(resolveUnref(clearColor)))
+  }
+
+  const init = () => {
+    const _canvas = unrefElement(canvas)
+
+    if (renderer.value || !_canvas) {
+      return
+    }
+
+    renderer.value = new WebGLRenderer({
+      canvas: _canvas,
+      alpha,
+      antialias,
+      context,
+      depth,
+      failIfMajorPerformanceCaveat,
+      logarithmicDepthBuffer,
+      powerPreference,
+      precision,
+      stencil,
+      preserveDrawingBuffer,
+      premultipliedAlpha,
+    })
+
+    updateRendererOptions()
+    updateRendererSize()
+    resume()
+
+    isReady.value = true
+  }
+
+  const dispose = () => {
+    if (!renderer.value) {
+      return
+    }
+
+    renderer.value.dispose()
+    renderer.value = undefined
+
+    isReady.value = false
+    pause()
+  }
+
+  watch([width, height, pixelRatio], updateRendererSize)
+  watch(
+    () => [
+      shadows,
+      shadowMapType,
+      outputEncoding,
+      physicallyCorrectLights,
+      toneMapping,
+      toneMappingExposure,
+      clearColor,
+    ],
+    updateRendererOptions,
+  )
+
+  watch(
+    () => [canvas, container],
+    () => {
+      if (unrefElement(canvas) && unrefElement(container)) {
+        init()
+      }
+    },
+    { immediate: true, deep: true },
+  )
+
+  return {
+    renderer,
+    isReady,
+    dispose,
+    aspectRatio,
+  }
+}
+
+export type UseRendererReturn = ReturnType<typeof useRenderer>

+ 32 - 0
packages/tres/src/core/useScene/component.ts

@@ -0,0 +1,32 @@
+import { useCamera } from '/@/core/'
+import type { Renderer } from 'three'
+import { defineComponent, inject, provide, Ref } from 'vue'
+import { useRenderLoop } from '../useRenderLoop'
+import { useScene } from './'
+
+/**
+ * Vue component for rendering a Tres component.
+ */
+export const Scene = defineComponent({
+  name: 'Scene',
+  setup(_props, { slots }) {
+    const { scene } = useScene()
+    const renderer = inject<Ref<Renderer>>('renderer')
+    const { activeCamera } = useCamera()
+    const { onLoop } = useRenderLoop()
+
+    provide('local-scene', scene)
+
+    onLoop(() => {
+      if (renderer?.value && activeCamera?.value && scene?.value) {
+        renderer.value.render(scene?.value, activeCamera.value)
+      }
+    })
+
+    return () => {
+      if (slots.default) {
+        return slots.default()
+      }
+    }
+  },
+})

+ 10 - 0
packages/tres/src/core/useScene/index.ts

@@ -0,0 +1,10 @@
+import { Scene } from 'three'
+import { shallowRef } from 'vue'
+
+const scene = shallowRef(new Scene())
+
+export function useScene() {
+  return {
+    scene,
+  }
+}

+ 10 - 0
packages/tres/src/core/useScene/useScene.test.ts

@@ -0,0 +1,10 @@
+import { Scene } from 'three'
+import { describe, test, expect } from 'vitest'
+import { useScene } from './useScene'
+
+describe('useScene()', () => {
+  test('should init acene', () => {
+    const { scene } = useScene()
+    expect(scene).toBeInstanceOf(Scene)
+  })
+})

+ 72 - 0
packages/tres/src/core/useTexture/index.ts

@@ -0,0 +1,72 @@
+import { isArray } from '@alvarosabu/utils'
+import { LoadingManager, Texture, TextureLoader } from 'three'
+
+export interface PBRMaterialOptions {
+  maps: string[]
+  ext: 'png' | 'jpg'
+}
+
+export interface PBRTextureMaps {
+  [key: string]: Texture | null
+}
+// eslint-disable-next-line require-await
+export async function useTexture(
+  paths: Array<string> | { [key: string]: string },
+): Promise<Texture | Array<Texture> | PBRTextureMaps> {
+  const loadingManager = new LoadingManager()
+  const textureLoader = new TextureLoader(loadingManager)
+
+  const loadTexture = (url: string): Promise<Texture> => {
+    return new Promise((resolve, reject) => {
+      textureLoader.load(
+        url,
+        texture => resolve(texture),
+        () => null,
+        () => {
+          reject(new Error('[useTextures] - Failed to load texture'))
+        },
+      )
+    })
+  }
+
+  if (isArray(paths)) {
+    const textures = await Promise.all((paths as Array<string>).map(path => loadTexture(path)))
+    if (paths.length > 1) {
+      return textures
+    } else {
+      return textures[0]
+    }
+  } else {
+    const { map, displacementMap, normalMap, roughnessMap, metalnessMap, aoMap } = paths as { [key: string]: string }
+    return {
+      map: map ? await loadTexture(map) : null,
+      displacementMap: displacementMap ? await loadTexture(displacementMap) : null,
+      normalMap: normalMap ? await loadTexture(normalMap) : null,
+      roughnessMap: roughnessMap ? await loadTexture(roughnessMap) : null,
+      metalnessMap: metalnessMap ? await loadTexture(metalnessMap) : null,
+      aoMap: aoMap ? await loadTexture(aoMap) : null,
+    }
+  }
+
+  /*   const getPbrTextures = async (path: string, options: PBRMaterialOptions = { maps: ['albedo'], ext: 'png' }) => {
+    const [albedoMap, normalMap, roughnessMap, metalnessMap] = await Promise.all(
+      options.maps.map(map => loadTexture(`${path}${map}.${options.ext}`)),
+    )
+
+    return {
+      map: albedoMap,
+      normalMap,
+      roughnessMap,
+      metalnessMap,
+    }
+  } */
+
+  /*  return new MeshStandardMaterial({
+      map: albedoMap,
+      normalMap,
+      roughnessMap,
+      metalnessMap,
+    })
+  } */
+  /*   return { textureLoader, loadTexture, getPbrMaterial } */
+}

+ 28 - 0
packages/tres/src/core/useTres/index.ts

@@ -0,0 +1,28 @@
+import { WebGLRenderer } from 'three'
+import { reactive, toRefs } from 'vue'
+import { Camera } from '/@/core'
+
+export interface TresState {
+  camera?: Camera
+  renderer?: WebGLRenderer
+  [key: string]: any
+}
+
+const state: TresState = reactive({})
+
+export function useTres() {
+  function getState(key: string) {
+    return state[key]
+  }
+
+  function setState(key: string, value: any) {
+    state[key] = value
+  }
+
+  return {
+    state,
+    ...toRefs(state),
+    getState,
+    setState,
+  }
+}

+ 10 - 0
packages/tres/src/env.d.ts

@@ -0,0 +1,10 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import type { DefineComponent } from 'vue'
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}
+
+declare module '*.glsl' {}

+ 35 - 0
packages/tres/src/examples/Basic.story.vue

@@ -0,0 +1,35 @@
+<script setup lang="ts">
+import { Color } from 'three'
+import { OrbitControls } from '@tresjs/cientos'
+
+const floorTeal = new Color('gray')
+</script>
+<template>
+  <Story title="Basic">
+    <Variant title="playground">
+      <TresCanvas clear-color="#82DBC5" shadows alpha>
+        <TresPerspectiveCamera :position="[11, 11, 11]" />
+        <OrbitControls />
+        <TresScene>
+          <TresMesh :position="[-2, 6, 0]" :rotation="[0, Math.PI, 0]" cast-shadow>
+            <TresConeGeometry :args="[1, 1.5, 3]" />
+            <TresMeshToonMaterial color="#82DBC5" />
+          </TresMesh>
+          <TresMesh :position="[0, 4, 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, 8, 4]" :intensity="0.7" cast-shadow />
+          <TresMesh :rotation="[-Math.PI / 2, 0, 0]" receive-shadow>
+            <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+            <TresMeshToonMaterial :color="floorTeal" />
+          </TresMesh>
+        </TresScene>
+      </TresCanvas>
+    </Variant>
+  </Story>
+</template>

+ 36 - 0
packages/tres/src/examples/GUI/Tweakpane.story.vue

@@ -0,0 +1,36 @@
+<script setup lang="ts">
+import { Color } from 'three'
+import { OrbitControls, useTweakPane } from '@tresjs/cientos'
+
+useTweakPane()
+const floorTeal = new Color('gray')
+</script>
+<template>
+  <Story title="GUI/TweakPane">
+    <Variant title="playground">
+      <TresCanvas clear-color="#82DBC5" shadows alpha>
+        <TresPerspectiveCamera :position="[11, 11, 11]" />
+        <OrbitControls />
+        <TresScene>
+          <TresMesh :position="[-2, 6, 0]" :rotation="[0, Math.PI, 0]" cast-shadow>
+            <TresConeGeometry :args="[1, 1.5, 3]" />
+            <TresMeshToonMaterial color="#82DBC5" />
+          </TresMesh>
+          <TresMesh :position="[0, 4, 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, 8, 4]" :intensity="0.7" cast-shadow />
+          <TresMesh :rotation="[-Math.PI / 2, 0, 0]" receive-shadow>
+            <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+            <TresMeshToonMaterial :color="floorTeal" />
+          </TresMesh>
+        </TresScene>
+      </TresCanvas>
+    </Variant>
+  </Story>
+</template>

+ 29 - 0
packages/tres/src/examples/cientos/controls/OrbitControls.story.vue

@@ -0,0 +1,29 @@
+<script setup lang="ts">
+import { OrbitControls } from '@tresjs/cientos'
+</script>
+<template>
+  <Story title="cientos/controls/OrbitControls">
+    <Variant title="playground">
+      <TresCanvas clear-color="#82DBC5" shadows alpha>
+        <OrbitControls />
+        <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :aspect="1" :near="0.1" :far="1000" />
+        <TresScene>
+          <TresMesh :position="[-2, 6, 0]" :rotation="[0, Math.PI, 0]" cast-shadow>
+            <TresConeGeometry :args="[1, 1.5, 3]" />
+            <TresMeshToonMaterial color="#82DBC5" />
+          </TresMesh>
+          <TresMesh :position="[0, 4, 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, 8, 4]" :intensity="0.7" cast-shadow />
+        </TresScene>
+      </TresCanvas>
+    </Variant>
+  </Story>
+</template>

+ 26 - 0
packages/tres/src/examples/lighting/RedBlue.story.vue

@@ -0,0 +1,26 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+import { OrbitControls } from '@tresjs/cientos'
+const redLightRef = ref(null)
+const blueLightRef = ref(null)
+</script>
+<template>
+  <Story title="lighting/RedBlue" icon="ph:lightbulb">
+    <Variant title="playground">
+      <TresCanvas shadows>
+        <OrbitControls />
+        <TresPerspectiveCamera :position="[5, 5, 5]" />
+        <TresScene>
+          <TresMesh :position="[0, 0, 0]" cast-shadow>
+            <TresSphereGeometry :args="[2, 32, 32]" />
+            <TresMeshStandardMaterial :roughness="0.4" />
+          </TresMesh>
+          <TresDirectionalLight ref="redLightRef" color="red" :position="[-8, 0, 0]" :intensity="0.7" cast-shadow />
+          <TresDirectionalLightHelper v-if="redLightRef" :args="[redLightRef, 5]" />
+          <TresDirectionalLight ref="blueLightRef" color="blue" :position="[8, 0, 0]" :intensity="0.7" cast-shadow />
+          <TresDirectionalLightHelper v-if="blueLightRef" :args="[blueLightRef, 5]" />
+        </TresScene>
+      </TresCanvas>
+    </Variant>
+  </Story>
+</template>

+ 8 - 0
packages/tres/src/examples/models/gltf/AkuAku.vue

@@ -0,0 +1,8 @@
+<script setup lang="ts">
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
+import { useLoader } from '/@/core'
+const { scene } = await useLoader(GLTFLoader, '/models/AkuAku.gltf')
+</script>
+<template>
+  <TresMesh v-bind="scene" />
+</template>

+ 7 - 0
packages/tres/src/examples/models/gltf/AkuAkuCientos.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts">
+import { useGLTF } from '@tresjs/cientos'
+const { scene } = await useGLTF('/models/AkuAku.gltf')
+</script>
+<template>
+  <TresMesh v-bind="scene" />
+</template>

+ 20 - 0
packages/tres/src/examples/models/gltf/Basic.story.vue

@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { OrbitControls } from '@tresjs/cientos'
+import AkuAku from './AkuAku.vue'
+</script>
+<template>
+  <Story title="models/GLTF/Basic">
+    <Variant title="playground">
+      <Suspense>
+        <TresCanvas clear-color="#82DBC5" shadows alpha>
+          <TresPerspectiveCamera :position="[11, 11, 11]" />
+          <OrbitControls />
+          <TresScene>
+            <AkuAku />
+            <TresDirectionalLight :position="[-4, 8, 4]" :intensity="1.5" cast-shadow />
+          </TresScene>
+        </TresCanvas>
+      </Suspense>
+    </Variant>
+  </Story>
+</template>

+ 19 - 0
packages/tres/src/examples/models/gltf/GLTFModel.story.vue

@@ -0,0 +1,19 @@
+<script setup lang="ts">
+import { OrbitControls, GLTFModel } from '@tresjs/cientos'
+</script>
+<template>
+  <Story title="models/GLTF/GLTFModel">
+    <Variant title="playground">
+      <Suspense>
+        <TresCanvas clear-color="#82DBC5" shadows alpha>
+          <TresPerspectiveCamera :position="[11, 11, 11]" />
+          <OrbitControls />
+          <TresScene>
+            <GLTFModel path="/models/AkuAku.gltf" />
+            <TresDirectionalLight :position="[-4, 8, 4]" :intensity="1.5" cast-shadow />
+          </TresScene>
+        </TresCanvas>
+      </Suspense>
+    </Variant>
+  </Story>
+</template>

+ 20 - 0
packages/tres/src/examples/models/gltf/useGLTF.story.vue

@@ -0,0 +1,20 @@
+<script setup lang="ts">
+import { OrbitControls } from '@tresjs/cientos'
+import AkuAku from './AkuAkuCientos.vue'
+</script>
+<template>
+  <Story title="models/GLTF/useGLTF">
+    <Variant title="playground">
+      <Suspense>
+        <TresCanvas clear-color="#82DBC5" shadows alpha>
+          <TresPerspectiveCamera :position="[11, 11, 11]" />
+          <OrbitControls />
+          <TresScene>
+            <AkuAku />
+            <TresDirectionalLight :position="[-4, 8, 4]" :intensity="1.5" cast-shadow />
+          </TresScene>
+        </TresCanvas>
+      </Suspense>
+    </Variant>
+  </Story>
+</template>

+ 46 - 0
packages/tres/src/examples/shaders/blob/Blob.story.vue

@@ -0,0 +1,46 @@
+<script setup lang="ts">
+import { Vector2 } from 'three'
+import { OrbitControls, useTweakPane } from '@tresjs/cientos'
+import vertexShader from './shaders/vertex.glsl'
+import fragmentShader from './shaders/fragment.glsl'
+import { useRenderLoop } from '/@/core'
+import { Ref, ref } from 'vue'
+import { TresInstance } from '/@/types'
+
+const blobRef: Ref<TresInstance | null> = ref(null)
+const uniforms = {
+  uTime: { value: 0 },
+  uAmplitude: { value: new Vector2(0.1, 0.1) },
+  uFrequency: { value: new Vector2(20, 5) },
+}
+
+useTweakPane()
+
+const { onLoop, resume } = useRenderLoop()
+resume()
+onLoop(({ _delta, elapsed }) => {
+  if (blobRef.value) {
+    blobRef.value.material.uniforms.uTime.value = elapsed
+  }
+})
+</script>
+<template>
+  <Story title="shaders/blob">
+    <Variant title="playground">
+      <TresCanvas clear-color="#111" shadows alpha>
+        <TresPerspectiveCamera :position="[11, 11, 11]" />
+        <OrbitControls />
+        <TresScene>
+          <TresMesh ref="blobRef" :position="[0, 4, 0]">
+            <TresSphereGeometry :args="[2, 32, 32]" />
+            <TresShaderMaterial :vertex-shader="vertexShader" :fragment-shader="fragmentShader" :uniforms="uniforms" />
+          </TresMesh>
+          <TresMesh :rotation="[-Math.PI / 2, 0, 0]">
+            <TresPlaneGeometry :args="[10, 10, 10, 10]" />
+            <TresMeshBasicMaterial color="#444" />
+          </TresMesh>
+        </TresScene>
+      </TresCanvas>
+    </Variant>
+  </Story>
+</template>

+ 6 - 0
packages/tres/src/examples/shaders/blob/shaders/fragment.glsl

@@ -0,0 +1,6 @@
+precision mediump float;
+varying vec2 vUv;
+
+void main() {
+    gl_FragColor = vec4(1.0, vUv.y, 0.5, 1.0);
+}

+ 15 - 0
packages/tres/src/examples/shaders/blob/shaders/vertex.glsl

@@ -0,0 +1,15 @@
+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;
+}

+ 30 - 0
packages/tres/src/index.ts

@@ -0,0 +1,30 @@
+import { App, Component } from 'vue'
+import { TresCanvas } from '/@/core/useRenderer/component'
+import { Scene } from '/@/core/useScene/component'
+import { useCatalogue, useInstanceCreator } from '/@/core'
+export * from '/@/core'
+export * from './keys'
+
+export interface TresOptions {
+  prefix?: string
+}
+export interface TresPlugin {
+  [key: string]: any
+  install: (app: App, options?: TresOptions) => void
+}
+
+const plugin: TresPlugin = {
+  install(app: App, options) {
+    const prefix = options?.prefix || 'Tres'
+    app.component(`${prefix}Canvas`, TresCanvas)
+    app.component(`${prefix}Scene`, Scene)
+    const { catalogue } = useCatalogue()
+    const { createComponentInstances } = useInstanceCreator(prefix)
+    const components = createComponentInstances(catalogue)
+    components.forEach(([key, cmp]) => {
+      app.component(key as string, cmp as Component)
+    })
+  },
+}
+
+export default plugin

+ 1 - 0
packages/tres/src/keys.ts

@@ -0,0 +1 @@
+export const UseTresStateSymbol = Symbol('UseTresState')

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác