Browse Source

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

alvarosabu 2 years ago
parent
commit
c839703a96
46 changed files with 1616 additions and 395 deletions
  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]
 
 env:
@@ -7,7 +7,7 @@ env:
 
 jobs:
   lint:
-    name: Lint all projects
+    name: Run Unit Tests
     runs-on: ubuntu-20.04
     steps:
       - name: Checkout
@@ -37,5 +37,5 @@ jobs:
 
       - 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.
 
-# 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.
 
@@ -170,7 +170,7 @@ Then you can use the new component in your template. Notice that the new compone
 </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.
 

+ 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>
 ```
 
+## 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+" />
 
 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 { 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>(
   animations: T[],
   modelRef?: Scene | Ref<Object3D | undefined | null>,

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

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

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

@@ -1,11 +1,42 @@
 import { TextureEncoding } from 'three'
 
 export type EnvironmentOptions = {
+  /**
+   * If true, the environment will be set as the scene's background.
+   *
+   * @type {boolean}
+   */
   background?: boolean
+  /**
+   * The blur radius of the environment.
+   *
+   * @type {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[]
+  /**
+   * The path to the files.
+   *
+   * @type {string}
+   */
   path?: string
+  /**
+   * The preset to use. If provided, the files and path props will be ignored.
+   *
+   * @type {EnvironmentPresetsType}
+   */
   preset?: EnvironmentPresetsType
+  /**
+   * The encoding of the environment.
+   *
+   * @type {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 { 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({
   files = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'],
   blur = 0,

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

@@ -6,6 +6,9 @@ import { useCientos } from '../useCientos'
 export const FBXModel = defineComponent({
   name: 'FBXModel',
   props: {
+    /*
+     * The path to the FBX file.
+     */
     path: {
       type: String,
       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 { 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> {
   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({
   name: 'GLTFModel',
   props: {
+    /**
+     * The path to the GLTF file.
+     *
+     */
     path: String,
+    /**
+     * Whether to use Draco compression.
+     *
+     */
     draco: Boolean,
+    /**
+     * The path to the Draco decoder.
+     *
+     */
     decoderPath: String,
   },
 

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

@@ -3,7 +3,19 @@ import { useLoader } from '@tresjs/core'
 import { Object3D } from 'three'
 
 export interface GLTFLoaderOptions {
+  /**
+   * Whether to use Draco compression.
+   *
+   * @type {boolean}
+   * @memberof GLTFLoaderOptions
+   */
   draco?: boolean
+  /**
+   * The path to the Draco decoder.
+   *
+   * @type {string}
+   * @memberof GLTFLoaderOptions
+   */
   decoderPath?: string
 }
 
@@ -16,6 +28,13 @@ export interface GLTFResult {
 
 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) {
   return (loader: GLTFLoader) => {
     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(
   path: string | string[],
   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 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) {
     pane = new Pane({
       container: (document.querySelector(selector) as HTMLElement) || undefined,
@@ -20,6 +26,10 @@ export const useTweakPane = (selector = 'tres-container') => {
     })
   }
 
+  /**
+   * Disposes the TweakPane instance.
+   *
+   */
   function disposeTweakPane() {
     if (pane) {
       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> {
   const pickedProperties = {} as Pick<T, K>
   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
 }
 
+/**
+ * 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 {
   const setterName = `set${prop[0].toUpperCase()}${prop.slice(1)}`
   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",
     "@types/three": "latest",
     "@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",
     "pathe": "^1.1.0",
     "rollup-plugin-analyzer": "^4.0.0",
     "rollup-plugin-visualizer": "^5.9.0",
     "three": "latest",
-    "vite": "^4.1.2",
+    "vite": "^4.1.4",
     "vite-plugin-banner": "^0.7.0",
     "vite-plugin-dts": "2.0.0-beta.1",
     "vite-plugin-glsl": "^1.1.2",
     "vite-plugin-inspect": "^0.7.15",
     "vite-plugin-require-transform": "^1.0.9",
-    "vitest": "^0.28.4",
+    "vitest": "^0.28.5",
     "vue-demi": "^0.13.11"
   }
 }

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

@@ -2,7 +2,6 @@
 import { useTweakPane } from '@tresjs/cientos'
 import TheEnvironment from '/@/components/TheEnvironment.vue'
 // import TheEvents from '/@/components/TheEvents.vue'
-import VectorSetProps from '/@/components/VectorSetProps.vue'
 
 useTweakPane()
 </script>
@@ -10,6 +9,20 @@ useTweakPane()
 <template>
   <Suspense>
     <TheEnvironment />
-    <!-- <VectorSetProps /> -->
   </Suspense>
 </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',
   shadows: true,
   alpha: false,
-  physicallyCorrectLights: true,
+
   shadowMapType: BasicShadowMap,
   outputEncoding: sRGBEncoding,
   toneMapping: NoToneMapping,
@@ -35,6 +35,7 @@ onLoop(({ elapsed }) => {
       <TresMesh ref="sphereRef" :position="[0, 4, 0]" cast-shadow>
         <TresSphereGeometry />
         <TresMeshToonMaterial color="#FBB03B" />
+        <!-- <TresMeshToonMaterial color="#FBB03B" /> -->
       </TresMesh>
       <TresDirectionalLight :position="[0, 8, 4]" :intensity="0.7" cast-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'
 import { reactive, ref } from 'vue'
 import { OrbitControls, useTweakPane, TransformControls } from '../../../cientos/src/'
+import { useCamera } from '../core'
 import { TresCanvas } from '../core/useRenderer/component'
 /* import { OrbitControls, GLTFModel } from '@tresjs/cientos' */
 
+const { updateCamera } = useCamera()
+
+updateCamera
+
 const state = reactive({
   clearColor: '#201919',
   shadows: true,

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

@@ -1,7 +1,7 @@
 import { useTres } from '/@/core/'
 import { PerspectiveCamera, OrthographicCamera } from 'three'
 
-import { computed, ComputedRef, watch } from 'vue'
+import { toRef, watch, Ref } from 'vue'
 
 export enum CameraType {
   Perspective = 'Perspective',
@@ -10,42 +10,115 @@ export enum CameraType {
 
 export type Camera = PerspectiveCamera | OrthographicCamera
 
-export type CameraState = {
-  cameras: Array<Camera>
-}
-
 export interface PerspectiveCameraOptions {
+  /**
+   * Camera frustum vertical field of view, from bottom to top of view, in degrees.
+   *
+   * @type {number}
+   * @memberof PerspectiveCameraOptions
+   */
   fov?: number
+  /**
+   * Camera frustum near plane.
+   *
+   * @type {number}
+   * @memberof PerspectiveCameraOptions
+   */
   near?: number
+  /**
+   * Camera frustum far plane.
+   *
+   * @type {number}
+   * @memberof PerspectiveCameraOptions
+   */
   far?: number
 }
 
 export interface OrthographicCameraOptions {
+  /**
+   * Camera frustum left plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   left?: number
+  /**
+   * Camera frustum right plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   right?: number
+  /**
+   * Camera frustum top plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   top?: number
+  /**
+   * Camera frustum bottom plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   bottom?: number
+  /**
+   * Camera frustum near plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   near?: number
+  /**
+   * Camera frustum far plane.
+   *
+   * @type {number}
+   * @memberof OrthographicCameraOptions
+   */
   far?: number
 }
 
 interface UseCameraReturn {
-  activeCamera: ComputedRef<Camera>
+  activeCamera: Ref<Camera | undefined>
   createCamera: (cameraType?: CameraType, options?: PerspectiveCameraOptions | OrthographicCameraOptions) => Camera
   updateCamera: () => void
   pushCamera: (camera: Camera) => void
-}
-
-const cameraState: CameraState = {
-  cameras: [],
+  clearCameras: () => void
 }
 
 const VERTICAL_FIELD_OF_VIEW = 45
 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 {
   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(
     cameraType = CameraType.Perspective,
     options?: PerspectiveCameraOptions | OrthographicCameraOptions,
@@ -56,8 +129,8 @@ export function useCamera(): UseCameraReturn {
         far: 1000,
         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 {
       const { left, right, top, bottom, near, far } = (options as OrthographicCameraOptions) || {
         left: -100,
@@ -68,42 +141,56 @@ export function useCamera(): UseCameraReturn {
         far: 1000,
       }
       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
   }
 
-  const activeCamera = computed(() => cameraState.cameras[0])
-  setState('camera', activeCamera)
-
+  /**
+   * Update camera aspect ratio and projection matrix
+   *
+   */
   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 {
-    /*     if (camera && currentCamera) {
-      currentCamera.value = camera
-      setState('camera', currentCamera)
-    } */
-    cameraState.cameras.push(camera)
+    state.cameras?.push(camera)
     if (camera instanceof PerspectiveCamera && state.aspectRatio) {
       camera.aspect = state.aspectRatio.value
     }
     camera.updateProjectionMatrix()
+    setState('camera', camera)
+  }
+
+  /**
+   * Clear cameras array
+   *
+   */
+  function clearCameras() {
+    state.cameras = []
   }
 
   if (state.aspectRatio) {
     watch(state.aspectRatio, updateCamera)
   }
   return {
-    activeCamera,
+    activeCamera: toRef(state, 'camera'),
     createCamera,
     updateCamera,
     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
 
 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') {
   const { logError } = useLogger()
   if (!localApp && app) {
@@ -16,6 +31,19 @@ export function useCatalogue(app?: App, prefix = 'Tres') {
   }
   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) => {
     if (!objects) {
       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 './'
+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_KEYS = ['r', 'g', 'b']
 
+/**
+ * Composable responsible for creating instances out of Three.js objects.
+ *
+ * @export
+ * @param {string} prefix
+ * @return {*}
+ */
 export function useInstanceCreator(prefix: string) {
   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) {
     if (!isDefined(props)) 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) {
     if (!isDefined(props)) return
     if (!isDefined(instance)) return
@@ -53,8 +79,8 @@ export function useInstanceCreator(prefix: string) {
             transformAxis = camelKey.substring(vecProps.length)
             if (!VECTOR3_AXIS.includes(transformAxis)) {
               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
           colorKey = camelKey.substring(props.length).toLowerCase()
           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 {
     const fragmentRegex = /^Symbol\(Fragment\)$/g
     const textRegex = /^Symbol\(Text\)$/g
+    const commentRegex = /^Symbol\(Comment\)$/g
     // Check if the vnode is a Fragment
     if (fragmentRegex.test(vnode.type.toString())) {
       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
     } else {
       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 {
-    /*
-     * 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()) {
       const internal = slots.default().map((vnode: TresVNode) => createInstanceFromVNode(vnode))
       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>) {
     return (
       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']>>
   : T
 
+/**
+ * Traverse an object and return all the nodes and materials
+ *
+ * @export
+ * @param {Object3D} object
+ * @return { [key: string]: any }
+ */
 export function trasverseObjects(object: Object3D) {
   const data: { [key: string]: any } = { nodes: {}, materials: {} }
   if (object) {
@@ -34,6 +41,29 @@ export function trasverseObjects(object: Object3D) {
 
 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[]>(
   Loader: T,
   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 { onUnmounted, provide, ref, shallowRef } from 'vue'
+import { onUnmounted, provide, Ref, ref, ShallowRef, shallowRef } from 'vue'
 import { useTres } from '/@/core'
 
 const raycaster = shallowRef(new Raycaster())
 const pointer = ref(new Vector2())
 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()
   setState('raycaster', raycaster.value)
   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.
  */
 
+const { logError, logWarning } = useLogger()
+
 export const TresCanvas = defineComponent({
   name: 'TresCanvas',
   props: {
     shadows: Boolean,
     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>,
     toneMapping: Number as PropType<ToneMapping>,
     toneMappingExposure: Number,
@@ -26,8 +38,6 @@ export const TresCanvas = defineComponent({
     preset: String as PropType<RendererPresetsType>,
   },
   setup(props, { slots, attrs }) {
-    const { logError } = useLogger()
-
     const canvas = ref<HTMLCanvasElement>()
     const container = ref<HTMLElement>()
 
@@ -54,22 +64,37 @@ export const TresCanvas = defineComponent({
             style: {
               position: 'relative',
               width: '100%',
-              height: '100vh',
+              height: '100%',
+              overflow: 'hidden',
+              pointerEvents: 'auto',
+              touchAction: 'none',
               ...(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 = {
   realistic: {
-    physicallyCorrectLights: true,
     outputEncoding: sRGBEncoding,
     toneMapping: ACESFilmicToneMapping,
     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).
    *
    * @default false
+   * @deprecated Use {@link WebGLRenderer.useLegacyLights useLegacyLights} instead.
    */
   physicallyCorrectLights?: MaybeComputedRef<boolean>
-
+  /**
+   * Whether to use legacy lighting mode.
+   *
+   * @type {MaybeComputedRef<boolean>}
+   * @memberof UseRendererOptions
+   */
+  useLegacyLights?: MaybeComputedRef<boolean>
   /**
    * Defines the output encoding of the renderer.
    * Can be LinearEncoding, sRGBEncoding
@@ -113,7 +120,8 @@ const isReady = ref(false)
  * Reactive Three.js WebGLRenderer instance
  *
  * @param canvas
- * @param options
+ * @param container
+ * @param {UseRendererOptions} [options]
  */
 export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef, options: UseRendererOptions) {
   // Defaults
@@ -129,6 +137,7 @@ export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef,
     shadows = false,
     shadowMapType = PCFShadowMap,
     physicallyCorrectLights = false,
+    useLegacyLights = false,
     outputEncoding = LinearEncoding,
     toneMapping = NoToneMapping,
     toneMappingExposure = 1,
@@ -177,7 +186,8 @@ export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef,
     renderer.value.outputEncoding = (resolveUnref(outputEncoding) as TextureEncoding) || LinearEncoding
     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 = () => {

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

@@ -19,9 +19,10 @@ export const Scene = defineComponent({
     setState('scene', scene.value)
 
     onLoop(() => {
+      if (!activeCamera.value) return
       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)
       }
     })

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

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

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

@@ -1,10 +1,10 @@
 import { Scene } from 'three'
 import { describe, test, expect } from 'vitest'
-import { useScene } from './useScene'
+import { useScene } from './'
 
 describe('useScene()', () => {
-  test('should init acene', () => {
+  test('should init a scene', () => {
     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'
 
 export interface PBRMaterialOptions {
+  /**
+   * List of texture maps to load.
+   *
+   * @type {string[]}
+   * @memberof PBRMaterialOptions
+   */
   maps: string[]
+  /**
+   * Path to the texture maps.
+   *
+   * @type {('png' | 'jpg')}
+   * @memberof PBRMaterialOptions
+   */
   ext: 'png' | 'jpg'
 }
 
@@ -10,12 +22,44 @@ export interface PBRTextureMaps {
   [key: string]: Texture | null
 }
 // 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(
   paths: Array<string> | { [key: string]: string },
 ): Promise<Texture | Array<Texture> | PBRTextureMaps> {
   const loadingManager = new LoadingManager()
   const textureLoader = new TextureLoader(loadingManager)
 
+  /**
+   * Load a texture.
+   *
+   * @param {string} url
+   * @return {*}  {Promise<Texture>}
+   */
   const loadTexture = (url: string): Promise<Texture> => {
     return new Promise((resolve, reject) => {
       textureLoader.load(
@@ -47,26 +91,4 @@ export async function useTexture(
       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 { ComputedRef, shallowReactive, toRefs } from 'vue'
+import { computed, ComputedRef, shallowReactive, toRefs } from 'vue'
 import { Camera } from '/@/core'
 
 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>
+  /**
+   * 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
+  /**
+   * 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
+  /**
+   * The raycaster.
+   *
+   * @see https://threejs.org/docs/index.html?q=raycas#api/en/core/Raycaster
+   *
+   * @type {Raycaster}
+   * @memberof TresState
+   */
   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
+  /**
+   * The current mouse position.
+   *
+   * @type {Vector2}
+   * @memberof TresState
+   */
   pointer?: Vector2
+  /**
+   * The current instance of the component.
+   *
+   * @type {*}
+   * @memberof TresState
+   */
   currentInstance?: any
+  /**
+   *  The current active scene control
+   *
+   * @type {((EventDispatcher & { enabled: boolean }) | null)}
+   * @memberof TresState
+   */
   controls?: (EventDispatcher & { enabled: boolean }) | null
   [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() {
+  /**
+   * Get a state value.
+   *
+   *
+   * @param {string} key
+   * @return {*}
+   */
   function getState(key: string) {
     return state[key]
   }
 
+  /**
+   * Set a state value.
+   *
+   * @param {string} key
+   * @param {*} value
+   */
   function setState(key: string, value: any) {
     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,
     "esModuleInterop": true,
     "lib": ["esnext", "dom"],
-    "types": ["vite/client", "node"],
+    "types": ["vite/client", "node", "vitest/globals"],
     "incremental": false,
     "skipLibCheck": true,
     "noUnusedLocals": true,

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

@@ -1,9 +1,14 @@
 {
   "compilerOptions": {
+    "baseUrl": ".",
     "composite": true,
     "module": "ESNext",
     "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(),
   ],
   test: {
-    environment: 'happy-dom',
+    environment: 'jsdom',
     globals: true,
     threads: false,
+    alias: {
+      '/@': resolve(__dirname, './src'),
+    },
   },
   build: {
     lib: {

File diff suppressed because it is too large
+ 208 - 271
pnpm-lock.yaml


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