Skip to content

Commit

Permalink
Merge pull request #108 from Tresjs/feature/37-better-state-managemen…
Browse files Browse the repository at this point in the history
…t-usetres

feat(core): better state management useTres
  • Loading branch information
alvarosabu authored Feb 16, 2023
2 parents 7473e79 + 2064344 commit c206b6b
Show file tree
Hide file tree
Showing 15 changed files with 79 additions and 93 deletions.
23 changes: 2 additions & 21 deletions docs/api/composables.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,32 +170,13 @@ Then you can use the new component in your template. Notice that the new compone
</template>
```

# useTres <Badge type="warning" text="experimental" />
# 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. It is still experimental and it is not recommended to use it in production because is highly like subject to change.
This composable aims to provide access to the state model which contains the default renderer, camera, scene, and other useful properties.

```ts
const { state } = useTres()

console.log(state.camera) // THREE.PerspectiveCamera
console.log(state.renderer) // THREE.WebGLRenderer
```

Until this composable is stable, it is recommended to use the `provide/inject` API to acces the elements you need. These are the available keys:

- `camera`: it returns the current active camera
- `renderer`: it returns the current active renderer
- `local-scene`: it returns the current active scene
- `catalogue`: it returns the current catalogue of components
- `extend` : it returns the `extend` function from the `useCatalogue` composable. Specially needed if you are a plugin author.
- `aspect-ratio`: it returns the current aspect ratio of the canvas

```ts
import { provide, inject } from 'vue'

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

