Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: extend catalog in cientos #26

Merged
merged 9 commits into from
Dec 8, 2022
56 changes: 30 additions & 26 deletions packages/cientos/src/core/OrbitControls.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,36 @@
<script setup lang="ts">
import { useRenderLoop } from '@tresjs/core'
import { Camera, WebGLRenderer } from 'three'
import { OrbitControls as OrbitControlsImp } from 'three-stdlib'
import { inject, type Ref, unref, watch } from 'vue'
<script lang="ts" setup>
import { Camera, Vector3, WebGLRenderer } from 'three'
import { OrbitControls } from 'three-stdlib'
import { inject, ref, type Ref } from 'vue'

let controls: OrbitControlsImp
import { useCientos } from './useCientos'

const camera = inject<Ref<Camera>>('camera')
const renderer = inject<Ref<WebGLRenderer>>('renderer')
watch(
[camera, renderer],
() => {
if (camera?.value && renderer?.value) {
if (controls) controls.reset()
controls = new OrbitControlsImp(camera.value, unref(renderer).domElement)
controls.enableDamping = true

const { onLoop } = useRenderLoop()

onLoop(() => {
if (controls) {
controls.update()
}
})
}
},
const props = withDefaults(
defineProps<{
makeDefault?: boolean
camera?: Camera
domElement?: HTMLElement
target?: Ref<Vector3>
enableDamping?: boolean
}>(),
{
deep: true,
makeDefault: false,
},
)

const controls = ref(null)
const camera = inject<Ref<Camera>>('camera')
const renderer = inject<Ref<WebGLRenderer>>('renderer')

const { extend } = useCientos()
extend({ OrbitControls })
</script>

<template>
<TresOrbitControls
v-if="camera && renderer"
ref="controls"
:args="[camera, renderer?.domElement]"
:enabling-dampling="enableDamping"
/>
</template>
12 changes: 12 additions & 0 deletions packages/cientos/src/core/useCientos.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { inject } from 'vue'

export function useCientos() {
const extend =
inject<(objects: any) => void>('extend') ||
(() => {
console.warn('No extend function provided')
})
return {
extend,
}
}
12 changes: 12 additions & 0 deletions packages/cientos/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { App } from 'vue'
/// <reference types="vite/client" />

declare module '*.vue' {
Expand All @@ -6,3 +7,14 @@ declare module '*.vue' {
const component: DefineComponent<{}, {}, any>
export default component
}

declare global {
// Define the window interface, with type annotations for the properties and methods of the window object
interface Window {
// Define the location property, with a type of Location
__TRES__: {
app: App
version: string
}
}
}
4 changes: 2 additions & 2 deletions packages/tres/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
/* import { Color } from 'three' */
import { useTweakPane, OrbitControls } from '../../cientos/src'
import { useTweakPane, OrbitControls, TransformControls } from '../../cientos/src'
/* import TestSphere from '/@/components/TestSphere.vue' */
import Text3D from '/@/components/Text3D.vue'
/* import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
Expand All @@ -25,8 +25,8 @@ useTweakPane()
physically-correct-lights
>
<TresPerspectiveCamera :position="[5, 5, 5]" :fov="45" :near="0.1" :far="1000" :look-at="[-8, 3, -3]" />
<OrbitControls />
<TresScene>
<OrbitControls />
<TresAmbientLight :intensity="0.5" />
<!-- <TresOrbitControls v-if="state.renderer" :args="[state.camera, state.renderer?.domElement]" /> -->
<Text3D />
Expand Down
23 changes: 17 additions & 6 deletions packages/tres/src/core/useCatalogue/index.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,38 @@
import { useInstanceCreator } from '/@/core'
import { App, ref, Component, Ref } from 'vue'
import * as THREE from 'three'
import { useInstanceCreator } from '/@/core'
import { useLogger } from '/@/composables'
import { TresCatalogue } from '/@/types'

const catalogue: Ref<TresCatalogue> = ref({ ...THREE })
const catalogue: Ref<TresCatalogue> = ref({ ...THREE, uuid: THREE.MathUtils.generateUUID() })

delete catalogue.value.Scene

let localApp: App

export function useCatalogue(app?: App, prefix = 'Tres') {
const { logMessage, logError } = useLogger()
if (!localApp && app) {
localApp = app
}
const { createComponentInstances } = useInstanceCreator(prefix)

const extend = (objects: any) => {
if (!objects) {
logError('No objects provided to extend catalogue')
return
}
catalogue.value = Object.assign(catalogue.value, objects)
const components = createComponentInstances(ref(objects))
logMessage('Adding objects to catalogue', { objects, catalogue: catalogue.value.uuid })

components.forEach(([key, cmp]) => {
localApp.component(key as string, cmp as Component)
})
if (localApp) {
components.forEach(([key, cmp]) => {
// If the component is not already registered, register it
if (!localApp._context.components[key as string]) {
localApp.component(key as string, cmp as Component)
}
})
}
}

return {
Expand Down
69 changes: 40 additions & 29 deletions packages/tres/src/core/useInstanceCreator/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint-disable new-cap */
/* eslint-disable @typescript-eslint/no-empty-function */
import { OrthographicCamera, PerspectiveCamera } from 'three'
import { defineComponent, Ref } from 'vue'
import { OrthographicCamera, PerspectiveCamera, Scene } from 'three'
import { defineComponent, inject, Ref } from 'vue'
import { isArray, isDefined, isFunction } from '@alvarosabu/utils'
import { normalizeVectorFlexibleParam } from '/@/utils/normalize'
import { useCamera, useScene } from '/@/core/'
Expand Down Expand Up @@ -59,34 +59,41 @@ export function useInstanceCreator(prefix: string) {
})
}

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)
function createInstanceFromVNode(vnode: TresVNode): TresInstance | TresInstance[] {
const regex = /^Symbol\(Fragment\)$/g
// Check if the vnode is a Fragment
if (regex.test(vnode.type.toString())) {
return vnode.children.map(child => createInstanceFromVNode(child as TresVNode)) as TresInstance[]
} else {
// if args prop is not defined, create a new instance of catalogue[vNodeType] without arguments
internalInstance = new catalogue.value[vNodeType]()
}
const vNodeType = ((vnode.type as TresVNodeType).name as string).replace(prefix, '')
const catalogue = inject<Ref<TresCatalogue>>('catalogue')
// check if args prop is defined on the vnode
let internalInstance
if (catalogue) {
if (vnode?.props?.args) {
// if args prop is defined, create new instance of catalogue[vNodeType] with the provided arguments
if (catalogue?.value[vNodeType]) {
internalInstance = new catalogue.value[vNodeType](...vnode.props.args)
} else {
logError(`There is no ${vNodeType} in the catalogue`, catalogue?.value.uuid)
}
} 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)
}
// 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
return internalInstance
}
}

