Tino Koch 2 luni în urmă
părinte
comite
25b3b7478c
2 a modificat fișierele cu 82 adăugiri și 10 ștergeri
  1. 2 1
      src/components/TresCanvas.vue
  2. 80 9
      src/core/setupRenderer.ts

+ 2 - 1
src/components/TresCanvas.vue

@@ -271,6 +271,7 @@ export interface TresCanvasProps extends /* @vue-ignore */ WebGLRendererProps {
     * - `low-power`: Tries to reduce power usage.
    * @see {@link https://threejs.org/docs/#api/en/renderers/WebGLRenderer}
    * @default 'default'
+   * @readonly
    */
   powerPreference?: MaybeRefOrGetter<WebGLPowerPreference>
   /**
@@ -318,7 +319,7 @@ export interface TresCanvasProps extends /* @vue-ignore */ WebGLRendererProps {
    * @see {@link https://threejs.org/docs/#api/en/constants/Renderer}
    * @default ACESFilmicToneMapping (Opinionated default by TresJS)
    */
-  toneMapping?: MaybeRefOrGetter<ToneMapping | number>
+  toneMapping?: MaybeRefOrGetter<ToneMapping>
 
   /**
    * Type of shadow map to use for shadow calculations

+ 80 - 9
src/core/setupRenderer.ts

@@ -1,11 +1,12 @@
-import type { TresContext, TresPartialContext } from '../composables/useTresContextProvider'
 import type { ColorRepresentation, Object3D, WebGLRenderer } from 'three'
-import { watch } from 'vue'
+import type { UnwrapRef, WatchHandle, WatchOptions } from 'vue'
+import { toValue, watch } from 'vue'
 import { useDevicePixelRatio } from '@vueuse/core'
 import { setPixelRatio } from '../utils'
 
-import { Mesh } from 'three'
+import { ACESFilmicToneMapping, Mesh } from 'three'
 import type { TresCanvasProps } from '../components/TresCanvas.vue'
+import type { TresRendererSetupContext } from 'src/composables/useRenderer'
 
 interface PropertyHandler<T = unknown> {
   set: (renderer: WebGLRenderer, value: T) => void
@@ -17,6 +18,62 @@ interface DirectProperty {
   immediate?: boolean
 }
 
+type NamesOfPropsThatCannotChange = keyof Pick<
+  TresCanvasProps,
+  'antialias' |
+  'camera' | // this is handled in useCameras // TODO camera should be handled
+  'stencil' |
+  'logarithmicDepthBuffer' |
+  'preserveDrawingBuffer' |
+  'powerPreference' |
+  'alpha' |
+  'renderer'
+>
+type NamesOfPropsThatCanChange = keyof Omit<
+  TresCanvasProps,
+  NamesOfPropsThatCannotChange
+>
+
+// const propsToWatch: {
+//   [K in NamesOfPropsThatCanChange]: {
+//     getter: (props: TresCanvasProps) => TresCanvasProps[K] // TODO check if there is a better type than any
+//     setter: (renderer: WebGLRenderer, value: TresCanvasProps[K]) => void
+//     watchOptions: WatchOptions
+//   }
+// } = {
+//   clearAlpha: {
+//     getter: ({ clearAlpha }) => clearAlpha,
+//     setter: (renderer, value) => renderer.setClearAlpha(value),
+//     watchOptions: { immediate: true },
+//   },
+// }
+
+// TODO test by using basic playground
+
+const propsToWatch = {
+  clearAlpha: {
+    getter: ({ clearAlpha }) => clearAlpha,
+    setter: (renderer, value) => {
+      renderer.setClearAlpha(toValue(value))
+    },
+    defaultValue: 1,
+    watchOptions: { immediate: true },
+  },
+  toneMapping: {
+    getter: ({ toneMapping }) => toneMapping,
+    setter: (renderer, value) => renderer.toneMapping = toValue(value),
+    watchOptions: { immediate: true },
+    defaultValue: ACESFilmicToneMapping, // TODO move to common place, this is opinionated
+  },
+} satisfies {
+  [K in NamesOfPropsThatCanChange]: {
+    getter: (props: TresCanvasProps) => TresCanvasProps[K] // TODO check if there is a better type than any
+    setter: (renderer: WebGLRenderer, value: Exclude<UnwrapRef<TresCanvasProps[K]>, undefined>) => void
+    watchOptions: WatchOptions
+    defaultValue: Exclude<UnwrapRef<TresCanvasProps[K]>, undefined>
+  }
+}
+
 // Properties that can be set directly on the renderer
 const directProperties: Record<string, DirectProperty> = {
   toneMapping: {
@@ -73,12 +130,12 @@ const rendererPropertyHandlers: Record<string, PropertyHandler<ColorRepresentati
 export function setupWebGLRenderer( // TODO object format? // TODO name like comosable
   initialRenderer: WebGLRenderer,
   options: TresCanvasProps,
-  ctx: TresPartialContext,
+  ctx: TresRendererSetupContext,
 ) {
   const { pixelRatio } = useDevicePixelRatio()
   const { invalidate } = ctx
 
-  function invalidateOnDemand() {
+  function invalidateIfOnDemandMode() {
     if (options.renderMode === 'on-demand') {
       invalidate()
     }
@@ -87,18 +144,32 @@ export function setupWebGLRenderer( // TODO object format? // TODO name like com
   // Watch DPR changes
   watch(() => options.dpr, (value) => {
     if (!value) { return }
-    invalidateOnDemand()
+    invalidateIfOnDemandMode()
     setPixelRatio(initialRenderer, pixelRatio.value, value as number)
   })
 
   // Watch size changes
   watch([ctx.sizes.width, ctx.sizes.height], () => {
     initialRenderer.setSize(ctx.sizes.width.value, ctx.sizes.height.value)
-    invalidateOnDemand()
+    invalidateIfOnDemandMode()
   }, {
     immediate: true,
   })
 
+  Object.values(propsToWatch).forEach(({ getter, setter, watchOptions, defaultValue }) => {
+    watch(
+      () => getter(options),
+      (value) => {
+        if (value === undefined) {
+          setter(initialRenderer, defaultValue)
+        }
+        setter(initialRenderer, value)
+        invalidateIfOnDemandMode()
+      },
+      watchOptions,
+    )
+  })
+
   // Watch properties that need setter methods
   Object.entries(rendererPropertyHandlers).forEach(([key, handler]) => {
     watch(
@@ -106,7 +177,7 @@ export function setupWebGLRenderer( // TODO object format? // TODO name like com
       (value) => {
         if (value === undefined) { return }
         handler.set(initialRenderer, value)
-        invalidateOnDemand()
+        invalidateIfOnDemandMode()
       },
       { immediate: handler.immediate },
     )
@@ -154,7 +225,7 @@ export function setupWebGLRenderer( // TODO object format? // TODO name like com
             ;(initialRenderer as unknown as Record<string, unknown>)[rendererKey] = value
           }
         }
-        invalidateOnDemand()
+        invalidateIfOnDemandMode()
       },
       { immediate: prop.immediate },
     )