| [key: string]: any | [key: string]: any | ||||
| } | } | ||||
| /** | |||||
| * Force a depth value in GBuffer. | |||||
| * This is useful to force center values like 0 to the depth. | |||||
| */ | |||||
| forcedLinearDepth?: number | |||||
| // todo: move these to respective plugins | // todo: move these to respective plugins | ||||
| property: [material, 'depthWrite'], | property: [material, 'depthWrite'], | ||||
| onChange: (ev)=>material.setDirty({uiChangeEvent: ev}), | onChange: (ev)=>material.setDirty({uiChangeEvent: ev}), | ||||
| }, | }, | ||||
| { | |||||
| type: 'checkbox', | |||||
| property: [material, 'colorWrite'], | |||||
| onChange: (ev)=>material.setDirty({uiChangeEvent: ev}), | |||||
| }, | |||||
| { | { | ||||
| type: 'slider', | type: 'slider', | ||||
| bounds: [0, 1], | bounds: [0, 1], |
| static CacheKeyForExtension(material: IMaterial, materialExtension: MaterialExtension): string { | static CacheKeyForExtension(material: IMaterial, materialExtension: MaterialExtension): string { | ||||
| let r = '' | let r = '' | ||||
| if (materialExtension.computeCacheKey) r += getOrCall(materialExtension.computeCacheKey, material) | if (materialExtension.computeCacheKey) r += getOrCall(materialExtension.computeCacheKey, material) | ||||
| else r += materialExtension.uuid | |||||
| if (materialExtension.extraDefines) r += Object.values(materialExtension.extraDefines).map(v=>getOrCall(v) ?? '').join('') | if (materialExtension.extraDefines) r += Object.values(materialExtension.extraDefines).map(v=>getOrCall(v) ?? '').join('') | ||||
| return r | return r | ||||
| } | } | ||||
| if (customMaterialExtensions) | if (customMaterialExtensions) | ||||
| for (const ext of customMaterialExtensions) { | for (const ext of customMaterialExtensions) { | ||||
| if (!ext.isCompatible || !ext.isCompatible(material) || material.materialExtensions.includes(ext)) continue | if (!ext.isCompatible || !ext.isCompatible(material) || material.materialExtensions.includes(ext)) continue | ||||
| else exts.push(ext) | |||||
| exts.push(ext) | |||||
| if (!ext.uuid) ext.uuid = generateUUID() | if (!ext.uuid) ext.uuid = generateUUID() | ||||
| if (!ext.__setDirty) ext.__setDirty = ()=>{ | if (!ext.__setDirty) ext.__setDirty = ()=>{ | ||||
| if (!ext.updateVersion) ext.updateVersion = 0 | if (!ext.updateVersion) ext.updateVersion = 0 | ||||
| if (!ext.setDirty) ext.setDirty = ext.__setDirty | if (!ext.setDirty) ext.setDirty = ext.__setDirty | ||||
| } | } | ||||
| if (!exts.length) return [] | |||||
| material.materialExtensions = [...material.materialExtensions || [], ...exts] | material.materialExtensions = [...material.materialExtensions || [], ...exts] | ||||
| .sort((a, b)=>(b.priority || 0) - (a.priority || 0)) | .sort((a, b)=>(b.priority || 0) - (a.priority || 0)) | ||||
| (material as any).__ext_afterRenderListen = true | (material as any).__ext_afterRenderListen = true | ||||
| material.addEventListener('afterRender', materialAfterRender) | material.addEventListener('afterRender', materialAfterRender) | ||||
| } | } | ||||
| material.needsUpdate = true | |||||
| return exts | return exts | ||||
| } | } | ||||
| * Check three.js docs for more info. | * Check three.js docs for more info. | ||||
| * Value can be a string or a function that returns a string | * Value can be a string or a function that returns a string | ||||
| * This will only be checked if `material.needsUpdate` is `true`, not on every render. | * This will only be checked if `material.needsUpdate` is `true`, not on every render. | ||||
| * Note: extension might never be registered if an empty string is returned. | |||||
| */ | */ | ||||
| computeCacheKey?: string | ((material: IMaterial) => string) | computeCacheKey?: string | ((material: IMaterial) => string) | ||||
| ['USE_DISPLACEMENTMAP']: this.uniforms.displacementMap.value ? 1 : undefined, | ['USE_DISPLACEMENTMAP']: this.uniforms.displacementMap.value ? 1 : undefined, | ||||
| ['DISPLACEMENTMAP_UV']: this.uniforms.displacementMap.value ? 'uv' : undefined, // todo use getChannel, see WebGLPrograms.js | ['DISPLACEMENTMAP_UV']: this.uniforms.displacementMap.value ? 'uv' : undefined, // todo use getChannel, see WebGLPrograms.js | ||||
| ['ALPHA_I_RGBA_PACKING']: material.userData.ALPHA_I_RGBA_PACKING ? 1 : undefined, | ['ALPHA_I_RGBA_PACKING']: material.userData.ALPHA_I_RGBA_PACKING ? 1 : undefined, | ||||
| ['FORCED_LINEAR_DEPTH']: material.userData.forcedLinearDepth ?? undefined, | |||||
| ['FORCED_LINEAR_DEPTH']: material.userData.forcedLinearDepth ?? undefined, // todo add to DepthBufferPlugin as well. | |||||
| }, material) | }, material) | ||||
| // todo: do the same in DepthBufferPlugin and NormalBufferPlugin | // todo: do the same in DepthBufferPlugin and NormalBufferPlugin |
| export {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer.js' | export {EffectComposer} from 'three/examples/jsm/postprocessing/EffectComposer.js' | ||||
| export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' | export {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js' | ||||
| export * from 'three/examples/jsm/utils/BufferGeometryUtils.js' |
| const warnEnabled = true | const warnEnabled = true | ||||
| const errorStr1 = 'shaderReplaceString: str must be passed if str is a RegExp and append/prepend is true' | |||||
| /** | /** | ||||
| * Replace a string in a shader function with added options to prepend, append, show warning when not found, and replace all occurrences. | * Replace a string in a shader function with added options to prepend, append, show warning when not found, and replace all occurrences. | ||||
| * @param shader - shader code | * @param shader - shader code | ||||
| * @param replaceAll - replace all occurrences | * @param replaceAll - replace all occurrences | ||||
| * @param prepend - prepend new string to old string | * @param prepend - prepend new string to old string | ||||
| * @param append - append new string to old string | * @param append - append new string to old string | ||||
| * @param _str - optional string to use for replacement. This must be passed if str is a RegExp and append/prepend is true | |||||
| */ | */ | ||||
| export function shaderReplaceString(shader: string, str: string|RegExp, newStr: string, { | export function shaderReplaceString(shader: string, str: string|RegExp, newStr: string, { | ||||
| replaceAll = false, | replaceAll = false, | ||||
| prepend = false, | prepend = false, | ||||
| append = false, | append = false, | ||||
| str: _str = undefined as string|undefined, | |||||
| } = {}) { | } = {}) { | ||||
| // todo: use safeReplaceString from ts-browser-helpers | // todo: use safeReplaceString from ts-browser-helpers | ||||
| const isStr = typeof str === 'string' | |||||
| if (warnEnabled /* && ThreeViewer.ViewerDebugging */) { | if (warnEnabled /* && ThreeViewer.ViewerDebugging */) { | ||||
| if (typeof str === 'string' ? !shader.includes(str) : !str.test(shader)) { | |||||
| if (isStr ? !shader.includes(str) : !str.test(shader)) { | |||||
| console.error(`${str} not found in shader`) | console.error(`${str} not found in shader`) | ||||
| return shader | return shader | ||||
| } | } | ||||
| } | } | ||||
| let s = newStr | let s = newStr | ||||
| _str = _str ?? (isStr ? str : undefined) | |||||
| if (prepend) { | if (prepend) { | ||||
| s = newStr + str | |||||
| if (typeof _str !== 'string') throw new Error(errorStr1) | |||||
| s = newStr + _str | |||||
| } else if (append) { | } else if (append) { | ||||
| s = str + newStr | |||||
| if (typeof _str !== 'string') throw new Error(errorStr1) | |||||
| s = _str + newStr | |||||
| } | } | ||||
| return replaceAll ? shader.replaceAll(str, s) : shader.replace(str, s) | return replaceAll ? shader.replaceAll(str, s) : shader.replace(str, s) | ||||
| } | } |