소스 검색

Merge remote-tracking branch 'origin/main' into feature/PamCameraMouse

alvarosabu 2 년 전
부모
커밋
c839703a96
46개의 변경된 파일1616개의 추가작업 그리고 395개의 파일을 삭제
  1. 41 0
      .github/workflows/lint.yml
  2. 4 4
      .github/workflows/test.yml
  3. 2 2
      docs/api/composables.md
  4. 29 0
      docs/api/renderer.md
  5. 9 0
      packages/cientos/src/core/useAnimations.ts
  6. 6 0
      packages/cientos/src/core/useCientos.ts
  7. 31 0
      packages/cientos/src/core/useEnvironment/const.ts
  8. 14 0
      packages/cientos/src/core/useEnvironment/index.ts
  9. 3 0
      packages/cientos/src/core/useFBX/component.ts
  10. 7 0
      packages/cientos/src/core/useFBX/index.ts
  11. 12 0
      packages/cientos/src/core/useGLTF/component.ts
  12. 31 0
      packages/cientos/src/core/useGLTF/index.ts
  13. 11 1
      packages/cientos/src/core/useTweakPane/index.ts
  14. 18 1
      packages/cientos/src/utils/index.ts
  15. 400 0
      packages/tres/build/config.gypi
  16. 1 0
      packages/tres/build/node_gyp_bins/python3
  17. 7 6
      packages/tres/package.json
  18. 15 2
      packages/tres/src/App.vue
  19. 22 0
      packages/tres/src/components/Responsiveness.vue
  20. 2 1
      packages/tres/src/components/TheBasic.vue
  21. 5 0
      packages/tres/src/components/TheExperience.vue
  22. 113 26
      packages/tres/src/core/useCamera/index.ts
  23. 101 0
      packages/tres/src/core/useCamera/useCamera.test.ts
  24. 28 0
      packages/tres/src/core/useCatalogue/index.ts
  25. 22 13
      packages/tres/src/core/useCatalogue/useCatalogue.test.ts
  26. 65 15
      packages/tres/src/core/useInstanceCreator/index.ts
  27. 27 0
      packages/tres/src/core/useInstanceCreator/useInstanceCreator.test.ts
  28. 30 0
      packages/tres/src/core/useLoader/index.ts
  29. 25 0
      packages/tres/src/core/useLoader/useLoader.test.ts
  30. 32 2
      packages/tres/src/core/useRaycaster/index.ts
  31. 22 0
      packages/tres/src/core/useRaycaster/useRaycaster.test.ts
  32. 39 14
      packages/tres/src/core/useRenderer/component.ts
  33. 0 1
      packages/tres/src/core/useRenderer/const.ts
  34. 13 3
      packages/tres/src/core/useRenderer/index.ts
  35. 2 1
      packages/tres/src/core/useScene/component.ts
  36. 6 0
      packages/tres/src/core/useScene/index.ts
  37. 3 3
      packages/tres/src/core/useScene/useScene.test.ts
  38. 44 22
      packages/tres/src/core/useTexture/index.ts
  39. 19 0
      packages/tres/src/core/useTexture/useTexture.test.ts
  40. 103 3
      packages/tres/src/core/useTres/index.ts
  41. 14 0
      packages/tres/src/core/useTres/useTres.test.ts
  42. 18 0
      packages/tres/src/utils/test-utils.ts
  43. 1 1
      packages/tres/tsconfig.json
  44. 7 2
      packages/tres/tsconfig.node.json
  45. 4 1
      packages/tres/vite.config.ts
  46. 208 271
      pnpm-lock.yaml

+ 41 - 0
.github/workflows/lint.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

+ 4 - 4
.github/workflows/test.yml

@@ -1,4 +1,4 @@
-name: Run linters
+name: Run unit tests
 on: [push]
 on: [push]
 
 
 env:
 env:
@@ -7,7 +7,7 @@ env:
 
 
 jobs:
 jobs:
   lint:
   lint:
-    name: Lint all projects
+    name: Run Unit Tests
     runs-on: ubuntu-20.04
     runs-on: ubuntu-20.04
     steps:
     steps:
       - name: Checkout
       - name: Checkout
@@ -37,5 +37,5 @@ jobs:
 
 
       - run: pnpm install --no-frozen-lockfile --shamefully-hoist
       - run: pnpm install --no-frozen-lockfile --shamefully-hoist
 
 
-      - name: Run Lint
-        run: pnpm run lint
+      - name: Run Tests
+        run: pnpm run test

+ 2 - 2
docs/api/composables.md

@@ -143,7 +143,7 @@ Then you can bind the textures to the material.
 
 
 Similar to above composable, the `useTexture` composable returns a promise, you can use it with `async/await` or `then/catch`. If you are using it on a component make sure you wrap it with a `Suspense` component.
 Similar to above composable, the `useTexture` composable returns a promise, you can use it with `async/await` or `then/catch`. If you are using it on a component make sure you wrap it with a `Suspense` component.
 
 
-# useCatalogue
+## useCatalogue
 
 
 The `useCatalogue` composable allows you to extend the internal catalogue of components. It returns a function that you can use to register new components.
 The `useCatalogue` composable allows you to extend the internal catalogue of components. It returns a function that you can use to register new components.
 
 
@@ -170,7 +170,7 @@ Then you can use the new component in your template. Notice that the new compone
 </template>
 </template>
 ```
 ```
 
 
-# useTres <Badge type="warning" text="^1.7.0" />
+## useTres <Badge type="warning" text="^1.7.0" />
 
 
 This composable aims to provide access to the state model which contains the default renderer, camera, scene, and other useful properties.
 This composable aims to provide access to the state model which contains the default renderer, camera, scene, and other useful properties.
 
 

+ 29 - 0
docs/api/renderer.md

@@ -13,6 +13,35 @@ The `Renderer` component is the main component of Tres. It's the one that create
 </template>
 </template>
 ```
 ```
 
 
+## Canvas size
+
+The `Renderer` component will use the parent element size as the canvas size. If you want to use the window size as the canvas size, you can set the `window-size` prop to `true`.
+
+```vue
+<template>
+  <TresCanvas window-size>
+    <!-- Your scene goes here -->
+  </TresCanvas>
+</template>
+```
+
+Or you can use CSS to set your app size.
+
+```css
+html,
+body {
+  margin: 0;
+  padding: 0;
+  height: 100%;
+  width: 100%;
+}
+#app {
+  height: 100%;
+  width: 100%;
+  background-color: #000;
+}
+```
+
 ## Presets <Badge warning text="v1.7.0+" />
 ## Presets <Badge warning text="v1.7.0+" />
 
 
 Tres comes with a few presets for the `Renderer` component. You can use them by setting the `preset` prop.
 Tres comes with a few presets for the `Renderer` component. You can use them by setting the `preset` prop.

+ 9 - 0
packages/cientos/src/core/useAnimations.ts

@@ -2,6 +2,15 @@ import { AnimationAction, AnimationClip, AnimationMixer, Object3D, Scene } from
 import { useRenderLoop } from '@tresjs/core'
 import { useRenderLoop } from '@tresjs/core'
 import { ref, Ref, shallowReactive } from 'vue'
 import { ref, Ref, shallowReactive } from 'vue'
 
 
