|
@@ -1,4 +1,5 @@
|
|
-import type { ColorRepresentation, ColorSpace, Object3D, Scene, ShadowMapType, ToneMapping } from 'three'
|
|
|
|
|
|
+import type { RendererLoop } from './../../core/loop'
|
|
|
|
+import type { ColorRepresentation, ColorSpace, Object3D, ShadowMapType, ToneMapping } from 'three'
|
|
|
|
|
|
import type { TresContext } from '../useTresContextProvider'
|
|
import type { TresContext } from '../useTresContextProvider'
|
|
|
|
|
|
@@ -8,12 +9,18 @@ import {
|
|
useDevicePixelRatio,
|
|
useDevicePixelRatio,
|
|
} from '@vueuse/core'
|
|
} from '@vueuse/core'
|
|
import { Material, Mesh, WebGLRenderer } from 'three'
|
|
import { Material, Mesh, WebGLRenderer } from 'three'
|
|
-import { computed, type MaybeRef, onUnmounted, type Reactive, readonly, ref, toValue, watch, watchEffect } from 'vue'
|
|
|
|
|
|
+import { computed, onUnmounted, ref, toValue, watch, watchEffect } from 'vue'
|
|
|
|
+import type { MaybeRef, ShallowRef } from 'vue'
|
|
|
|
+import type { Renderer } from 'three/webgpu'
|
|
|
|
|
|
// Solution taken from Thretle that actually support different versions https://github.com/threlte/threlte/blob/5fa541179460f0dadc7dc17ae5e6854d1689379e/packages/core/src/lib/lib/useRenderer.ts
|
|
// Solution taken from Thretle that actually support different versions https://github.com/threlte/threlte/blob/5fa541179460f0dadc7dc17ae5e6854d1689379e/packages/core/src/lib/lib/useRenderer.ts
|
|
import { setPixelRatio } from '../../utils'
|
|
import { setPixelRatio } from '../../utils'
|
|
|
|
|
|
import { logWarning } from '../../utils/logger'
|
|
import { logWarning } from '../../utils/logger'
|
|
|
|
+import type { SizesType } from '../useSizes'
|
|
|
|
+import type { UseCameraReturn } from '../useCamera'
|
|
|
|
+import type { TresScene } from '../../types'
|
|
|
|
+import { isFunction, isObject } from '../../utils/is'
|
|
|
|
|
|
/**
|
|
/**
|
|
* If set to 'on-demand', the scene will only be rendered when the current frame is invalidated
|
|
* If set to 'on-demand', the scene will only be rendered when the current frame is invalidated
|
|
@@ -22,6 +29,8 @@ import { logWarning } from '../../utils/logger'
|
|
*/
|
|
*/
|
|
export type RenderMode = 'always' | 'on-demand' | 'manual'
|
|
export type RenderMode = 'always' | 'on-demand' | 'manual'
|
|
|
|
|
|
|
|
+export type TresRenderer = WebGLRenderer | Renderer
|
|
|
|
+
|
|
export interface RendererOptions {
|
|
export interface RendererOptions {
|
|
/**
|
|
/**
|
|
* WebGL Context options (Readonly because they are passed to the renderer constructor)
|
|
* WebGL Context options (Readonly because they are passed to the renderer constructor)
|
|
@@ -172,11 +181,19 @@ export interface RendererOptions {
|
|
* Custom WebGL renderer instance
|
|
* Custom WebGL renderer instance
|
|
* Allows using a pre-configured renderer instead of creating a new one
|
|
* Allows using a pre-configured renderer instead of creating a new one
|
|
*/
|
|
*/
|
|
- // renderer?: (ctx: TresRendererSetupContext) => Promise<TresRenderer> | TresRenderers
|
|
|
|
|
|
+ renderer?: (ctx: TresRendererSetupContext) => TresRenderer
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export interface TresRendererSetupContext {
|
|
|
|
+ sizes: SizesType
|
|
|
|
+ scene: ShallowRef<TresScene>
|
|
|
|
+ camera: UseCameraReturn
|
|
|
|
+ loop: RendererLoop
|
|
|
|
+ canvas: MaybeRef<HTMLCanvasElement>
|
|
}
|
|
}
|
|
|
|
|
|
export interface UseRendererOptions {
|
|
export interface UseRendererOptions {
|
|
- scene: Scene
|
|
|
|
|
|
+ scene: ShallowRef<TresScene>
|
|
canvas: MaybeRef<HTMLCanvasElement>
|
|
canvas: MaybeRef<HTMLCanvasElement>
|
|
options: RendererOptions
|
|
options: RendererOptions
|
|
contextParts: Pick<TresContext, 'sizes' | 'camera' | 'loop'>
|
|
contextParts: Pick<TresContext, 'sizes' | 'camera' | 'loop'>
|
|
@@ -188,25 +205,33 @@ export function useRendererManager(
|
|
canvas,
|
|
canvas,
|
|
options,
|
|
options,
|
|
contextParts: { sizes, loop, camera },
|
|
contextParts: { sizes, loop, camera },
|
|
- }:
|
|
|
|
- {
|
|
|
|
- scene: Scene
|
|
|
|
- canvas: MaybeRef<HTMLCanvasElement>
|
|
|
|
- options: Reactive<RendererOptions>
|
|
|
|
- contextParts: Pick<TresContext, 'sizes' | 'camera' | 'loop'>
|
|
|
|
- },
|
|
|
|
|
|
+ }: UseRendererOptions,
|
|
) {
|
|
) {
|
|
- const renderer = new WebGLRenderer({
|
|
|
|
- ...options,
|
|
|
|
- canvas: unrefElement(canvas),
|
|
|
|
- })
|
|
|
|
|
|
+ const getRenderer = () => {
|
|
|
|
+ if (isFunction(options.renderer)) {
|
|
|
|
+ return options.renderer({
|
|
|
|
+ sizes,
|
|
|
|
+ scene,
|
|
|
|
+ camera,
|
|
|
|
+ loop,
|
|
|
|
+ canvas,
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return new WebGLRenderer({
|
|
|
|
+ ...options,
|
|
|
|
+ canvas: unrefElement(canvas),
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const renderer = getRenderer()
|
|
|
|
|
|
const frames = ref(0)
|
|
const frames = ref(0)
|
|
const maxFrames = 60
|
|
const maxFrames = 60
|
|
const canBeInvalidated = computed(() => toValue(options.renderMode) === 'on-demand' && frames.value === 0)
|
|
const canBeInvalidated = computed(() => toValue(options.renderMode) === 'on-demand' && frames.value === 0)
|
|
|
|
|
|
const forceMaterialUpdate = () =>
|
|
const forceMaterialUpdate = () =>
|
|
- scene.traverse((child: Object3D) => {
|
|
|
|
|
|
+ scene.value.traverse((child: Object3D) => {
|
|
if (child instanceof Mesh && child.material instanceof Material) {
|
|
if (child instanceof Mesh && child.material instanceof Material) {
|
|
child.material.needsUpdate = true
|
|
child.material.needsUpdate = true
|
|
}
|
|
}
|
|
@@ -242,11 +267,24 @@ export function useRendererManager(
|
|
|
|
|
|
const isModeAlways = computed(() => toValue(options.renderMode) === 'always')
|
|
const isModeAlways = computed(() => toValue(options.renderMode) === 'always')
|
|
|
|
|
|
- const renderEventHook = createEventHook<WebGLRenderer>()
|
|
|
|
|
|
+ const renderEventHook = createEventHook<TresRenderer>()
|
|
|
|
+
|
|
|
|
+ // be aware that the WebGLRenderer does not extend from Renderer
|
|
|
|
+ const isRenderer = (value: unknown): value is Renderer =>
|
|
|
|
+ isObject(value) && 'isRenderer' in value && Boolean(value.isRenderer)
|
|
|
|
+
|
|
|
|
+ const readyEventHook = createEventHook<TresRenderer>()
|
|
|
|
+ let hasTriggeredReady = false
|
|
|
|
+
|
|
|
|
+ if (isRenderer(renderer)) {
|
|
|
|
+ // Initialize the WebGPU context
|
|
|
|
+ renderer.init()
|
|
|
|
+ readyEventHook.trigger(renderer)
|
|
|
|
+ }
|
|
|
|
|
|
loop.register(() => {
|
|
loop.register(() => {
|
|
if (camera.activeCamera.value && frames.value) {
|
|
if (camera.activeCamera.value && frames.value) {
|
|
- renderer.render(scene, camera.activeCamera.value)
|
|
|
|
|
|
+ renderer.render(scene.value, camera.activeCamera.value)
|
|
|
|
|
|
renderEventHook.trigger(renderer)
|
|
renderEventHook.trigger(renderer)
|
|
}
|
|
}
|
|
@@ -256,13 +294,6 @@ export function useRendererManager(
|
|
: Math.max(0, frames.value - 1)
|
|
: Math.max(0, frames.value - 1)
|
|
}, 'render')
|
|
}, 'render')
|
|
|
|
|
|
- const isReady = computed(() =>
|
|
|
|
- !!(renderer.domElement.width && renderer.domElement.height),
|
|
|
|
- )
|
|
|
|
-
|
|
|
|
- const readyEventHook = createEventHook<WebGLRenderer>()
|
|
|
|
- let hasTriggeredReady = false
|
|
|
|
-
|
|
|
|
// Watch the sizes and invalidate the renderer when they change
|
|
// Watch the sizes and invalidate the renderer when they change
|
|
watch([sizes.width, sizes.height], () => {
|
|
watch([sizes.width, sizes.height], () => {
|
|
renderer.setSize(sizes.width.value, sizes.height.value)
|
|
renderer.setSize(sizes.width.value, sizes.height.value)
|
|
@@ -362,12 +393,13 @@ export function useRendererManager(
|
|
|
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
renderer.dispose()
|
|
renderer.dispose()
|
|
- renderer.forceContextLoss()
|
|
|
|
|
|
+ if ('forceContextLoss' in renderer) {
|
|
|
|
+ renderer.forceContextLoss()
|
|
|
|
+ }
|
|
})
|
|
})
|
|
|
|
|
|
return {
|
|
return {
|
|
instance: renderer,
|
|
instance: renderer,
|
|
- isReady: readonly(isReady),
|
|
|
|
advance,
|
|
advance,
|
|
onRender: renderEventHook.on,
|
|
onRender: renderEventHook.on,
|
|
onReady: readyEventHook.on,
|
|
onReady: readyEventHook.on,
|