|
@@ -1,5 +1,5 @@
|
|
-import { toValue, useElementSize, useFps, useMemory, useRafFn, useWindowSize } from '@vueuse/core'
|
|
|
|
-import { inject, provide, readonly, shallowRef, computed, ref, onUnmounted } from 'vue'
|
|
|
|
|
|
+import { toValue, useElementSize, useFps, useMemory, useRafFn, useWindowSize, refDebounced } from '@vueuse/core'
|
|
|
|
+import { inject, provide, readonly, shallowRef, computed, ref, onUnmounted, watchEffect } from 'vue'
|
|
import type { Camera, EventDispatcher, Scene, WebGLRenderer } from 'three'
|
|
import type { Camera, EventDispatcher, Scene, WebGLRenderer } from 'three'
|
|
import { Raycaster } from 'three'
|
|
import { Raycaster } from 'three'
|
|
import type { ComputedRef, DeepReadonly, MaybeRef, MaybeRefOrGetter, Ref, ShallowRef } from 'vue'
|
|
import type { ComputedRef, DeepReadonly, MaybeRef, MaybeRefOrGetter, Ref, ShallowRef } from 'vue'
|
|
@@ -8,6 +8,39 @@ import { useCamera } from '../useCamera'
|
|
import type { UseRendererOptions } from '../useRenderer'
|
|
import type { UseRendererOptions } from '../useRenderer'
|
|
import { useRenderer } from '../useRenderer'
|
|
import { useRenderer } from '../useRenderer'
|
|
import { extend } from '../../core/catalogue'
|
|
import { extend } from '../../core/catalogue'
|
|
|
|
+import { useLogger } from '../useLogger'
|
|
|
|
+
|
|
|
|
+export interface InternalState {
|
|
|
|
+ priority: Ref<number>
|
|
|
|
+ frames: Ref<number>
|
|
|
|
+ maxFrames: number
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export interface RenderState {
|
|
|
|
+ /**
|
|
|
|
+ * If set to 'on-demand', the scene will only be rendered when the current frame is invalidated
|
|
|
|
+ * If set to 'manual', the scene will only be rendered when advance() is called
|
|
|
|
+ * If set to 'always', the scene will be rendered every frame
|
|
|
|
+ */
|
|
|
|
+ mode: Ref<'always' | 'on-demand' | 'manual'>
|
|
|
|
+ priority: Ref<number>
|
|
|
|
+ frames: Ref<number>
|
|
|
|
+ maxFrames: number
|
|
|
|
+ canBeInvalidated: ComputedRef<boolean>
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+export interface PerformanceState {
|
|
|
|
+ maxFrames: number
|
|
|
|
+ fps: {
|
|
|
|
+ value: number
|
|
|
|
+ accumulator: number[]
|
|
|
|
+ }
|
|
|
|
+ memory: {
|
|
|
|
+ currentMem: number
|
|
|
|
+ allocatedMem: number
|
|
|
|
+ accumulator: number[]
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
|
|
export interface TresContext {
|
|
export interface TresContext {
|
|
scene: ShallowRef<Scene>
|
|
scene: ShallowRef<Scene>
|
|
@@ -18,18 +51,16 @@ export interface TresContext {
|
|
controls: Ref<(EventDispatcher & { enabled: boolean }) | null>
|
|
controls: Ref<(EventDispatcher & { enabled: boolean }) | null>
|
|
renderer: ShallowRef<WebGLRenderer>
|
|
renderer: ShallowRef<WebGLRenderer>
|
|
raycaster: ShallowRef<Raycaster>
|
|
raycaster: ShallowRef<Raycaster>
|
|
- perf: {
|
|
|
|
- maxFrames: number
|
|
|
|
- fps: {
|
|
|
|
- value: number
|
|
|
|
- accumulator: number[]
|
|
|
|
- }
|
|
|
|
- memory: {
|
|
|
|
- currentMem: number
|
|
|
|
- allocatedMem: number
|
|
|
|
- accumulator: number[]
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ perf: PerformanceState
|
|
|
|
+ render: RenderState
|
|
|
|
+ /**
|
|
|
|
+ * Invalidates the current frame when renderMode === 'on-demand'
|
|
|
|
+ */
|
|
|
|
+ invalidate: () => void
|
|
|
|
+ /**
|
|
|
|
+ * Advance one frame when renderMode === 'manual'
|
|
|
|
+ */
|
|
|
|
+ advance: () => void
|
|
registerCamera: (camera: Camera) => void
|
|
registerCamera: (camera: Camera) => void
|
|
setCameraActive: (cameraOrUuid: Camera | string) => void
|
|
setCameraActive: (cameraOrUuid: Camera | string) => void
|
|
deregisterCamera: (camera: Camera) => void
|
|
deregisterCamera: (camera: Camera) => void
|
|
@@ -41,28 +72,41 @@ export function useTresContextProvider({
|
|
windowSize,
|
|
windowSize,
|
|
disableRender,
|
|
disableRender,
|
|
rendererOptions,
|
|
rendererOptions,
|
|
|
|
+ emit,
|
|
}: {
|
|
}: {
|
|
scene: Scene
|
|
scene: Scene
|
|
canvas: MaybeRef<HTMLCanvasElement>
|
|
canvas: MaybeRef<HTMLCanvasElement>
|
|
windowSize: MaybeRefOrGetter<boolean>
|
|
windowSize: MaybeRefOrGetter<boolean>
|
|
disableRender: MaybeRefOrGetter<boolean>
|
|
disableRender: MaybeRefOrGetter<boolean>
|
|
rendererOptions: UseRendererOptions
|
|
rendererOptions: UseRendererOptions
|
|
|
|
+ emit: (event: string, ...args: any[]) => void
|
|
}): TresContext {
|
|
}): TresContext {
|
|
|
|
|
|
|
|
+ const { logWarning } = useLogger()
|
|
|
|
+
|
|
const elementSize = computed(() =>
|
|
const elementSize = computed(() =>
|
|
toValue(windowSize)
|
|
toValue(windowSize)
|
|
? useWindowSize()
|
|
? useWindowSize()
|
|
: useElementSize(toValue(canvas).parentElement),
|
|
: useElementSize(toValue(canvas).parentElement),
|
|
)
|
|
)
|
|
|
|
|
|
- const width = computed(() => elementSize.value.width.value)
|
|
|
|
- const height = computed(() => elementSize.value.height.value)
|
|
|
|
|
|
+ const reactiveSize = shallowRef({
|
|
|
|
+ width: 0,
|
|
|
|
+ height: 0,
|
|
|
|
+ })
|
|
|
|
+ const debouncedReactiveSize = refDebounced(reactiveSize, 10)
|
|
|
|
+ const unWatchSize = watchEffect(() => {
|
|
|
|
+ reactiveSize.value = {
|
|
|
|
+ width: elementSize.value.width.value,
|
|
|
|
+ height: elementSize.value.height.value,
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
|
|
- const aspectRatio = computed(() => width.value / height.value)
|
|
|
|
|
|
+ const aspectRatio = computed(() => debouncedReactiveSize.value.width / debouncedReactiveSize.value.height)
|
|
|
|
|
|
const sizes = {
|
|
const sizes = {
|
|
- height,
|
|
|
|
- width,
|
|
|
|
|
|
+ height: computed(() => debouncedReactiveSize.value.height),
|
|
|
|
+ width: computed(() => debouncedReactiveSize.value.width),
|
|
aspectRatio,
|
|
aspectRatio,
|
|
}
|
|
}
|
|
const localScene = shallowRef<Scene>(scene)
|
|
const localScene = shallowRef<Scene>(scene)
|
|
@@ -74,16 +118,47 @@ export function useTresContextProvider({
|
|
setCameraActive,
|
|
setCameraActive,
|
|
} = useCamera({ sizes, scene })
|
|
} = useCamera({ sizes, scene })
|
|
|
|
|
|
|
|
+ // Render state
|
|
|
|
+
|
|
|
|
+ const render: RenderState = {
|
|
|
|
+ mode: ref<'always' | 'on-demand' | 'manual'>(rendererOptions.renderMode || 'always'),
|
|
|
|
+ priority: ref(0),
|
|
|
|
+ frames: ref(0),
|
|
|
|
+ maxFrames: 60,
|
|
|
|
+ canBeInvalidated: computed(() => render.mode.value === 'on-demand' && render.frames.value === 0),
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function invalidate(frames = 1) {
|
|
|
|
+ // Increase the frame count, ensuring not to exceed a maximum if desired
|
|
|
|
+ if (rendererOptions.renderMode === 'on-demand') {
|
|
|
|
+ render.frames.value = Math.min(render.maxFrames, render.frames.value + frames)
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ logWarning('`invalidate` can only be used when `renderMode` is set to `on-demand`')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function advance() {
|
|
|
|
+ if (rendererOptions.renderMode === 'manual') {
|
|
|
|
+ render.frames.value = 1
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ logWarning('`advance` can only be used when `renderMode` is set to `manual`')
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
const { renderer } = useRenderer(
|
|
const { renderer } = useRenderer(
|
|
{
|
|
{
|
|
scene,
|
|
scene,
|
|
canvas,
|
|
canvas,
|
|
options: rendererOptions,
|
|
options: rendererOptions,
|
|
- contextParts: { sizes, camera },
|
|
|
|
|
|
+ emit,
|
|
|
|
+ // TODO: replace contextParts with full ctx at https://github.com/Tresjs/tres/issues/516
|
|
|
|
+ contextParts: { sizes, camera, render, invalidate, advance },
|
|
disableRender,
|
|
disableRender,
|
|
})
|
|
})
|
|
|
|
|
|
- const toProvide: TresContext = {
|
|
|
|
|
|
+ const ctx: TresContext = {
|
|
sizes,
|
|
sizes,
|
|
scene: localScene,
|
|
scene: localScene,
|
|
camera,
|
|
camera,
|
|
@@ -103,17 +178,23 @@ export function useTresContextProvider({
|
|
accumulator: [],
|
|
accumulator: [],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
|
|
+ render,
|
|
|
|
+ advance,
|
|
extend,
|
|
extend,
|
|
|
|
+ invalidate,
|
|
registerCamera,
|
|
registerCamera,
|
|
setCameraActive,
|
|
setCameraActive,
|
|
deregisterCamera,
|
|
deregisterCamera,
|
|
}
|
|
}
|
|
|
|
|
|
- provide('useTres', toProvide)
|
|
|
|
|
|
+ provide('useTres', ctx)
|
|
|
|
+
|
|
|
|
+ // Add context to scene.userData
|
|
|
|
+ ctx.scene.value.userData.tres__context = ctx
|
|
|
|
|
|
// Performance
|
|
// Performance
|
|
const updateInterval = 100 // Update interval in milliseconds
|
|
const updateInterval = 100 // Update interval in milliseconds
|
|
- const fps = useFps({ every: updateInterval })
|
|
|
|
|
|
+ const fps = useFps({ every: updateInterval })
|
|
const { isSupported, memory } = useMemory({ interval: updateInterval })
|
|
const { isSupported, memory } = useMemory({ interval: updateInterval })
|
|
const maxFrames = 160
|
|
const maxFrames = 160
|
|
let lastUpdateTime = performance.now()
|
|
let lastUpdateTime = performance.now()
|
|
@@ -122,34 +203,34 @@ export function useTresContextProvider({
|
|
|
|
|
|
// Update WebGL Memory Usage (Placeholder for actual logic)
|
|
// Update WebGL Memory Usage (Placeholder for actual logic)
|
|
// perf.memory.value = calculateMemoryUsage(gl)
|
|
// perf.memory.value = calculateMemoryUsage(gl)
|
|
- if (toProvide.scene.value) {
|
|
|
|
- toProvide.perf.memory.allocatedMem = calculateMemoryUsage(toProvide.scene.value as unknown as TresObject)
|
|
|
|
|
|
+ if (ctx.scene.value) {
|
|
|
|
+ ctx.perf.memory.allocatedMem = calculateMemoryUsage(ctx.scene.value as unknown as TresObject)
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+
|
|
// Update memory usage
|
|
// Update memory usage
|
|
if (timestamp - lastUpdateTime >= updateInterval) {
|
|
if (timestamp - lastUpdateTime >= updateInterval) {
|
|
lastUpdateTime = timestamp
|
|
lastUpdateTime = timestamp
|
|
|
|
|
|
// Update FPS
|
|
// Update FPS
|
|
- toProvide.perf.fps.accumulator.push(fps.value as never)
|
|
|
|
|
|
+ ctx.perf.fps.accumulator.push(fps.value as never)
|
|
|
|
|
|
- if (toProvide.perf.fps.accumulator.length > maxFrames) {
|
|
|
|
- toProvide.perf.fps.accumulator.shift()
|
|
|
|
|
|
+ if (ctx.perf.fps.accumulator.length > maxFrames) {
|
|
|
|
+ ctx.perf.fps.accumulator.shift()
|
|
}
|
|
}
|
|
|
|
|
|
- toProvide.perf.fps.value = fps.value
|
|
|
|
|
|
+ ctx.perf.fps.value = fps.value
|
|
|
|
|
|
// Update memory
|
|
// Update memory
|
|
if (isSupported.value && memory.value) {
|
|
if (isSupported.value && memory.value) {
|
|
- toProvide.perf.memory.accumulator.push(memory.value.usedJSHeapSize / 1024 / 1024 as never)
|
|
|
|
|
|
+ ctx.perf.memory.accumulator.push(memory.value.usedJSHeapSize / 1024 / 1024 as never)
|
|
|
|
|
|
- if (toProvide.perf.memory.accumulator.length > maxFrames) {
|
|
|
|
- toProvide.perf.memory.accumulator.shift()
|
|
|
|
|
|
+ if (ctx.perf.memory.accumulator.length > maxFrames) {
|
|
|
|
+ ctx.perf.memory.accumulator.shift()
|
|
}
|
|
}
|
|
|
|
|
|
- toProvide.perf.memory.currentMem
|
|
|
|
- = toProvide.perf.memory.accumulator.reduce((a, b) => a + b, 0) / toProvide.perf.memory.accumulator.length
|
|
|
|
-
|
|
|
|
|
|
+ ctx.perf.memory.currentMem
|
|
|
|
+ = ctx.perf.memory.accumulator.reduce((a, b) => a + b, 0) / ctx.perf.memory.accumulator.length
|
|
|
|
+
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -157,29 +238,30 @@ export function useTresContextProvider({
|
|
// Devtools
|
|
// Devtools
|
|
let accumulatedTime = 0
|
|
let accumulatedTime = 0
|
|
const interval = 1 // Interval in milliseconds, e.g., 1000 ms = 1 second
|
|
const interval = 1 // Interval in milliseconds, e.g., 1000 ms = 1 second
|
|
-
|
|
|
|
- const { pause, resume } = useRafFn(({ delta }) => {
|
|
|
|
|
|
+
|
|
|
|
+ const { pause } = useRafFn(({ delta }) => {
|
|
if (!window.__TRES__DEVTOOLS__) return
|
|
if (!window.__TRES__DEVTOOLS__) return
|
|
|
|
|
|
updatePerformanceData({ timestamp: performance.now() })
|
|
updatePerformanceData({ timestamp: performance.now() })
|
|
-
|
|
|
|
|
|
+
|
|
// Accumulate the delta time
|
|
// Accumulate the delta time
|
|
accumulatedTime += delta
|
|
accumulatedTime += delta
|
|
-
|
|
|
|
|
|
+
|
|
// Check if the accumulated time is greater than or equal to the interval
|
|
// Check if the accumulated time is greater than or equal to the interval
|
|
if (accumulatedTime >= interval) {
|
|
if (accumulatedTime >= interval) {
|
|
- window.__TRES__DEVTOOLS__.cb(toProvide)
|
|
|
|
-
|
|
|
|
|
|
+ window.__TRES__DEVTOOLS__.cb(ctx)
|
|
|
|
+
|
|
// Reset the accumulated time
|
|
// Reset the accumulated time
|
|
accumulatedTime = 0
|
|
accumulatedTime = 0
|
|
}
|
|
}
|
|
- }, { immediate: true })
|
|
|
|
-
|
|
|
|
|
|
+ }, { immediate: true })
|
|
|
|
+
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
|
|
+ unWatchSize()
|
|
pause()
|
|
pause()
|
|
})
|
|
})
|
|
|
|
|
|
- return toProvide
|
|
|
|
|
|
+ return ctx
|
|
}
|
|
}
|
|
|
|
|
|
export function useTresContext(): TresContext {
|
|
export function useTresContext(): TresContext {
|