+/**
+ * Creates an AnimationMixer and returns it.
+ *
+ * @export
+ * @template T
+ * @param {T[]} animations
+ * @param {(Scene | Ref<Object3D | undefined | null>)} [modelRef]
+ * @return {*}
+ */
 export function useAnimations<T extends AnimationClip>(
 export function useAnimations<T extends AnimationClip>(
   animations: T[],
   animations: T[],
   modelRef?: Scene | Ref<Object3D | undefined | null>,
   modelRef?: Scene | Ref<Object3D | undefined | null>,

+ 6 - 0
packages/cientos/src/core/useCientos.ts

@@ -1,6 +1,12 @@
 import { useTres } from '@tresjs/core'
 import { useTres } from '@tresjs/core'
 import { inject } from 'vue'
 import { inject } from 'vue'
 
 
+/**
+ * Allows to use and extend the state of the core package.
+ *
+ * @export
+ * @return {*}
+ */
 export function useCientos() {
 export function useCientos() {
   const extend =
   const extend =
     inject<(objects: any) => void>('extend') ||
     inject<(objects: any) => void>('extend') ||

+ 31 - 0
packages/cientos/src/core/useEnvironment/const.ts

@@ -1,11 +1,42 @@
 import { TextureEncoding } from 'three'
 import { TextureEncoding } from 'three'
 
 
 export type EnvironmentOptions = {
 export type EnvironmentOptions = {
+  /**
+   * If true, the environment will be set as the scene's background.
+   *
+   * @type {boolean}
+   */
   background?: boolean
   background?: boolean
+  /**
+   * The blur radius of the environment.
+   *
+   * @type {number}
+   */
   blur?: number
   blur?: number
+  /**
+   * The files to load. If a string is provided, it will be loaded as an equirectangular texture.
+   * If an array is provided, it will be loaded as a cube texture.
+   *
+   * @type {(string | string[])}
+   */
   files?: string | string[]
   files?: string | string[]
+  /**
+   * The path to the files.
+   *
+   * @type {string}
+   */
   path?: string
   path?: string
+  /**
+   * The preset to use. If provided, the files and path props will be ignored.
+   *
+   * @type {EnvironmentPresetsType}
+   */
   preset?: EnvironmentPresetsType
   preset?: EnvironmentPresetsType
+  /**
+   * The encoding of the environment.
+   *
+   * @type {TextureEncoding}
+   */
   encoding?: TextureEncoding
   encoding?: TextureEncoding
 }
 }
 
 

+ 14 - 0
packages/cientos/src/core/useEnvironment/index.ts

@@ -12,6 +12,20 @@ import { RGBELoader } from 'three-stdlib'
 import { useCientos } from '../useCientos'
 import { useCientos } from '../useCientos'
 import { EnvironmentOptions, environmentPresets } from './const'
 import { EnvironmentOptions, environmentPresets } from './const'
 
 
+/**
+ * Component that loads an environment map and sets it as the scene's background and environment.
+ *
+ * @export
+ * @param {Partial<EnvironmentOptions>} {
+ *   files = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'],
+ *   blur = 0,
+ *   background = false,
+ *   path = undefined,
+ *   preset = undefined,
+ *   encoding = undefined,
+ * }
+ * @return {*}  {(Promise<Texture | CubeTexture>)}
+ */
 export async function useEnvironment({
 export async function useEnvironment({
   files = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'],
   files = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'],
   blur = 0,
   blur = 0,

+ 3 - 0
packages/cientos/src/core/useFBX/component.ts

@@ -6,6 +6,9 @@ import { useCientos } from '../useCientos'
 export const FBXModel = defineComponent({
 export const FBXModel = defineComponent({
   name: 'FBXModel',
   name: 'FBXModel',
   props: {
   props: {
+    /*
+     * The path to the FBX file.
+     */
     path: {
     path: {
       type: String,
       type: String,
       required: true,
       required: true,

+ 7 - 0
packages/cientos/src/core/useFBX/index.ts

@@ -2,6 +2,13 @@ import { FBXLoader } from 'three-stdlib'
 import { useLoader } from '@tresjs/core'
 import { useLoader } from '@tresjs/core'
 import { Object3D } from 'three'
 import { Object3D } from 'three'
 
 
+/**
+ * Loads an FBX file and returns a THREE.Object3D.
+ *
+ * @export
+ * @param {(string | string[])} path
+ * @return {*}  {Promise<Object3D>}
+ */
 export async function useFBX(path: string | string[]): Promise<Object3D> {
 export async function useFBX(path: string | string[]): Promise<Object3D> {
   return (await useLoader(FBXLoader, path)) as unknown as Object3D
   return (await useLoader(FBXLoader, path)) as unknown as Object3D
 }
 }

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

@@ -5,8 +5,20 @@ import { useCientos } from '../useCientos'
 export const GLTFModel = defineComponent({
 export const GLTFModel = defineComponent({
   name: 'GLTFModel',
   name: 'GLTFModel',
   props: {
   props: {
+    /**
+     * The path to the GLTF file.
+     *
+     */
     path: String,
     path: String,
+    /**
+     * Whether to use Draco compression.
+     *
+     */
     draco: Boolean,
     draco: Boolean,
+    /**
+     * The path to the Draco decoder.
+     *
+     */
     decoderPath: String,
     decoderPath: String,
   },
   },
 
 

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

@@ -3,7 +3,19 @@ import { useLoader } from '@tresjs/core'
 import { Object3D } from 'three'
 import { Object3D } from 'three'
 
 
 export interface GLTFLoaderOptions {
 export interface GLTFLoaderOptions {
+  /**
+   * Whether to use Draco compression.
+   *
+   * @type {boolean}
+   * @memberof GLTFLoaderOptions
+   */
   draco?: boolean
   draco?: boolean
+  /**
+   * The path to the Draco decoder.
+   *
+   * @type {string}
+   * @memberof GLTFLoaderOptions
+   */
   decoderPath?: string
   decoderPath?: string
 }
 }
 
 
@@ -16,6 +28,13 @@ export interface GLTFResult {
 
 
 let dracoLoader: DRACOLoader | null = null
 let dracoLoader: DRACOLoader | null = null
 
 
+/**
+ * Sets the extensions for the GLTFLoader.
+ *
+ * @param {GLTFLoaderOptions} options
+ * @param {(loader: GLTFLoader) => void} [extendLoader]
+ * @return {*}
+ */
 function setExtensions(options: GLTFLoaderOptions, extendLoader?: (loader: GLTFLoader) => void) {
 function setExtensions(options: GLTFLoaderOptions, extendLoader?: (loader: GLTFLoader) => void) {
   return (loader: GLTFLoader) => {
   return (loader: GLTFLoader) => {
     if (extendLoader) {
     if (extendLoader) {
@@ -30,6 +49,18 @@ function setExtensions(options: GLTFLoaderOptions, extendLoader?: (loader: GLTFL
     }
     }
   }
   }
 }
 }
+
+/**
+ * Loads a GLTF file and returns a THREE.Object3D.
+ *
+ * @export
+ * @param {(string | string[])} path
+ * @param {GLTFLoaderOptions} [options={
+ *     draco: false,
+ *   }]
+ * @param {(loader: GLTFLoader) => void} [extendLoader]
+ * @return {*}  {Promise<GLTFResult>}
+ */
 export async function useGLTF(
 export async function useGLTF(
   path: string | string[],
   path: string | string[],
   options: GLTFLoaderOptions = {
   options: GLTFLoaderOptions = {

+ 11 - 1
packages/cientos/src/core/useTweakPane/index.ts

@@ -7,7 +7,13 @@ type TweakPane = Pane & { addBlade(blade: any): void }
 let pane: TweakPane
 let pane: TweakPane
 let fpsGraph: any
 let fpsGraph: any
 
 
-export const useTweakPane = (selector = 'tres-container') => {
+export /**
+ * Creates a TweakPane instance and returns it.
+ *
+ * @param {string} [selector='tres-container']
+ * @return {*}
+ */
+const useTweakPane = (selector = 'tres-container') => {
   if (!pane) {
   if (!pane) {
     pane = new Pane({
     pane = new Pane({
       container: (document.querySelector(selector) as HTMLElement) || undefined,
       container: (document.querySelector(selector) as HTMLElement) || undefined,
@@ -20,6 +26,10 @@ export const useTweakPane = (selector = 'tres-container') => {
     })
     })
   }
   }
 
 
+  /**
+   * Disposes the TweakPane instance.
+   *
+   */
   function disposeTweakPane() {
   function disposeTweakPane() {
     if (pane) {
     if (pane) {
       pane.dispose()
       pane.dispose()

+ 18 - 1
packages/cientos/src/utils/index.ts

@@ -1,4 +1,13 @@
-// Update the function signature to explicitly specify the type of the props parameter
+/**
+ * Update the function signature to explicitly specify the type of the props parameter
+ *
+ * @export
+ * @template T
+ * @template K
+ * @param {T} obj
+ * @param {K[]} props
+ * @return {*}  {Pick<T, K>}
+ */
 export function pick<T extends object, K extends keyof T>(obj: T, props: K[]): Pick<T, K> {
 export function pick<T extends object, K extends keyof T>(obj: T, props: K[]): Pick<T, K> {
   const pickedProperties = {} as Pick<T, K>
   const pickedProperties = {} as Pick<T, K>
   for (const prop of props) {
   for (const prop of props) {
@@ -9,6 +18,14 @@ export function pick<T extends object, K extends keyof T>(obj: T, props: K[]): P
   return pickedProperties
   return pickedProperties
 }
 }
 
 
+/**
+ * Check if the object has a setter for the given property
+ *
+ * @export
+ * @param {*} obj
+ * @param {string} prop
+ * @return {*}  {boolean}
+ */
 export function hasSetter(obj: any, prop: string): boolean {
 export function hasSetter(obj: any, prop: string): boolean {
   const setterName = `set${prop[0].toUpperCase()}${prop.slice(1)}`
   const setterName = `set${prop[0].toUpperCase()}${prop.slice(1)}`
   return obj[setterName] !== undefined
   return obj[setterName] !== undefined

+ 400 - 0
packages/tres/build/config.gypi

@@ -0,0 +1,400 @@
+# Do not edit. File was generated by node-gyp's "configure" step
+{
+  "target_defaults": {
+    "cflags": [],
+    "default_configuration": "Release",
+    "defines": [],
+    "include_dirs": [],
+    "libraries": [],
+    "msvs_configuration_platform": "ARM64",
+    "xcode_configuration_platform": "arm64"
+  },
+  "variables": {
+    "arm_fpu": "neon",
+    "asan": 0,
+    "coverage": "false",
+    "dcheck_always_on": 0,
+    "debug_nghttp2": "false",
+    "debug_node": "false",
+    "enable_lto": "false",
+    "enable_pgo_generate": "false",
+    "enable_pgo_use": "false",
+    "error_on_warn": "false",
+    "force_dynamic_crt": 0,
+    "host_arch": "arm64",
+    "icu_data_in": "../../deps/icu-tmp/icudt72l.dat",
+    "icu_endianness": "l",
+    "icu_gyp_path": "tools/icu/icu-generic.gyp",
+    "icu_path": "deps/icu-small",
+    "icu_small": "false",
+    "icu_ver_major": "72",
+    "is_debug": 0,
+    "libdir": "lib",
+    "llvm_version": "12.0",
+    "napi_build_version": "8",
+    "node_builtin_shareable_builtins": [
+      "deps/cjs-module-lexer/lexer.js",
+      "deps/cjs-module-lexer/dist/lexer.js",
+      "deps/undici/undici.js"
+    ],
+    "node_byteorder": "little",
+    "node_debug_lib": "false",
+    "node_enable_d8": "false",
+    "node_enable_v8_vtunejit": "false",
+    "node_fipsinstall": "false",
+    "node_install_corepack": "true",
+    "node_install_npm": "true",
+    "node_library_files": [
+      "lib/_http_agent.js",
+      "lib/_http_client.js",
+      "lib/_http_common.js",
+      "lib/_http_incoming.js",
+      "lib/_http_outgoing.js",
+      "lib/_http_server.js",
+      "lib/_stream_duplex.js",
+      "lib/_stream_passthrough.js",
+      "lib/_stream_readable.js",
+      "lib/_stream_transform.js",
+      "lib/_stream_wrap.js",
+      "lib/_stream_writable.js",
+      "lib/_tls_common.js",
+      "lib/_tls_wrap.js",
+      "lib/assert.js",
+      "lib/assert/strict.js",
+      "lib/async_hooks.js",
+      "lib/buffer.js",
+      "lib/child_process.js",
+      "lib/cluster.js",
+      "lib/console.js",
+      "lib/constants.js",
+      "lib/crypto.js",
+      "lib/dgram.js",
+      "lib/diagnostics_channel.js",
+      "lib/dns.js",
+      "lib/dns/promises.js",
+      "lib/domain.js",
+      "lib/events.js",
+      "lib/fs.js",
+      "lib/fs/promises.js",
+      "lib/http.js",
+      "lib/http2.js",
+      "lib/https.js",
+      "lib/inspector.js",
+      "lib/internal/abort_controller.js",
+      "lib/internal/assert.js",
+      "lib/internal/assert/assertion_error.js",
+      "lib/internal/assert/calltracker.js",
+      "lib/internal/async_hooks.js",
+      "lib/internal/blob.js",
+      "lib/internal/blocklist.js",
+      "lib/internal/bootstrap/browser.js",
+      "lib/internal/bootstrap/loaders.js",
+      "lib/internal/bootstrap/node.js",
+      "lib/internal/bootstrap/switches/does_not_own_process_state.js",
+      "lib/internal/bootstrap/switches/does_own_process_state.js",
+      "lib/internal/bootstrap/switches/is_main_thread.js",
+      "lib/internal/bootstrap/switches/is_not_main_thread.js",
+      "lib/internal/buffer.js",
+      "lib/internal/child_process.js",
+      "lib/internal/child_process/serialization.js",
+      "lib/internal/cli_table.js",
+      "lib/internal/cluster/child.js",
+      "lib/internal/cluster/primary.js",
+      "lib/internal/cluster/round_robin_handle.js",
+      "lib/internal/cluster/shared_handle.js",
+      "lib/internal/cluster/utils.js",
+      "lib/internal/cluster/worker.js",
+      "lib/internal/console/constructor.js",
+      "lib/internal/console/global.js",
+      "lib/internal/constants.js",
+      "lib/internal/crypto/aes.js",
+      "lib/internal/crypto/certificate.js",
+      "lib/internal/crypto/cfrg.js",
+      "lib/internal/crypto/cipher.js",
+      "lib/internal/crypto/diffiehellman.js",
+      "lib/internal/crypto/ec.js",
+      "lib/internal/crypto/hash.js",
+      "lib/internal/crypto/hashnames.js",
+      "lib/internal/crypto/hkdf.js",
+      "lib/internal/crypto/keygen.js",
+      "lib/internal/crypto/keys.js",
+      "lib/internal/crypto/mac.js",
+      "lib/internal/crypto/pbkdf2.js",
+      "lib/internal/crypto/random.js",
+      "lib/internal/crypto/rsa.js",
+      "lib/internal/crypto/scrypt.js",
+      "lib/internal/crypto/sig.js",
+      "lib/internal/crypto/util.js",
+      "lib/internal/crypto/webcrypto.js",
+      "lib/internal/crypto/x509.js",
+      "lib/internal/debugger/inspect.js",
+      "lib/internal/debugger/inspect_client.js",
+      "lib/internal/debugger/inspect_repl.js",
+      "lib/internal/dgram.js",
+      "lib/internal/dns/callback_resolver.js",
+      "lib/internal/dns/promises.js",
+      "lib/internal/dns/utils.js",
+      "lib/internal/dtrace.js",
+      "lib/internal/encoding.js",
+      "lib/internal/error_serdes.js",
+      "lib/internal/errors.js",
+      "lib/internal/event_target.js",
+      "lib/internal/file.js",
+      "lib/internal/fixed_queue.js",
+      "lib/internal/freelist.js",
+      "lib/internal/freeze_intrinsics.js",
+      "lib/internal/fs/cp/cp-sync.js",
+      "lib/internal/fs/cp/cp.js",
+      "lib/internal/fs/dir.js",
+      "lib/internal/fs/promises.js",
+      "lib/internal/fs/read_file_context.js",
+      "lib/internal/fs/recursive_watch.js",
+      "lib/internal/fs/rimraf.js",
+      "lib/internal/fs/streams.js",
+      "lib/internal/fs/sync_write_stream.js",
+      "lib/internal/fs/utils.js",
+      "lib/internal/fs/watchers.js",
+      "lib/internal/heap_utils.js",
+      "lib/internal/histogram.js",
+      "lib/internal/http.js",
+      "lib/internal/http2/compat.js",
+      "lib/internal/http2/core.js",
+      "lib/internal/http2/util.js",
+      "lib/internal/idna.js",
+      "lib/internal/inspector_async_hook.js",
+      "lib/internal/js_stream_socket.js",
+      "lib/internal/legacy/processbinding.js",
+      "lib/internal/linkedlist.js",
+      "lib/internal/main/check_syntax.js",
+      "lib/internal/main/environment.js",
+      "lib/internal/main/eval_stdin.js",
+      "lib/internal/main/eval_string.js",
+      "lib/internal/main/inspect.js",
+      "lib/internal/main/mksnapshot.js",
+      "lib/internal/main/print_help.js",
+      "lib/internal/main/prof_process.js",
+      "lib/internal/main/repl.js",
+      "lib/internal/main/run_main_module.js",
+      "lib/internal/main/test_runner.js",
+      "lib/internal/main/watch_mode.js",
+      "lib/internal/main/worker_thread.js",
+      "lib/internal/mime.js",
+      "lib/internal/modules/cjs/helpers.js",
+      "lib/internal/modules/cjs/loader.js",
+      "lib/internal/modules/esm/assert.js",
+      "lib/internal/modules/esm/create_dynamic_module.js",
+      "lib/internal/modules/esm/fetch_module.js",
+      "lib/internal/modules/esm/formats.js",
+      "lib/internal/modules/esm/get_format.js",
+      "lib/internal/modules/esm/handle_process_exit.js",
+      "lib/internal/modules/esm/initialize_import_meta.js",
+      "lib/internal/modules/esm/load.js",
+      "lib/internal/modules/esm/loader.js",
+      "lib/internal/modules/esm/module_job.js",
+      "lib/internal/modules/esm/module_map.js",
+      "lib/internal/modules/esm/package_config.js",
+      "lib/internal/modules/esm/resolve.js",
+      "lib/internal/modules/esm/translators.js",
+      "lib/internal/modules/package_json_reader.js",
+      "lib/internal/modules/run_main.js",
+      "lib/internal/net.js",
+      "lib/internal/options.js",
+      "lib/internal/per_context/domexception.js",
+      "lib/internal/per_context/messageport.js",
+      "lib/internal/per_context/primordials.js",
+      "lib/internal/perf/event_loop_delay.js",
+      "lib/internal/perf/event_loop_utilization.js",
+      "lib/internal/perf/nodetiming.js",
+      "lib/internal/perf/observe.js",
+      "lib/internal/perf/performance.js",
+      "lib/internal/perf/performance_entry.js",
+      "lib/internal/perf/resource_timing.js",
+      "lib/internal/perf/timerify.js",
+      "lib/internal/perf/usertiming.js",
+      "lib/internal/perf/utils.js",
+      "lib/internal/policy/manifest.js",
+      "lib/internal/policy/sri.js",
+      "lib/internal/priority_queue.js",
+      "lib/internal/process/esm_loader.js",
+      "lib/internal/process/execution.js",
+      "lib/internal/process/per_thread.js",
+      "lib/internal/process/policy.js",
+      "lib/internal/process/pre_execution.js",
+      "lib/internal/process/promises.js",
+      "lib/internal/process/report.js",
+      "lib/internal/process/signal.js",
+      "lib/internal/process/task_queues.js",
+      "lib/internal/process/warning.js",
+      "lib/internal/process/worker_thread_only.js",
+      "lib/internal/promise_hooks.js",
+      "lib/internal/querystring.js",
+      "lib/internal/readline/callbacks.js",
+      "lib/internal/readline/emitKeypressEvents.js",
+      "lib/internal/readline/interface.js",
+      "lib/internal/readline/promises.js",
+      "lib/internal/readline/utils.js",
+      "lib/internal/repl.js",
+      "lib/internal/repl/await.js",
+      "lib/internal/repl/history.js",
+      "lib/internal/repl/utils.js",
+      "lib/internal/socket_list.js",
+      "lib/internal/socketaddress.js",
+      "lib/internal/source_map/prepare_stack_trace.js",
+      "lib/internal/source_map/source_map.js",
+      "lib/internal/source_map/source_map_cache.js",
+      "lib/internal/stream_base_commons.js",
+      "lib/internal/streams/add-abort-signal.js",
+      "lib/internal/streams/buffer_list.js",
+      "lib/internal/streams/compose.js",
+      "lib/internal/streams/destroy.js",
+      "lib/internal/streams/duplex.js",
+      "lib/internal/streams/duplexify.js",
+      "lib/internal/streams/end-of-stream.js",
+      "lib/internal/streams/from.js",
+      "lib/internal/streams/lazy_transform.js",
+      "lib/internal/streams/legacy.js",
+      "lib/internal/streams/operators.js",
+      "lib/internal/streams/passthrough.js",
+      "lib/internal/streams/pipeline.js",
+      "lib/internal/streams/readable.js",
+      "lib/internal/streams/state.js",
+      "lib/internal/streams/transform.js",
+      "lib/internal/streams/utils.js",
+      "lib/internal/streams/writable.js",
+      "lib/internal/structured_clone.js",
+      "lib/internal/test/binding.js",
+      "lib/internal/test/transfer.js",
+      "lib/internal/test_runner/harness.js",
+      "lib/internal/test_runner/mock.js",
+      "lib/internal/test_runner/runner.js",
+      "lib/internal/test_runner/tap_checker.js",
+      "lib/internal/test_runner/tap_lexer.js",
+      "lib/internal/test_runner/tap_parser.js",
+      "lib/internal/test_runner/tap_stream.js",
+      "lib/internal/test_runner/test.js",
+      "lib/internal/test_runner/utils.js",
+      "lib/internal/test_runner/yaml_parser.js",
+      "lib/internal/timers.js",
+      "lib/internal/tls/secure-context.js",
+      "lib/internal/tls/secure-pair.js",
+      "lib/internal/trace_events_async_hooks.js",
+      "lib/internal/tty.js",
+      "lib/internal/url.js",
+      "lib/internal/util.js",
+      "lib/internal/util/colors.js",
+      "lib/internal/util/comparisons.js",
+      "lib/internal/util/debuglog.js",
+      "lib/internal/util/inspect.js",
+      "lib/internal/util/inspector.js",
+      "lib/internal/util/iterable_weak_map.js",
+      "lib/internal/util/parse_args/parse_args.js",
+      "lib/internal/util/parse_args/utils.js",
+      "lib/internal/util/types.js",
+      "lib/internal/v8/startup_snapshot.js",
+      "lib/internal/v8_prof_polyfill.js",
+      "lib/internal/v8_prof_processor.js",
+      "lib/internal/validators.js",
+      "lib/internal/vm.js",
+      "lib/internal/vm/module.js",
+      "lib/internal/wasm_web_api.js",
+      "lib/internal/watch_mode/files_watcher.js",
+      "lib/internal/watchdog.js",
+      "lib/internal/webstreams/adapters.js",
+      "lib/internal/webstreams/compression.js",
+      "lib/internal/webstreams/encoding.js",
+      "lib/internal/webstreams/queuingstrategies.js",
+      "lib/internal/webstreams/readablestream.js",
+      "lib/internal/webstreams/transfer.js",
+      "lib/internal/webstreams/transformstream.js",
+      "lib/internal/webstreams/util.js",
+      "lib/internal/webstreams/writablestream.js",
+      "lib/internal/worker.js",
+      "lib/internal/worker/io.js",
+      "lib/internal/worker/js_transferable.js",
+      "lib/module.js",
+      "lib/net.js",
+      "lib/os.js",
+      "lib/path.js",
+      "lib/path/posix.js",
+      "lib/path/win32.js",
+      "lib/perf_hooks.js",
+      "lib/process.js",
+      "lib/punycode.js",
+      "lib/querystring.js",
+      "lib/readline.js",
+      "lib/readline/promises.js",
+      "lib/repl.js",
+      "lib/stream.js",
+      "lib/stream/consumers.js",
+      "lib/stream/promises.js",
+      "lib/stream/web.js",
+      "lib/string_decoder.js",
+      "lib/sys.js",
+      "lib/test.js",
+      "lib/timers.js",
+      "lib/timers/promises.js",
+      "lib/tls.js",
+      "lib/trace_events.js",
+      "lib/tty.js",
+      "lib/url.js",
+      "lib/util.js",
+      "lib/util/types.js",
+      "lib/v8.js",
+      "lib/vm.js",
+      "lib/wasi.js",
+      "lib/worker_threads.js",
+      "lib/zlib.js"
+    ],
+    "node_module_version": 108,
+    "node_no_browser_globals": "false",
+    "node_prefix": "/",
+    "node_release_urlbase": "https://nodejs.org/download/release/",
+    "node_shared": "false",
+    "node_shared_brotli": "false",
+    "node_shared_cares": "false",
+    "node_shared_http_parser": "false",
+    "node_shared_libuv": "false",
+    "node_shared_nghttp2": "false",
+    "node_shared_nghttp3": "false",
+    "node_shared_ngtcp2": "false",
+    "node_shared_openssl": "false",
+    "node_shared_zlib": "false",
+    "node_tag": "",
+    "node_target_type": "executable",
+    "node_use_bundled_v8": "true",
+    "node_use_dtrace": "true",
+    "node_use_etw": "false",
+    "node_use_node_code_cache": "true",
+    "node_use_node_snapshot": "true",
+    "node_use_openssl": "true",
+    "node_use_v8_platform": "true",
+    "node_with_ltcg": "false",
+    "node_without_node_options": "false",
+    "openssl_is_fips": "false",
+    "openssl_quic": "true",
+    "ossfuzz": "false",
+    "shlib_suffix": "108.dylib",
+    "target_arch": "arm64",
+    "v8_enable_31bit_smis_on_64bit_arch": 0,
+    "v8_enable_gdbjit": 0,
+    "v8_enable_hugepage": 0,
+    "v8_enable_i18n_support": 1,
+    "v8_enable_inspector": 1,
+    "v8_enable_javascript_promise_hooks": 1,
+    "v8_enable_lite_mode": 0,
+    "v8_enable_object_print": 1,
+    "v8_enable_pointer_compression": 0,
+    "v8_enable_shared_ro_heap": 1,
+    "v8_enable_webassembly": 1,
+    "v8_no_strict_aliasing": 1,
+    "v8_optimized_debug": 1,
+    "v8_promise_internal_field_count": 1,
+    "v8_random_seed": 0,
+    "v8_trace_maps": 0,
+    "v8_use_siphash": 1,
+    "want_separate_host_toolset": 0,
+    "xcode_version": "12.0",
+    "nodedir": "/Users/alvarosabu/Library/Caches/node-gyp/18.14.1",
+    "standalone_static_library": 1
+  }
+}

+ 1 - 0
packages/tres/build/node_gyp_bins/python3

@@ -0,0 +1 @@
+/Library/Developer/CommandLineTools/usr/bin/python3

+ 7 - 6
packages/tres/package.json

@@ -56,22 +56,23 @@
     "@tresjs/cientos": "workspace:^1.7.0",
     "@tresjs/cientos": "workspace:^1.7.0",
     "@types/three": "latest",
     "@types/three": "latest",
     "@vitejs/plugin-vue": "^4.0.0",
     "@vitejs/plugin-vue": "^4.0.0",
-    "@vitest/coverage-c8": "^0.28.4",
-    "@vitest/ui": "^0.28.4",
-    "gl": "6.0.2",
-    "happy-dom": "^8.2.6",
+    "@vitest/coverage-c8": "^0.28.5",
+    "@vitest/ui": "^0.28.5",
+    "@vue/test-utils": "^2.3.0",
+    "happy-dom": "^8.7.1",
+    "jsdom": "^21.1.0",
     "kolorist": "^1.7.0",
     "kolorist": "^1.7.0",
     "pathe": "^1.1.0",
     "pathe": "^1.1.0",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-visualizer": "^5.9.0",
     "rollup-plugin-visualizer": "^5.9.0",
     "three": "latest",
     "three": "latest",
-    "vite": "^4.1.2",
+    "vite": "^4.1.4",
     "vite-plugin-banner": "^0.7.0",
     "vite-plugin-banner": "^0.7.0",
     "vite-plugin-dts": "2.0.0-beta.1",
     "vite-plugin-dts": "2.0.0-beta.1",
     "vite-plugin-glsl": "^1.1.2",
     "vite-plugin-glsl": "^1.1.2",
     "vite-plugin-inspect": "^0.7.15",
     "vite-plugin-inspect": "^0.7.15",
     "vite-plugin-require-transform": "^1.0.9",
     "vite-plugin-require-transform": "^1.0.9",
-    "vitest": "^0.28.4",
+    "vitest": "^0.28.5",
     "vue-demi": "^0.13.11"
     "vue-demi": "^0.13.11"
   }
   }
 }
 }

+ 15 - 2
packages/tres/src/App.vue

@@ -2,7 +2,6 @@
 import { useTweakPane } from '@tresjs/cientos'
 import { useTweakPane } from '@tresjs/cientos'
 import TheEnvironment from '/@/components/TheEnvironment.vue'
 import TheEnvironment from '/@/components/TheEnvironment.vue'
 // import TheEvents from '/@/components/TheEvents.vue'
 // import TheEvents from '/@/components/TheEvents.vue'
-import VectorSetProps from '/@/components/VectorSetProps.vue'
 
 
 useTweakPane()
 useTweakPane()
 </script>
 </script>
@@ -10,6 +9,20 @@ useTweakPane()
 <template>
 <template>
   <Suspense>
   <Suspense>
     <TheEnvironment />
     <TheEnvironment />
-    <!-- <VectorSetProps /> -->
   </Suspense>
   </Suspense>
 </template>
 </template>
+
+<style>
+html,
+body {
+  margin: 0;
+  padding: 0;
+  height: 100%;
+  width: 100%;
+}
+#app {
+  height: 100%;
+  width: 100%;
+  background-color: #000;
+}
+</style>

+ 22 - 0
packages/tres/src/components/Responsiveness.vue

@@ -0,0 +1,22 @@
+<script setup lang="ts">
+import TheEnvironment from './TheEnvironment.vue'
+</script>
+<template>
+  <div class="modal">
+    <TheEnvironment />
+  </div>
+</template>
+
+<style>
+.modal {
+  position: absolute;
+  top: 50%;
+  left: 50%;
+  right: 0;
+  bottom: 0;
+  width: 200px;
+  height: 200px;
+  background-color: rgba(0, 0, 0, 0.5);
+  z-index: 100;
+}
+</style>

+ 2 - 1
packages/tres/src/components/TheBasic.vue

@@ -10,7 +10,7 @@ const state = reactive({
   clearColor: '#201919',
   clearColor: '#201919',
   shadows: true,
   shadows: true,
   alpha: false,
   alpha: false,
-  physicallyCorrectLights: true,
+
   shadowMapType: BasicShadowMap,
   shadowMapType: BasicShadowMap,
   outputEncoding: sRGBEncoding,
   outputEncoding: sRGBEncoding,
   toneMapping: NoToneMapping,
   toneMapping: NoToneMapping,
@@ -35,6 +35,7 @@ onLoop(({ elapsed }) => {
       <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
       <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
         <TresSphereGeometry />
         <TresSphereGeometry />
         <TresMeshToonMaterial color="#FBB03B" />
         <TresMeshToonMaterial color="#FBB03B" />
+        <!-- <TresMeshToonMaterial color="#FBB03B" /> -->
       </TresMesh>
       </TresMesh>
       <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.7" cast-shadow />
       <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.7" cast-shadow />
       <TresMesh :rotation="[-Math.PI / 2, 0, 0]" receive-shadow>
       <TresMesh :rotation="[-Math.PI / 2, 0, 0]" receive-shadow>

+ 5 - 0
packages/tres/src/components/TheExperience.vue

@@ -15,9 +15,14 @@ import {
 } from 'three'
 } from 'three'
 import { reactive, ref } from 'vue'
 import { reactive, ref } from 'vue'
 import { OrbitControls, useTweakPane, TransformControls } from '../../../cientos/src/'
 import { OrbitControls, useTweakPane, TransformControls } from '../../../cientos/src/'
+import { useCamera } from '../core'
 import { TresCanvas } from '../core/useRenderer/component'
 import { TresCanvas } from '../core/useRenderer/component'
 /* import { OrbitControls, GLTFModel } from '@tresjs/cientos' */
 /* import { OrbitControls, GLTFModel } from '@tresjs/cientos' */
 
 
+const { updateCamera } = useCamera()
+
+updateCamera
+
 const state = reactive({
 const state = reactive({
   clearColor: '#201919',
   clearColor: '#201919',
   shadows: true,
   shadows: true,

+ 113 - 26
packages/tres/src/core/useCamera/index.ts

@@ -1,7 +1,7 @@
 import { useTres } from '/@/core/'
 import { useTres } from '/@/core/'
 import { PerspectiveCamera, OrthographicCamera } from 'three'
 import { PerspectiveCamera, OrthographicCamera } from 'three'
 
 
-import { computed, ComputedRef, watch } from 'vue'
+import { toRef, watch, Ref } from 'vue'
 
 
 export enum CameraType {
 export enum CameraType {
   Perspective = 'Perspective',
   Perspective = 'Perspective',
@@ -10,42 +10,115 @@ export enum CameraType {
 
 
 export type Camera = PerspectiveCamera | OrthographicCamera
 export type Camera = PerspectiveCamera | OrthographicCamera
 
 
-export type CameraState = {
-  cameras: Array<Camera>
-}
-
 export interface PerspectiveCameraOptions {
 export interface PerspectiveCameraOptions {
+  /**
+   * Camera frustum vertical field of view, from bottom to top of view, in degrees.
+   *
+   * @type {number}
+   * @memberof PerspectiveCameraOptions
+   */
   fov?: number
   fov?: number
+  /**
+   * Camera frustum near plane.
+   *
+   * @type {number}
+   * @memberof PerspectiveCameraOptions
+   */
   near?: number
   near?: number
+  /**
+   * Camera frustum far plane.
+   *
+   * @type {number}
+   * @memberof PerspectiveCameraOptions
+   */
   far?: number
   far?: number
 }
 }
 
 
 export interface OrthographicCameraOptions {
 export interface OrthographicCameraOptions {
+  /**
+   * Camera frustum left plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   left?: number
   left?: number
+  /**
+   * Camera frustum right plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   right?: number
   right?: number
+  /**
+   * Camera frustum top plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   top?: number
   top?: number
+  /**
+   * Camera frustum bottom plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   bottom?: number
   bottom?: number
+  /**
+   * Camera frustum near plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   near?: number
   near?: number
+  /**
+   * Camera frustum far plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   far?: number
   far?: number
 }
 }
 
 
 interface UseCameraReturn {
 interface UseCameraReturn {
-  activeCamera: ComputedRef<Camera>
+  activeCamera: Ref<Camera | undefined>
   createCamera: (cameraType?: CameraType, options?: PerspectiveCameraOptions | OrthographicCameraOptions) => Camera
   createCamera: (cameraType?: CameraType, options?: PerspectiveCameraOptions | OrthographicCameraOptions) => Camera
   updateCamera: () => void
   updateCamera: () => void
   pushCamera: (camera: Camera) => void
   pushCamera: (camera: Camera) => void
-}
-
-const cameraState: CameraState = {
-  cameras: [],
+  clearCameras: () => void
 }
 }
 
 
 const VERTICAL_FIELD_OF_VIEW = 45
 const VERTICAL_FIELD_OF_VIEW = 45
 let camera: Camera
 let camera: Camera
 
 
+/**
+ * Create and update cameras
+ *
+ * ```ts
+ * import { useCamera } from '@tresjs/core'
+ * const { createCamera, updateCamera } = useCamera()
+ * const camera = createCamera(new PerspectiveCamera(45, 1, 0.1, 1000))
+ * updateCamera()
+ * ```
+ *
+ * @export
+ * @return {*}  {UseCameraReturn}
+ */
 export function useCamera(): UseCameraReturn {
 export function useCamera(): UseCameraReturn {
   const { state, setState } = useTres()
   const { state, setState } = useTres()
 
 
+  /**
+   * Create camera and push to Tres `state.cameras` array
+   *
+   * ```ts
+   * import { useCamera } from '@tresjs/core'
+   * const { createCamera } = useCamera()
+   * const camera = createCamera(new PerspectiveCamera(45, 1, 0.1, 1000))
+   * ```
+   *
+   * @param {*} [cameraType=CameraType.Perspective]
+   * @param {(PerspectiveCameraOptions | OrthographicCameraOptions)} [options]
+   * @return {*}
+   */
   function createCamera(
   function createCamera(
     cameraType = CameraType.Perspective,
     cameraType = CameraType.Perspective,
     options?: PerspectiveCameraOptions | OrthographicCameraOptions,
     options?: PerspectiveCameraOptions | OrthographicCameraOptions,
@@ -56,8 +129,8 @@ export function useCamera(): UseCameraReturn {
         far: 1000,
         far: 1000,
         fov: VERTICAL_FIELD_OF_VIEW,
         fov: VERTICAL_FIELD_OF_VIEW,
       }
       }
-      camera = new PerspectiveCamera(fov, state.aspectRatio?.value || 1, near, far)
-      cameraState.cameras.push(camera as PerspectiveCamera)
+      camera = new PerspectiveCamera(fov, state.aspectRatio?.value || window.innerWidth / window.innerHeight, near, far)
+      state.cameras?.push(camera as PerspectiveCamera)
     } else {
     } else {
       const { left, right, top, bottom, near, far } = (options as OrthographicCameraOptions) || {
       const { left, right, top, bottom, near, far } = (options as OrthographicCameraOptions) || {
         left: -100,
         left: -100,
@@ -68,42 +141,56 @@ export function useCamera(): UseCameraReturn {
         far: 1000,
         far: 1000,
       }
       }
       camera = new OrthographicCamera(left, right, top, bottom, near, far)
       camera = new OrthographicCamera(left, right, top, bottom, near, far)
-      cameraState.cameras.push(camera as OrthographicCamera)
+      state.cameras?.push(camera as OrthographicCamera)
     }
     }
+    state.camera = camera
+
+    setState('camera', state.camera)
 
 
-    cameraState.cameras.push(camera)
     return camera
     return camera
   }
   }
 
 
-  const activeCamera = computed(() => cameraState.cameras[0])
-  setState('camera', activeCamera)
-
+  /**
+   * Update camera aspect ratio and projection matrix
+   *
+   */
   function updateCamera() {
   function updateCamera() {
-    if (activeCamera.value instanceof PerspectiveCamera && state.aspectRatio) {
-      activeCamera.value.aspect = state.aspectRatio.value
+    if (state.camera instanceof PerspectiveCamera && state.aspectRatio) {
+      state.camera.aspect = state.aspectRatio.value
     }
     }
-    activeCamera.value.updateProjectionMatrix()
+    state.camera?.updateProjectionMatrix()
   }
   }
 
 
+  /**
+   * Push camera to cameras array and update aspect ratio if camera is PerspectiveCamera
+   *
+   * @param {Camera} camera
+   */
   function pushCamera(camera: Camera): void {
   function pushCamera(camera: Camera): void {
-    /*     if (camera && currentCamera) {
-      currentCamera.value = camera
-      setState('camera', currentCamera)
-    } */
-    cameraState.cameras.push(camera)
+    state.cameras?.push(camera)
     if (camera instanceof PerspectiveCamera && state.aspectRatio) {
     if (camera instanceof PerspectiveCamera && state.aspectRatio) {
       camera.aspect = state.aspectRatio.value
       camera.aspect = state.aspectRatio.value
     }
     }
     camera.updateProjectionMatrix()
     camera.updateProjectionMatrix()
+    setState('camera', camera)
+  }
+
+  /**
+   * Clear cameras array
+   *
+   */
+  function clearCameras() {
+    state.cameras = []
   }
   }
 
 
   if (state.aspectRatio) {
   if (state.aspectRatio) {
     watch(state.aspectRatio, updateCamera)
     watch(state.aspectRatio, updateCamera)
   }
   }
   return {
   return {
-    activeCamera,
+    activeCamera: toRef(state, 'camera'),
     createCamera,
     createCamera,
     updateCamera,
     updateCamera,
     pushCamera,
     pushCamera,
+    clearCameras,
   }
   }
 }
 }

+ 101 - 0
packages/tres/src/core/useCamera/useCamera.test.ts

@@ -0,0 +1,101 @@
+import { computed } from 'vue'
+import { OrthographicCamera, PerspectiveCamera } from 'three'
+import { describe, test, expect, vi, afterEach } from 'vitest'
+import { withSetup } from '/@/utils/test-utils'
+import { CameraType, useCamera } from '.'
+
+const [composable, app] = withSetup(() => useCamera())
+const aspectRatio = computed(() => 1)
+app.provide('aspect-ratio', aspectRatio)
+
+describe('useCamera', () => {
+  afterEach(() => {
+    composable.clearCameras()
+    app.unmount()
+  })
+  describe('createCamera', () => {
+    test('should create a camera', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective)
+      expect(camera).toBeDefined()
+    })
+    test('should create a perspective camera', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective)
+      expect(camera.type).toBe('PerspectiveCamera')
+    })
+    test('should create a perspective camera with default options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective)
+      expect((camera as PerspectiveCamera).fov).toBe(45)
+      expect((camera as PerspectiveCamera).near).toBe(0.1)
+      expect((camera as PerspectiveCamera).far).toBe(1000)
+    })
+    test('should create a perspective camera with custom options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Perspective, {
+        fov: 60,
+        near: 1,
+        far: 100,
+      })
+      expect((camera as PerspectiveCamera).fov).toBe(60)
+      expect((camera as PerspectiveCamera).near).toBe(1)
+      expect((camera as PerspectiveCamera).far).toBe(100)
+    })
+    test('should create an orthographic camera', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Orthographic)
+      expect(camera.type).toBe('OrthographicCamera')
+    })
+    test('should create an orthographic camera with default options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Orthographic)
+      expect((camera as OrthographicCamera).near).toBe(0.1)
+      expect((camera as OrthographicCamera).far).toBe(1000)
+      expect((camera as OrthographicCamera).left).toBe(-100)
+      expect((camera as OrthographicCamera).right).toBe(100)
+      expect((camera as OrthographicCamera).top).toBe(100)
+      expect((camera as OrthographicCamera).bottom).toBe(-100)
+    })
+
+    test('should create an orthographic camera with custom options', () => {
+      const { createCamera } = composable
+      const camera = createCamera(CameraType.Orthographic, {
+        near: 1,
+        far: 100,
+        left: -50,
+        right: 50,
+        top: 50,
+        bottom: -50,
+      })
+      expect((camera as OrthographicCamera).near).toBe(1)
+      expect((camera as OrthographicCamera).far).toBe(100)
+      expect((camera as OrthographicCamera).left).toBe(-50)
+      expect((camera as OrthographicCamera).right).toBe(50)
+      expect((camera as OrthographicCamera).top).toBe(50)
+      expect((camera as OrthographicCamera).bottom).toBe(-50)
+    })
+  })
+  describe('activeCamera', () => {
+    test('should return the latest camera', () => {
+      const { createCamera, activeCamera } = composable
+      createCamera(CameraType.Perspective)
+      expect(activeCamera.value.type).toBe('PerspectiveCamera')
+    })
+    test('should return the latest camera if used more than once', () => {
+      const { createCamera, activeCamera } = composable
+      createCamera(CameraType.Perspective)
+      createCamera(CameraType.Orthographic)
+      expect(activeCamera.value.type).toBe('OrthographicCamera')
+    })
+  })
+  describe('updateCamera', () => {
+    test('should update the current camera with aspect ratio change', () => {
+      const { activeCamera, createCamera, updateCamera } = composable
+      createCamera(CameraType.Perspective)
+      const updateProjectionMatrix = vi.spyOn(activeCamera.value, 'updateProjectionMatrix')
+      updateCamera()
+      expect(updateProjectionMatrix).toHaveBeenCalled()
+    })
+  })
+})

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

@@ -9,6 +9,21 @@ const catalogue: Ref<TresCatalogue> = ref({ ...THREE, uuid: THREE.MathUtils.gene
 delete catalogue.value.Scene
 delete catalogue.value.Scene
 
 
 let localApp: App
 let localApp: App
+
+/**
+ * State for the catalogue of THREE objects
+ *
+ * ```ts
+ * const { catalogue } = useCatalogue()
+ *
+ * console.log(catalogue.value.Mesh) // Mesh
+ * ```
+ *
+ * @export
+ * @param {App} [app]
+ * @param {string} [prefix='Tres']
+ * @return {*}
+ */
 export function useCatalogue(app?: App, prefix = 'Tres') {
 export function useCatalogue(app?: App, prefix = 'Tres') {
   const { logError } = useLogger()
   const { logError } = useLogger()
   if (!localApp && app) {
   if (!localApp && app) {
@@ -16,6 +31,19 @@ export function useCatalogue(app?: App, prefix = 'Tres') {
   }
   }
   const { createComponentInstances } = useInstanceCreator(prefix)
   const { createComponentInstances } = useInstanceCreator(prefix)
 
 
+  /**
+   * Extend the catalogue with new THREE objects
+   *
+   * ```ts
+   * const { catalog, extend } = useCatalogue()
+   *
+   * extend({ MyObject: { foo: 'bar' } })
+   *
+   * console.log(catalog.value.MyObject.foo) // bar
+   * ```
+   *
+   * @param {*} objects
+   */
   const extend = (objects: any) => {
   const extend = (objects: any) => {
     if (!objects) {
     if (!objects) {
       logError('No objects provided to extend catalogue')
       logError('No objects provided to extend catalogue')

+ 22 - 13
packages/tres/src/core/useCatalogue/useCatalogue.test.ts

@@ -1,19 +1,28 @@
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
-import { describe, test, expect } from 'vitest'
+import { createApp } from 'vue'
+import { withSetup } from '/@/utils/test-utils'
 import { useCatalogue } from './'
 import { useCatalogue } from './'
+const [composable, app] = withSetup(() => useCatalogue())
 
 
-describe('useCatalogue()', () => {
-  test('should init catalog', () => {
-    const { catalogue } = useCatalogue()
-    expect(catalogue).toHaveProperty('Mesh')
+describe('useCatalogue', () => {
+  it('should fill the catalogue with THREE objects', () => {
+    const { catalogue } = composable
+
+    expect(catalogue.value).toHaveProperty('Mesh')
+    expect(catalogue.value).toHaveProperty('MeshBasicMaterial')
   })
   })
-  test('should be able to extend catalogue', () => {
-    const { catalogue, extend } = useCatalogue()
-    extend({ OrbitControls })
-    expect(catalogue).toHaveProperty('OrbitControls')
+  it('should skip Scene object', () => {
+    const { catalogue } = composable
+
+    expect(catalogue.value).not.toHaveProperty('Scene')
   })
   })
-  test('catalog objects should be instanciable', () => {
-    const { catalogue } = useCatalogue()
-    expect(new catalogue.PerspectiveCamera()).toHaveProperty('type', 'PerspectiveCamera')
+  it('should extend the catalogue with objects', () => {
+    const app = createApp({})
+    const { extend, catalogue } = useCatalogue(app)
+
+    extend({ MyObject: { foo: 'bar' } })
+
+    expect(catalogue.value.MyObject.foo).toEqual('bar')
   })
   })
+
+  // TODO: find a way to mock createComponentInstances to test the component registration
 })
 })

+ 65 - 15
packages/tres/src/core/useInstanceCreator/index.ts

@@ -15,9 +15,24 @@ const VECTOR3_AXIS = ['X', 'Y', 'Z']
 const COLOR_PROPS = ['color']
 const COLOR_PROPS = ['color']
 const COLOR_KEYS = ['r', 'g', 'b']
 const COLOR_KEYS = ['r', 'g', 'b']
 
 
+/**
+ * Composable responsible for creating instances out of Three.js objects.
+ *
+ * @export
+ * @param {string} prefix
+ * @return {*}
+ */
 export function useInstanceCreator(prefix: string) {
 export function useInstanceCreator(prefix: string) {
   const { /* logMessage, */ logError } = useLogger()
   const { /* logMessage, */ logError } = useLogger()
 
 
+  /**
+   * Process props to `.setAttribute` on instance.
+   *
+   * @example `position` prop will be converted to `setPosition` method call.
+   *
+   * @param {Record<string, any>} props
+   * @param {TresInstance} instance
+   */
   function processSetAttributes(props: Record<string, any>, instance: TresInstance) {
   function processSetAttributes(props: Record<string, any>, instance: TresInstance) {
     if (!isDefined(props)) return
     if (!isDefined(props)) return
     if (!isDefined(instance)) return
     if (!isDefined(instance)) return
@@ -28,6 +43,17 @@ export function useInstanceCreator(prefix: string) {
     })
     })
   }
   }
 
 
+  /**
+   *  Process props to set properties on instance.
+   *
+   * It will also normalize vector3 props and check if the instances property has a `set` method.
+   * If it does, it will call the `set` method with the value, spread if it's an array.
+   *
+   * @example `position=[0,0,0]` prop will be converted to `instance.position.set(0,0,0)` property.
+   *
+   * @param {Record<string, any>} props
+   * @param {TresInstance} instance
+   */
   function processProps(props: Record<string, any>, instance: TresInstance) {
   function processProps(props: Record<string, any>, instance: TresInstance) {
     if (!isDefined(props)) return
     if (!isDefined(props)) return
     if (!isDefined(instance)) return
     if (!isDefined(instance)) return
@@ -53,8 +79,8 @@ export function useInstanceCreator(prefix: string) {
             transformAxis = camelKey.substring(vecProps.length)
             transformAxis = camelKey.substring(vecProps.length)
             if (!VECTOR3_AXIS.includes(transformAxis)) {
             if (!VECTOR3_AXIS.includes(transformAxis)) {
               logError(
               logError(
-                `There was an error setting ${key} property`,
-                `${transformAxis} is not a valid axis for ${transformProps}`,
+                // eslint-disable-next-line max-len
+                `There was an error setting ${key} property, ${transformAxis} is not a valid axis for ${transformProps}`,
               )
               )
             }
             }
           }
           }
@@ -67,7 +93,7 @@ export function useInstanceCreator(prefix: string) {
           colorProps = props
           colorProps = props
           colorKey = camelKey.substring(props.length).toLowerCase()
           colorKey = camelKey.substring(props.length).toLowerCase()
           if (!COLOR_KEYS.includes(colorKey)) {
           if (!COLOR_KEYS.includes(colorKey)) {
-            logError(`There was an error setting ${key} property`, `${colorKey} is not a valid axis for ${colorProps}`)
+            logError(`There was an error setting ${key} property , ${colorKey} is not a valid axis for ${colorProps}`)
           }
           }
         }
         }
       })
       })
@@ -124,13 +150,20 @@ export function useInstanceCreator(prefix: string) {
     })
     })
   }
   }
 
 
+  /**
+   * Proccess slots to add children to instance.
+   *
+   * @param {TresVNode} vnode
+   * @return {*}  {(TresInstance | TresInstance[] | undefined)}
+   */
   function createInstanceFromVNode(vnode: TresVNode): TresInstance | TresInstance[] | undefined {
   function createInstanceFromVNode(vnode: TresVNode): TresInstance | TresInstance[] | undefined {
     const fragmentRegex = /^Symbol\(Fragment\)$/g
     const fragmentRegex = /^Symbol\(Fragment\)$/g
     const textRegex = /^Symbol\(Text\)$/g
     const textRegex = /^Symbol\(Text\)$/g
+    const commentRegex = /^Symbol\(Comment\)$/g
     // Check if the vnode is a Fragment
     // Check if the vnode is a Fragment
     if (fragmentRegex.test(vnode.type.toString())) {
     if (fragmentRegex.test(vnode.type.toString())) {
       return vnode.children.map(child => createInstanceFromVNode(child as TresVNode)) as TresInstance[]
       return vnode.children.map(child => createInstanceFromVNode(child as TresVNode)) as TresInstance[]
-    } else if (textRegex.test(vnode.type.toString())) {
+    } else if (textRegex.test(vnode.type.toString()) || commentRegex.test(vnode.type.toString())) {
       return
       return
     } else {
     } else {
       const vNodeType = ((vnode.type as TresVNodeType).name as string).replace(prefix, '')
       const vNodeType = ((vnode.type as TresVNodeType).name as string).replace(prefix, '')
@@ -172,18 +205,29 @@ export function useInstanceCreator(prefix: string) {
     }
     }
   }
   }
 
 
+  /**
+   * Create a new instance of a ThreeJS object based on the component attrs and slots.
+   *
+   * Checks if the component has slots,
+   * if it does, it will create a new Object3D instance passing the slots instances as properties
+   * Example:
+   *
+   * ```vue
+   * <TresMesh>
+   *  <TresBoxGeometry />
+   *  <TresMeshBasicMaterial />
+   * </TresMesh>
+   * ```
+   *
+   * will create a new Mesh instance with a BoxGeometry and a MeshBasicMaterial
+   * const mesh = new Mesh(new BoxGeometry(), new MeshBasicMaterial())
+   *
+   * @param {*} threeObj
+   * @param {TresAttributes} attrs
+   * @param {Record<string, any>} slots
+   * @return {*}  {TresInstance}
+   */
   function createInstance(threeObj: any, attrs: TresAttributes, slots: Record<string, any>): TresInstance {
   function createInstance(threeObj: any, attrs: TresAttributes, slots: Record<string, any>): TresInstance {
-    /*
-     * Checks if the component has slots,
-     * if it does, it will create a new Object3D instance passing the slots instances as properties
-     * Example:
-     * <TresMesh>
-     *  <TresBoxGeometry />
-     *  <TresMeshBasicMaterial />
-     * </TresMesh>
-     * will create a new Mesh instance with a BoxGeometry and a MeshBasicMaterial
-     * const mesh = new Mesh(new BoxGeometry(), new MeshBasicMaterial())
-     */
     if (slots.default && slots?.default()) {
     if (slots.default && slots?.default()) {
       const internal = slots.default().map((vnode: TresVNode) => createInstanceFromVNode(vnode))
       const internal = slots.default().map((vnode: TresVNode) => createInstanceFromVNode(vnode))
       if (threeObj.name === 'Group') {
       if (threeObj.name === 'Group') {
@@ -201,6 +245,12 @@ export function useInstanceCreator(prefix: string) {
     }
     }
   }
   }
 
 
+  /**
+   * Creates a new component instance for each object in the catalogue
+   *
+   * @param {Ref<TresCatalogue>} catalogue
+   * @return {*}
+   */
   function createComponentInstances(catalogue: Ref<TresCatalogue>) {
   function createComponentInstances(catalogue: Ref<TresCatalogue>) {
     return (
     return (
       Object.entries(catalogue.value)
       Object.entries(catalogue.value)

+ 27 - 0
packages/tres/src/core/useInstanceCreator/useInstanceCreator.test.ts

@@ -0,0 +1,27 @@
+import { describe } from 'vitest'
+import { shallowRef } from 'vue'
+import { useInstanceCreator } from '.'
+import { useTres } from '../useTres'
+import { withSetup } from '/@/utils/test-utils'
+
+const [composable, app] = withSetup(() => useInstanceCreator('Tres'))
+
+describe('useInstanceCreator', () => {
+  // TODO: understand why this is not working
+  it.todo('should create component instances', () => {
+    const { createComponentInstances } = composable
+    const catalogue = shallowRef({
+      TresBox: { name: 'TresBox' },
+      TresSphere: { name: 'TresSphere' },
+      TresPlane: { name: 'TresPlane' },
+    })
+    app.provide('catalogue', catalogue)
+
+    app.provide('useTres', useTres())
+    const components = createComponentInstances(catalogue)
+    expect(components).toHaveLength(3)
+    expect(components[0][0]).toBe('TresBox')
+    expect(components[1][0]).toBe('TresSphere')
+    expect(components[2][0]).toBe('TresPlane')
+  })
+})

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

@@ -17,6 +17,13 @@ export type LoaderReturnType<T, L extends LoaderProto<T>> = T extends unknown
   ? Awaited<ReturnType<InstanceType<L>['loadAsync']>>
   ? Awaited<ReturnType<InstanceType<L>['loadAsync']>>
   : T
   : T
 
 
+/**
+ * Traverse an object and return all the nodes and materials
+ *
+ * @export
+ * @param {Object3D} object
+ * @return { [key: string]: any }
+ */
 export function trasverseObjects(object: Object3D) {
 export function trasverseObjects(object: Object3D) {
   const data: { [key: string]: any } = { nodes: {}, materials: {} }
   const data: { [key: string]: any } = { nodes: {}, materials: {} }
   if (object) {
   if (object) {
@@ -34,6 +41,29 @@ export function trasverseObjects(object: Object3D) {
 
 
 export type Extensions<T extends { prototype: LoaderProto<any> }> = (loader: T['prototype']) => void
 export type Extensions<T extends { prototype: LoaderProto<any> }> = (loader: T['prototype']) => void
 
 
+/**
+ * Load resources using THREE loaders and return the result as a promise
+ *
+ * @see https://tresjs.org/api/composables.html#useloader
+ * @see https://threejs.org/docs/index.html?q=loader#api/en/loaders/Loader
+ *
+ * ```ts
+ * import { useLoader } from '@tresjs/core'
+ * import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
+ *
+ * const { scene } = await useLoader(THREE.GLTFLoader, 'path/to/asset.gltf')
+ * ```
+ *
+ * @export
+ * @template T
+ * @template U
+ * @param {T} Loader
+ * @param {U} url
+ * @param {Extensions<T>} [extensions]
+ * @param {(event: ProgressEvent<EventTarget>) => void} [onProgress]
+ * @param {(proto: TresLoader<T>) => void} [cb]
+ * @return {*}
+ */
 export async function useLoader<T extends LoaderProto<T>, U extends string | string[]>(
 export async function useLoader<T extends LoaderProto<T>, U extends string | string[]>(
   Loader: T,
   Loader: T,
   url: U,
   url: U,

+ 25 - 0
packages/tres/src/core/useLoader/useLoader.test.ts

@@ -0,0 +1,25 @@
+import { useLoader } from './'
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
+
+describe('useLoader', () => {
+  test('is defined', () => {
+    expect(useLoader).toBeDefined()
+  })
+  /* test('loads a glTF file using GLTFLoader and returns the result', async () => {
+    const gltfUrl = 'https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf/aku-aku/AkuAku.gltf'
+    const { scene } = await useLoader(GLTFLoader, gltfUrl)
+    expect(scene).toBeDefined()
+  })
+
+  test('applies extensions to the loader before loading', async () => {
+    const gltfUrl = '/aku-aku/AkuAku.gltf'
+    const extensions = (loader: GLTFLoader) => {
+      loader.setPath('https://raw.githubusercontent.com/Tresjs/assets/main/models/gltf')
+    }
+    const { scene } = await useLoader(GLTFLoader, gltfUrl, extensions)
+
+    expect(scene).toBeDefined()
+  }) */
+})
+
+// TODO: find a way to test this

+ 32 - 2
packages/tres/src/core/useRaycaster/index.ts

@@ -1,12 +1,42 @@
 import { Raycaster, Vector2 } from 'three'
 import { Raycaster, Vector2 } from 'three'
-import { onUnmounted, provide, ref, shallowRef } from 'vue'
+import { onUnmounted, provide, Ref, ref, ShallowRef, shallowRef } from 'vue'
 import { useTres } from '/@/core'
 import { useTres } from '/@/core'
 
 
 const raycaster = shallowRef(new Raycaster())
 const raycaster = shallowRef(new Raycaster())
 const pointer = ref(new Vector2())
 const pointer = ref(new Vector2())
 const currentInstance = ref(null)
 const currentInstance = ref(null)
 
 
-export function useRaycaster() {
+/**
+ * Raycaster composable return type
+ *
+ * @export
+ * @interface UseRaycasterReturn
+ */
+export interface UseRaycasterReturn {
+  /**
+   * Raycaster instance
+   *
+   * @type {ShallowRef<Raycaster>}
+   * @memberof UseRaycasterReturn
+   */
+  raycaster: ShallowRef<Raycaster>
+  /**
+   * Pointer position
+   *
+   * @type {Ref<Vector2>}
+   * @memberof UseRaycasterReturn
+   */
+  pointer: Ref<Vector2>
+}
+
+/**
+ * Composable to provide raycaster support and pointer information
+ *
+ * @see https://threejs.org/docs/index.html?q=raycas#api/en/core/Raycaster
+ * @export
+ * @return {*} {UseRaycasterReturn}
+ */
+export function useRaycaster(): UseRaycasterReturn {
   const { setState } = useTres()
   const { setState } = useTres()
   setState('raycaster', raycaster.value)
   setState('raycaster', raycaster.value)
   setState('pointer', pointer)
   setState('pointer', pointer)

+ 22 - 0
packages/tres/src/core/useRaycaster/useRaycaster.test.ts

@@ -0,0 +1,22 @@
+import { Raycaster, Vector2 } from 'three'
+import { useRaycaster } from '.'
+import { withSetup } from '/@/utils/test-utils'
+import { Window } from 'happy-dom'
+
+const [composable, app] = withSetup(() => useRaycaster())
+
+describe('useRaycaster', () => {
+  afterEach(() => {
+    app.unmount()
+  })
+  test('provides raycaster', () => {
+    const { raycaster } = composable
+    expect(raycaster).toBeDefined()
+    expect(raycaster.value).toBeInstanceOf(Raycaster)
+  })
+  test('provides pointer', () => {
+    const { pointer } = composable
+    expect(pointer).toBeDefined()
+    expect(pointer.value).toBeInstanceOf(Vector2)
+  })
+})

+ 39 - 14
packages/tres/src/core/useRenderer/component.ts

@@ -9,12 +9,24 @@ import { TresVNodeType } from '/@/types'
  * Vue component for rendering a Tres component.
  * Vue component for rendering a Tres component.
  */
  */
 
 
+const { logError, logWarning } = useLogger()
+
 export const TresCanvas = defineComponent({
 export const TresCanvas = defineComponent({
   name: 'TresCanvas',
   name: 'TresCanvas',
   props: {
   props: {
     shadows: Boolean,
     shadows: Boolean,
     shadowMapType: Number as PropType<ShadowMapType>,
     shadowMapType: Number as PropType<ShadowMapType>,
-    physicallyCorrectLights: Boolean,
+    physicallyCorrectLights: {
+      type: Boolean,
+      default: false,
+      validator: (value: boolean) => {
+        if (value) {
+          logWarning('physicallyCorrectLights is deprecated. Use useLegacyLights instead.')
+        }
+        return true
+      },
+    },
+    useLegacyLights: Boolean,
     outputEncoding: Number as PropType<TextureEncoding>,
     outputEncoding: Number as PropType<TextureEncoding>,
     toneMapping: Number as PropType<ToneMapping>,
     toneMapping: Number as PropType<ToneMapping>,
     toneMappingExposure: Number,
     toneMappingExposure: Number,
@@ -26,8 +38,6 @@ export const TresCanvas = defineComponent({
     preset: String as PropType<RendererPresetsType>,
     preset: String as PropType<RendererPresetsType>,
   },
   },
   setup(props, { slots, attrs }) {
   setup(props, { slots, attrs }) {
-    const { logError } = useLogger()
-
     const canvas = ref<HTMLCanvasElement>()
     const canvas = ref<HTMLCanvasElement>()
     const container = ref<HTMLElement>()
     const container = ref<HTMLElement>()
 
 
@@ -54,22 +64,37 @@ export const TresCanvas = defineComponent({
             style: {
             style: {
               position: 'relative',
               position: 'relative',
               width: '100%',
               width: '100%',
-              height: '100vh',
+              height: '100%',
+              overflow: 'hidden',
+              pointerEvents: 'auto',
+              touchAction: 'none',
               ...(attrs.style as Record<string, unknown>),
               ...(attrs.style as Record<string, unknown>),
             },
             },
           },
           },
           [
           [
-            h('canvas', {
-              ref: canvas,
-              style: {
-                width: '100%',
-                height: '100%',
-                position: props.windowSize ? 'fixed' : 'absolute',
-                top: 0,
-                left: 0,
+            h(
+              'div',
+              {
+                style: {
+                  width: '100%',
+                  height: '100%',
+                },
               },
               },
-            }),
-            slots.default(),
+              [
+                h('canvas', {
+                  ref: canvas,
+                  style: {
+                    display: 'block',
+                    width: '100%',
+                    height: '100%',
+                    position: props.windowSize ? 'fixed' : 'absolute',
+                    top: 0,
+                    left: 0,
+                  },
+                }),
+                slots.default(),
+              ],
+            ),
           ],
           ],
         )
         )
       }
       }

+ 0 - 1
packages/tres/src/core/useRenderer/const.ts

@@ -2,7 +2,6 @@ import { ACESFilmicToneMapping, PCFSoftShadowMap, sRGBEncoding } from 'three'
 
 
 export const rendererPresets = {
 export const rendererPresets = {
   realistic: {
   realistic: {
-    physicallyCorrectLights: true,
     outputEncoding: sRGBEncoding,
     outputEncoding: sRGBEncoding,
     toneMapping: ACESFilmicToneMapping,
     toneMapping: ACESFilmicToneMapping,
     toneMappingExposure: 3,
     toneMappingExposure: 3,

+ 13 - 3
packages/tres/src/core/useRenderer/index.ts

@@ -48,9 +48,16 @@ export interface UseRendererOptions extends WebGLRendererParameters {
    * See the [lights / physical example](https://threejs.org/examples/#webgl_lights_physical).
    * See the [lights / physical example](https://threejs.org/examples/#webgl_lights_physical).
    *
    *
    * @default false
    * @default false
+   * @deprecated Use {@link WebGLRenderer.useLegacyLights useLegacyLights} instead.
    */
    */
   physicallyCorrectLights?: MaybeComputedRef<boolean>
   physicallyCorrectLights?: MaybeComputedRef<boolean>
-
+  /**
+   * Whether to use legacy lighting mode.
+   *
+   * @type {MaybeComputedRef<boolean>}
+   * @memberof UseRendererOptions
+   */
+  useLegacyLights?: MaybeComputedRef<boolean>
   /**
   /**
    * Defines the output encoding of the renderer.
    * Defines the output encoding of the renderer.
    * Can be LinearEncoding, sRGBEncoding
    * Can be LinearEncoding, sRGBEncoding
@@ -113,7 +120,8 @@ const isReady = ref(false)
  * Reactive Three.js WebGLRenderer instance
  * Reactive Three.js WebGLRenderer instance
  *
  *
  * @param canvas
  * @param canvas
- * @param options
+ * @param container
+ * @param {UseRendererOptions} [options]
  */
  */
 export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef, options: UseRendererOptions) {
 export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef, options: UseRendererOptions) {
   // Defaults
   // Defaults
@@ -129,6 +137,7 @@ export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef,
     shadows = false,
     shadows = false,
     shadowMapType = PCFShadowMap,
     shadowMapType = PCFShadowMap,
     physicallyCorrectLights = false,
     physicallyCorrectLights = false,
+    useLegacyLights = false,
     outputEncoding = LinearEncoding,
     outputEncoding = LinearEncoding,
     toneMapping = NoToneMapping,
     toneMapping = NoToneMapping,
     toneMappingExposure = 1,
     toneMappingExposure = 1,
@@ -177,7 +186,8 @@ export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef,
     renderer.value.outputEncoding = (resolveUnref(outputEncoding) as TextureEncoding) || LinearEncoding
     renderer.value.outputEncoding = (resolveUnref(outputEncoding) as TextureEncoding) || LinearEncoding
     if (clearColor?.value) renderer.value.setClearColor(normalizeColor(resolveUnref(clearColor) as TresColor))
     if (clearColor?.value) renderer.value.setClearColor(normalizeColor(resolveUnref(clearColor) as TresColor))
 
 
-    renderer.value.physicallyCorrectLights = resolveUnref(physicallyCorrectLights) as boolean
+    /*    renderer.value.physicallyCorrectLights = resolveUnref(physicallyCorrectLights) as boolean */
+    renderer.value.useLegacyLights = resolveUnref(useLegacyLights) as boolean
   }
   }
 
 
   const init = () => {
   const init = () => {

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

@@ -19,9 +19,10 @@ export const Scene = defineComponent({
     setState('scene', scene.value)
     setState('scene', scene.value)
 
 
     onLoop(() => {
     onLoop(() => {
+      if (!activeCamera.value) return
       raycaster.value.setFromCamera(pointer.value, activeCamera.value)
       raycaster.value.setFromCamera(pointer.value, activeCamera.value)
 
 
-      if (renderer?.value && activeCamera?.value && scene?.value) {
+      if (renderer?.value && activeCamera && scene?.value) {
         renderer.value.render(scene?.value, activeCamera.value)
         renderer.value.render(scene?.value, activeCamera.value)
       }
       }
     })
     })

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

@@ -3,6 +3,12 @@ import { shallowRef } from 'vue'
 
 
 const scene = shallowRef(new Scene())
 const scene = shallowRef(new Scene())
 
 
+/**
+ * Composable for accessing the scene.
+ *
+ * @export
+ * @return {*} {ShallowRef<Scene>}
+ */
 export function useScene() {
 export function useScene() {
   return {
   return {
     scene,
     scene,

+ 3 - 3
packages/tres/src/core/useScene/useScene.test.ts

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

+ 44 - 22
packages/tres/src/core/useTexture/index.ts

@@ -2,7 +2,19 @@ import { isArray } from '@alvarosabu/utils'
 import { LoadingManager, Texture, TextureLoader } from 'three'
 import { LoadingManager, Texture, TextureLoader } from 'three'
 
 
 export interface PBRMaterialOptions {
 export interface PBRMaterialOptions {
+  /**
+   * List of texture maps to load.
+   *
+   * @type {string[]}
+   * @memberof PBRMaterialOptions
+   */
   maps: string[]
   maps: string[]
+  /**
+   * Path to the texture maps.
+   *
+   * @type {('png' | 'jpg')}
+   * @memberof PBRMaterialOptions
+   */
   ext: 'png' | 'jpg'
   ext: 'png' | 'jpg'
 }
 }
 
 
@@ -10,12 +22,44 @@ export interface PBRTextureMaps {
   [key: string]: Texture | null
   [key: string]: Texture | null
 }
 }
 // eslint-disable-next-line require-await
 // eslint-disable-next-line require-await
+/**
+ * Composable for loading textures.
+ *
+ * @see https://tresjs.org/examples/load-textures.html
+ *
+ * ```ts
+ * import { useTexture } from 'tres'
+ *
+ * const pbrTexture = await useTexture({
+ *  map: 'path/to/texture.png',
+ *  displacementMap: 'path/to/displacement-map.png',
+ *  roughnessMap: 'path/to/roughness-map.png',
+ *  normalMap: 'path/to/normal-map.png',
+ *  ambientOcclusionMap: 'path/to/ambient-occlusion-map.png',
+ * })
+ * ```
+ * Then you can use the texture in your material.
+ *
+ * ```vue
+ * <TresMeshStandardMaterial v-bind="pbrTexture" />
+ * ```
+ *
+ * @export
+ * @param {(Array<string> | { [key: string]: string })} paths
+ * @return {*}  {(Promise<Texture | Array<Texture> | PBRTextureMaps>)}
+ */
 export async function useTexture(
 export async function useTexture(
   paths: Array<string> | { [key: string]: string },
   paths: Array<string> | { [key: string]: string },
 ): Promise<Texture | Array<Texture> | PBRTextureMaps> {
 ): Promise<Texture | Array<Texture> | PBRTextureMaps> {
   const loadingManager = new LoadingManager()
   const loadingManager = new LoadingManager()
   const textureLoader = new TextureLoader(loadingManager)
   const textureLoader = new TextureLoader(loadingManager)
 
 
+  /**
+   * Load a texture.
+   *
+   * @param {string} url
+   * @return {*}  {Promise<Texture>}
+   */
   const loadTexture = (url: string): Promise<Texture> => {
   const loadTexture = (url: string): Promise<Texture> => {
     return new Promise((resolve, reject) => {
     return new Promise((resolve, reject) => {
       textureLoader.load(
       textureLoader.load(
@@ -47,26 +91,4 @@ export async function useTexture(
       aoMap: aoMap ? await loadTexture(aoMap) : 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 } */
 }
 }

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

@@ -0,0 +1,19 @@
+import { LoadingManager, Texture, TextureLoader } from 'three'
+import { useTexture } from '.'
+
+describe('useTexture', () => {
+  afterEach(() => {
+    vi.restoreAllMocks()
+  })
+  // TODO: Add tests, maybe mock the texture loader?
+  it.todo('should load a single texture', async () => {
+    const loadingManager = new LoadingManager()
+    const textureLoader = new TextureLoader(loadingManager)
+
+    const spy = vi.spyOn(textureLoader, 'load').mockImplementation(() => {})
+    const texture = await useTexture([
+      'https://github.com/Tresjs/assets/blob/main/textures/stylized-grass/stylized-grass1_albedo.png?raw=true',
+    ])
+    expect(spy).toHaveBeenCalledTimes(1)
+  })
+})

+ 103 - 3
packages/tres/src/core/useTres/index.ts

@@ -1,27 +1,127 @@
 import { Clock, EventDispatcher, Raycaster, Scene, Vector2, WebGLRenderer } from 'three'
 import { Clock, EventDispatcher, Raycaster, Scene, Vector2, WebGLRenderer } from 'three'
-import { ComputedRef, shallowReactive, toRefs } from 'vue'
+import { computed, ComputedRef, shallowReactive, toRefs } from 'vue'
 import { Camera } from '/@/core'
 import { Camera } from '/@/core'
 
 
 export interface TresState {
 export interface TresState {
-  camera?: ComputedRef<Camera>
+  /**
+   * The active camera used for rendering the scene.
+   *
+   * @see https://threejs.org/docs/index.html?q=camera#api/en/cameras/Camera
+   *
+   * @type {Camera}
+   * @memberof TresState
+   */
+  camera?: Camera
+  /**
+   * All cameras available in the scene.
+   *
+   * @see https://threejs.org/docs/index.html?q=camera#api/en/cameras/Camera
+   *
+   * @type {Camera[]}
+   * @memberof TresState
+   */
+  cameras?: Camera[]
+  /**
+   * The aspect ratio of the scene.
+   *
+   * @type {ComputedRef<number>}
+   * @memberof TresState
+   */
   aspectRatio?: ComputedRef<number>
   aspectRatio?: ComputedRef<number>
+  /**
+   * The WebGLRenderer used to display the scene using WebGL.
+   *
+   * @see https://threejs.org/docs/index.html?q=webglren#api/en/renderers/WebGLRenderer
+   *
+   * @type {WebGLRenderer}
+   * @memberof TresState
+   */
   renderer?: WebGLRenderer
   renderer?: WebGLRenderer
+  /**
+   * The scene. This is the place where you place objects, lights and cameras.
+   *
+   * @see https://threejs.org/docs/index.html?q=scene#api/en/scenes/Scene
+   *
+   * @type {Scene}
+   * @memberof TresState
+   */
   scene?: Scene
   scene?: Scene
+  /**
+   * The raycaster.
+   *
+   * @see https://threejs.org/docs/index.html?q=raycas#api/en/core/Raycaster
+   *
+   * @type {Raycaster}
+   * @memberof TresState
+   */
   raycaster?: Raycaster
   raycaster?: Raycaster
+
+  /**
+   * Object for keeping track of time. This uses `performance.now` if it is available,
+   * otherwise it reverts to the less accurate `Date.now`.
+   *
+   * @see https://threejs.org/docs/index.html?q=clock#api/en/core/Clock
+   *
+   * @type {Clock}
+   * @memberof TresState
+   */
   clock?: Clock
   clock?: Clock
+  /**
+   * The current mouse position.
+   *
+   * @type {Vector2}
+   * @memberof TresState
+   */
   pointer?: Vector2
   pointer?: Vector2
+  /**
+   * The current instance of the component.
+   *
+   * @type {*}
+   * @memberof TresState
+   */
   currentInstance?: any
   currentInstance?: any
+  /**
+   *  The current active scene control
+   *
+   * @type {((EventDispatcher & { enabled: boolean }) | null)}
+   * @memberof TresState
+   */
   controls?: (EventDispatcher & { enabled: boolean }) | null
   controls?: (EventDispatcher & { enabled: boolean }) | null
   [key: string]: any
   [key: string]: any
 }
 }
 
 
-const state: TresState = shallowReactive({})
+const state: TresState = shallowReactive({
+  camera: undefined,
+  cameras: [],
+  aspectRatio: computed(() => window.innerWidth / window.innerHeight),
+})
 
 
+/**
+ * The Tres state.
+ *
+ * @see https://threejs.org/docs/index.html?q=scene#api/en/scenes/Scene
+ *
+ * @export
+ * @return {*} {TresState, getState, setState}
+ */
 export function useTres() {
 export function useTres() {
+  /**
+   * Get a state value.
+   *
+   *
+   * @param {string} key
+   * @return {*}
+   */
   function getState(key: string) {
   function getState(key: string) {
     return state[key]
     return state[key]
   }
   }
 
 
+  /**
+   * Set a state value.
+   *
+   * @param {string} key
+   * @param {*} value
+   */
   function setState(key: string, value: any) {
   function setState(key: string, value: any) {
     state[key] = value
     state[key] = value
   }
   }

+ 14 - 0
packages/tres/src/core/useTres/useTres.test.ts

@@ -0,0 +1,14 @@
+import { useTres } from '.'
+
+describe('useTres', () => {
+  it('should set the state', () => {
+    const { state, setState } = useTres()
+    setState('foo', 'bar')
+    expect(state.foo).toBe('bar')
+  })
+  it('should get the state', () => {
+    const { setState, getState } = useTres()
+    setState('foo', 'bar')
+    expect(getState('foo')).toBe('bar')
+  })
+})

+ 18 - 0
packages/tres/src/utils/test-utils.ts

@@ -0,0 +1,18 @@
+import { createApp } from 'vue'
+
+export function withSetup(composable) {
+  let result
+  const app = createApp({
+    setup() {
+      result = composable()
+      // suppress missing template warning
+      return () => {
+        return null
+      }
+    },
+  })
+  app.mount(document.createElement('div'))
+  // return the result and the app instance
+  // for testing provide / unmount
+  return [result, app]
+}

+ 1 - 1
packages/tres/tsconfig.json

@@ -10,7 +10,7 @@
     "resolveJsonModule": true,
     "resolveJsonModule": true,
     "esModuleInterop": true,
     "esModuleInterop": true,
     "lib": ["esnext", "dom"],
     "lib": ["esnext", "dom"],
-    "types": ["vite/client", "node"],
+    "types": ["vite/client", "node", "vitest/globals"],
     "incremental": false,
     "incremental": false,
     "skipLibCheck": true,
     "skipLibCheck": true,
     "noUnusedLocals": true,
     "noUnusedLocals": true,

+ 7 - 2
packages/tres/tsconfig.node.json

@@ -1,9 +1,14 @@
 {
 {
   "compilerOptions": {
   "compilerOptions": {
+    "baseUrl": ".",
     "composite": true,
     "composite": true,
     "module": "ESNext",
     "module": "ESNext",
     "moduleResolution": "Node",
     "moduleResolution": "Node",
-    "allowSyntheticDefaultImports": true
+    "allowSyntheticDefaultImports": true,
+    "paths": {
+      "/@/*": ["src/*"]
+    },
+    "types": ["vitest/globals"]
   },
   },
-  "include": ["vite.config.ts"]
+  "include": ["vite.config.ts", "src/**/*.test.ts"]
 }
 }

+ 4 - 1
packages/tres/vite.config.ts

@@ -43,9 +43,12 @@ export default defineConfig({
     Inspect(),
     Inspect(),
   ],
   ],
   test: {
   test: {
-    environment: 'happy-dom',
+    environment: 'jsdom',
     globals: true,
     globals: true,
     threads: false,
     threads: false,
+    alias: {
+      '/@': resolve(__dirname, './src'),
+    },
   },
   },
   build: {
   build: {
     lib: {
     lib: {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 208 - 271
pnpm-lock.yaml


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.