Explorar el Código

Add support to preview, export RenderTargetTexture and DataTexture in tweakpaneImageInput

master
Palash Bansal hace 3 años
padre
commit
de5b708a1f
No account linked to committer's email address
Se han modificado 1 ficheros con 88 adiciones y 21 borrados
  1. 88
    21
      src/plugins/ui/tweakpane/tpImageInputGenerator.ts

+ 88
- 21
src/plugins/ui/tweakpane/tpImageInputGenerator.ts Ver fichero

import type {FolderApi} from 'tweakpane' import type {FolderApi} from 'tweakpane'
import {UiObjectConfig} from 'uiconfig.js' import {UiObjectConfig} from 'uiconfig.js'
import {imageBitmapToBase64, makeTextSvg} from 'ts-browser-helpers' import {imageBitmapToBase64, makeTextSvg} from 'ts-browser-helpers'
import {generateUUID} from '../../../three'
import {generateUUID, textureToDataUrl} from '../../../three'
import {ITexture, upgradeTexture} from '../../../core' import {ITexture, upgradeTexture} from '../../../core'
import {LinearSRGBColorSpace, RepeatWrapping, SRGBColorSpace, Texture} from 'three'
import {
FloatType,
HalfFloatType,
LinearSRGBColorSpace,
RepeatWrapping,
SRGBColorSpace,
Texture,
WebGLRenderTarget,
} from 'three'
import {CustomContextMenu} from '../../../utils' import {CustomContextMenu} from '../../../utils'
import {TweakpaneUiPlugin} from './TweakpaneUiPlugin' import {TweakpaneUiPlugin} from './TweakpaneUiPlugin'
import {IRenderTarget} from '../../../rendering'
import {EXRExporter2} from '../../../assetmanager'


const staticData = { const staticData = {
placeholderVal: 'placeholder', placeholderVal: 'placeholder',
tempMap: {} as any, tempMap: {} as any,
} }


