Quellcode durchsuchen

Add context menu in RenderTargetPreviewPlugin, support half-float in renderTargetToDataUrl,

master
Palash Bansal vor 3 Jahren
Ursprung
Commit
d30fc61b34
Es ist kein Account mit der E-Mail-Adresse des Committers verbunden

+ 1
- 1
.gitignore Datei anzeigen

@@ -1,6 +1,6 @@
dist
docs
index.html
./index.html
examples/**/*.js
examples/**/*.js.map


+ 20
- 0
src/plugins/ui/RenderTargetPreviewPlugin.css Datei anzeigen

@@ -47,3 +47,23 @@
content: '+';
line-height: 20px;
}
.RenderTargetPreviewPluginContextMenu{
position: absolute;
background: #333e;
min-width: 8rem;
color: white;
font-size: 0.8rem;
overflow: hidden;
border-radius: 8px;
box-shadow: 2px 2px 10px #6666;
z-index: 100000;
font-family: monospace;
}
.RenderTargetPreviewPluginContextMenu div{
cursor: pointer;
padding: 6px 10px;
transition: background-color 0.25s ease-in-out;
}
.RenderTargetPreviewPluginContextMenu div:hover{
background: #1a1a1c;
}

+ 56
- 4
src/plugins/ui/RenderTargetPreviewPlugin.ts Datei anzeigen

@@ -1,9 +1,8 @@
import {ThreeViewer} from '../../viewer'
import {AViewerPluginSync, ThreeViewer} from '../../viewer'
import {IRenderTarget} from '../../rendering'
import {createDiv, createStyles, getOrCall, onChange, ValOrFunc} from 'ts-browser-helpers'
import {Vector4} from 'three'
import {Vector4, WebGLRenderTarget} from 'three'
import styles from './RenderTargetPreviewPlugin.css'
import {AViewerPluginSync} from '../../viewer/AViewerPlugin'

export class RenderTargetPreviewPlugin <TEvent extends string> extends AViewerPluginSync<TEvent> {
static readonly PluginType = 'RenderTargetPreviewPlugin'
@@ -47,7 +46,7 @@ export class RenderTargetPreviewPlugin <TEvent extends string> extends AViewerPl
if (!this._viewer) return

for (const target of this.targetBlocks) {
if (!target.visible) return
if (!target.visible) continue
const rt = getOrCall(target.target)
if (!rt) {
// todo draw white or pink
@@ -85,6 +84,35 @@ export class RenderTargetPreviewPlugin <TEvent extends string> extends AViewerPl
else div.classList.remove('RenderTargetPreviewPluginCollapsed')
this._viewer?.setDirty()
}
header.oncontextmenu = (e) => {
e.preventDefault()
e.stopPropagation()
const menu = document.createElement('div')
menu.classList.add('RenderTargetPreviewPluginContextMenu')
menu.style.left = e.clientX + 'px'
menu.style.top = e.clientY + 'px'
const download = document.createElement('div')
download.innerText = 'Download'
download.onclick = () => {
this.downloadTarget(target)
menu.remove()
}
const remove = document.createElement('div')
remove.innerText = 'Remove'
remove.onclick = () => {
this.removeTarget(target)
menu.remove()
}
const cancel = document.createElement('div')
cancel.innerText = 'Cancel'
cancel.onclick = () => {
menu.remove()
}
menu.appendChild(download)
menu.appendChild(remove)
menu.appendChild(cancel)
document.body.appendChild(menu)
}
div.appendChild(header)
this.mainDiv.appendChild(div)
this.targetBlocks.push(targetDef)
@@ -102,6 +130,29 @@ export class RenderTargetPreviewPlugin <TEvent extends string> extends AViewerPl
this.refreshUi()
return this
}
downloadTarget(target1: ValOrFunc<IRenderTarget|undefined>): this {
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
console.warn('todo: multi target')
return this
}
const canvas = this._viewer?.canvas
if (!canvas) return this
// todo: encoding?
const dataUrl = this._viewer.renderManager.renderTargetToDataUrl(target as WebGLRenderTarget, 'image/jpeg')
const link = document.createElement('a')
document.body.appendChild(link)
link.style.display = 'none'
link.href = dataUrl
link.download = 'image.png'
link.click()
document.body.removeChild(link)
return this
}

