Browse Source

Merge branch 'main' into docs/7-document-args-and-properties

Alvaro 2 years ago
parent
commit
f3c80b0c2e

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

@@ -12,6 +12,7 @@ watch(
   [camera, renderer],
   () => {
     if (camera?.value && renderer?.value) {
+      if (controls) controls.reset()
       controls = new OrbitControlsImp(camera.value, unref(renderer).domElement)
       controls.enableDamping = true
 

+ 9 - 9
packages/tres/src/App.vue

@@ -1,25 +1,25 @@
 <script setup lang="ts">
-import { unref } from 'vue'
+import { useCatalogue } from '/@/core'
 import { Color } from 'three'
-import { useTweakPane } from '@tresjs/cientos'
-import { useTres } from './core'
+import { useTweakPane, OrbitControls } from '../../cientos/src'
 import TestSphere from '/@/components/TestSphere.vue'
+import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry'
 
 const colorTeal = new Color('teal')
 useTweakPane()
 
-const { state } = useTres()
+const { extend } = useCatalogue()
 
-const meshPosition = [0, 1, 0]
+extend({ TextGeometry: TextGeometry })
 </script>
 <template>
   <Suspense>
     <TresCanvas shadows alpha power-preference="high-performance" preserve-drawing-buffer physically-correct-lights>
-      <TresPerspectiveCamera :position="[0, 5, 5]" :fov="45" :near="0.1" :far="1000" />
+      <TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
       <TresScene>
-        <TresOrbitControls v-if="state.renderer" :args="[unref(state), state.renderer?.domElement]" />
-        <TresMesh :position="meshPosition" :scale="1">
-          <TresSphereGeometry />
+        <OrbitControls />
+        <TresMesh>
+          <TresTextGeometry :args="['Awiwi']" :position="[-8, 3, -3]" :scale="0.5" />
           <TresMeshToonMaterial :color="colorTeal" />
         </TresMesh>
         <TestSphere />

+ 19 - 6
packages/tres/src/core/useCatalogue/index.ts

@@ -1,14 +1,27 @@
+import { useInstanceCreator } from '/@/core'
+import { App, ref, Component, Ref } from 'vue'
 import * as THREE from 'three'
+import { TresCatalogue } from '/@/types'
 
-let catalogue: {
-  [key: string]: any
-} = { ...THREE }
+const catalogue: Ref<TresCatalogue> = ref({ ...THREE })
 
-delete catalogue.Scene
+delete catalogue.value.Scene
+
+let localApp: App
+
+export function useCatalogue(app?: App, prefix = 'Tres') {
+  if (!localApp && app) {
+    localApp = app
+  }
+  const { createComponentInstances } = useInstanceCreator(prefix)
 
-export function useCatalogue() {
   const extend = (objects: any) => {
-    catalogue = Object.assign(catalogue, objects)
+    catalogue.value = Object.assign(catalogue.value, objects)
+    const components = createComponentInstances(ref(objects))
+
+    components.forEach(([key, cmp]) => {
+      localApp.component(key as string, cmp as Component)
+    })
   }
 
   return {

+ 120 - 81
packages/tres/src/core/useInstanceCreator/index.ts

@@ -1,108 +1,145 @@
+import { OrthographicCamera } from 'three'
 /* eslint-disable new-cap */
 /* eslint-disable @typescript-eslint/no-empty-function */
 import { PerspectiveCamera } from 'three'
-import { defineComponent } from 'vue'
-import { isArray, isDefined } from '@alvarosabu/utils'
+import { defineComponent, Ref } from 'vue'
+import { isArray, isDefined, isFunction } from '@alvarosabu/utils'
 import { normalizeVectorFlexibleParam } from '/@/utils/normalize'
 import { useCamera, useScene } from '/@/core/'
 import { useLogger } from '/@/composables'
-import { TresInstance, TresVNodeType } from '../../types'
+import { TresCatalogue, TresInstance, TresVNode, TresVNodeType } from '../../types'
 
 const VECTOR3_PROPS = ['rotation', 'scale', 'position']
 
+type TresAttributes = Record<string, any> & { args?: number[] }
 export function useInstanceCreator(prefix: string) {
   const { logMessage, logError } = useLogger()
 
-  function createComponentInstances(newObjects: Record<string, any>) {
-    return Object.entries(newObjects)
+  function processProps(props: Record<string, any>, instance: TresInstance) {
+    if (!isDefined(props)) return
+    if (!isDefined(instance)) return
+
+    Object.entries(props).forEach(([key, value]) => {
+      const camelKey = key.replace(/(-\w)/g, m => m[1].toUpperCase())
+
+      // Ignore property args which is use for initial instance construction
+      if (camelKey === 'args' || value === undefined) return
+
+      // Normalize vector3 props
+      if (VECTOR3_PROPS.includes(camelKey) && value) {
+        value = normalizeVectorFlexibleParam(value)
+      }
+
+      if (props.ref) {
+        props.ref = instance
+      }
+
+      try {
+        // Check if the property has a "set" method
+        if (instance[camelKey] && isDefined(instance[camelKey].set)) {
+          // Call the "set" method with the value, spread if it's an array
+          instance[camelKey].set(...(isArray(value) ? value : [value]))
+        } else {
+          // Convert empty strings to `true`
+          if (value === '') {
+            value = true
+          }
+
+          // Check if the property is a function
+          if (isFunction(instance[camelKey])) {
+            // Call the function with the value, spread if it's an array
+            instance[camelKey](...(isArray(value) ? value : [value]))
+            return
+          }
+
+          // Set the property to the value
+          instance[camelKey] = value
+        }
+      } catch (error: unknown) {
+        logError(`There was an error setting ${camelKey} property`, error as Error)
+      }
+    })
+  }
+
+  function createInstanceFromVNode(vnode: TresVNode, catalogue: Ref<TresCatalogue>): TresInstance {
+    const vNodeType = ((vnode.type as TresVNodeType).name as string).replace(prefix, '')
+
+    // check if args prop is defined on the vnode
+    let internalInstance
+    if (vnode?.props?.args) {
+      // if args prop is defined, create new instance of catalogue[vNodeType] with the provided arguments
+      internalInstance = new catalogue.value[vNodeType](...vnode.props.args)
+    } else {
+      // if args prop is not defined, create a new instance of catalogue[vNodeType] without arguments
+      internalInstance = new catalogue.value[vNodeType]()
+    }
+
+    // check if props is defined on the vnode
+    if (vnode?.props) {
+      // if props is defined, process the props and pass the internalInstance to update its properties
+      processProps(vnode.props, internalInstance)
+    }
+
+    return internalInstance
+  }
+
+  function createInstance(
+    catalogue: Ref<TresCatalogue>,
+    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, catalogue))
+      return new threeObj(...internal)
+    } else {
+      // Creates a new THREE instance, if args is present, spread it on the constructor
+      return attrs.args ? new threeObj(...attrs.args) : new threeObj()
+    }
+  }
+
+  function createComponentInstances(catalogue: Ref<TresCatalogue>) {
+    return Object.entries(catalogue.value)
       .filter(([_key, value]) => (value as { prototype: any })?.prototype?.constructor?.toString().includes('class'))
-      .map(([key, value]) => {
+      .map(([key, threeObj]) => {
         const name = `${prefix}${key}`
         const cmp = defineComponent({
           name,
           setup(props, { slots, attrs, ...ctx }) {
-            logMessage(name, {
-              props,
-              slots,
-              attrs,
-              ctx,
-            })
-            let instance: TresInstance
             const { scene } = useScene()
             const { pushCamera } = useCamera()
 
-            function createInstance() {
-              if (slots?.default && slots?.default()) {
-                const internal = slots.default().map(vnode => {
-                  let internalInstance
-                  const vNodeType = ((vnode.type as TresVNodeType).name as string).replace(prefix, '')
-                  if (vnode?.props?.args) {
-                    internalInstance = new newObjects[vNodeType](...vnode.props.args)
-                  } else {
-                    internalInstance = new newObjects[vNodeType]()
-                  }
-                  if (vnode?.props) {
-                    processProps(vnode.props, internalInstance)
-                  }
-
-                  return internalInstance
-                })
-
-                instance = new value(...internal)
-              } else if ((attrs as { args: [] }).args) {
-                instance = new value(...(attrs as { args: [] }).args)
-              } else {
-                instance = new value()
-              }
-
-              processProps(attrs, instance)
-              if (name.includes('Camera')) {
-                pushCamera(instance as unknown as PerspectiveCamera)
-              }
-              if (instance.isObject3D) {
-                scene?.value.add(instance)
-              }
-
-              logMessage('Instance added', scene)
-
-              ctx.expose(instance)
+            const instance = createInstance(catalogue, threeObj, attrs, slots)
+            processProps(attrs, instance)
+            // If the instance is a camera, push it to the camera stack
+            if (instance instanceof PerspectiveCamera || instance instanceof OrthographicCamera) {
+              pushCamera(instance)
             }
 
-            function processProps(attrs: any, instance: TresInstance) {
-              Object.entries(attrs).forEach(([key, value]) => {
-                const camel = key.replace(/(-\w)/g, m => m[1].toUpperCase())
-                if (camel === 'args' || value === undefined) {
-                  return
-                }
-                // Check if property is a Vector3
-                if (VECTOR3_PROPS.includes(key) && value) {
-                  value = normalizeVectorFlexibleParam(value)
-                }
-                try {
-                  if (instance[camel] && isDefined(instance[camel].set)) {
-                    if (isArray(value)) {
-                      instance[camel].set(...(value as Array<number>))
-                    } else {
-                      instance[camel].set(value)
-                    }
-                  } else {
-                    if (value === '') {
-                      value = true
-                    }
-                    instance[camel] = value
-                  }
-                } catch (error: unknown) {
-                  logError(`There was an error setting ${camel} property`, error as Error)
-                }
-              })
-
-              if (attrs.ref) {
-                attrs.ref = instance
-              }
+            // If the instance is a valid Object3D, add it to the scene
+            if (instance.isObject3D) {
+              scene?.value.add(instance)
             }
 
-            createInstance()
-
+            ctx.expose(instance)
+            logMessage(name, {
+              props,
+              slots,
+              attrs,
+              ctx,
+              scene,
+            })
             return () => {}
           },
         })
@@ -113,5 +150,7 @@ export function useInstanceCreator(prefix: string) {
 
   return {
     createComponentInstances,
+    processProps,
+    createInstanceFromVNode,
   }
 }

+ 10 - 3
packages/tres/src/index.ts

@@ -1,4 +1,4 @@
-import { App, Component } from 'vue'
+import { App, Component, watchEffect } from 'vue'
 import { TresCanvas } from '/@/core/useRenderer/component'
 import { Scene } from '/@/core/useScene/component'
 import { useCatalogue, useInstanceCreator } from '/@/core'
@@ -19,12 +19,19 @@ const plugin: TresPlugin = {
     const prefix = options?.prefix || 'Tres'
     app.component(`${prefix}Canvas`, TresCanvas)
     app.component(`${prefix}Scene`, Scene)
-    const { catalogue } = useCatalogue()
+    const { catalogue } = useCatalogue(app, prefix)
     const { createComponentInstances } = useInstanceCreator(prefix)
-    const components = createComponentInstances(options?.extends ? { ...catalogue, ...options.extends } : catalogue)
+    const components = createComponentInstances(catalogue)
+    /*  const components = createComponentInstances(
+      options?.extends ? { ...catalogue, ...options.extends } : catalogue,
+    ) */
     components.forEach(([key, cmp]) => {
       app.component(key as string, cmp as Component)
     })
+
+    watchEffect(() => {
+      console.log({ catalogue })
+    })
   },
 }
 

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

@@ -3,6 +3,7 @@ import { VNode, VNodeTypes } from 'vue'
 
 export type TresInstance = Object3D<Event> & { [key: string]: any }
 
+export type TresCatalogue = Record<string, any>
 export type TresVNodeType = VNodeTypes & {
   name?: string | Array<string>
   __name?: string