function proxyGetValue(cc: any) {
function proxyGetValue(cc: any, viewer: ThreeViewer) {
if (cc?.get) cc = cc.get() if (cc?.get) cc = cc.get()
let ret: any = undefined let ret: any = undefined
if (!cc) return staticData.placeholderVal if (!cc) return staticData.placeholderVal
if (cc.isRenderTargetTexture && !cc.image.tp_src) {
cc.image.tp_src = staticData.renderTarImage
}
if (cc.isDataTexture && !cc.image.tp_src) {
cc.image.tp_src = staticData.dataTexImage
}
if (cc.isCompressedTexture && !cc.image.tp_src) { if (cc.isCompressedTexture && !cc.image.tp_src) {
cc.image.tp_src = staticData.compressedTexImage cc.image.tp_src = staticData.compressedTexImage
} }
// } // }
if (cc.isTexture) { if (cc.isTexture) {
// console.warn('here') // console.warn('here')
if (cc.image && (cc.image instanceof ImageBitmap || cc.image instanceof HTMLImageElement || cc.image instanceof HTMLVideoElement) && !cc.image.tp_src) {
cc.image.tp_src = imageBitmapToBase64(cc.image, 160)
if (cc.image && !cc.image.tp_src) {
if (cc.image instanceof ImageBitmap || cc.image instanceof HTMLImageElement || cc.image instanceof HTMLVideoElement) {
cc.image.tp_src = imageBitmapToBase64(cc.image, 160)
} else if (cc.isRenderTargetTexture) {
if (cc.__target) cc.image.tp_src = viewer.renderManager.renderTargetToDataUrl(cc.__target) // todo; update preview when renderTarget updates?
} else {
cc.image.tp_src = textureToDataUrl(cc, 160, false, 'image/png', 90) // this supports DataTexture also
}

if (!cc.image.tp_src) {
if (cc.isRenderTargetTexture) cc.image.tp_src = staticData.renderTarImage
else if (cc.isDataTexture) cc.image.tp_src = staticData.dataTexImage
}
} }
if (cc.image) { if (cc.image) {
ret = cc.image.tp_src_uuid ret = cc.image.tp_src_uuid
setterTex(isStr ? '' : null, config, renderer) setterTex(isStr ? '' : null, config, renderer)
} }


function downloadImage(config: UiObjectConfig) {
const cc = config.__proxy.value_
let vcv = cc?.image ?? config.uiRef.controller_.valueController.value.rawValue
if (vcv && (vcv instanceof ImageBitmap || vcv instanceof HTMLImageElement || vcv instanceof HTMLVideoElement) && !(vcv as any).src)
function downloadImage(config: UiObjectConfig, _: TweakpaneUiPlugin, viewer: ThreeViewer) {
CustomContextMenu.Remove()
const tex = config.__proxy.value_
if (!tex) return
let vcv = tex.image ?? config.uiRef.controller_.valueController.value.rawValue
if (tex.__rootBlob && !tex.__rootBlob.objectUrl) tex.__rootBlob.objectUrl = URL.createObjectURL(tex.__rootBlob)
let src = tex.__rootBlob ? tex.__rootBlob.objectUrl : tex.userData.rootPath || vcv?.src
let revokeSrc = false

// HTML image/video/bitmap
if (vcv && (vcv instanceof ImageBitmap || vcv instanceof HTMLImageElement || vcv instanceof HTMLVideoElement) && !src)
vcv = imageBitmapToBase64(vcv) vcv = imageBitmapToBase64(vcv)

let name = tex.__rootBlob ? tex.__rootBlob.name || 'image.' + (tex.__rootBlob.ext || 'png') : null

// Render target texture
if (!src && tex.isRenderTargetTexture) {
const target1 = tex.__target as IRenderTarget
if (target1.isWebGLRenderTarget) {
const val = viewer.renderManager.exportRenderTarget(target1 as WebGLRenderTarget)
if (!val) {
console.error('cannot export render target', vcv, tex, target1, config)
return
}
name = 'renderTarget.' + (val.ext || 'png')
src = URL.createObjectURL(val)
revokeSrc = true
} else {
console.error('Render target not supported', vcv, tex, target1, config)
return
}
}
// data texture
if (!src && tex.isDataTexture) {
if (tex.type !== HalfFloatType && tex.type !== FloatType) {
console.error('Only Float and HalfFloat Data texture export is supported', vcv, tex, config)
return
}
const buffer = new EXRExporter2().parse(undefined as any, tex)
const val: Blob|undefined = new Blob([buffer], {type: 'image/x-exr'})
if (!val) {
console.error('cannot export data texture', vcv, tex, config)
return
}
name = 'dataTexture.exr'
src = URL.createObjectURL(val)
}


if (!src) {
console.error('cannot export image', vcv, tex, config)
return
}

const link = document.createElement('a') const link = document.createElement('a')
document.body.appendChild(link) document.body.appendChild(link)
link.style.display = 'none' link.style.display = 'none'
link.href = vcv?.src ?? vcv
link.download = 'image.png'
// link.target = '_blank'
link.href = src
link.download = name || (src.startsWith('data:') ? 'image.png' : src.split('/').pop() ?? 'image.png')
link.target = '_blank'
link.click() link.click()
document.body.removeChild(link)
if (revokeSrc) setTimeout(()=>{
document.body.removeChild(link)
URL.revokeObjectURL(src)
}, 1000)
} }


async function imageFromUrl(renderer: TweakpaneUiPlugin, config: UiObjectConfig, viewer: ThreeViewer) { async function imageFromUrl(renderer: TweakpaneUiPlugin, config: UiObjectConfig, viewer: ThreeViewer) {
Object.defineProperty(config.__proxy, 'value', { Object.defineProperty(config.__proxy, 'value', {
get: () => { get: () => {
config.__proxy.value_ = renderer.methods.getValue(config) config.__proxy.value_ = renderer.methods.getValue(config)
return proxyGetValue(config.__proxy.value_)
return proxyGetValue(config.__proxy.value_, viewer)
}, },
set: (v: any) => { set: (v: any) => {
config.__proxy.value_ = renderer.methods.getValue(config) config.__proxy.value_ = renderer.methods.getValue(config)
const isPlaceholder = cv === staticData.placeholderVal || cv?.isPlaceholder const isPlaceholder = cv === staticData.placeholderVal || cv?.isPlaceholder
const items: any = isPlaceholder ? {} : { const items: any = isPlaceholder ? {} : {
['remove image']: () => removeImage(config, renderer), ['remove image']: () => removeImage(config, renderer),
['download image']: () => downloadImage(config),
['download image']: () => downloadImage(config, renderer, viewer),
} }
const menu = CustomContextMenu.Create({ const menu = CustomContextMenu.Create({
...items, ...items,

Cargando…
Cancelar
Guardar