refreshUi(): void {
if (!this.mainDiv) return
@@ -114,6 +165,7 @@ export class RenderTargetPreviewPlugin <TEvent extends string> extends AViewerPl
if (!this.mainDiv.parentElement) this._viewer.container?.appendChild(this.mainDiv)
this.mainDiv.style.display = this.enabled ? 'flex' : 'none'
this.mainDiv.style.zIndex = parseInt(this._viewer.canvas.style.zIndex || '0') + 1 + ''
this._viewer?.setDirty()
}

dispose() {

+ 18
- 5
src/rendering/RenderManager.ts Datei anzeigen

@@ -1,4 +1,5 @@
import {
HalfFloatType,
IUniform,
NoColorSpace,
NoToneMapping,
@@ -10,9 +11,8 @@ import {
WebGLRenderer,
WebGLRenderTarget,
} from 'three'
import {IPassID, IPipelinePass, sortPasses} from '../postprocessing'
import {EffectComposer2, IPassID, IPipelinePass, sortPasses} from '../postprocessing'
import {IRenderTarget} from './RenderTarget'
import {EffectComposer2} from '../postprocessing/EffectComposer2'
import {RenderTargetManager} from './RenderTargetManager'
import {IShaderPropertiesUpdater} from '../materials'
import {
@@ -263,16 +263,29 @@ export class RenderManager extends RenderTargetManager<IRenderManagerEvent, IRen
}
}

/**
* Only to be used for testing. To do it properly, render the target to the main canvas(with proper encoding and type conversion) and call canvas.toDataURL()
* @param target
* @param mimeType
* @param quality
*/
renderTargetToDataUrl(target: WebGLRenderTarget, mimeType = 'image/png', quality = 90): string {
const buffer = new Uint8Array(target.width * target.height * 4)
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer)
const canvas = document.createElement('canvas')
canvas.width = target.width
canvas.height = target.height
const ctx = canvas.getContext('2d')
if (!ctx) throw new Error('Unable to get 2d context')
const imageData = ctx.createImageData(target.width, target.height, {colorSpace: ['display-p3', 'srgb'].includes(target.texture.colorSpace) ? <PredefinedColorSpace>target.texture.colorSpace : undefined})
imageData.data.set(buffer)
if (target.texture.type === HalfFloatType) {
const buffer = new Uint16Array(target.width * target.height * 4)
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, buffer)
for (let i = 0; i < buffer.length; i++) {
imageData.data[i] = buffer[i] / 15360 * 255 // todo check packing
}
} else {
// todo: handle rgbm to srgb conversion?
this._renderer.readRenderTargetPixels(target, 0, 0, target.width, target.height, imageData.data)
}
ctx.putImageData(imageData, 0, 0)
const string = canvas.toDataURL(mimeType, quality)
canvas.remove()

+ 7
- 2
src/rendering/RenderTargetManager.ts Datei anzeigen

@@ -144,10 +144,15 @@ export abstract class RenderTargetManager<E extends BaseEvent = BaseEvent, ET ex
if (Array.isArray(this.texture)) {
this.texture.forEach(t => {
t.colorSpace = options.colorSpace
t.toJSON = () => ({})
t.toJSON = () => {
console.warn('Multiple render target texture.toJSON not supported yet.')
return {}
}
})
} else {
this.texture.toJSON = () => ({}) // so that it doesn't get serialized
this.texture.toJSON = () => ({ // todo use readRenderTargetPixels as data url or data buffer.
isRenderTargetTexture: true,
}) // so that it doesn't get serialized
}
}


Laden…
Abbrechen
Speichern