diff --git a/docs/api/renderer.md b/docs/api/renderer.md index cf148bf18..e0f266731 100644 --- a/docs/api/renderer.md +++ b/docs/api/renderer.md @@ -83,6 +83,7 @@ renderer.shadowMap.type: PCFSoftShadowMap | **clearColor** | The color the renderer will use to clear the canvas. | `#000000` | | **windowSize** | Whether to use the window size as the canvas size or the parent element. | `false` | | **disableRender** | Disable render on requestAnimationFrame, usefull for PostProcessing | `false` | +| **camera** | A manual camera to be used by the renderer. | | ## Defaults diff --git a/playground/components.d.ts b/playground/components.d.ts index 228cc37c4..111081ceb 100644 --- a/playground/components.d.ts +++ b/playground/components.d.ts @@ -11,6 +11,7 @@ declare module '@vue/runtime-core' { export interface GlobalComponents { AkuAku: typeof import('./src/components/gltf/AkuAku.vue')['default'] AnimatedModel: typeof import('./src/components/AnimatedModel.vue')['default'] + Cameras: typeof import('./src/components/Cameras.vue')['default'] FBXModels: typeof import('./src/components/FBXModels.vue')['default'] Gltf: typeof import('./src/components/gltf/index.vue')['default'] MeshWobbleMaterial: typeof import('./src/components/meshWobbleMaterial/index.vue')['default'] diff --git a/playground/src/components/Cameras.vue b/playground/src/components/Cameras.vue new file mode 100644 index 000000000..44aa7217c --- /dev/null +++ b/playground/src/components/Cameras.vue @@ -0,0 +1,113 @@ + + + diff --git a/playground/src/pages/index.vue b/playground/src/pages/index.vue index 44b22ffbc..4ca74bf35 100644 --- a/playground/src/pages/index.vue +++ b/playground/src/pages/index.vue @@ -1,6 +1,6 @@ diff --git a/src/components/TresCanvas.ts b/src/components/TresCanvas.ts index 165e9fbd3..8bbd24294 100644 --- a/src/components/TresCanvas.ts +++ b/src/components/TresCanvas.ts @@ -1,7 +1,7 @@ import { TresScene } from './TresScene' import { defineComponent, h } from 'vue' import { ShadowMapType, TextureEncoding, ToneMapping } from 'three' -import { useTresProvider } from '/@/composables' +import { CameraType, useTresProvider } from '/@/composables' import { RendererPresetsType } from '/@/composables/useRenderer/const' export interface TresCanvasProps { @@ -19,6 +19,7 @@ export interface TresCanvasProps { windowSize?: boolean preset?: RendererPresetsType disableRender?: boolean + camera?: CameraType } /** * Vue component for rendering a Tres component. @@ -41,6 +42,7 @@ export const TresCanvas = defineComponent({ 'windowSize', 'preset', 'disableRender', + 'camera', ] as unknown as undefined, setup(props, { slots, expose }) { const tres = useTresProvider() diff --git a/src/components/TresScene.ts b/src/components/TresScene.ts index d2228dda8..e2bb7bdce 100644 --- a/src/components/TresScene.ts +++ b/src/components/TresScene.ts @@ -1,8 +1,8 @@ -import { App, defineComponent, h, onMounted, onUnmounted, provide, ref, watchEffect } from 'vue' +import { App, defineComponent, h, onMounted, onUnmounted, ref, watch, watchEffect } from 'vue' import * as THREE from 'three' import { ShadowMapType, TextureEncoding, ToneMapping } from 'three' import { createTres } from '/@/core/renderer' -import { TRES_CONTEXT_KEY, useLogger } from '/@/composables' +import { CameraType, TRES_CONTEXT_KEY, useLogger } from '/@/composables' import { useCamera, useRenderer, useRenderLoop, useRaycaster, useTres } from '/@/composables' import { extend } from '/@/core/catalogue' import { RendererPresetsType } from '/@/composables/useRenderer/const' @@ -25,6 +25,7 @@ export interface TresSceneProps { windowSize?: boolean preset?: RendererPresetsType disableRender?: boolean + camera?: CameraType } /** * Vue component for rendering a Tres component. @@ -49,6 +50,7 @@ export const TresScene = defineComponent({ 'windowSize', 'preset', 'disableRender', + 'camera', ] as unknown as undefined, setup(props, { slots, expose }) { if (props.physicallyCorrectLights === true) { @@ -69,7 +71,8 @@ export const TresScene = defineComponent({ const internal = slots && slots.default && slots.default() if (internal?.length > 0) { - isCameraAvailable.value = internal.some((node: TresObject) => isString(node.type) && node.type.includes('Camera')) + isCameraAvailable.value = + internal.some((node: TresObject) => isString(node.type) && node.type.includes('Camera')) || props.camera if (!isCameraAvailable.value) { logWarning('No camera found in the scene, please add one!') } @@ -83,10 +86,14 @@ export const TresScene = defineComponent({ setState('renderer', null) }) + const { activeCamera, pushCamera, clearCameras } = useCamera() + function initRenderer() { const { renderer } = useRenderer(props) - const { activeCamera } = useCamera() + if (props.camera) { + pushCamera(props.camera as any) + } const { onLoop } = useRenderLoop() @@ -153,6 +160,16 @@ export const TresScene = defineComponent({ import.meta.hot.on('vite:afterUpdate', dispose) } + watch( + () => props.camera, + camera => { + if (camera) { + clearCameras() + pushCamera(camera as any) + } + }, + ) + return () => { return h( h(