console.log(camera.value) // THREE.PerspectiveCamera
console.log(renderer.value) // THREE.WebGLRenderer
```
14 changes: 5 additions & 9 deletions packages/cientos/src/core/OrbitControls.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
<script lang="ts" setup>
import { useTres } from '@tresjs/core'
import { Camera, Vector3, WebGLRenderer } from 'three'
import { Camera, Vector3 } from 'three'
import { OrbitControls } from 'three-stdlib'
import { inject, ref, watch, type Ref } from 'vue'
import { ref, watch, watchEffect, unref, type Ref } from 'vue'
import { useCientos } from './useCientos'
Expand All @@ -19,13 +18,10 @@ const props = withDefaults(
},
)
const { setState } = useTres()
const { state, setState, extend } = useCientos()
const controls = ref(null)
const camera = inject<Ref<Camera>>('camera')
const renderer = inject<Ref<WebGLRenderer>>('renderer')
const { extend } = useCientos()
extend({ OrbitControls })
watch(controls, value => {
Expand All @@ -39,9 +35,9 @@ watch(controls, value => {

<template>
<TresOrbitControls
v-if="camera && renderer"
v-if="state.camera && state.renderer"
ref="controls"
:args="[camera, renderer?.domElement]"
:args="[unref(state.camera), state.renderer?.domElement]"
:enabling-dampling="enableDamping"
/>
</template>
13 changes: 5 additions & 8 deletions packages/cientos/src/core/TransformControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Camera, Object3D, Scene, WebGLRenderer, type Event } from 'three'
import { TransformControls as TransformControlsImp } from 'three-stdlib'
import { inject, computed, type Ref, unref, watch, shallowRef, ShallowRef, onUnmounted } from 'vue'
import { pick, hasSetter } from '../utils'
import { useCientos } from './useCientos'
const props = withDefaults(
defineProps<{
Expand All @@ -29,9 +30,7 @@ const emit = defineEmits(['dragging', 'change', 'mouseDown', 'mouseUp', 'objectC
let controls: ShallowRef<TransformControlsImp | undefined> = shallowRef()
const camera = inject<Ref<Camera>>('camera')
const renderer = inject<Ref<WebGLRenderer>>('renderer')
const scene = inject<Ref<Scene>>('local-scene')
const { state } = useCientos()
const transformProps = computed(() =>
pick(props, [
Expand All @@ -48,8 +47,6 @@ const transformProps = computed(() =>
'showZ',
]),
)
const { state } = useTres()
const onChange = () => emit('change', controls.value)
const onMouseDown = () => emit('mouseDown', controls.value)
const onMouseUp = () => emit('mouseUp', controls.value)
Expand All @@ -71,11 +68,11 @@ function addEventListeners(controls: TransformControlsImp) {
watch(
() => props.object,
() => {
if (camera?.value && renderer?.value && scene?.value && props.object) {
controls.value = new TransformControlsImp(camera.value, unref(renderer).domElement)
if (state.camera?.value && state.renderer && state.scene && props.object) {
controls.value = new TransformControlsImp(state.camera.value, unref(state.renderer).domElement)
controls.value.attach(unref(props.object))
scene.value.add(unref(controls) as TransformControlsImp)
state.scene.add(unref(controls) as TransformControlsImp)
addEventListeners(unref(controls) as TransformControlsImp)
}
Expand Down
5 changes: 5 additions & 0 deletions packages/cientos/src/core/useCientos.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useTres } from '@tresjs/core'
import { inject } from 'vue'

export function useCientos() {
Expand All @@ -6,7 +7,11 @@ export function useCientos() {
(() => {
console.warn('No extend function provided')
})

const { state, setState } = inject('useTres', useTres())
return {
state,
setState,
extend,
}
}
7 changes: 4 additions & 3 deletions packages/cientos/src/core/useFBX/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Object3D, Scene } from 'three'
import { defineComponent, inject, Ref } from 'vue'
import { useFBX } from '.'
import { useCientos } from '../useCientos'

export const FBXModel = defineComponent({
name: 'FBXModel',
Expand All @@ -11,7 +12,7 @@ export const FBXModel = defineComponent({
},
},
async setup(props, { expose }) {
const scene = inject<Ref<Scene>>('local-scene')
const { state } = useCientos()
let model: Object3D | null = null

function getModel() {
Expand All @@ -21,8 +22,8 @@ export const FBXModel = defineComponent({

model = await useFBX(props.path as string)

if (scene?.value && model.isObject3D) {
scene.value.add(model)
if (state.scene && model.isObject3D) {
state.scene.add(model)
}
return () => {
model
Expand Down
10 changes: 5 additions & 5 deletions packages/cientos/src/core/useGLTF/component.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Scene } from 'three'
import { defineComponent, inject, Ref } from 'vue'
import { defineComponent } from 'vue'
import { useGLTF } from '.'
import { useCientos } from '../useCientos'

export const GLTFModel = defineComponent({
name: 'GLTFModel',
Expand All @@ -11,15 +11,15 @@ export const GLTFModel = defineComponent({
},

async setup(props, { expose }) {
const scene = inject<Ref<Scene>>('local-scene')
const { state } = useCientos()

function getModel() {
return model
}
expose({ getModel })
const { scene: model } = await useGLTF(props.path as string, { draco: props.draco, decoderPath: props.decoderPath })
if (scene?.value) {
scene.value.add(model)
if (state.scene) {
state.scene.add(model)
}
return () => {
model
Expand Down
1 change: 1 addition & 0 deletions packages/tres/src/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './useScene/'
export * from './useLoader'
export * from './useTexture'
export * from './useTres'
export * from './useRaycaster'
40 changes: 19 additions & 21 deletions packages/tres/src/core/useCamera/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useTres } from '/@/core/'
import { PerspectiveCamera, OrthographicCamera } from 'three'

import { computed, ComputedRef, watch, inject, Ref } from 'vue'
import { computed, ComputedRef, watch } from 'vue'

export enum CameraType {
Perspective = 'Perspective',
Expand Down Expand Up @@ -36,17 +36,15 @@ interface UseCameraReturn {
pushCamera: (camera: Camera) => void
}

const state: CameraState = {
const cameraState: CameraState = {
cameras: [],
}

const VERTICAL_FIELD_OF_VIEW = 45
let camera: Camera

export function useCamera(): UseCameraReturn {
const aspectRatio = inject<ComputedRef<number>>('aspect-ratio')

const { setState } = useTres()
const { state, setState } = useTres()

function createCamera(
cameraType = CameraType.Perspective,
Expand All @@ -58,8 +56,8 @@ export function useCamera(): UseCameraReturn {
far: 1000,
fov: VERTICAL_FIELD_OF_VIEW,
}
camera = new PerspectiveCamera(fov, aspectRatio?.value || 1, near, far)
state.cameras.push(camera as PerspectiveCamera)
camera = new PerspectiveCamera(fov, state.aspectRatio?.value || 1, near, far)
cameraState.cameras.push(camera as PerspectiveCamera)
} else {
const { left, right, top, bottom, near, far } = (options as OrthographicCameraOptions) || {
left: -100,
Expand All @@ -70,37 +68,37 @@ export function useCamera(): UseCameraReturn {
far: 1000,
}
camera = new OrthographicCamera(left, right, top, bottom, near, far)
state.cameras.push(camera as OrthographicCamera)
cameraState.cameras.push(camera as OrthographicCamera)
}

state.cameras.push(camera)
cameraState.cameras.push(camera)
return camera
}

const activeCamera = computed(() => state.cameras[0])
const activeCamera = computed(() => cameraState.cameras[0])
setState('camera', activeCamera)

function updateCamera() {
if (activeCamera.value instanceof PerspectiveCamera && aspectRatio) {
activeCamera.value.aspect = aspectRatio.value
if (activeCamera.value instanceof PerspectiveCamera && state.aspectRatio) {
activeCamera.value.aspect = state.aspectRatio.value
}
activeCamera.value.updateProjectionMatrix()
}

function pushCamera(camera: Camera): void {
const currentCamera = inject<Ref<Camera>>('camera')
if (camera && currentCamera) {
/* if (camera && currentCamera) {
currentCamera.value = camera
setState('camera', currentCamera.value)
}
state.cameras.push(camera)
if (camera instanceof PerspectiveCamera && aspectRatio) {
camera.aspect = aspectRatio.value
setState('camera', currentCamera)
} */
cameraState.cameras.push(camera)
if (camera instanceof PerspectiveCamera && state.aspectRatio) {
camera.aspect = state.aspectRatio.value
}
camera.updateProjectionMatrix()
}

if (aspectRatio) {
watch(aspectRatio, updateCamera)
if (state.aspectRatio) {
watch(state.aspectRatio, updateCamera)
}
return {
activeCamera,
Expand Down
25 changes: 12 additions & 13 deletions packages/tres/src/core/useInstanceCreator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import { useEventListener } from '@vueuse/core'

import { isArray, isDefined, isFunction } from '@alvarosabu/utils'
import { normalizeVectorFlexibleParam } from '/@/utils/normalize'
import { useCamera, useCatalogue, useRenderLoop, useScene } from '/@/core/'
import { useCamera, useCatalogue, useRenderLoop, useScene, useTres } from '/@/core/'
import { useLogger } from '/@/composables'
import { TresAttributes, TresCatalogue, TresInstance, TresVNode, TresVNodeType, TresEvent } from '/@/types'
import { useRaycaster } from '../useRaycaster'

const VECTOR3_PROPS = ['rotation', 'scale', 'position']

Expand Down Expand Up @@ -159,12 +160,10 @@ export function useInstanceCreator(prefix: string) {
const cmp = defineComponent({
name,
setup(_props, { slots, attrs, ...ctx }) {
const { scene: fallback } = useScene()
const { state } = useTres()
const { onLoop } = useRenderLoop()
const scene = inject<Ref<Scene>>('local-scene') || fallback
/* const { raycaster } = useRaycaster() */
const raycaster = inject<Ref<Raycaster>>('raycaster') /*
const currentInstance = inject<Ref>('currentInstance') */
const scene = state.scene
const raycaster = state.raycaster
const { pushCamera } = useCamera()

let instance = createInstance(threeObj, attrs, slots)
Expand All @@ -176,15 +175,15 @@ export function useInstanceCreator(prefix: string) {

// If the instance is a valid Object3D, add it to the scene
if (instance.isObject3D) {
scene?.value.add(instance)
scene?.add(instance)
}

let prevInstance: TresEvent | null = null
let currentInstance: TresEvent | null = null
if (instance.isMesh) {
onLoop(() => {
if (instance && raycaster?.value) {
const intersects = raycaster?.value.intersectObjects(scene.value.children)
if (instance && raycaster && scene?.children) {
const intersects = raycaster.intersectObjects(scene?.children)

if (intersects.length > 0) {
currentInstance = intersects[0]
Expand Down Expand Up @@ -214,21 +213,21 @@ export function useInstanceCreator(prefix: string) {
})
}

if (scene?.value && instance.isFog) {
scene.value.fog = instance as unknown as FogBase
if (scene && instance.isFog) {
scene.fog = instance as unknown as FogBase
}

if (import.meta.hot) {
import.meta.hot.on('vite:beforeUpdate', () => {
scene.value.remove(instance)
scene?.remove(instance)
})

import.meta.hot.on('vite:afterUpdate', () => {
instance = createInstance(threeObj, attrs, slots)
processProps(attrs, instance)

if (instance.isObject3D) {
scene?.value.add(instance)
scene?.add(instance)
}
})
}
Expand Down
6 changes: 6 additions & 0 deletions packages/tres/src/core/useRaycaster/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { Raycaster, Vector2 } from 'three'
import { onUnmounted, provide, ref, shallowRef } from 'vue'
import { useTres } from '/@/core'

const raycaster = shallowRef(new Raycaster())
const pointer = ref(new Vector2())
const currentInstance = ref(null)

export function useRaycaster() {
const { setState } = useTres()
setState('raycaster', raycaster.value)
setState('pointer', pointer)
setState('currentInstance', currentInstance)

provide('raycaster', raycaster)
provide('pointer', pointer)
provide('currentInstance', currentInstance)
Expand Down
3 changes: 0 additions & 3 deletions packages/tres/src/core/useRenderer/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,8 @@ export const TresCanvas = defineComponent({

const { renderer, dispose, aspectRatio } = useRenderer(canvas, container, props)

const activeCamera = shallowRef()

provide('aspect-ratio', aspectRatio)
provide('renderer', renderer)
provide('camera', activeCamera)

if (slots.default && !slots.default().some(node => (node.type as TresVNodeType).name === 'Scene')) {
logError('TresCanvas must contain a Scene component.')
Expand Down
1 change: 1 addition & 0 deletions packages/tres/src/core/useRenderer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ export function useRenderer(canvas: MaybeElementRef, container: MaybeElementRef,
const { setState } = useTres()
setState('renderer', renderer.value)
setState('clock', new Clock())
setState('aspectRatio', aspectRatio)
updateRendererOptions()
updateRendererSize()
resume()
Expand Down
Loading

0 comments on commit c206b6b

Please sign in to comment.