diff --git a/javascript/MaterialXView/source/index.js b/javascript/MaterialXView/source/index.js index ddd1ec8ffc..5bec18a177 100644 --- a/javascript/MaterialXView/source/index.js +++ b/javascript/MaterialXView/source/index.js @@ -10,11 +10,12 @@ import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'; import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'; import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader.js'; +import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js'; import { Viewer } from './viewer.js' import { dropHandler, dragOverHandler, setLoadingCallback } from './dropHandling.js'; -let renderer, composer, orbitControls; +let renderer = null, composer = null, orbitControls = null, fxaaPass = null; // Turntable option. For now the step size is fixed. let turntableEnabled = false; @@ -69,6 +70,18 @@ function init() const gammaCorrectionPass = new ShaderPass(GammaCorrectionShader); composer.addPass(gammaCorrectionPass); + // Leave off for now as it does not seem to help that much and slows down rendering + // a fair bit. + var wantAntialias = false; + if (wantAntialias) + { + console.log("Using FXAA antialiasing"); + fxaaPass = new ShaderPass(FXAAShader); + const pixelRatio = renderer.getPixelRatio(); + fxaaPass.material.uniforms['resolution'].value.set(1 / (pixelRatio*window.innerWidth), 1 / (pixelRatio*window.innerHeight)); + composer.addPass(fxaaPass); + } + window.addEventListener('resize', onWindowResize); // Set up controls @@ -137,6 +150,15 @@ function onWindowResize() viewer.getScene().updateCamera(); viewer.getScene().setUpdateTransforms(); renderer.setSize(window.innerWidth, window.innerHeight); + + // Update FXAA pass + if (fxaaPass) + { + composer.setSize(window.innerWidth, window.innerHeight); + const pixelRatio = renderer.getPixelRatio(); + fxaaPass.material.uniforms['resolution'].value.set(1 / ( window.innerWidth * pixelRatio), 1 / + ( window.innerHeight * pixelRatio )); + } } function animate() diff --git a/javascript/MaterialXView/source/viewer.js b/javascript/MaterialXView/source/viewer.js index 01006530c8..01ca74ab44 100644 --- a/javascript/MaterialXView/source/viewer.js +++ b/javascript/MaterialXView/source/viewer.js @@ -395,7 +395,7 @@ export class Scene #_camera = null; // Background color - #_backgroundColor = 0x4c4c52; + #_backgroundColor = "rgb(128, 128, 128)"; // Background texture #_backgroundTexture = null; @@ -429,6 +429,19 @@ export class Editor this._gui = new GUI( { title: "Property Editor" } ); this._gui.close(); + + this.folderColor = '#333333'; + this.itemColor = '#334444'; + } + + getFolderColor() + { + return this.folderColor; + } + + getItemColor() + { + return this.itemColor; } // Update ui properties @@ -644,6 +657,8 @@ export class Material } } + viewer.updateUISettings(viewer.getEditor().getGUI()); + // Create a new shader for each material node. // Only create the shader once even if assigned more than once. var startGenTime = performance.now(); @@ -754,7 +769,7 @@ export class Material u_envMatrix: { value: getLightRotation() }, u_envRadiance: { value: radianceTexture }, u_envRadianceMips: { value: Math.trunc(Math.log2(Math.max(radianceTexture.image.width, radianceTexture.image.height))) + 1 }, - u_envRadianceSamples: { value: 16 }, + u_envRadianceSamples: { value: viewer.getEnvironmentSampleCount() }, u_envIrradiance: { value: irradianceTexture }, u_refractionEnv: { value: true } }); @@ -776,29 +791,38 @@ export class Material // Update property editor const gui = viewer.getEditor().getGUI(); - this.updateEditor(elem, shader, newMaterial, gui); + this.updateEditor(elem, shader, newMaterial, gui, viewer); if (logDetailedTime) console.log("- Per material generate time: ", performance.now() - startGenerateMat, "ms"); return newMaterial; - } + } // // Update property editor for a given MaterialX element, it's shader, and // Three material // - updateEditor(elem, shader, material, gui) + updateEditor(elem, shader, material, gui, viewer) { var startTime = performance.now(); const elemPath = elem.getNamePath(); - var matUI = gui.addFolder(elemPath + ' Properties'); - const uniformBlocks = Object.values(shader.getStage('pixel').getUniformBlocks()); + + const itemColor = viewer.getEditor().getItemColor(); + const folderColor = viewer.getEditor().getFolderColor(); + var uniformToUpdate; const ignoreList = ['u_envRadianceMips', 'u_envRadianceSamples', 'u_alphaThreshold']; + // Material UI + const uniformBlocks = Object.values(shader.getStage('pixel').getUniformBlocks()); var folderList = new Map(); + var matUI = gui.addFolder(elemPath + ' Properties'); + var dom = matUI.domElement; + var title = dom.querySelector('.title'); + title.style.backgroundColor = folderColor; + folderList[elemPath] = matUI; uniformBlocks.forEach(uniforms => @@ -825,8 +849,13 @@ export class Material continue; } - let currentNode = currentElem ? currentElem.getParent() : null; - let uiname; + let currentNode = null; + if (currentElem.getParent() && currentElem.getParent().getNamePath() != "") + { + currentNode = currentElem.getParent(); + } + let uiname = ""; + let nodeDefInput = null; if (currentNode) { let currentNodePath = currentNode.getNamePath(); @@ -843,10 +872,10 @@ export class Material // Check for ui attributes var nodeDef = currentNode.getNodeDef(); if (nodeDef) { - let input = nodeDef.getActiveInput(name); - if (input) { - uiname = input.getAttribute('uiname'); - let uifolderName = input.getAttribute('uifolder'); + nodeDefInput = nodeDef.getActiveInput(name); + if (nodeDefInput) { + uiname = nodeDefInput.getAttribute('uiname'); + let uifolderName = nodeDefInput.getAttribute('uifolder'); if (uifolderName && uifolderName.length) { let newFolderName = currentNodePath + '/' + uifolderName; currentFolder = folderList[newFolderName]; @@ -895,22 +924,57 @@ export class Material case 'float': uniformToUpdate = material.uniforms[name]; + var minValue = 0.0; + var maxValue = 100.0; + var step = 0; + if (nodeDefInput) { + if (nodeDefInput.hasAttribute('uimin')) + minValue = parseFloat(nodeDefInput.getAttribute('uimin')); + if (nodeDefInput.hasAttribute('uimax')) + maxValue = parseFloat(nodeDefInput.getAttribute('uimax')); + if (nodeDefInput.hasAttribute('uistep')) + step = parseFloat(nodeDefInput.getAttribute('uistep')); + } + if (step == 0) + { + step = (maxValue - minValue) / 1000.0; + } + //console.log(name, minValue, maxValue, step); if (uniformToUpdate && value != null) { - currentFolder.add(material.uniforms[name], 'value').name(path); + var w = currentFolder.add(material.uniforms[name], 'value', minValue, maxValue, step).name(path); + w.domElement.style.backgroundColor = itemColor; } break; case 'integer': + var minValue = 0; + var maxValue = 100; + var step = 0; + if (nodeDefInput) { + if (nodeDefInput.hasAttribute('uimin')) + minValue = parseInt(nodeDefInput.getAttribute('uimin')); + if (nodeDefInput.hasAttribute('uimax')) + maxValue = parseInt(nodeDefInput.getAttribute('uimax')); + if (nodeDefInput.hasAttribute('uistep')) + step = parseInt(nodeDefInput.getAttribute('uistep')); + } + if (step == 0) + { + step = 1 / (maxValue - minValue); + } + //console.log(name, minValue, maxValue); uniformToUpdate = material.uniforms[name]; if (uniformToUpdate && value != null) { - currentFolder.add(material.uniforms[name], 'value').name(path); + var w = currentFolder.add(material.uniforms[name], 'value', minValue, maxValue, step).name(path); + w.domElement.style.backgroundColor = itemColor; } break; case 'boolean': uniformToUpdate = material.uniforms[name]; if (uniformToUpdate && value != null) { - currentFolder.add(material.uniforms[name], 'value').name(path); + var w = currentFolder.add(material.uniforms[name], 'value').name(path); + w.domElement.style.backgroundColor = itemColor; } break; @@ -921,7 +985,8 @@ export class Material if (uniformToUpdate && value != null) { let vecFolder = currentFolder.addFolder(path); Object.keys(material.uniforms[name].value).forEach((key) => { - vecFolder.add(material.uniforms[name].value, key).name(path + "." + key); + var w = vecFolder.add(material.uniforms[name].value, key).name(path + "." + key); + w.domElement.style.backgroundColor = itemColor; }) } break; @@ -936,12 +1001,13 @@ export class Material const color3 = new THREE.Color(dummy.color); color3.fromArray(material.uniforms[name].value); dummy.color = color3.getHex(); - currentFolder.addColor(dummy, 'color').name(path) + let w = currentFolder.addColor(dummy, 'color').name(path) .onChange(function (value) { const color3 = new THREE.Color(value); material.uniforms[name].value.set(color3.toArray()); } ); + w.domElement.style.backgroundColor = itemColor; } break; @@ -959,11 +1025,15 @@ export class Material item = currentFolder.add(material.uniforms[name], 'value'); item.name(path); item.readonly(true); + item.domElement.style.backgroundColor = itemColor; } break; default: break; } + + // Start with sub-folders closed + currentFolder.close(); } } }); @@ -1002,6 +1072,14 @@ export class Viewer this.fileLoader = new THREE.FileLoader(); this.hdrLoader = new RGBELoader(); + + // Options + this.envSampleCount = 4; + } + + getEnvironmentSampleCount() + { + return this.envSampleCount; } // @@ -1046,6 +1124,75 @@ export class Viewer this.irradianceTexture = prepareEnvTexture(irradianceTexture, renderer.capabilities); } + updateEnvironmentSamples(sampleCount, controller) { + if (!sampleCount) + { + return; + } + + if (sampleCount > 64) + { + sampleCount = 64; + } + else + { + // If the value is not a power of 2, find the nearest power of 2 + sampleCount = Math.pow(2, Math.round(Math.log2(sampleCount))); + } + + // Update all shader uniforms + for (let i = 0; i < this.materials.length; i++) + { + let material = this.materials[i]; + if (material) + { + for (let j = 0; j < material._materials.length; j++) + { + if (material._materials[j]) + { + let shader = material._materials[j].getShader(); + shader.uniforms["u_envRadianceSamples"] = { value: sampleCount }; + } + } + } + } + + // Update the GUI to reflect the change + controller.setValue(sampleCount); + + this.envSampleCount = sampleCount; + } + + // Update UI settings + updateUISettings(gui) + { + // Settings UI + var settingsUI = gui.addFolder('Settings'); + var settingsUIDom = settingsUI.domElement; + var title = settingsUIDom.querySelector('.title'); + title.style.backgroundColor = this.getEditor().getFolderColor(); + + var dummy = { + value: this.envSampleCount + }; + var envSampleControl = settingsUI.add(dummy, 'value', 1); + if (envSampleControl) + { + envSampleControl.name('Environment Samples'); + envSampleControl.domElement.style.backgroundColor = this.getEditor().getItemColor(); + envSampleControl.onFinishChange(value => + this.updateEnvironmentSamples(value, envSampleControl)); + } + var dummyBool = { + bool: this.getScene().getShowBackgroundTexture() + }; + var w = settingsUI.add(dummyBool, 'bool').name('Show Environment'); + w.onChange(value => this.getScene().toggleBackgroundTexture()); + w.domElement.style.backgroundColor = this.getEditor().getItemColor(); + + settingsUI.close(); + } + getEditor() { return this.editor; }