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.

195 lines 8.62 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { AViewerPluginSync } from '../../viewer'; import { createDiv, createStyles, getOrCall, onChange } from 'ts-browser-helpers'; import { SRGBColorSpace, Vector4 } from 'three'; import styles from './RenderTargetPreviewPlugin.css?inline'; import { CustomContextMenu } from '../../utils'; import { uiFolderContainer, uiToggle } from 'uiconfig.js'; import { ExtendedCopyPass } from '../../postprocessing'; /** * RenderTargetPreviewPlugin is a useful development and debugging plugin that renders any registered render-target to the screen in small collapsable panels. * * @category Plugins */ let RenderTargetPreviewPlugin = class RenderTargetPreviewPlugin extends AViewerPluginSync { constructor(enabled = true) { super(); this.enabled = true; this.toJSON = null; this.mainDiv = createDiv({ id: 'RenderTargetPreviewPluginContainer', addToBody: false }); this.targetBlocks = []; this._postRender = () => { if (!this._viewer) return; for (const targetBlock of this.targetBlocks) { if (!targetBlock.visible) continue; const rt = getOrCall(targetBlock.target); if (!rt) { // todo draw white or pink continue; } const rect = targetBlock.div.getBoundingClientRect(); let tex = rt.texture; const canvasRect = this._viewer.canvas.getBoundingClientRect(); rect.x = rect.x - canvasRect.x; rect.y = canvasRect.height + canvasRect.y - rect.y - rect.height; if (Array.isArray(tex)) { // todo support multi target this._viewer.console.warn('Multi target preview not supported yet, rendering just the first one'); tex = tex[0]; } const outputColorSpace = this._viewer.renderManager.webglRenderer.outputColorSpace; if (!targetBlock.originalColorSpace) this._viewer.renderManager.webglRenderer.outputColorSpace = SRGBColorSpace; this._viewer.renderManager.blit(null, { source: tex, clear: !targetBlock.transparent, // transparent: targetBlock.transparent, // todo respectColorSpace: !targetBlock.originalColorSpace, viewport: new Vector4(rect.x, rect.y, rect.width, rect.height), material: targetBlock.material, }); this._viewer.renderManager.webglRenderer.outputColorSpace = outputColorSpace; } }; this.enabled = enabled; } onAdded(viewer) { super.onAdded(viewer); viewer.addEventListener('postRender', this._postRender); this.stylesheet = createStyles(styles, viewer.container); this.refreshUi(); } onRemove(viewer) { viewer.removeEventListener('postRender', this._postRender); this.stylesheet?.remove(); this.stylesheet = undefined; this.refreshUi(); super.onRemove(viewer); } /** * * @param target - render target or a function that returns a render target * @param name - name of the target * @param transparent - if true, the target will be rendered with transparency * @param originalColorSpace - if true, the target will be rendered in its original color space * @param visible - initial visibility * @param material - snippet for {@link ExtendedCopyPass} or a custom {@link ExtendedShaderMaterial} or three.js ShaderMaterial. Example to read just the red channel `(s)=>s + ' = vec4(' + s + '.r);'` */ addTarget(target, name, transparent = false, originalColorSpace = false, visible = true, material) { if (!target) return this; const div = document.createElement('div'); const targetDef = { target, name, transparent, div, originalColorSpace, visible }; if (material) targetDef.material = material?.isMaterial ? material : new ExtendedCopyPass(material).material; div.classList.add('RenderTargetPreviewPluginTarget'); if (!targetDef.visible) div.classList.add('RenderTargetPreviewPluginCollapsed'); const header = document.createElement('div'); header.classList.add('RenderTargetPreviewPluginTargetHeader'); header.innerText = name; header.onclick = () => { targetDef.visible = !targetDef.visible; if (!targetDef.visible) div.classList.add('RenderTargetPreviewPluginCollapsed'); else div.classList.remove('RenderTargetPreviewPluginCollapsed'); this._viewer?.setDirty(); }; header.oncontextmenu = (e) => { e.preventDefault(); e.stopPropagation(); CustomContextMenu.Create({ 'Download': () => this.downloadTarget(target), 'Remove': () => this.removeTarget(target), }, e.clientX, e.clientY); }; div.appendChild(header); this.mainDiv.appendChild(div); this.targetBlocks.push(targetDef); this.refreshUi(); // todo auto remove target on dispose? return this; } removeTarget(target) { const index = this.targetBlocks.findIndex(t => t.target === target); if (index >= 0) { const t = this.targetBlocks[index]; this.targetBlocks.splice(index, 1); t.div.remove(); } this.refreshUi(); return this; } downloadTarget(target1) { if (!this._viewer) return this; const target = getOrCall(target1); if (!target) return this; const tex = target.texture; if (Array.isArray(tex)) { // todo support multi target this._viewer.dialog.alert('Multi target not supported yet'); this._viewer.console.warn('todo: support multi target export'); return this; } const canvas = this._viewer?.canvas; if (!canvas) return this; const blob = this._viewer.renderManager.exportRenderTarget(target); const url = URL.createObjectURL(blob); // todo use file transfer or viewer downloadBlob const link = document.createElement('a'); document.body.appendChild(link); link.style.display = 'none'; link.href = url; link.download = 'renderTarget.' + (blob.ext || 'png'); link.click(); document.body.removeChild(link); URL.revokeObjectURL(url); return this; } refreshUi() { if (!this.mainDiv) return; if (!this._viewer) { if (this.mainDiv.parentElement) this.mainDiv.remove(); this.mainDiv.style.display = 'none'; this.mainDiv.style.zIndex = '1000'; return; } if (!this.mainDiv.parentElement) this._viewer.container?.appendChild(this.mainDiv); this.mainDiv.style.display = !this.isDisabled() ? 'flex' : 'none'; this.mainDiv.style.zIndex = parseInt(this._viewer.canvas.style.zIndex || '0') + 1 + ''; this._viewer?.setDirty(); } setDirty() { this.refreshUi(); } dispose() { for (const target of this.targetBlocks) { this.removeTarget(target.target); } super.dispose(); } }; RenderTargetPreviewPlugin.PluginType = 'RenderTargetPreviewPlugin'; __decorate([ uiToggle('Enabled'), onChange(RenderTargetPreviewPlugin.prototype.refreshUi) ], RenderTargetPreviewPlugin.prototype, "enabled", void 0); RenderTargetPreviewPlugin = __decorate([ uiFolderContainer('Render Target Preview Plugin') ], RenderTargetPreviewPlugin); export { RenderTargetPreviewPlugin }; //# sourceMappingURL=RenderTargetPreviewPlugin.js.map