| import {ThreeViewer} from '../../../viewer' | import {ThreeViewer} from '../../../viewer' | ||||
| 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 {getOrCall, imageBitmapToBase64, makeTextSvg} from 'ts-browser-helpers' | |||||
| import {generateUUID, textureToDataUrl} from '../../../three' | import {generateUUID, textureToDataUrl} from '../../../three' | ||||
| import {ITexture, upgradeTexture} from '../../../core' | import {ITexture, upgradeTexture} from '../../../core' | ||||
| import { | import { | ||||
| dataTexImage: makeTextSvg('Data Texture'), | dataTexImage: makeTextSvg('Data Texture'), | ||||
| lutCubeTexImage: makeTextSvg('CUBE Texture'), | lutCubeTexImage: makeTextSvg('CUBE Texture'), | ||||
| compressedTexImage: makeTextSvg('Compressed Texture'), | compressedTexImage: makeTextSvg('Compressed Texture'), | ||||
| textureMap: {} as any, | |||||
| imageMap: {} as any, | imageMap: {} as any, | ||||
| tempMap: {} as any, | tempMap: {} as any, | ||||
| } | } | ||||
| } | } | ||||
| const setterTex = (v1: any, config: UiObjectConfig, renderer: TweakpaneUiPlugin)=>{ | const setterTex = (v1: any, config: UiObjectConfig, renderer: TweakpaneUiPlugin)=>{ | ||||
| if (v1?.isTexture) { | |||||
| if (v1 && v1.isTexture) { | |||||
| if (!v1.isDataTexture) { | if (!v1.isDataTexture) { | ||||
| const key = renderer.methods.getBinding(config)[1] + '' | const key = renderer.methods.getBinding(config)[1] + '' | ||||
| const isLinear = ['normalMap', 'aoMap', 'emissiveMap', 'roughnessMap', 'metalnessMap', 'displacementMap', 'bumpMap', 'alphaMap'].includes(key) | const isLinear = ['normalMap', 'aoMap', 'emissiveMap', 'roughnessMap', 'metalnessMap', 'displacementMap', 'bumpMap', 'alphaMap'].includes(key) | ||||
| } else { | } else { | ||||
| v1.needsUpdate = true | v1.needsUpdate = true | ||||
| } | } | ||||
| if (!staticData.textureMap[v1.image?.id]) staticData.textureMap[v1.image?.id] = v1 | |||||
| } | } | ||||
| config.__proxy.value_ = v1 | config.__proxy.value_ = v1 | ||||
| renderer.methods.setValue(config, v1, {last: true}, false) | renderer.methods.setValue(config, v1, {last: true}, false) | ||||
| setterTex(iMapKey, config, renderer) | setterTex(iMapKey, config, renderer) | ||||
| return | return | ||||
| } | } | ||||
| if (cc?.image === v | |||||
| || cc?.image?.src === v.src | |||||
| || cc?.image?.tp_src === v.tp_src && v.tp_src != null | |||||
| || cc?.image?.tp_src === v.src && v.src != null | |||||
| || cc?.image?.src === v.tp_src && v.tp_src != null | |||||
| ) return | |||||
| if (cc === v || cc && ( | |||||
| cc.image === v | |||||
| || cc.image?.src === v.src | |||||
| || cc.image?.tp_src === v.tp_src && v.tp_src != null | |||||
| || cc.image?.tp_src === v.src && v.src != null | |||||
| || cc.image?.src === v.tp_src && v.tp_src != null | |||||
| )) return | |||||
| if (v instanceof File) { // v.src must be from createObjectURL. | if (v instanceof File) { // v.src must be from createObjectURL. | ||||
| viewer.assetManager.importer.importSingle<ITexture>({file: v, path: (v as any).src}).then(texture => { | viewer.assetManager.importer.importSingle<ITexture>({file: v, path: (v as any).src}).then(texture => { | ||||
| } | } | ||||
| setterTex(texture, config, renderer) | setterTex(texture, config, renderer) | ||||
| }) | }) | ||||
| } else if (v.isTexture) { | |||||
| setterTex(v, config, renderer) | |||||
| } else { // HTMLImageElement, ImageBitmap, HTMLVideoElement | } else { // HTMLImageElement, ImageBitmap, HTMLVideoElement | ||||
| const tex: ITexture = new Texture(v) | |||||
| let tex: ITexture = staticData.textureMap[v.id] || staticData.textureMap[v.src] || staticData.textureMap[v.tp_src] | |||||
| if (tex) { | |||||
| setterTex(tex, config, renderer) | |||||
| return | |||||
| } | |||||
| tex = new Texture(v) | |||||
| upgradeTexture.call(tex) | upgradeTexture.call(tex) | ||||
| tex.assetType = 'texture' | tex.assetType = 'texture' | ||||
| tex.needsUpdate = true | tex.needsUpdate = true | ||||
| 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_, viewer) | |||||
| const ret = proxyGetValue(config.__proxy.value_, viewer) | |||||
| if (!staticData.textureMap[ret.id ?? ret]) staticData.textureMap[ret.id ?? ret] = config.__proxy.value_ | |||||
| return ret | |||||
| }, | }, | ||||
| set: (v: any) => { | set: (v: any) => { | ||||
| config.__proxy.value_ = renderer.methods.getValue(config) | |||||
| if (getOrCall(config.readOnly)) return | |||||
| config.__proxy.value_ = renderer.methods.getValue(config) // current value | |||||
| proxySetValue(v, config.__proxy.value_, config, viewer, renderer) | proxySetValue(v, config.__proxy.value_, config, viewer, renderer) | ||||
| }, | }, | ||||
| }) | }) | ||||
| const cv = config.uiRef.controller_.valueController.value.rawValue | const cv = config.uiRef.controller_.valueController.value.rawValue | ||||
| 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), | |||||
| ['download image']: () => downloadImage(config, renderer, viewer), | ['download image']: () => downloadImage(config, renderer, viewer), | ||||
| } | } | ||||
| const menu = CustomContextMenu.Create({ | |||||
| ...items, | |||||
| const readOnly = getOrCall(config.readOnly) | |||||
| if (!isPlaceholder && !readOnly) Object.assign(items, { | |||||
| ['remove image']: () => removeImage(config, renderer), | |||||
| }) | |||||
| if (!readOnly) Object.assign(items, { | |||||
| ['set/replace image']: () => inp.click(), | ['set/replace image']: () => inp.click(), | ||||
| ['from url']: async() => imageFromUrl(renderer, config, viewer), | ['from url']: async() => imageFromUrl(renderer, config, viewer), | ||||
| }) | |||||
| const menu = CustomContextMenu.Create({ | |||||
| ...items, | |||||
| 'cancel': () => {return}, | 'cancel': () => {return}, | ||||
| }, 2, rect.height + 8, false, true) | }, 2, rect.height + 8, false, true) | ||||
| target.parentElement?.appendChild(menu) | target.parentElement?.appendChild(menu) |