import { useWindowSize } from '@vueuse/core' import { computed, Ref, watch } from 'vue' import { PCFSoftShadowMap, TextureEncoding, ToneMapping, WebGLRenderer, WebGLRendererParameters, Color } from 'three' // TODO: this is gonna be part of @tres/cientos pkg import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js' import { useCamera, useLoop, useScene } from '/@/core' import { normalizeColor } from '/@/utils/normalize' import { useLogger } from '/@/composables' export interface RendererOptions extends WebGLRendererParameters { shadows?: boolean controls?: boolean physicallyCorrectLights?: boolean outputEncoding?: TextureEncoding toneMapping?: ToneMapping context?: WebGLRenderingContext | undefined powerPreference?: string preserveDrawingBuffer?: boolean } let renderer: WebGLRenderer | null let controls: OrbitControls let disposeLoop: () => void export function useRenderer(canvas: Ref, options?: RendererOptions) { const { width, height } = useWindowSize() const { logMessage } = useLogger() const aspectRatio = computed(() => width.value / height.value) const { scene } = useScene() const { activeCamera: camera } = useCamera() function setRealisticShadows() { if (renderer) { renderer.shadowMap.enabled = true renderer.shadowMap.type = PCFSoftShadowMap } } function updateRenderer() { if (renderer) { renderer.setSize(width.value, height.value) renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)) } } function disposeRenderer() { if (renderer) { renderer.dispose() renderer = null } logMessage('disposeRenderer', renderer) disposeLoop() } watch(aspectRatio, updateRenderer) function initRender() { logMessage('Render init', { renderer, canvas, scene, }) if (!renderer && canvas.value && scene) { const setup = { canvas: canvas.value, ...options, } if (options?.context) { setup.context = options.context } renderer = new WebGLRenderer(setup) if (options?.shadows) { setRealisticShadows() } renderer.physicallyCorrectLights = options?.physicallyCorrectLights || false if (options?.outputEncoding) { renderer.outputEncoding = options.outputEncoding } if (options?.toneMapping) { renderer.toneMapping = options.toneMapping renderer.toneMappingExposure = 1 } updateRenderer() renderer.render(scene, camera.value) controls = new OrbitControls(camera.value, renderer.domElement) controls.enableDamping = true const ctx = useLoop(() => { if (scene && renderer) { renderer.render(scene, camera.value) } controls.update() }) disposeLoop = ctx.disposeLoop } } function setClearColor(color: Color | string | number) { if (renderer) { renderer.setClearColor(normalizeColor(color)) } } initRender() return { renderer, setClearColor, updateRenderer, disposeRenderer, } }