import { BlendFunction } from 'postprocessing' import React, { useState } from 'react' import { useTranslation } from 'react-i18next' import { Color } from 'three' import { Engine } from '@xrengine/engine/src/ecs/classes/Engine' import { configureEffectComposer } from '@xrengine/engine/src/renderer/functions/configureEffectComposer' import { getPostProcessingSceneMetadataState } from '@xrengine/engine/src/renderer/WebGLRendererSystem' import { Effects } from '@xrengine/engine/src/scene/constants/PostProcessing' import { useHookstate } from '@xrengine/hyperflux' import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' import Checkbox from '@mui/material/Checkbox' import Collapse from '@mui/material/Collapse' import IconButton from '@mui/material/IconButton' import BooleanInput from '../inputs/BooleanInput' import ColorInput from '../inputs/ColorInput' import CompoundNumericInput from '../inputs/CompoundNumericInput' import InputGroup from '../inputs/InputGroup' import SelectInput from '../inputs/SelectInput' import styles from '../styles.module.scss' import NodeEditor from './NodeEditor' import PropertyGroup from './PropertyGroup' enum PropertyTypes { BlendFunction, Number, Boolean, Color, KernelSize, SMAAPreset, EdgeDetectionMode, PredicationMode } type EffectPropertyDetail = { propertyType: PropertyTypes; name: string; min?: number; max?: number; step?: number } type EffectPropertiesType = { [key: string]: EffectPropertyDetail } type EffectOptionsType = { [key in Effects]: EffectPropertiesType } const EffectsOptions: EffectOptionsType = { // FXAAEffect: { // blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' } // }, SMAAEffect: { blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, preset: { propertyType: PropertyTypes.SMAAPreset, name: 'Preset' }, edgeDetectionMode: { propertyType: PropertyTypes.EdgeDetectionMode, name: 'Edge Detection Mode' }, predicationMode: { propertyType: PropertyTypes.PredicationMode, name: 'Predication Mode' } }, OutlineEffect: { blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, edgeStrength: { propertyType: PropertyTypes.Number, name: 'Edge Strength', min: -1, max: 1, step: 0.01 }, pulseSpeed: { propertyType: PropertyTypes.Number, name: 'Pulse Speed', min: -1, max: 1, step: 0.01 }, visibleEdgeColor: { propertyType: PropertyTypes.Color, name: 'Visible Edge Color' }, hiddenEdgeColor: { propertyType: PropertyTypes.Color, name: 'Hidden Edge Color' }, resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: -1, max: 1, step: 0.01 }, kernelSize: { propertyType: PropertyTypes.KernelSize, name: 'Kernel Size' }, blur: { propertyType: PropertyTypes.Boolean, name: 'Blur' }, xRay: { propertyType: PropertyTypes.Boolean, name: 'XRay' } }, SSAOEffect: { blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, distanceScaling: { propertyType: PropertyTypes.Boolean, name: 'Distance Scaling' }, depthAwareUpsampling: { propertyType: PropertyTypes.Boolean, name: 'Depth Aware Upsampling' }, samples: { propertyType: PropertyTypes.Number, name: 'Samples', min: 1, max: 32, step: 1 }, rings: { propertyType: PropertyTypes.Number, name: 'Rings', min: -1, max: 1, step: 0.01 }, // Render up to a distance of ~20 world units. distanceThreshold: { propertyType: PropertyTypes.Number, name: 'Distance Threshold', min: -1, max: 1, step: 0.01 }, // with an additional ~2.5 units of falloff. distanceFalloff: { propertyType: PropertyTypes.Number, name: 'Distance Falloff', min: -1, max: 1, step: 0.01 }, minRadiusScale: { propertyType: PropertyTypes.Number, name: 'Min Radius Scale', min: -1, max: 1, step: 0.01 }, bias: { propertyType: PropertyTypes.Number, name: 'Bias', min: -1, max: 1, step: 0.01 }, radius: { propertyType: PropertyTypes.Number, name: 'Radius', min: -1, max: 1, step: 0.01 }, intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: -1, max: 1, step: 0.01 }, fade: { propertyType: PropertyTypes.Number, name: 'Fade', min: -1, max: 1, step: 0.01 } }, SSREffect: { intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: 0, max: 3, step: 0.01 }, exponent: { propertyType: PropertyTypes.Number, name: 'Exponent', min: 0.125, max: 8, step: 0.125 }, distance: { propertyType: PropertyTypes.Number, name: 'Distance', min: 0.001, max: 10, step: 0.1 }, fade: { propertyType: PropertyTypes.Number, name: 'Fade', min: 0.01, max: 20, step: 0.01 }, roughnessFade: { propertyType: PropertyTypes.Number, name: 'Roughness Fade', min: 0, max: 1, step: 0.01 }, thickness: { propertyType: PropertyTypes.Number, name: 'Thickness', min: 0, max: 10, step: 0.01 }, ior: { propertyType: PropertyTypes.Number, name: 'ior', min: 1, max: 2.33333, step: 0.01 }, maxRoughness: { propertyType: PropertyTypes.Number, name: 'Max Roughness', min: 0, max: 1, step: 0.01 }, maxDepthDifference: { propertyType: PropertyTypes.Number, name: 'Max Depth Difference', min: 0, max: 100, step: 0.1 }, blend: { propertyType: PropertyTypes.Number, name: 'Blend', min: 0, max: 1, step: 0.001 }, correction: { propertyType: PropertyTypes.Number, name: 'Correction', min: 0, max: 1, step: 0.0001 }, correctionRadius: { propertyType: PropertyTypes.Number, name: 'Correction Radius', min: 1, max: 4, step: 1 }, blur: { propertyType: PropertyTypes.Number, name: 'Blur', min: 0, max: 1, step: 0.01 }, blurKernel: { propertyType: PropertyTypes.Number, name: 'Blur Kernel', min: 0, max: 5, step: 1 }, blurSharpness: { propertyType: PropertyTypes.Number, name: 'Blur Sharpness', min: 0, max: 100, step: 1 }, jitter: { propertyType: PropertyTypes.Number, name: 'Jitter', min: 0, max: 4, step: 0.01 }, jitterRoughness: { propertyType: PropertyTypes.Number, name: 'Jitter Roughness', min: 0, max: 4, step: 0.01 }, steps: { propertyType: PropertyTypes.Number, name: 'Steps', min: 1, max: 256, step: 1 }, refineSteps: { propertyType: PropertyTypes.Number, name: 'Refine Steps', min: 0, max: 16, step: 1 }, missedRays: { propertyType: PropertyTypes.Boolean, name: 'Missed Rays' }, resolutionScale: { propertyType: PropertyTypes.Number, name: 'Resolution Scale', min: 0.125, max: 1, step: 0.125 }, velocityResolutionScale: { propertyType: PropertyTypes.Number, name: 'Velocity Resolution Scale', min: 0.125, max: 1, step: 0.125 } }, DepthOfFieldEffect: { blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, bokehScale: { propertyType: PropertyTypes.Number, name: 'Bokeh Scale', min: -1, max: 1, step: 0.01 }, focalLength: { propertyType: PropertyTypes.Number, name: 'Focal Length', min: -1, max: 1, step: 0.01 }, focusDistance: { propertyType: PropertyTypes.Number, name: 'Focus Distance', min: -1, max: 1, step: 0.01 } }, BloomEffect: { blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, kernelSize: { propertyType: PropertyTypes.KernelSize, name: 'Kernel Size' }, intensity: { propertyType: PropertyTypes.Number, name: 'Intensity', min: -1, max: 1, step: 0.01 }, luminanceSmoothing: { propertyType: PropertyTypes.Number, name: 'Luminance Smoothing', min: -1, max: 1, step: 0.01 }, luminanceThreshold: { propertyType: PropertyTypes.Number, name: 'Luminance Threshold', min: -1, max: 1, step: 0.01 } }, ToneMappingEffect: { blendFunction: { propertyType: PropertyTypes.BlendFunction, name: 'Blend Function' }, adaptive: { propertyType: PropertyTypes.Boolean, name: 'Adaptive' }, adaptationRate: { propertyType: PropertyTypes.Number, name: 'Adaptation Rate', min: -1, max: 1, step: 0.01 }, averageLuminance: { propertyType: PropertyTypes.Number, name: 'Average Luminance', min: -1, max: 1, step: 0.01 }, maxLuminance: { propertyType: PropertyTypes.Number, name: 'Max Luminance', min: -1, max: 1, step: 0.01 }, middleGrey: { propertyType: PropertyTypes.Number, name: 'Middle Grey', min: -1, max: 1, step: 0.01 } // resolution:{ propertyType:PostProcessingPropertyTypes.Number, name:"Resolution" } }, BrightnessContrastEffect: { brightness: { propertyType: PropertyTypes.Number, name: 'Brightness', min: -1, max: 1, step: 0.01 }, contrast: { propertyType: PropertyTypes.Number, name: 'Contrast', min: -1, max: 1, step: 0.01 } }, HueSaturationEffect: { hue: { propertyType: PropertyTypes.Number, name: 'Hue', min: -1, max: 1, step: 0.01 }, saturation: { propertyType: PropertyTypes.Number, name: 'Saturation', min: -1, max: 1, step: 0.01 } }, ColorDepthEffect: { bits: { propertyType: PropertyTypes.Number, name: 'Bits', min: -1, max: 1, step: 0.01 } }, LinearTosRGBEffect: {} } const BlendFunctionSelect = Object.entries(BlendFunction).map(([label, value]) => { return { label, value } }) const KernelSizeSelect = [ { label: 'VERY_SMALL', value: 0 }, { label: 'SMALL', value: 1 }, { label: 'MEDIUM', value: 2 }, { label: 'LARGE', value: 3 }, { label: 'VERY_LARGE', value: 4 }, { label: 'HUGE', value: 5 } ] const SMAAPreset = [ { label: 'LOW', value: 0 }, { label: 'MEDIUM', value: 1 }, { label: 'HIGH', value: 2 }, { label: 'ULTRA', value: 3 } ] const EdgeDetectionMode = [ { label: 'DEPTH', value: 0 }, { label: 'LUMA', value: 1 }, { label: 'COLOR', value: 2 } ] const PredicationMode = [ { label: 'DISABLED', value: 0 }, { label: 'DEPTH', value: 1 }, { label: 'CUSTOM', value: 2 } ] export const PostProcessingSettingsEditor = () => { const { t } = useTranslation() const [openSettings, setOpenSettings] = useState(false) const postprocessing = useHookstate(getPostProcessingSceneMetadataState(Engine.instance.currentWorld)) if (!postprocessing.value) return null const getPropertyValue = (keys: string[]): any => { if (keys.length < 1) return null let value = postprocessing.effects keys.forEach((element) => { if (value[element] != null && value[element] !== '') { value = value[element] } }) return value } const setPropertyValue = (prop, val) => { prop.set(val) // trigger re-render - @todo find out why just setting the value doesnt trigger the reactor configureEffectComposer() } const renderProperty = (propertyDetail: EffectPropertyDetail, propertyPath: string[], index: number) => { let renderVal = <> switch (propertyDetail.propertyType) { case PropertyTypes.Number: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} /> ) break case PropertyTypes.Boolean: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} value={getPropertyValue(propertyPath).value} /> ) break case PropertyTypes.BlendFunction: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} value={getPropertyValue(propertyPath).value} /> ) break case PropertyTypes.Color: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), '#' + value)} /> ) break case PropertyTypes.KernelSize: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} value={getPropertyValue(propertyPath).value} /> ) break case PropertyTypes.SMAAPreset: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} value={getPropertyValue(propertyPath).value} /> ) break case PropertyTypes.EdgeDetectionMode: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} value={getPropertyValue(propertyPath).value} /> ) break case PropertyTypes.PredicationMode: renderVal = ( setPropertyValue(getPropertyValue(propertyPath), value)} value={getPropertyValue(propertyPath).value} /> ) break default: renderVal = <>Can't Determine type of property } return (
{renderVal}
) } const renderEffectsTypes = (effectName: Effects) => { const effect = EffectsOptions[effectName] return Object.keys(effect).map((prop, index) => renderProperty(effect[prop], [effectName, prop], index)) } const renderEffects = () => { const items = Object.keys(EffectsOptions).map((effect: Effects) => { return (
postprocessing.effects[effect].isActive.set(e.target.checked)} checked={postprocessing.effects[effect]?.isActive?.value} /> {effect} {postprocessing.effects[effect].isActive.value &&
{renderEffectsTypes(effect)}
}
) }) return
{items}
} return ( postprocessing.enabled.set(val)} /> setOpenSettings(!openSettings)} className={styles.collapseBtn} aria-label="expand" size="small" > {openSettings ? : } {renderEffects()} ) }