UNPKG

threepipe

Version:

A modern 3D viewer framework built on top of three.js, written in TypeScript, designed to make creating high-quality, modular, and extensible 3D experiences on the web simple and enjoyable.

151 lines (135 loc) 5.83 kB
import {AViewerPluginEventMap, AViewerPluginSync, ThreeViewer} from '../../viewer' import {IRenderTarget} from '../../rendering' import {ICamera, IObjectExtension} from '../../core' import {uiFolderContainer, UiObjectConfig, uiToggle} from 'uiconfig.js' import type {RenderTargetPreviewPlugin} from '../ui/RenderTargetPreviewPlugin' export interface VirtualCamerasPluginEventMap extends AViewerPluginEventMap { preRenderCamera: {camera: VirtualCamera} preBlitCamera: {camera: VirtualCamera, readBuffer: WebGLTexture} postRenderCamera: {camera: VirtualCamera} } export interface VirtualCamera { camera: ICamera target: IRenderTarget enabled: boolean } @uiFolderContainer('Virtual Cameras') export class VirtualCamerasPlugin extends AViewerPluginSync<VirtualCamerasPluginEventMap> { public static readonly PluginType = 'VirtualCamerasPlugin' @uiToggle() enabled = true toJSON: any = undefined // disable serialization constructor(enabled = true) { super() this.enabled = enabled } cameras: VirtualCamera[] = [] protected _viewerListeners = { preRender: () => { if (this.isDisabled() || !this._viewer) return const viewer = this._viewer for (const v of this.cameras) { if (!v.enabled) continue const camera = v.camera try { this.dispatchEvent({type: 'preRenderCamera', camera: v}) viewer.scene.renderCamera = camera viewer.renderManager.render(viewer.scene, false) const source = viewer.renderManager.composer.readBuffer.texture this.dispatchEvent({type: 'preBlitCamera', camera: v, readBuffer: source}) viewer.renderManager.blit(v.target, {source}) this.dispatchEvent({type: 'postRenderCamera', camera: v}) } catch (e: any) { viewer.console.error(e) v.enabled = false if (viewer.debug) throw e } } }, } private _objectExtension: IObjectExtension = { uuid: 'VirtualCameraPluginExt', isCompatible: object => object.isCamera, getUiConfig: (object): UiObjectConfig[]|undefined => { if (!object.isCamera) return undefined return [{ type: 'button', label: 'Add Virtual Camera', hidden: ()=>!!this.cameras.find(f=>f.camera === object), onClick: () => { if (!this._viewer) return this.addCamera(object as ICamera, undefined, true) object.setDirty && object.setDirty() return ()=>{ this.removeCamera(object as ICamera) object.setDirty && object.setDirty() } }, }, { type: 'button', label: 'Virtual Camera Enabled', hidden: ()=>!this.cameras.find(f=>f.camera === object), getValue: ()=>{ const vCam = this.cameras.find(f => f.camera === object) return vCam ? vCam.enabled : false }, setValue: ()=>{ const vCam = this.cameras.find(f => f.camera === object) if (vCam) { vCam.enabled = !vCam.enabled return vCam.enabled } return false }, }, { type: 'button', label: 'Remove Virtual Camera', hidden: ()=>!this.cameras.find(f=>f.camera === object), onClick: () => { if (!this._viewer) return this.removeCamera(object as ICamera) object.setDirty && object.setDirty() return ()=>{ this.addCamera(object as ICamera, undefined, true) object.setDirty && object.setDirty() } }, }] }, } onAdded(viewer: ThreeViewer) { super.onAdded(viewer) // todo save camera state in userData? viewer.object3dManager.registerObjectExtension(this._objectExtension) } onRemove(viewer: ThreeViewer) { viewer.object3dManager.unregisterObjectExtension(this._objectExtension) super.onRemove(viewer) } addCamera(camera: ICamera, target?: IRenderTarget, addTargetPreview = false): VirtualCamera { if (!this._viewer) throw 'Plugin not added to viewer' target = target ?? this._viewer.renderManager.composerTarget.clone(true) target.name = camera.name + '_virtualCamTarget' const vCam: VirtualCamera = {camera, target, enabled: true} this.cameras.push(vCam) // todo: track for jitter in progressive or something else for jittering if (addTargetPreview) { const rt = this._viewer.getPlugin<RenderTargetPreviewPlugin>('RenderTargetPreviewPlugin') rt?.addTarget(target, camera.name, false, false, true) } return vCam } removeCamera(camera: ICamera): boolean { if (!this._viewer) throw 'Plugin not added to viewer' const index = this.cameras.findIndex(f => f.camera === camera) if (index >= 0) { const vCam = this.cameras[index] this.cameras.splice(index, 1) const rt = this._viewer.getPlugin<RenderTargetPreviewPlugin>('RenderTargetPreviewPlugin') rt?.removeTarget(vCam.target) vCam.target.dispose() return true } return false } }