function createInstance(
catalogue: Ref<TresCatalogue>,
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
Expand All @@ -99,8 +106,8 @@ export function useInstanceCreator(prefix: string) {
* 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)
const internal = slots.default().map((vnode: TresVNode) => createInstanceFromVNode(vnode))
return new threeObj(...internal.flat())
} else {
// Creates a new THREE instance, if args is present, spread it on the constructor
return attrs.args ? new threeObj(...attrs.args) : new threeObj()
Expand All @@ -115,10 +122,12 @@ export function useInstanceCreator(prefix: string) {
const cmp = defineComponent({
name,
setup(props, { slots, attrs, ...ctx }) {
const { scene } = useScene()
const { scene: fallback } = useScene()
const scene = inject<Ref<Scene>>('local-scene') || fallback
const catalogue = inject<Ref<TresCatalogue>>('catalogue')
const { pushCamera } = useCamera()

const instance = createInstance(catalogue, threeObj, attrs, slots)
const instance = createInstance(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) {
Expand All @@ -132,6 +141,8 @@ export function useInstanceCreator(prefix: string) {

ctx.expose(instance)
logMessage(name, {
sceneuuid: scene?.value.uuid,
catalogue: catalogue?.value.uuid,
props,
slots,
attrs,
Expand Down
15 changes: 9 additions & 6 deletions packages/tres/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { App, Component, watchEffect } from 'vue'
import { App, Component } from 'vue'
import { TresCanvas } from '/@/core/useRenderer/component'
import { Scene } from '/@/core/useScene/component'
import { useCatalogue, useInstanceCreator } from '/@/core'
export * from '/@/core'
export * from './keys'

import { version } from '../package.json'
export interface TresOptions {
prefix?: string
extends?: Record<string, unknown>
Expand All @@ -19,7 +19,9 @@ const plugin: TresPlugin = {
const prefix = options?.prefix || 'Tres'
app.component(`${prefix}Canvas`, TresCanvas)
app.component(`${prefix}Scene`, Scene)
const { catalogue } = useCatalogue(app, prefix)
const { catalogue, extend } = useCatalogue(app, prefix)
app.provide('catalogue', catalogue)
app.provide('extend', extend)
const { createComponentInstances } = useInstanceCreator(prefix)
const components = createComponentInstances(catalogue)
/* const components = createComponentInstances(
Expand All @@ -29,9 +31,10 @@ const plugin: TresPlugin = {
app.component(key as string, cmp as Component)
})

watchEffect(() => {
console.log({ catalogue })
})
window.__TRES__ = {
app,
version,
}
},
}

Expand Down
7 changes: 1 addition & 6 deletions packages/tres/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
import { createApp } from 'vue'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import App from './App.vue'
import plugin from '.'
import './style.css'

export const app = createApp(App)

app.use(plugin, {
extends: {
OrbitControls,
},
})
app.use(plugin)
app.mount('#app')
11 changes: 11 additions & 0 deletions packages/tres/src/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,14 @@ export type TresVNodeType = VNodeTypes & {
}
export type TresVNode = VNode & { children?: Array<VNode>; type: TresVNodeType }
export type TresAttributes = Record<string, any> & { args?: number[] }

declare global {
// Define the window interface, with type annotations for the properties and methods of the window object
interface Window {
// Define the location property, with a type of Location
__TRES__: {
app: App
version: string
}
}
}