diff --git a/documents/Specification/MaterialX.Specification.md b/documents/Specification/MaterialX.Specification.md index 895b4756c5..42b6cbbcaf 100644 --- a/documents/Specification/MaterialX.Specification.md +++ b/documents/Specification/MaterialX.Specification.md @@ -1084,6 +1084,12 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m * `in1` (float or colorN or vectorN): the value or nodename for the primary input * `in2` (same type as `in1` or float): the modulo value or nodename to divide by, cannot be 0 in any channel; default is 1.0 in all channels, which effectively returns the fractional part of a float value + + +* **`invert`**: subtract the incoming float/color/vector from "amount" in all channels, outputting: `amount - in`. + * `in` (float or colorN or vectorN): the value or nodename for the primary input + * `amount` (same type as `in` or float): the value or nodename to subtract from; default is 1.0 in all channels + * **`absval`**: the per-channel absolute value of the incoming float/color/vector. diff --git a/javascript/MaterialXTest/browser/esslShaderGenerator.spec.js b/javascript/MaterialXTest/browser/esslShaderGenerator.spec.js index 657310aeeb..dceba112e0 100644 --- a/javascript/MaterialXTest/browser/esslShaderGenerator.spec.js +++ b/javascript/MaterialXTest/browser/esslShaderGenerator.spec.js @@ -1,6 +1,6 @@ // MaterialX is served through a script tag in the test setup. -function createStandardSurfaceMaterial(mx) +function createStandardSurfaceMaterial(mx) { const doc = mx.createDocument(); const ssName = 'SR_default'; @@ -15,19 +15,21 @@ function createStandardSurfaceMaterial(mx) return doc; } -describe('Generate ESSL Shaders', function () +describe('Generate ESSL Shaders', function () { let mx; const canvas = document.createElement('canvas'); const gl = canvas.getContext('webgl2'); - + this.timeout(60000); - before(async function () { + before(async function () + { mx = await MaterialX(); }); - it('Compile Shaders', () => { + it('Compile Shaders', () => + { const doc = createStandardSurfaceMaterial(mx); const gen = new mx.EsslShaderGenerator(); @@ -37,7 +39,7 @@ describe('Generate ESSL Shaders', function () doc.importLibrary(stdlib); const elem = mx.findRenderableElement(doc); - try + try { const mxShader = gen.generate(elem.getNamePath(), elem, genContext); @@ -47,7 +49,7 @@ describe('Generate ESSL Shaders', function () const glVertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(glVertexShader, vShader); gl.compileShader(glVertexShader); - if (!gl.getShaderParameter(glVertexShader, gl.COMPILE_STATUS)) + if (!gl.getShaderParameter(glVertexShader, gl.COMPILE_STATUS)) { console.error("-------- VERTEX SHADER FAILED TO COMPILE: ----------------"); console.error("--- VERTEX SHADER LOG ---"); @@ -61,7 +63,7 @@ describe('Generate ESSL Shaders', function () const glPixelShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(glPixelShader, fShader); gl.compileShader(glPixelShader); - if (!gl.getShaderParameter(glPixelShader, gl.COMPILE_STATUS)) + if (!gl.getShaderParameter(glPixelShader, gl.COMPILE_STATUS)) { console.error("-------- PIXEL SHADER FAILED TO COMPILE: ----------------"); console.error("--- PIXEL SHADER LOG ---"); @@ -75,7 +77,7 @@ describe('Generate ESSL Shaders', function () catch (errPtr) { console.error("-------- Failed code generation: ----------------"); - console.error(mx.getExceptionMessage(errPtr)); + console.error(mx.getExceptionMessage(errPtr)); } }); }); diff --git a/javascript/MaterialXTest/browser/karma.conf.js b/javascript/MaterialXTest/browser/karma.conf.js index aec860e8b5..6dd8051319 100644 --- a/javascript/MaterialXTest/browser/karma.conf.js +++ b/javascript/MaterialXTest/browser/karma.conf.js @@ -1,10 +1,11 @@ -module.exports = function(config) { +module.exports = function (config) +{ config.set({ basePath: '../', // base is the javascript folder files: [ { pattern: '_build/JsMaterialXGenShader.js', watched: false, included: true, served: true }, { pattern: '_build/JsMaterialXGenShader.wasm', watched: false, included: false, served: true }, - {pattern: '_build/JsMaterialXGenShader.data', watched: false, included: false, served: true, nocache: true }, + { pattern: '_build/JsMaterialXGenShader.data', watched: false, included: false, served: true, nocache: true }, { pattern: 'browser/*.spec.js', watched: true, included: true, served: true }, ], mime: { diff --git a/javascript/MaterialXTest/codeExamples.spec.js b/javascript/MaterialXTest/codeExamples.spec.js index 17f2559b49..8cff44c3fe 100644 --- a/javascript/MaterialXTest/codeExamples.spec.js +++ b/javascript/MaterialXTest/codeExamples.spec.js @@ -2,8 +2,10 @@ import { expect } from 'chai'; import Module from './_build/JsMaterialXCore.js'; import { getMtlxStrings } from './testHelpers'; -describe('Code Examples', () => { - it('Building a MaterialX Document', async () => { +describe('Code Examples', () => +{ + it('Building a MaterialX Document', async () => + { const mx = await Module(); // Create a document. const doc = mx.createDocument(); @@ -76,7 +78,8 @@ describe('Code Examples', () => { // expect(roughness.getBoundValue(material).getValueString()).to.equal('0.5'); }); - it('Traversing a Document Tree', async () => { + it('Traversing a Document Tree', async () => + { const xmlStr = getMtlxStrings( ['standard_surface_greysphere_calibration.mtlx'], '../../resources/Materials/Examples/StandardSurface' @@ -92,13 +95,16 @@ describe('Code Examples', () => { let elementCount = 0; let nodeCount = 0; let fileCount = 0; - for(let elem of elements) { + for (let elem of elements) + { elementCount++; // Display the filename of each image node. - if (elem.isANode('image')) { + if (elem.isANode('image')) + { nodeCount++; const input = elem.getInput('file'); - if (input) { + if (input) + { fileCount++; const filename = input.getValueString(); expect(elem.getName()).to.equal('image1'); @@ -111,7 +117,8 @@ describe('Code Examples', () => { expect(fileCount).to.equal(1); }); - it('Building a MaterialX Document', async () => { + it('Building a MaterialX Document', async () => + { const xmlStr = getMtlxStrings(['standard_surface_marble_solid.mtlx'], '../../resources/Materials/Examples/StandardSurface')[0]; const mx = await Module(); diff --git a/javascript/MaterialXTest/customBindings.spec.js b/javascript/MaterialXTest/customBindings.spec.js index e44cc7e85e..740189f0ef 100644 --- a/javascript/MaterialXTest/customBindings.spec.js +++ b/javascript/MaterialXTest/customBindings.spec.js @@ -2,15 +2,18 @@ import { expect } from 'chai'; import Module from './_build/JsMaterialXCore.js'; import { getMtlxStrings } from './testHelpers'; -describe('Custom Bindings', () => { +describe('Custom Bindings', () => +{ const examplesPath = '../../resources/Materials/Examples/StandardSurface'; - let mx; - before(async () => { + let mx; + before(async () => + { mx = await Module(); }); - it('Optional parameters work as expected', () => { + it('Optional parameters work as expected', () => + { const doc = mx.createDocument(); // Call a method without optional argument const nodeGraph = doc.addNodeGraph(); @@ -29,7 +32,8 @@ describe('Custom Bindings', () => { expect(() => { nodeGraph.addNode(); }).to.throw; }); - it('Vector <-> Array conversion', () => { + it('Vector <-> Array conversion', () => + { // Functions that return vectors in C++ should return an array in JS const doc = mx.createDocument(); const nodeGraph = doc.addNodeGraph(); @@ -66,7 +70,8 @@ describe('Custom Bindings', () => { expect(nodes[2].getName()).to.equal('anotherNode'); // Name set explicitly at creation time }); - it('C++ exception handling', () => { + it('C++ exception handling', () => + { // Exceptions that are thrown and caught in C++ shouldn't bubble up to JS const doc = mx.createDocument(); const nodeGraph1 = doc.addNodeGraph(); @@ -79,15 +84,18 @@ describe('Custom Bindings', () => { // Exceptions that are not caught in C++ should throw with an exception pointer nodeGraph1.addNode('node', 'node1'); expect(() => { nodeGraph1.addNode('node', 'node1'); }).to.throw; - try { + try + { nodeGraph1.addNode('node', 'node1'); - } catch (errPtr) { + } catch (errPtr) + { expect(errPtr).to.be.a('number'); expect(mx.getExceptionMessage(errPtr)).to.be.a('string'); } }); - it('getReferencedSourceUris', async () => { + it('getReferencedSourceUris', async () => + { const doc = mx.createDocument(); const filename = 'standard_surface_look_brass_tiled.mtlx'; await mx.readFromXmlFile(doc, filename, examplesPath); @@ -98,7 +106,8 @@ describe('Custom Bindings', () => { expect(sourceUris.includes('standard_surface_brass_tiled.mtlx')).to.be.true; }); - it('Should invoke correct instance of \'validate\'', () => { + it('Should invoke correct instance of \'validate\'', () => + { // We check whether the correct function is called by provoking an error message that is specific to the // function that we expect to be called. const message = {}; @@ -126,7 +135,8 @@ describe('Custom Bindings', () => { expect(message.message).to.include('Unit type definition does not exist in document') }); - it('StringResolver name substitution getters', () => { + it('StringResolver name substitution getters', () => + { const fnTestData = { fnKey: 'fnValue', fnKey1: 'fnValue1' @@ -156,7 +166,8 @@ describe('Custom Bindings', () => { expect(gnSubs).to.deep.equal(gnTestData); }); - it('getShaderNodes', async () => { + it('getShaderNodes', async () => + { const doc = mx.createDocument(); const fileNames = ['standard_surface_marble_solid.mtlx']; const mtlxStrs = getMtlxStrings(fileNames, examplesPath); @@ -175,14 +186,16 @@ describe('Custom Bindings', () => { expect(shaderNodes.length).to.equal(0); }); - it('createValidName', () => { + it('createValidName', () => + { const testString = '_Note_:Please,turn.this+-into*1#valid\nname for_me'; const replaceRegex = /[^a-zA-Z0-9_:]/g expect(mx.createValidName(testString)).to.equal(testString.replace(replaceRegex, '_')); expect(mx.createValidName(testString, '-')).to.equal(testString.replace(replaceRegex, '-')); }); - it('getVersionIntegers', () => { + it('getVersionIntegers', () => + { const versionStringArr = mx.getVersionString().split('.').map((value) => parseInt(value, 10)); // Global getVersionIntegers diff --git a/javascript/MaterialXTest/document.spec.js b/javascript/MaterialXTest/document.spec.js index 01dc828f3f..ac2adba9ff 100644 --- a/javascript/MaterialXTest/document.spec.js +++ b/javascript/MaterialXTest/document.spec.js @@ -1,30 +1,37 @@ import { expect } from 'chai'; import Module from './_build/JsMaterialXCore.js'; -describe('Document', () => { +describe('Document', () => +{ let mx, doc; - before(async () => { + before(async () => + { mx = await Module(); // Create a document. doc = mx.createDocument(); }); - function expectError(type, cb) { - try { + function expectError(type, cb) + { + try + { cb(); throw new Error('Expected function to throw!'); - } catch (exceptionPtr) { + } catch (exceptionPtr) + { const message = mx.getExceptionMessage(exceptionPtr); expect(message.indexOf(type) !== -1).to.be.true; } } let nodeGraph; - it('Build document', () => { + it('Build document', () => + { // Create a node graph with constant and image sources. nodeGraph = doc.addNodeGraph(); expect(nodeGraph).to.exist; - expectError('Child name is not unique: nodegraph1', () => { + expectError('Child name is not unique: nodegraph1', () => + { doc.addNodeGraph(nodeGraph.getName()); }); const constant = nodeGraph.addNode('constant'); diff --git a/javascript/MaterialXTest/element.spec.js b/javascript/MaterialXTest/element.spec.js index e22c374851..cbb672c5c2 100644 --- a/javascript/MaterialXTest/element.spec.js +++ b/javascript/MaterialXTest/element.spec.js @@ -1,145 +1,166 @@ import { expect } from 'chai'; import Module from './_build/JsMaterialXCore.js'; -describe('Element', () => { - let mx, doc, valueTypes; +describe('Element', () => +{ + let mx, doc, valueTypes; - const primitiveValueTypes = { - Integer: 10, - Boolean: true, - String: 'test', - Float: 15, - IntegerArray: [1,2,3,4,5], - FloatArray: [12, 14], // Not using actual floats to avoid precision problems - StringArray: ['first', 'second'], - BooleanArray:[true, true, false], - } + const primitiveValueTypes = { + Integer: 10, + Boolean: true, + String: 'test', + Float: 15, + IntegerArray: [1, 2, 3, 4, 5], + FloatArray: [12, 14], // Not using actual floats to avoid precision problems + StringArray: ['first', 'second'], + BooleanArray: [true, true, false], + } - before(async () => { - mx = await Module(); - doc = mx.createDocument(); - valueTypes = { - Color3: new mx.Color3(1, 0, 0.5), - Color4: new mx.Color4(0, 1, 0.5, 1), - Vector2: new mx.Vector2(0, 1), - Vector3: new mx.Vector3(0, 1, 2), - Vector4: new mx.Vector4(0, 1, 2, 1), - Matrix33: new mx.Matrix33(0, 1, 2, 3, 4, 5, 6, 7, 8), - Matrix44: new mx.Matrix44(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), - }; - }); + before(async () => + { + mx = await Module(); + doc = mx.createDocument(); + valueTypes = { + Color3: new mx.Color3(1, 0, 0.5), + Color4: new mx.Color4(0, 1, 0.5, 1), + Vector2: new mx.Vector2(0, 1), + Vector3: new mx.Vector3(0, 1, 2), + Vector4: new mx.Vector4(0, 1, 2, 1), + Matrix33: new mx.Matrix33(0, 1, 2, 3, 4, 5, 6, 7, 8), + Matrix44: new mx.Matrix44(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15), + }; + }); - describe('value setters', () => { - const checkValue = (types, assertionCallback) => { - const elem = doc.addChildOfCategory('geomprop'); - Object.keys(types).forEach((typeName) => { - const setFn = `setValue${typeName}`; - elem[setFn](types[typeName]); - assertionCallback(elem.getValue().getData(), typeName); - }); - }; + describe('value setters', () => + { + const checkValue = (types, assertionCallback) => + { + const elem = doc.addChildOfCategory('geomprop'); + Object.keys(types).forEach((typeName) => + { + const setFn = `setValue${typeName}`; + elem[setFn](types[typeName]); + assertionCallback(elem.getValue().getData(), typeName); + }); + }; - it('should work with expected type', () => { - checkValue(valueTypes, (returnedValue, typeName) => { - expect(returnedValue).to.be.an.instanceof(mx[`${typeName}`]); - expect(returnedValue.equals(valueTypes[typeName])).to.equal(true); - }); - }); + it('should work with expected type', () => + { + checkValue(valueTypes, (returnedValue, typeName) => + { + expect(returnedValue).to.be.an.instanceof(mx[`${typeName}`]); + expect(returnedValue.equals(valueTypes[typeName])).to.equal(true); + }); + }); - it('should work with expected primitive type', () => { - checkValue(primitiveValueTypes, (returnedValue, typeName) => { - expect(returnedValue).to.eql(primitiveValueTypes[typeName]); - }); - }); + it('should work with expected primitive type', () => + { + checkValue(primitiveValueTypes, (returnedValue, typeName) => + { + expect(returnedValue).to.eql(primitiveValueTypes[typeName]); + }); + }); - it('should fail for incorrect type', () => { - const elem = doc.addChildOfCategory('geomprop'); - expect(()=>elem.Matrix33(true)).to.throw(); + it('should fail for incorrect type', () => + { + const elem = doc.addChildOfCategory('geomprop'); + expect(() => elem.Matrix33(true)).to.throw(); + }); }); - }); - describe('typed value setters', () => { - const checkTypes = (types, assertionCallback) => { - const elem = doc.addChildOfCategory('geomprop'); - Object.keys(types).forEach((typeName) => { - const setFn = `setTypedAttribute${typeName}`; - const getFn = `getTypedAttribute${typeName}`; - elem[setFn](typeName, types[typeName]); - assertionCallback(elem[getFn](typeName), types[typeName]); - }); - }; + describe('typed value setters', () => + { + const checkTypes = (types, assertionCallback) => + { + const elem = doc.addChildOfCategory('geomprop'); + Object.keys(types).forEach((typeName) => + { + const setFn = `setTypedAttribute${typeName}`; + const getFn = `getTypedAttribute${typeName}`; + elem[setFn](typeName, types[typeName]); + assertionCallback(elem[getFn](typeName), types[typeName]); + }); + }; - it('should work with expected custom type', () => { - checkTypes(valueTypes, (returnedValue, originalValue) => { - expect(returnedValue.equals(originalValue)).to.equal(true); - }); - }); + it('should work with expected custom type', () => + { + checkTypes(valueTypes, (returnedValue, originalValue) => + { + expect(returnedValue.equals(originalValue)).to.equal(true); + }); + }); - it('should work with expected primitive type', () => { - checkTypes(primitiveValueTypes, (returnedValue, originalValue) => { - expect(returnedValue).to.eql(originalValue); - }); - }); + it('should work with expected primitive type', () => + { + checkTypes(primitiveValueTypes, (returnedValue, originalValue) => + { + expect(returnedValue).to.eql(originalValue); + }); + }); - it('should fail for incorrect type', () => { - const elem = doc.addChildOfCategory('geomprop'); - expect(()=>elem.setTypedAttributeColor3('wrongType', true)).to.throw(); + it('should fail for incorrect type', () => + { + const elem = doc.addChildOfCategory('geomprop'); + expect(() => elem.setTypedAttributeColor3('wrongType', true)).to.throw(); + }); }); - }); - it('factory invocation should match specialized functions', () => { - // List based in source/MaterialXCore/Element.cpp - const elemtypeArr = [ - 'Backdrop', - 'Collection', - 'GeomInfo', - 'MaterialAssign', - 'PropertySetAssign', - 'Visibility', - 'GeomPropDef', - 'Look', - 'LookGroup', - 'PropertySet', - 'TypeDef', - 'AttributeDef', - 'NodeGraph', - 'Implementation', - 'Node', - 'NodeDef', - 'Variant', - 'Member', - 'TargetDef', - 'GeomProp', - 'Input', - 'Output', - 'Property', - 'PropertyAssign', - 'Unit', - 'UnitDef', - 'UnitTypeDef', - 'VariantAssign', - 'VariantSet', - ]; + it('factory invocation should match specialized functions', () => + { + // List based in source/MaterialXCore/Element.cpp + const elemtypeArr = [ + 'Backdrop', + 'Collection', + 'GeomInfo', + 'MaterialAssign', + 'PropertySetAssign', + 'Visibility', + 'GeomPropDef', + 'Look', + 'LookGroup', + 'PropertySet', + 'TypeDef', + 'AttributeDef', + 'NodeGraph', + 'Implementation', + 'Node', + 'NodeDef', + 'Variant', + 'Member', + 'TargetDef', + 'GeomProp', + 'Input', + 'Output', + 'Property', + 'PropertyAssign', + 'Unit', + 'UnitDef', + 'UnitTypeDef', + 'VariantAssign', + 'VariantSet', + ]; - elemtypeArr.forEach((typeName) => { - const specializedFn = `addChild${typeName}`; - const factoryName = typeName.toLowerCase(); - const type = mx[typeName]; - expect(doc[specializedFn]()).to.be.an.instanceof(type); - expect(doc.addChildOfCategory(factoryName)).to.be.an.instanceof(type); - }); + elemtypeArr.forEach((typeName) => + { + const specializedFn = `addChild${typeName}`; + const factoryName = typeName.toLowerCase(); + const type = mx[typeName]; + expect(doc[specializedFn]()).to.be.an.instanceof(type); + expect(doc.addChildOfCategory(factoryName)).to.be.an.instanceof(type); + }); - const specialElemType = { - 'MaterialX': mx.Document, - 'Comment': mx.CommentElement, - 'Generic': mx.GenericElement, - }; + const specialElemType = { + 'MaterialX': mx.Document, + 'Comment': mx.CommentElement, + 'Generic': mx.GenericElement, + }; - Object.keys(specialElemType).forEach((typeName) => { - const specializedFn = `addChild${typeName}`; - const factoryName = typeName.toLowerCase(); - expect(doc[specializedFn]()).to.be.an.instanceof(specialElemType[typeName]); - expect(doc.addChildOfCategory(factoryName)).to.be.an.instanceof(specialElemType[typeName]); + Object.keys(specialElemType).forEach((typeName) => + { + const specializedFn = `addChild${typeName}`; + const factoryName = typeName.toLowerCase(); + expect(doc[specializedFn]()).to.be.an.instanceof(specialElemType[typeName]); + expect(doc.addChildOfCategory(factoryName)).to.be.an.instanceof(specialElemType[typeName]); + }); }); - }); }); diff --git a/javascript/MaterialXTest/environ.spec.js b/javascript/MaterialXTest/environ.spec.js index 2ac71dc044..2277e0b259 100644 --- a/javascript/MaterialXTest/environ.spec.js +++ b/javascript/MaterialXTest/environ.spec.js @@ -1,13 +1,16 @@ import { expect } from 'chai';; import Module from './_build/JsMaterialXCore.js'; -describe('Environ', () => { +describe('Environ', () => +{ let mx; - before(async () => { + before(async () => + { mx = await Module(); }); - it('Environment variables', () => { + it('Environment variables', () => + { expect(mx.getEnviron(mx.MATERIALX_SEARCH_PATH_ENV_VAR)).to.equal(''); mx.setEnviron(mx.MATERIALX_SEARCH_PATH_ENV_VAR, 'test'); expect(mx.getEnviron(mx.MATERIALX_SEARCH_PATH_ENV_VAR)).to.equal('test'); diff --git a/javascript/MaterialXTest/testHelpers.js b/javascript/MaterialXTest/testHelpers.js index c434400c6a..a75c2e6144 100644 --- a/javascript/MaterialXTest/testHelpers.js +++ b/javascript/MaterialXTest/testHelpers.js @@ -1,9 +1,11 @@ var fs = require('fs'); var path = require('path'); -export function getMtlxStrings(fileNames, subPath) { +export function getMtlxStrings(fileNames, subPath) +{ const mtlxStrs = []; - for (let i = 0; i < fileNames.length; i++) { + for (let i = 0; i < fileNames.length; i++) + { const p = path.resolve(subPath, fileNames[parseInt(i, 10)]); const t = fs.readFileSync(p, 'utf8'); mtlxStrs.push(t); diff --git a/javascript/MaterialXTest/traversal.spec.js b/javascript/MaterialXTest/traversal.spec.js index 4f422bb5cc..62a895f852 100644 --- a/javascript/MaterialXTest/traversal.spec.js +++ b/javascript/MaterialXTest/traversal.spec.js @@ -1,13 +1,16 @@ import { expect } from 'chai'; import Module from './_build/JsMaterialXCore.js'; -describe('Traversal', () => { +describe('Traversal', () => +{ let mx; - before(async () => { + before(async () => + { mx = await Module(); }); - it('Traverse Graph', () => { + it('Traverse Graph', () => + { // Create a document. const doc = mx.createDocument(); // Create a node graph with the following structure: @@ -36,67 +39,79 @@ describe('Traversal', () => { mix.setConnectedNode('bg', contrast); mix.setConnectedNode('mask', noise3d); output.setConnectedNode(mix); - + expect(doc.validate()).to.be.true; - + // Traverse the document tree (implicit iterator). let nodeCount = 0; - for (let elem of doc.traverseTree()) { - if (elem instanceof mx.Node) { + for (let elem of doc.traverseTree()) + { + if (elem instanceof mx.Node) + { nodeCount++; } } expect(nodeCount).to.equal(7); - + // Traverse the document tree (explicit iterator) let treeIter = doc.traverseTree(); nodeCount = 0; let maxElementDepth = 0; - for (let elem of treeIter) { - if (elem instanceof mx.Node) { + for (let elem of treeIter) + { + if (elem instanceof mx.Node) + { nodeCount++; } maxElementDepth = Math.max(maxElementDepth, treeIter.getElementDepth()); } expect(nodeCount).to.equal(7); expect(maxElementDepth).to.equal(3); - + // Traverse the document tree (prune subtree). nodeCount = 0; treeIter = doc.traverseTree(); - for (let elem of treeIter) { - if (elem instanceof mx.Node) { + for (let elem of treeIter) + { + if (elem instanceof mx.Node) + { nodeCount++; } - if (elem instanceof mx.NodeGraph) { + if (elem instanceof mx.NodeGraph) + { treeIter.setPruneSubtree(true); } } expect(nodeCount).to.equal(0); - + // Traverse upstream from the graph output (implicit iterator) nodeCount = 0; - for (let edge of output.traverseGraph()) { + for (let edge of output.traverseGraph()) + { const upstreamElem = edge.getUpstreamElement(); const connectingElem = edge.getConnectingElement(); const downstreamElem = edge.getDownstreamElement(); - if (upstreamElem instanceof mx.Node) { + if (upstreamElem instanceof mx.Node) + { nodeCount++; - if (downstreamElem instanceof mx.Node) { + if (downstreamElem instanceof mx.Node) + { expect(connectingElem instanceof mx.Input).to.be.true; } } } expect(nodeCount).to.equal(7); - + // Traverse upstream from the graph output (explicit iterator) nodeCount = 0; maxElementDepth = 0; let maxNodeDepth = 0; let graphIter = output.traverseGraph(); - for (let edge of graphIter) { + for (let edge of graphIter) + { const upstreamElem = edge.getUpstreamElement(); - if (upstreamElem instanceof mx.Node) { + if (upstreamElem instanceof mx.Node) + { nodeCount++; } maxElementDepth = Math.max(maxElementDepth, graphIter.getElementDepth()); @@ -105,22 +120,25 @@ describe('Traversal', () => { expect(nodeCount).to.equal(7); expect(maxElementDepth).to.equal(3); expect(maxNodeDepth).to.equal(3); - + // Traverse upstream from the graph output (prune subgraph) nodeCount = 0; graphIter = output.traverseGraph(); - for (let edge of graphIter) { + for (let edge of graphIter) + { const upstreamElem = edge.getUpstreamElement(); expect(upstreamElem.getSelf()).to.be.an.instanceof(mx.Element); - if (upstreamElem instanceof mx.Node) { + if (upstreamElem instanceof mx.Node) + { nodeCount++; } - if (upstreamElem.getCategory() === 'multiply') { + if (upstreamElem.getCategory() === 'multiply') + { graphIter.setPruneSubgraph(true); } } expect(nodeCount).to.equal(5); - + // Create and detect a cycle multiply.setConnectedNode('in2', mix); expect(output.hasUpstreamCycle()).to.be.true; @@ -128,7 +146,7 @@ describe('Traversal', () => { multiply.setConnectedNode('in2', constant); expect(output.hasUpstreamCycle()).to.be.false; expect(doc.validate()).to.be.true; - + // Create and detect a loop contrast.setConnectedNode('in', contrast); expect(output.hasUpstreamCycle()).to.be.true; @@ -137,10 +155,12 @@ describe('Traversal', () => { expect(output.hasUpstreamCycle()).to.be.false; expect(doc.validate()).to.be.true; }); - - describe("Traverse inheritance", () => { + + describe("Traverse inheritance", () => + { let nodeDefInheritanceLevel2, nodeDefInheritanceLevel1, nodeDefParent; - beforeEach(() => { + beforeEach(() => + { const doc = mx.createDocument(); nodeDefParent = doc.addNodeDef(); nodeDefParent.setName('BaseClass'); @@ -152,23 +172,29 @@ describe('Traversal', () => { nodeDefInheritanceLevel1.setInheritsFrom(nodeDefParent); }); - it('for of loop', () => { + it('for of loop', () => + { const inheritanceIterator = nodeDefInheritanceLevel2.traverseInheritance(); let inheritanceChainLength = 0; - for(const elem of inheritanceIterator) { - if (elem instanceof mx.NodeDef) { + for (const elem of inheritanceIterator) + { + if (elem instanceof mx.NodeDef) + { inheritanceChainLength++; } } expect(inheritanceChainLength).to.equal(2);; }); - it('while loop', () => { + it('while loop', () => + { const inheritanceIterator = nodeDefInheritanceLevel2.traverseInheritance(); let inheritanceChainLength = 0; let elem = inheritanceIterator.next(); - while (!elem.done) { - if (elem.value instanceof mx.NodeDef) { + while (!elem.done) + { + if (elem.value instanceof mx.NodeDef) + { inheritanceChainLength++; } elem = inheritanceIterator.next(); diff --git a/javascript/MaterialXTest/types.spec.js b/javascript/MaterialXTest/types.spec.js index 7fae8b30c7..fc909c8c94 100644 --- a/javascript/MaterialXTest/types.spec.js +++ b/javascript/MaterialXTest/types.spec.js @@ -1,13 +1,16 @@ import { expect } from 'chai';; import Module from './_build/JsMaterialXCore.js'; -describe('Types', () => { +describe('Types', () => +{ let mx; - before(async () => { + before(async () => + { mx = await Module(); }); - it('Vectors', () => { + it('Vectors', () => + { const v1 = new mx.Vector3(1, 2, 3); let v2 = new mx.Vector3(2, 4, 6); @@ -59,10 +62,13 @@ describe('Types', () => { expect(v4.notEquals(v2)).to.be.true; }); - function multiplyMatrix(matrix, val) { + function multiplyMatrix(matrix, val) + { const clonedMatrix = matrix.copy(); - for (let i = 0; i < clonedMatrix.numRows(); ++i) { - for (let k = 0; k < clonedMatrix.numColumns(); ++k) { + for (let i = 0; i < clonedMatrix.numRows(); ++i) + { + for (let k = 0; k < clonedMatrix.numColumns(); ++k) + { const v = clonedMatrix.getItem(i, k); clonedMatrix.setItem(i, k, v * val); } @@ -70,10 +76,13 @@ describe('Types', () => { return clonedMatrix; } - function divideMatrix(matrix, val) { + function divideMatrix(matrix, val) + { const clonedMatrix = matrix.copy(); - for (let i = 0; i < clonedMatrix.numRows(); ++i) { - for (let k = 0; k < clonedMatrix.numColumns(); ++k) { + for (let i = 0; i < clonedMatrix.numRows(); ++i) + { + for (let k = 0; k < clonedMatrix.numColumns(); ++k) + { const v = clonedMatrix.getItem(i, k); clonedMatrix.setItem(i, k, v / val); } @@ -81,18 +90,19 @@ describe('Types', () => { return clonedMatrix; } - it('Matrices', () => { + it('Matrices', () => + { // Translation and scale const trans = mx.Matrix44.createTranslation(new mx.Vector3(1, 2, 3)); const scale = mx.Matrix44.createScale(new mx.Vector3(2, 2, 2)); expect(trans.equals(new mx.Matrix44(1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 1, 2, 3, 1))); + 0, 1, 0, 0, + 0, 0, 1, 0, + 1, 2, 3, 1))); expect(scale.equals(new mx.Matrix44(2, 0, 0, 0, - 0, 2, 0, 0, - 0, 0, 2, 0, - 0, 0, 0, 1))); + 0, 2, 0, 0, + 0, 0, 2, 0, + 0, 0, 0, 1))); // Indexing operators expect(trans.getItem(3, 2)).to.equal(3); @@ -103,9 +113,9 @@ describe('Types', () => { // Matrix methods expect(trans.getTranspose().equals( new mx.Matrix44(1, 0, 0, 1, - 0, 1, 0, 2, - 0, 0, 1, 3, - 0, 0, 0, 1) + 0, 1, 0, 2, + 0, 0, 1, 3, + 0, 0, 0, 1) )).to.be.true; expect(scale.getTranspose().equals(scale)).to.be.true; expect(trans.getDeterminant()).to.equal(1); @@ -120,17 +130,17 @@ describe('Types', () => { let prod4 = trans; prod4 = prod4.multiply(scale); expect(prod1.equals(new mx.Matrix44(2, 0, 0, 0, - 0, 2, 0, 0, - 0, 0, 2, 0, - 2, 4, 6, 1))); + 0, 2, 0, 0, + 0, 0, 2, 0, + 2, 4, 6, 1))); expect(prod2.equals(new mx.Matrix44(2, 0, 0, 0, - 0, 2, 0, 0, - 0, 0, 2, 0, - 1, 2, 3, 1))); + 0, 2, 0, 0, + 0, 0, 2, 0, + 1, 2, 3, 1))); expect(prod3.equals(new mx.Matrix44(2, 0, 0, 0, - 0, 2, 0, 0, - 0, 0, 2, 0, - 2, 4, 6, 2))); + 0, 2, 0, 0, + 0, 0, 2, 0, + 2, 4, 6, 2))); expect(prod4.equals(prod1)); // Matrix division diff --git a/javascript/MaterialXTest/value.spec.js b/javascript/MaterialXTest/value.spec.js index 1b06d3f59b..6a9091ce47 100644 --- a/javascript/MaterialXTest/value.spec.js +++ b/javascript/MaterialXTest/value.spec.js @@ -1,13 +1,16 @@ import { expect } from 'chai';; import Module from './_build/JsMaterialXCore.js'; -describe('Value', () => { +describe('Value', () => +{ let mx; - before(async () => { + before(async () => + { mx = await Module(); }); - it('Create values of different types', () => { + it('Create values of different types', () => + { const testValues = { integer: '1', boolean: 'true', @@ -26,7 +29,8 @@ describe('Value', () => { stringarray: "'one', 'two', 'three'", }; - for (let type in testValues) { + for (let type in testValues) + { const value = testValues[String(type)]; const newValue = mx.Value.createValueFromStrings(value, type); const typeString = newValue.getTypeString(); diff --git a/javascript/MaterialXTest/xmlIo.spec.js b/javascript/MaterialXTest/xmlIo.spec.js index e569b9be76..670b0df43d 100644 --- a/javascript/MaterialXTest/xmlIo.spec.js +++ b/javascript/MaterialXTest/xmlIo.spec.js @@ -4,7 +4,8 @@ import { getMtlxStrings } from './testHelpers'; const TIMEOUT = 60000; -describe('XmlIo', () => { +describe('XmlIo', () => +{ let mx; // These should be relative to cwd @@ -24,18 +25,23 @@ describe('XmlIo', () => { 'UsdPreviewSurface/usd_preview_surface_plastic.mtlx', ]; - async function readStdLibrary(asString = false) { + async function readStdLibrary(asString = false) + { const libs = []; let iterable = libraryFilenames; - if (asString) { + if (asString) + { const libraryMtlxStrings = getMtlxStrings(libraryFilenames, libraryPath); iterable = libraryMtlxStrings; } - for (let file of iterable) { + for (let file of iterable) + { const lib = mx.createDocument(); - if (asString) { + if (asString) + { await mx.readFromXmlString(lib, file, libraryPath); - } else { + } else + { await mx.readFromXmlFile(lib, file, libraryPath); } libs.push(lib); @@ -43,12 +49,15 @@ describe('XmlIo', () => { return libs; } - async function readAndValidateExamples(examples, libraries, readFunc, searchPath = undefined) { - for (let file of examples) { + async function readAndValidateExamples(examples, libraries, readFunc, searchPath = undefined) + { + for (let file of examples) + { const doc = mx.createDocument(); await readFunc(doc, file, searchPath); // Import stdlib into the current document and validate it. - for (let lib of libraries) { + for (let lib of libraries) + { doc.importLibrary(lib); } expect(doc.validate()).to.be.true; @@ -56,8 +65,10 @@ describe('XmlIo', () => { // Make sure the document does actually contain something. let valueElementCount = 0; const treeIter = doc.traverseTree(); - for(const elem of treeIter) { - if (elem instanceof mx.ValueElement) { + for (const elem of treeIter) + { + if (elem instanceof mx.ValueElement) + { valueElementCount++; } } @@ -65,17 +76,20 @@ describe('XmlIo', () => { }; } - before(async () => { + before(async () => + { mx = await Module(); }); - it('Read XML from file', async () => { + it('Read XML from file', async () => + { // Read the standard library const libs = await readStdLibrary(false); // Read and validate the example documents. await readAndValidateExamples(exampleFilenames, libs, - async (document, file, sp) => { + async (document, file, sp) => + { await mx.readFromXmlFile(document, file, sp); }, examplesPath); @@ -90,14 +104,16 @@ describe('XmlIo', () => { expect(copy.equals(doc)).to.be.true; }).timeout(TIMEOUT); - it('Read XML from string', async () => { + it('Read XML from string', async () => + { // Read the standard library const libs = await readStdLibrary(true); // Read and validate each example document. const examplesStrings = getMtlxStrings(exampleFilenames, examplesPath); await readAndValidateExamples(examplesStrings, libs, - async (document, file) => { + async (document, file) => + { await mx.readFromXmlString(document, file); }); @@ -112,14 +128,16 @@ describe('XmlIo', () => { expect(copy.equals(doc)).to.be.true; }).timeout(TIMEOUT); - it('Read XML with recursive includes', async () => { + it('Read XML with recursive includes', async () => + { const doc = mx.createDocument(); await mx.readFromXmlFile(doc, includeTestPath + '/root.mtlx'); expect(doc.getChild('paint_semigloss')).to.exist; expect(doc.validate()).to.be.true; }); - it('Locate XML includes via search path', async () => { + it('Locate XML includes via search path', async () => + { const searchPath = includeTestPath + ';' + includeTestPath + '/folder'; const filename = 'non_relative_includes.mtlx'; const doc = mx.createDocument(); @@ -137,7 +155,8 @@ describe('XmlIo', () => { expect(doc2.equals(doc)).to.be.true; }); - it('Locate XML includes via environment variable', async () => { + it('Locate XML includes via environment variable', async () => + { const searchPath = includeTestPath + ';' + includeTestPath + '/folder'; const filename = 'non_relative_includes.mtlx'; @@ -160,13 +179,16 @@ describe('XmlIo', () => { expect(doc2.equals(doc)).to.be.true; }); - it('Locate XML includes via absolute search paths', async () => { + it('Locate XML includes via absolute search paths', async () => + { let absolutePath; - if (typeof window === 'object') { + if (typeof window === 'object') + { // We're in the browser const cwd = window.location.origin + window.location.pathname; absolutePath = cwd + '/' + includeTestPath; - } else if (typeof process === 'object') { + } else if (typeof process === 'object') + { // We're in Node const nodePath = require('path'); absolutePath = nodePath.resolve(includeTestPath); @@ -175,22 +197,26 @@ describe('XmlIo', () => { await mx.readFromXmlFile(doc, 'root.mtlx', absolutePath); }); - it('Detect XML include cycles', async () => { + it('Detect XML include cycles', async () => + { const doc = mx.createDocument(); expect(async () => await mx.readFromXmlFile(doc, includeTestPath + '/cycle.mtlx')).to.throw; }); - it('Disabling XML includes', async () => { + it('Disabling XML includes', async () => + { const doc = mx.createDocument(); const readOptions = new mx.XmlReadOptions(); readOptions.readXIncludes = false; expect(async () => await mx.readFromXmlFile(doc, includeTestPath + '/cycle.mtlx', readOptions)).to.not.throw; }); - it('Write to XML string', async () => { + it('Write to XML string', async () => + { // Read all example documents and write them to an XML string const searchPath = libraryPath + ';' + examplesPath; - for (let filename of exampleFilenames) { + for (let filename of exampleFilenames) + { const doc = mx.createDocument(); await mx.readFromXmlFile(doc, filename, searchPath); @@ -206,7 +232,8 @@ describe('XmlIo', () => { }; }); - it('Prepend include tag', () => { + it('Prepend include tag', () => + { const doc = mx.createDocument(); const includePath = "SomePath"; const writeOptions = new mx.XmlWriteOptions(); @@ -216,7 +243,8 @@ describe('XmlIo', () => { }); // Node only, because we cannot read from a downloaded file in the browser - it('Write XML to file', async () => { + it('Write XML to file', async () => + { const filename = '_build/testFile.mtlx'; const includeRegex = //g; const doc = mx.createDocument(); diff --git a/javascript/MaterialXView/source/dropHandling.js b/javascript/MaterialXView/source/dropHandling.js index 8e4b674e0e..f54b77115c 100644 --- a/javascript/MaterialXView/source/dropHandling.js +++ b/javascript/MaterialXView/source/dropHandling.js @@ -5,7 +5,8 @@ const debugFileHandling = false; let loadingCallback = null; let sceneLoadingCallback = null; -export function setLoadingCallback(cb) { +export function setLoadingCallback(cb) +{ loadingCallback = cb; } @@ -14,7 +15,8 @@ export function setSceneLoadingCallback(cb) sceneLoadingCallback = cb; } -export function dropHandler(ev) { +export function dropHandler(ev) +{ if (debugFileHandling) console.log('File(s) dropped', ev.dataTransfer.items, ev.dataTransfer.files); // Prevent default behavior (Prevent file from being opened) @@ -26,14 +28,15 @@ export function dropHandler(ev) { let haveGetAsEntry = false; if (ev.dataTransfer.items.length > 0) - haveGetAsEntry = - ("getAsEntry" in ev.dataTransfer.items[0]) || - ("webkitGetAsEntry" in ev.dataTransfer.items[0]); + haveGetAsEntry = + ("getAsEntry" in ev.dataTransfer.items[0]) || + ("webkitGetAsEntry" in ev.dataTransfer.items[0]); // Useful for debugging file handling on platforms that don't support newer file system APIs // haveGetAsEntry = false; - if (haveGetAsEntry) { + if (haveGetAsEntry) + { for (var i = 0; i < ev.dataTransfer.items.length; i++) { let item = ev.dataTransfer.items[i]; @@ -47,7 +50,7 @@ export function dropHandler(ev) { for (var i = 0; i < ev.dataTransfer.items.length; i++) { let item = ev.dataTransfer.items[i]; - + // API when there's no "getAsEntry" support console.log(item.kind, item); if (item.kind === 'file') @@ -59,32 +62,40 @@ export function dropHandler(ev) { else if (item.kind === 'directory') { var dirReader = item.createReader(); - dirReader.readEntries(function(entries) { - for (var i = 0; i < entries.length; i++) { - console.log(entries[i].name); - var entry = entries[i]; - if (entry.isFile) { - entry.file(function(file) { - testAndLoadFile(file); - }); + dirReader.readEntries(function (entries) + { + for (var i = 0; i < entries.length; i++) + { + console.log(entries[i].name); + var entry = entries[i]; + if (entry.isFile) + { + entry.file(function (file) + { + testAndLoadFile(file); + }); + } } - } }); } } - } else { - for (var i = 0; i < ev.dataTransfer.files.length; i++) { + } else + { + for (var i = 0; i < ev.dataTransfer.files.length; i++) + { let file = ev.dataTransfer.files[i]; testAndLoadFile(file); } } } -export function dragOverHandler(ev) { +export function dragOverHandler(ev) +{ ev.preventDefault(); } -async function getBufferFromFile(fileEntry) { +async function getBufferFromFile(fileEntry) +{ if (fileEntry instanceof ArrayBuffer) return fileEntry; if (fileEntry instanceof String) return fileEntry; @@ -96,98 +107,120 @@ async function getBufferFromFile(fileEntry) { if (debugFileHandling) console.log("reading ", fileEntry, "as text?", readAsText); if (debugFileHandling) console.log("getBufferFromFile", fileEntry); - const buffer = await new Promise((resolve, reject) => { - function readFile(file) { + const buffer = await new Promise((resolve, reject) => + { + function readFile(file) + { var reader = new FileReader(); - reader.onloadend = function(e) { + reader.onloadend = function (e) + { if (debugFileHandling) console.log("loaded", "should be text?", readAsText, this.result); resolve(this.result); }; - + if (readAsText) reader.readAsText(file); else reader.readAsArrayBuffer(file); } - if ("file" in fileEntry) { - fileEntry.file(function(file) { + if ("file" in fileEntry) + { + fileEntry.file(function (file) + { readFile(file); - }, (e) => { + }, (e) => + { console.error("Error reading file ", e); }); } - else { + else + { readFile(fileEntry); } }); return buffer; } -async function handleFilesystemEntries(entries) { +async function handleFilesystemEntries(entries) +{ const allFiles = []; const fileIgnoreList = [ - '.gitignore', - 'README.md', - '.DS_Store', + '.gitignore', + 'README.md', + '.DS_Store', ] const dirIgnoreList = [ - '.git', - 'node_modules', + '.git', + 'node_modules', ] let isGLB = false; let haveMtlx = false; - for (let entry of entries) { - if (debugFileHandling) console.log("file entry", entry) - if (entry.isFile) { - if (debugFileHandling) - console.log("single file", entry); - if (fileIgnoreList.includes(entry.name)) { - continue; - } - allFiles.push(entry); - - if (entry.name.endsWith('glb')) { - isGLB = true; - break; - } - } - else if (entry.isDirectory) { - if (dirIgnoreList.includes(entry.name)) { - continue; + for (let entry of entries) + { + if (debugFileHandling) console.log("file entry", entry) + if (entry.isFile) + { + if (debugFileHandling) + console.log("single file", entry); + if (fileIgnoreList.includes(entry.name)) + { + continue; + } + allFiles.push(entry); + + if (entry.name.endsWith('glb')) + { + isGLB = true; + break; + } } - const files = await readDirectory(entry); - if (debugFileHandling) console.log("all files", files); - for (const file of files) { - if (fileIgnoreList.includes(file.name)) { - continue; - } - allFiles.push(file); + else if (entry.isDirectory) + { + if (dirIgnoreList.includes(entry.name)) + { + continue; + } + const files = await readDirectory(entry); + if (debugFileHandling) console.log("all files", files); + for (const file of files) + { + if (fileIgnoreList.includes(file.name)) + { + continue; + } + allFiles.push(file); + } } - } } const imageLoader = new THREE.ImageLoader(); // unpack zip files first - for (const fileEntry of allFiles) { + for (const fileEntry of allFiles) + { // special case: zip archives - if (fileEntry.fullPath.toLowerCase().endsWith('.zip')) { - await new Promise(async (resolve, reject) => { + if (fileEntry.fullPath.toLowerCase().endsWith('.zip')) + { + await new Promise(async (resolve, reject) => + { const arrayBuffer = await getBufferFromFile(fileEntry); // use fflate to unpack them and add the files to the cache - fflate.unzip(new Uint8Array(arrayBuffer), (error, unzipped) => { + fflate.unzip(new Uint8Array(arrayBuffer), (error, unzipped) => + { // push these files into allFiles - for (const [filePath, buffer] of Object.entries(unzipped)) { + for (const [filePath, buffer] of Object.entries(unzipped)) + { // mock FileEntry for easier usage downstream const blob = new Blob([buffer]); const newFileEntry = { fullPath: "/" + filePath, name: filePath.split('/').pop(), - file: (callback) => { + file: (callback) => + { callback(blob); }, isFile: true, @@ -202,11 +235,14 @@ async function handleFilesystemEntries(entries) { } // sort so mtlx files come first - allFiles.sort((a, b) => { - if (a.name.endsWith('.mtlx') && !b.name.endsWith('.mtlx')) { + allFiles.sort((a, b) => + { + if (a.name.endsWith('.mtlx') && !b.name.endsWith('.mtlx')) + { return -1; } - if (!a.name.endsWith('.mtlx') && b.name.endsWith('.mtlx')) { + if (!a.name.endsWith('.mtlx') && b.name.endsWith('.mtlx')) + { return 1; } return 0; @@ -231,19 +267,22 @@ async function handleFilesystemEntries(entries) { return; } - if (debugFileHandling) { + if (debugFileHandling) + { console.log("- All files", allFiles); } // put all files in three' Cache - for (const fileEntry of allFiles) { + for (const fileEntry of allFiles) + { const allowedFileTypes = [ 'png', 'jpg', 'jpeg' ]; const ext = fileEntry.fullPath.split('.').pop(); - if (!allowedFileTypes.includes(ext)) { + if (!allowedFileTypes.includes(ext)) + { // console.log("skipping file", fileEntry.fullPath); continue; } @@ -272,37 +311,49 @@ async function handleFilesystemEntries(entries) { } } -async function readDirectory(directory) { +async function readDirectory(directory) +{ let entries = []; - let getEntries = async (directory) => { + let getEntries = async (directory) => + { let dirReader = directory.createReader(); - await new Promise((resolve, reject) => { - dirReader.readEntries( - async (results) => { - if (results.length) { - // entries = entries.concat(results); - for (let entry of results) { - if (entry.isDirectory) { - await getEntries(entry); - } - else { - entries.push(entry); - } - } - } - resolve(); - }, - (error) => { - /* handle error — error is a FileError object */ - }, - )} - )}; + await new Promise((resolve, reject) => + { + dirReader.readEntries( + async (results) => + { + if (results.length) + { + // entries = entries.concat(results); + for (let entry of results) + { + if (entry.isDirectory) + { + await getEntries(entry); + } + else + { + entries.push(entry); + } + } + } + resolve(); + }, + (error) => + { + /* handle error — error is a FileError object */ + }, + ) + } + ) + }; await getEntries(directory); return entries; } -async function testAndLoadFile(file) { +async function testAndLoadFile(file) +{ let ext = file.name.split('.').pop(); if (debugFileHandling) console.log(file.name + ", " + file.size + ", " + ext); @@ -314,7 +365,8 @@ async function testAndLoadFile(file) { fullPath: "/" + file.name, name: file.name.split('/').pop(), isFile: true, - file: (callback) => { + file: (callback) => + { callback(file); } }; diff --git a/javascript/MaterialXView/source/helper.js b/javascript/MaterialXView/source/helper.js index 98441a605f..2b7f0f7d5b 100644 --- a/javascript/MaterialXView/source/helper.js +++ b/javascript/MaterialXView/source/helper.js @@ -45,8 +45,8 @@ function fromVector(value, dimension) } else { - outValue = []; - for(let i = 0; i < dimension; ++i) + outValue = []; + for (let i = 0; i < dimension; ++i) outValue.push(0.0); } @@ -69,13 +69,13 @@ function fromMatrix(matrix, dimension) { vec.push(matrix.getItem(i, k)); } - } + } } else { for (let i = 0; i < dimension; ++i) vec.push(0.0); } - + return vec; } @@ -89,7 +89,7 @@ function fromMatrix(matrix, dimension) */ function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, flipY) { - let outValue; + let outValue; switch (type) { case 'float': @@ -97,7 +97,7 @@ function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, case 'boolean': outValue = value; break; - case 'vector2': + case 'vector2': outValue = fromVector(value, 2); break; case 'vector3': @@ -123,11 +123,11 @@ function toThreeUniform(type, value, name, uniforms, textureLoader, searchPath, if (texture) setTextureParameters(texture, name, uniforms, flipY); outValue = texture; - } + } break; case 'samplerCube': case 'string': - break; + break; default: // struct outValue = toThreeUniform(value); @@ -184,33 +184,33 @@ function getMinFilter(type, generateMipmaps) * @param {mx.Uniforms} uniforms * @param {mx.TextureFilter.generateMipmaps} generateMipmaps */ - function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true) - { - const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR); - const base = name.substring(0, idx) || name; - - texture.generateMipmaps = generateMipmaps; - texture.wrapS = THREE.RepeatWrapping; - texture.wrapT = THREE.RepeatWrapping; - texture.magFilter = THREE.LinearFilter; - texture.flipY = flipY; - - if (uniforms.find(base + UADDRESS_MODE_SUFFIX)) - { - const uaddressmode = uniforms.find(base + UADDRESS_MODE_SUFFIX).getValue().getData(); - texture.wrapS = getWrapping(uaddressmode); - } - - if (uniforms.find(base + VADDRESS_MODE_SUFFIX)) - { - const vaddressmode = uniforms.find(base + VADDRESS_MODE_SUFFIX).getValue().getData(); - texture.wrapT = getWrapping(vaddressmode); - } - - const filterType = uniforms.find(base + FILTER_TYPE_SUFFIX) ? uniforms.get(base + FILTER_TYPE_SUFFIX).value : -1; - texture.minFilter = getMinFilter(filterType, generateMipmaps); - } - +function setTextureParameters(texture, name, uniforms, flipY = true, generateMipmaps = true) +{ + const idx = name.lastIndexOf(IMAGE_PROPERTY_SEPARATOR); + const base = name.substring(0, idx) || name; + + texture.generateMipmaps = generateMipmaps; + texture.wrapS = THREE.RepeatWrapping; + texture.wrapT = THREE.RepeatWrapping; + texture.magFilter = THREE.LinearFilter; + texture.flipY = flipY; + + if (uniforms.find(base + UADDRESS_MODE_SUFFIX)) + { + const uaddressmode = uniforms.find(base + UADDRESS_MODE_SUFFIX).getValue().getData(); + texture.wrapS = getWrapping(uaddressmode); + } + + if (uniforms.find(base + VADDRESS_MODE_SUFFIX)) + { + const vaddressmode = uniforms.find(base + VADDRESS_MODE_SUFFIX).getValue().getData(); + texture.wrapT = getWrapping(vaddressmode); + } + + const filterType = uniforms.find(base + FILTER_TYPE_SUFFIX) ? uniforms.get(base + FILTER_TYPE_SUFFIX).value : -1; + texture.minFilter = getMinFilter(filterType, generateMipmaps); +} + /** * Return the global light rotation matrix */ @@ -269,7 +269,7 @@ export function registerLights(mx, lights, genContext) lightData.push({ type: lightTypesBound[nodeName], direction: rotatedLightDirection, - color: new THREE.Vector3(...lightColor), + color: new THREE.Vector3(...lightColor), intensity: lightIntensity }); } @@ -290,16 +290,17 @@ export function getUniformValues(shaderStage, textureLoader, searchPath, flipY) let threeUniforms = {}; const uniformBlocks = Object.values(shaderStage.getUniformBlocks()); - uniformBlocks.forEach(uniforms => { + uniformBlocks.forEach(uniforms => + { if (!uniforms.empty()) { for (let i = 0; i < uniforms.size(); ++i) { - const variable = uniforms.get(i); + const variable = uniforms.get(i); const value = variable.getValue()?.getData(); const name = variable.getVariable(); - threeUniforms[name] = new THREE.Uniform(toThreeUniform(variable.getType().getName(), value, name, uniforms, - textureLoader, searchPath, flipY)); + threeUniforms[name] = new THREE.Uniform(toThreeUniform(variable.getType().getName(), value, name, uniforms, + textureLoader, searchPath, flipY)); } } }); diff --git a/javascript/MaterialXView/source/index.js b/javascript/MaterialXView/source/index.js index bffae2db8d..026653ca60 100644 --- a/javascript/MaterialXView/source/index.js +++ b/javascript/MaterialXView/source/index.js @@ -25,7 +25,8 @@ let captureRequested = false; // Get URL options. Fallback to defaults if not specified. let materialFilename = new URLSearchParams(document.location.search).get("file"); -if (!materialFilename) { +if (!materialFilename) +{ materialFilename = 'Materials/Examples/StandardSurface/standard_surface_default.mtlx'; } @@ -37,7 +38,7 @@ viewer.getEditor().updateProperties(0.9); function captureFrame() { let canvas = document.getElementById('webglcanvas'); - var url = canvas.toDataURL(); + var url = canvas.toDataURL(); var link = document.createElement('a'); link.setAttribute('href', url); link.setAttribute('target', '_blank'); @@ -45,7 +46,7 @@ function captureFrame() link.click(); } -function init() +function init() { let canvas = document.getElementById('webglcanvas'); let context = canvas.getContext('webgl2'); @@ -53,7 +54,8 @@ function init() // Handle material selection changes let materialsSelect = document.getElementById('materials'); materialsSelect.value = materialFilename; - materialsSelect.addEventListener('change', (e) => { + materialsSelect.addEventListener('change', (e) => + { materialFilename = e.target.value; viewer.getEditor().initialize(); viewer.getMaterial().loadMaterials(viewer, materialFilename); @@ -65,7 +67,8 @@ function init() const scene = viewer.getScene(); let geometrySelect = document.getElementById('geometry'); geometrySelect.value = scene.getGeometryURL(); - geometrySelect.addEventListener('change', (e) => { + geometrySelect.addEventListener('change', (e) => + { console.log('Change geometry to:', e.target.value); scene.setGeometryURL(e.target.value); scene.loadGeometry(viewer, orbitControls); @@ -95,14 +98,17 @@ function init() // Set up controls orbitControls = new OrbitControls(scene.getCamera(), renderer.domElement); - orbitControls.addEventListener('change', () => { + orbitControls.addEventListener('change', () => + { viewer.getScene().setUpdateTransforms(); }) // Add hotkey 'f' to capture the current frame and save an image file. // See check inside the render loop when a capture can be performed. - document.addEventListener('keydown', (event) => { - if (event.key === 'f') { + document.addEventListener('keydown', (event) => + { + if (event.key === 'f') + { captureRequested = true; } }); @@ -116,12 +122,14 @@ function init() new Promise(resolve => hdrLoader.load('Lights/san_giuseppe_bridge_split.hdr', resolve)), new Promise(resolve => hdrLoader.load('Lights/irradiance/san_giuseppe_bridge_split.hdr', resolve)), new Promise(resolve => fileLoader.load('Lights/san_giuseppe_bridge_split.mtlx', resolve)), - new Promise(function (resolve) { - MaterialX().then((module) => { + new Promise(function (resolve) + { + MaterialX().then((module) => + { resolve(module); }); - }) - ]).then(async ([radianceTexture, irradianceTexture, lightRigXml, mxIn]) => + }) + ]).then(async ([radianceTexture, irradianceTexture, lightRigXml, mxIn]) => { // Initialize viewer + lighting await viewer.initialize(mxIn, renderer, radianceTexture, irradianceTexture, lightRigXml); @@ -137,10 +145,12 @@ function init() viewer.getMaterial().updateMaterialAssignments(viewer); canvas.addEventListener("keydown", handleKeyEvents, true); - - }).then(() => { + + }).then(() => + { animate(); - }).catch(err => { + }).catch(err => + { console.error(Number.isInteger(err) ? this.getMx().getExceptionMessage(err) : err); }) @@ -148,7 +158,8 @@ function init() document.addEventListener('drop', dropHandler, false); document.addEventListener('dragover', dragOverHandler, false); - setLoadingCallback(file => { + setLoadingCallback(file => + { materialFilename = file.fullPath || file.name; viewer.getEditor().initialize(); viewer.getMaterial().loadMaterials(viewer, materialFilename); @@ -156,25 +167,26 @@ function init() viewer.getScene().setUpdateTransforms(); }); - setSceneLoadingCallback(file => { + setSceneLoadingCallback(file => + { let glbFileName = file.fullPath || file.name; console.log('Drop geometry to:', glbFileName); scene.setGeometryURL(glbFileName); - scene.loadGeometry(viewer, orbitControls); + scene.loadGeometry(viewer, orbitControls); }); // enable three.js Cache so that dropped files can reference each other THREE.Cache.enabled = true; } -function onWindowResize() +function onWindowResize() { viewer.getScene().updateCamera(); - viewer.getScene().setUpdateTransforms(); + viewer.getScene().setUpdateTransforms(); renderer.setSize(window.innerWidth, window.innerHeight); } -function animate() +function animate() { requestAnimationFrame(animate); @@ -182,7 +194,7 @@ function animate() { turntableStep = (turntableStep + 1) % 360; var turntableAngle = turntableStep * (360.0 / turntableSteps) / 180.0 * Math.PI; - viewer.getScene()._scene.rotation.y = turntableAngle ; + viewer.getScene()._scene.rotation.y = turntableAngle; viewer.getScene().setUpdateTransforms(); } diff --git a/javascript/MaterialXView/source/viewer.js b/javascript/MaterialXView/source/viewer.js index 9992dd6985..06236fdac9 100644 --- a/javascript/MaterialXView/source/viewer.js +++ b/javascript/MaterialXView/source/viewer.js @@ -9,7 +9,7 @@ import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js'; import { prepareEnvTexture, getLightRotation, findLights, registerLights, getUniformValues } from './helper.js' import { Group } from 'three'; -import GUI from 'lil-gui'; +import GUI from 'lil-gui'; const ALL_GEOMETRY_SPECIFIER = "*"; const NO_GEOMETRY_SPECIFIER = ""; @@ -21,18 +21,18 @@ var logDetailedTime = false; /* Scene management */ -export class Scene +export class Scene { - constructor() + constructor() { this._geometryURL = new URLSearchParams(document.location.search).get("geom"); if (!this._geometryURL) { - this._geometryURL = 'Geometry/shaderball.glb'; + this._geometryURL = 'Geometry/shaderball.glb'; } } - initialize() + initialize() { this._scene = new THREE.Scene(); this._scene.background = new THREE.Color(this.#_backgroundColor); @@ -64,9 +64,10 @@ export class Scene } // Utility to perform geometry file load - loadGeometryFile(geometryFilename, loader) + loadGeometryFile(geometryFilename, loader) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => + { loader.load(geometryFilename, data => resolve(data), null, reject); }); } @@ -82,8 +83,9 @@ export class Scene const gltfData = await this.loadGeometryFile(this.getGeometryURL(), this.#_gltfLoader); - const scene = this.getScene(); - while (scene.children.length > 0) { + const scene = this.getScene(); + while (scene.children.length > 0) + { scene.remove(scene.children[0]); } @@ -100,7 +102,7 @@ export class Scene else { this.#_rootNode = model; - } + } scene.add(model); console.log("- Scene load time: ", performance.now() - geomLoadTime, "ms"); @@ -123,7 +125,7 @@ export class Scene { var startUpdateSceneTime = performance.now(); var uvTime = 0; - var normalTime = 0 ; + var normalTime = 0; var tangentTime = 0; var streamTime = 0; var bboxTime = 0; @@ -136,41 +138,47 @@ export class Scene let theScene = viewer.getScene(); let flipV = theScene.getFlipGeometryV(); - - this._scene.traverse((child) => { - if (child.isMesh) { + + this._scene.traverse((child) => + { + if (child.isMesh) + { var startUVTime = performance.now(); - if (!child.geometry.attributes.uv) { + if (!child.geometry.attributes.uv) + { const posCount = child.geometry.attributes.position.count; const uvs = []; const pos = child.geometry.attributes.position.array; - - for (let i = 0; i < posCount; i++) { + + for (let i = 0; i < posCount; i++) + { uvs.push((pos[i * 3] - bsphere.center.x) / bsphere.radius); uvs.push((pos[i * 3 + 1] - bsphere.center.y) / bsphere.radius); } - + child.geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); } else if (flipV) { const uvCount = child.geometry.attributes.position.count; const uvs = child.geometry.attributes.uv.array; - for (let i = 0; i < uvCount; i++) { - let v = 1.0-(uvs[i*2 +1]); - uvs[i*2+1] = v; + for (let i = 0; i < uvCount; i++) + { + let v = 1.0 - (uvs[i * 2 + 1]); + uvs[i * 2 + 1] = v; } } uvTime += performance.now() - startUVTime; - - if (!child.geometry.attributes.normal) { + + if (!child.geometry.attributes.normal) + { var startNormalTime = performance.new(); child.geometry.computeVertexNormals(); normalTime += performance.now() - startNormalTime; } - - if (child.geometry.getIndex()) + + if (child.geometry.getIndex()) { if (!child.geometry.attributes.tangent) { @@ -179,7 +187,7 @@ export class Scene tangentTime += performance.now() - startTangentTime; } } - + // Use default MaterialX naming convention. var startStreamTime = performance.now(); child.geometry.attributes.i_position = child.geometry.attributes.position; @@ -199,7 +207,7 @@ export class Scene console.log(' - Stream Update time: ', streamTime); console.log(' - Bounds compute time: ', bboxTime); } - + // Update the background this._scene.background = this.getBackground(); @@ -208,10 +216,10 @@ export class Scene camera.position.y = bsphere.center.y; camera.position.z = bsphere.radius * 2.0; camera.updateProjectionMatrix(); - + orbitControls.target = bsphere.center; orbitControls.update(); - } + } setUpdateTransforms() { @@ -231,10 +239,13 @@ export class Scene const scene = this.getScene(); const camera = this.getCamera(); - scene.traverse((child) => { - if (child.isMesh) { + scene.traverse((child) => + { + if (child.isMesh) + { const uniforms = child.material.uniforms; - if (uniforms) { + if (uniforms) + { uniforms.u_worldMatrix.value = child.matrixWorld; uniforms.u_viewProjectionMatrix.value = this.#_viewProjMat.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); @@ -242,8 +253,8 @@ export class Scene uniforms.u_viewPosition.value = camera.getWorldPosition(this.#_worldViewPos); if (uniforms.u_worldInverseTransposeMatrix) - uniforms.u_worldInverseTransposeMatrix.value = - new THREE.Matrix4().setFromMatrix3(this.#_normalMat.getNormalMatrix(child.matrixWorld)); + uniforms.u_worldInverseTransposeMatrix.value = + new THREE.Matrix4().setFromMatrix3(this.#_normalMat.getNormalMatrix(child.matrixWorld)); } } }); @@ -283,9 +294,9 @@ export class Scene const scene = this.getScene(); const camera = this.getCamera(); - scene.traverse((child) => + scene.traverse((child) => { - if (child.isMesh) + if (child.isMesh) { const dagPath = this.getDagPath(child).join('/'); @@ -336,19 +347,23 @@ export class Scene camera.updateProjectionMatrix(); } - getScene() { + getScene() + { return this._scene; } - getCamera() { + getCamera() + { return this._camera; } - getGeometryURL() { + getGeometryURL() + { return this._geometryURL; } - setGeometryURL(url) { + setGeometryURL(url) + { this._geometryURL = url; } @@ -367,7 +382,7 @@ export class Scene this.#_showBackgroundTexture = enable; } - getBackground() + getBackground() { if (this.#_backgroundTexture && this.#_showBackgroundTexture) { @@ -424,31 +439,34 @@ export class Editor initialize() { Array.from(document.getElementsByClassName('lil-gui')).forEach( - function (element, index, array) { - if (element.className) { + function (element, index, array) + { + if (element.className) + { element.remove(); } } ); - this._gui = new GUI( { title: "Property Editor" } ); + this._gui = new GUI({ title: "Property Editor" }); this._gui.close(); } // Update ui properties // - Hide close button // - Update transparency so scene shows through if overlapping - updateProperties(targetOpacity = 1) + updateProperties(targetOpacity = 1) { // Set opacity Array.from(document.getElementsByClassName('dg')).forEach( - function (element, index, array) { + function (element, index, array) + { element.style.opacity = targetOpacity; } ); } - getGUI() + getGUI() { return this._gui; } @@ -491,7 +509,7 @@ class MaterialAssign { return this._material; } - + getGeometry() { return this._geometry; @@ -547,13 +565,13 @@ export class Material } // If no material file is selected, we programmatically create a default material as a fallback - static createFallbackMaterial(doc) + static createFallbackMaterial(doc) { let ssNode = doc.getChild('Generated_Default_Shader'); if (ssNode) { return ssNode; - } + } const ssName = 'Generated_Default_Shader'; ssNode = doc.addChildOfCategory('standard_surface', ssName); ssNode.setType('surfaceshader'); @@ -568,7 +586,8 @@ export class Material async loadMaterialFile(loader, materialFilename) { - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => + { loader.load(materialFilename, data => resolve(data), null, reject); }); } @@ -585,7 +604,7 @@ export class Material doc.importLibrary(viewer.getLibrary()); viewer.setDocument(doc); - const fileloader = viewer.getFileLoader(); + const fileloader = viewer.getFileLoader(); let mtlxMaterial = await viewer.getMaterial().loadMaterialFile(fileloader, materialFilename); @@ -598,7 +617,7 @@ export class Material // location. if (!materialFilename) materialFilename = "/"; const paths = materialFilename.split('/'); - paths.pop(); + paths.pop(); const searchPath = paths.join('/'); // Load material @@ -613,18 +632,18 @@ export class Material // and assign to the associated geometry. If there are no looks // then the first material is found and assignment to all the // geometry. - this.clearMaterials(); + this.clearMaterials(); var looks = doc.getLooks(); if (looks.length) { for (let look of looks) { const materialAssigns = look.getMaterialAssigns(); - for (let materialAssign of materialAssigns) + for (let materialAssign of materialAssigns) { let matName = materialAssign.getMaterial(); if (matName) - { + { let mat = doc.getChild(matName); var shader; if (mat) @@ -672,8 +691,8 @@ export class Material // The identifier used is "*" to mean the entire scene. const materialNodes = doc.getMaterialNodes(); let shaderList = []; - let foundRenderable = false; - for (let i=0; i 0) { continue } let outputs = nodeGraph.getOutputs(); - for (let j=0; j + uniformBlocks.forEach(uniforms => { - if (!uniforms.empty()) + if (!uniforms.empty()) { - for (let i = 0; i < uniforms.size(); ++i) + for (let i = 0; i < uniforms.size(); ++i) { const variable = uniforms.get(i); const value = variable.getValue()?.getData(); let name = variable.getVariable(); - if (ignoreList.includes(name)) { + if (ignoreList.includes(name)) + { continue; } let currentFolder = matUI; let currentElemPath = variable.getPath(); - if (!currentElemPath || currentElemPath.length == 0) { + if (!currentElemPath || currentElemPath.length == 0) + { continue; } let currentElem = elem.getDocument().getDescendant(currentElemPath); - if (!currentElem) { + if (!currentElem) + { continue; } let currentNode = null; if (currentElem.getParent() && currentElem.getParent().getNamePath() != "") - { + { currentNode = currentElem.getParent(); } let uiname = ""; let nodeDefInput = null; - if (currentNode) { + if (currentNode) + { let currentNodePath = currentNode.getNamePath(); var pathSplit = currentNodePath.split('/'); - if (pathSplit.length) { + if (pathSplit.length) + { currentNodePath = pathSplit[0]; } currentFolder = folderList[currentNodePath]; - if (!currentFolder) { + if (!currentFolder) + { currentFolder = matUI.addFolder(currentNodePath); folderList[currentNodePath] = currentFolder; } // Check for ui attributes var nodeDef = currentNode.getNodeDef(); - if (nodeDef) { + if (nodeDef) + { // Remove node name from shader uniform name for non root nodes let lookup_name = name.replace(currentNode.getName() + '_', ''); nodeDefInput = nodeDef.getActiveInput(lookup_name); - if (nodeDefInput) + if (nodeDefInput) { uiname = nodeDefInput.getAttribute('uiname'); let uifolderName = nodeDefInput.getAttribute('uifolder'); - if (uifolderName && uifolderName.length) { + if (uifolderName && uifolderName.length) + { let newFolderName = currentNodePath + '/' + uifolderName; currentFolder = folderList[newFolderName]; - if (!currentFolder) + if (!currentFolder) { currentFolder = matUI.addFolder(uifolderName); - currentFolder.domElement.classList.add('peditorfolder'); + currentFolder.domElement.classList.add('peditorfolder'); folderList[newFolderName] = currentFolder; } } @@ -1106,17 +1135,21 @@ export class Material // Determine UI name to use let path = name; let interfaceName = currentElem.getAttribute("interfacename"); - if (interfaceName && interfaceName.length) { + if (interfaceName && interfaceName.length) + { const graph = currentNode.getParent(); if (graph) { const graphInput = graph.getInput(interfaceName); - if (graphInput) { + if (graphInput) + { let uiname = graphInput.getAttribute('uiname'); - if (uiname.length) { + if (uiname.length) + { path = uiname; } - else { + else + { path = graphInput.getName(); } } @@ -1126,11 +1159,14 @@ export class Material path = interfaceName; } } - else { - if (!uiname) { + else + { + if (!uiname) + { uiname = currentElem.getAttribute('uiname'); } - if (uiname && uiname.length) { + if (uiname && uiname.length) + { path = uiname; } } @@ -1236,33 +1272,35 @@ export class Material { let w = currentFolder.add(material.uniforms[name], 'value', minValue, maxValue, step).name(path); w.domElement.classList.add('peditoritem'); - } + } else { // Map enumList strings to values // Map to 0..N if no values are specified via enumvalues attribute if (enumValues.length == 0) - { + { for (let i = 0; i < enumList.length; ++i) { enumValues.push(i); } } const enumeration = {}; - enumList.forEach((str, index) => { + enumList.forEach((str, index) => + { enumeration[str] = enumValues[index]; }); - + // Function to handle enum drop-down - function handleDropdownChange(value) { + function handleDropdownChange(value) + { if (material.uniforms[name]) { material.uniforms[name].value = value; - } - } + } + } const defaultOption = enumList[value]; // Set the default selected option const dropdownController = currentFolder.add(enumeration, defaultOption, enumeration).name(path); - dropdownController.onChange(handleDropdownChange); + dropdownController.onChange(handleDropdownChange); dropdownController.domElement.classList.add('peditoritem'); } } @@ -1273,7 +1311,7 @@ export class Material if (uniformToUpdate && value != null) { let w = currentFolder.add(material.uniforms[name], 'value').name(path); - w.domElement.classList.add('peditoritem'); + w.domElement.classList.add('peditoritem'); } break; @@ -1287,7 +1325,7 @@ export class Material var maxValue = [DEFAULT_MAX, DEFAULT_MAX, DEFAULT_MAX, DEFAULT_MAX]; var step = [0, 0, 0, 0]; - if (nodeDefInput) + if (nodeDefInput) { if (nodeDefInput.hasAttribute('uisoftmin')) minValue = nodeDefInput.getAttribute('uisoftmin').split(',').map(Number); @@ -1300,7 +1338,7 @@ export class Material maxValue = nodeDefInput.getAttribute('uimax').split(',').map(Number); if (nodeDefInput.hasAttribute('uistep')) - step = nodeDefInput.getAttribute('uistep').split(',').map(Number); + step = nodeDefInput.getAttribute('uistep').split(',').map(Number); } for (let i = 0; i < 4; ++i) { @@ -1312,10 +1350,11 @@ export class Material const keyString = ["x", "y", "z", "w"]; let vecFolder = currentFolder.addFolder(path); - Object.keys(material.uniforms[name].value).forEach((key) => { - let w = vecFolder.add(material.uniforms[name].value, + Object.keys(material.uniforms[name].value).forEach((key) => + { + let w = vecFolder.add(material.uniforms[name].value, key, minValue[key], maxValue[key], step[key]).name(keyString[key]); - w.domElement.classList.add('peditoritem'); + w.domElement.classList.add('peditoritem'); }) } break; @@ -1333,11 +1372,12 @@ export class Material color3.fromArray(material.uniforms[name].value); dummy.color = color3.getHex(); let w = currentFolder.addColor(dummy, 'color').name(path) - .onChange(function (value) { + .onChange(function (value) + { const color3 = new THREE.Color(value); material.uniforms[name].value.set(color3.toArray()); }); - w.domElement.classList.add('peditoritem'); + w.domElement.classList.add('peditoritem'); } break; @@ -1351,7 +1391,8 @@ export class Material break; case 'string': console.log('String: ', name); - if (value != null) { + if (value != null) + { var dummy = { thevalue: value @@ -1359,7 +1400,7 @@ export class Material let item = currentFolder.add(dummy, 'thevalue'); item.name(path); item.disable(true); - item.domElement.classList.add('peditoritem'); + item.domElement.classList.add('peditoritem'); } break; default: @@ -1388,7 +1429,7 @@ export class Material Keeps track of local scene, and property editor as well as current MaterialX document and assocaited material, shader and lighting information. */ -export class Viewer +export class Viewer { static create() { @@ -1402,7 +1443,7 @@ export class Viewer this.materials.push(new Material()); this.fileLoader = new THREE.FileLoader(); - this.hdrLoader = new RGBELoader(); + this.hdrLoader = new RGBELoader(); } // @@ -1447,15 +1488,18 @@ export class Viewer this.irradianceTexture = prepareEnvTexture(irradianceTexture, renderer.capabilities); } - getEditor() { + getEditor() + { return this.editor; } - getScene() { + getScene() + { return this.scene; } - getMaterial() { + getMaterial() + { return this.materials[0]; } @@ -1469,46 +1513,57 @@ export class Viewer return this.hdrLoader; } - setDocument(doc) { + setDocument(doc) + { this.doc = doc; } - getDocument() { + getDocument() + { return this.doc; } - getLibrary() { + getLibrary() + { return this.stdlib; } - getLightRig() { + getLightRig() + { return this.lightRigDoc; } - getMx() { + getMx() + { return this.mx; } - getGenerator() { + getGenerator() + { return this.generator; } - getGenContext() { + getGenContext() + { return this.genContext; } - getLights() { + getLights() + { return this.lights; } - getLightData() { + getLightData() + { return this.lightData; } - getRadianceTexture() { + getRadianceTexture() + { return this.radianceTexture; } - getIrradianceTexture() { + getIrradianceTexture() + { return this.irradianceTexture; } diff --git a/javascript/MaterialXView/webpack.config.js b/javascript/MaterialXView/webpack.config.js index bf6673284a..cd33a443f2 100644 --- a/javascript/MaterialXView/webpack.config.js +++ b/javascript/MaterialXView/webpack.config.js @@ -6,75 +6,75 @@ const HtmlWebpackPlugin = require('html-webpack-plugin') const stdSurfaceMaterials = "../../resources/Materials/Examples/StandardSurface"; const stdSurfaceMaterialsBaseURL = "Materials/Examples/StandardSurface"; let dirent = fs.readdirSync(stdSurfaceMaterials).filter( - function (file) { if (file.lastIndexOf(".mtlx") > -1) return file; } + function (file) { if (file.lastIndexOf(".mtlx") > -1) return file; } ) let materials = dirent - .map((fileName) => ({name: fileName, value: `${stdSurfaceMaterialsBaseURL}/${fileName}`})); + .map((fileName) => ({ name: fileName, value: `${stdSurfaceMaterialsBaseURL}/${fileName}` })); const usdSurfaceMaterials = "../../resources/Materials/Examples/UsdPreviewSurface"; const usdSurfaceMaterialsBaseURL = "Materials/Examples/UsdPreviewSurface"; dirent = fs.readdirSync(usdSurfaceMaterials).filter( - function (file) { if (file.lastIndexOf(".mtlx") > -1) return file; } + function (file) { if (file.lastIndexOf(".mtlx") > -1) return file; } ) let usdMaterials = dirent - .map((fileName) => ({name: fileName, value: `${usdSurfaceMaterialsBaseURL}/${fileName}`})); + .map((fileName) => ({ name: fileName, value: `${usdSurfaceMaterialsBaseURL}/${fileName}` })); const gltfSurfaceMaterials = "../../resources/Materials/Examples/GltfPbr"; const gltfSurfaceMaterialsBaseURL = "Materials/Examples/GltfPbr"; dirent = fs.readdirSync(gltfSurfaceMaterials).filter( - function (file) { if (file.lastIndexOf(".mtlx") > -1) return file; } + function (file) { if (file.lastIndexOf(".mtlx") > -1) return file; } ) let gltfMaterials = dirent - .map((fileName) => ({name: fileName, value: `${gltfSurfaceMaterialsBaseURL}/${fileName}`})); + .map((fileName) => ({ name: fileName, value: `${gltfSurfaceMaterialsBaseURL}/${fileName}` })); -materials = materials.concat( usdMaterials ); -materials = materials.concat( gltfMaterials ); +materials = materials.concat(usdMaterials); +materials = materials.concat(gltfMaterials); const geometryFiles = "../../resources/Geometry"; const geometryFilesURL = "Geometry"; dirent = fs.readdirSync(geometryFiles).filter( - function (file) { if (file.lastIndexOf(".glb") > -1) return file; } + function (file) { if (file.lastIndexOf(".glb") > -1) return file; } ) let geometry = dirent - .map((fileName) => ({name: fileName, value: `${geometryFilesURL}/${fileName}`})); - + .map((fileName) => ({ name: fileName, value: `${geometryFilesURL}/${fileName}` })); + module.exports = { - entry: './source/index.js', - output: { - filename: 'main.js', - path: path.resolve(__dirname, 'dist') - }, - mode: "development", - plugins: [ - new HtmlWebpackPlugin({ - templateParameters: { - materials, - geometry - }, - template: 'index.ejs' - }), - new CopyPlugin({ - patterns: [ - { - context: "../../resources/Images", - from: "*.*", - to: "Images", - }, - { - context: "../../resources/Geometry/", - from: "*.glb", - to: "Geometry", - }, - { from: "./public", to: 'public' }, - { context: "../../resources/Lights", from: "*.*", to: "Lights" }, - { context: "../../resources/Lights/irradiance", from: "*.*", to: "Lights/irradiance" }, - { from: stdSurfaceMaterials, to: stdSurfaceMaterialsBaseURL }, - { from: usdSurfaceMaterials, to: usdSurfaceMaterialsBaseURL }, - { from: gltfSurfaceMaterials, to: gltfSurfaceMaterialsBaseURL }, - { from: "../build/bin/JsMaterialXGenShader.wasm" }, - { from: "../build/bin/JsMaterialXGenShader.js" }, - { from: "../build/bin/JsMaterialXGenShader.data" }, - ], - }), - ] + entry: './source/index.js', + output: { + filename: 'main.js', + path: path.resolve(__dirname, 'dist') + }, + mode: "development", + plugins: [ + new HtmlWebpackPlugin({ + templateParameters: { + materials, + geometry + }, + template: 'index.ejs' + }), + new CopyPlugin({ + patterns: [ + { + context: "../../resources/Images", + from: "*.*", + to: "Images", + }, + { + context: "../../resources/Geometry/", + from: "*.glb", + to: "Geometry", + }, + { from: "./public", to: 'public' }, + { context: "../../resources/Lights", from: "*.*", to: "Lights" }, + { context: "../../resources/Lights/irradiance", from: "*.*", to: "Lights/irradiance" }, + { from: stdSurfaceMaterials, to: stdSurfaceMaterialsBaseURL }, + { from: usdSurfaceMaterials, to: usdSurfaceMaterialsBaseURL }, + { from: gltfSurfaceMaterials, to: gltfSurfaceMaterialsBaseURL }, + { from: "../build/bin/JsMaterialXGenShader.wasm" }, + { from: "../build/bin/JsMaterialXGenShader.js" }, + { from: "../build/bin/JsMaterialXGenShader.data" }, + ], + }), + ] }; diff --git a/libraries/nprlib/nprlib_defs.mtlx b/libraries/nprlib/nprlib_defs.mtlx index a8d282d0c4..dfbaa5b5f6 100644 --- a/libraries/nprlib/nprlib_defs.mtlx +++ b/libraries/nprlib/nprlib_defs.mtlx @@ -34,8 +34,8 @@ - - + + diff --git a/libraries/nprlib/nprlib_ng.mtlx b/libraries/nprlib/nprlib_ng.mtlx index c287c2ef79..0bd7e334e5 100644 --- a/libraries/nprlib/nprlib_ng.mtlx +++ b/libraries/nprlib/nprlib_ng.mtlx @@ -32,10 +32,9 @@ - - - - + + + diff --git a/resources/Materials/TestSuite/nprlib/edge_brighten.mtlx b/resources/Materials/TestSuite/nprlib/edge_brighten.mtlx index dc06b76d98..8ce3affd40 100644 --- a/resources/Materials/TestSuite/nprlib/edge_brighten.mtlx +++ b/resources/Materials/TestSuite/nprlib/edge_brighten.mtlx @@ -1,17 +1,16 @@ - - - - + + + - - + + - - + + diff --git a/source/MaterialXGenShader/ShaderNode.cpp b/source/MaterialXGenShader/ShaderNode.cpp index ff3c8fdb10..6e951b06e6 100644 --- a/source/MaterialXGenShader/ShaderNode.cpp +++ b/source/MaterialXGenShader/ShaderNode.cpp @@ -244,6 +244,10 @@ ShaderNodePtr ShaderNode::create(const ShaderGraph* parent, const string& name, newNode->_classification = Classification::SHADER | Classification::SURFACE | Classification::CLOSURE; } } + else if (*primaryOutput->getType() == *Type::VOLUMESHADER) + { + newNode->_classification = Classification::SHADER | Classification::VOLUME | Classification::CLOSURE; + } else if (*primaryOutput->getType() == *Type::LIGHTSHADER) { newNode->_classification = Classification::LIGHT | Classification::SHADER | Classification::CLOSURE; diff --git a/source/MaterialXGraphEditor/CMakeLists.txt b/source/MaterialXGraphEditor/CMakeLists.txt index f5f22be2c5..557b134cff 100644 --- a/source/MaterialXGraphEditor/CMakeLists.txt +++ b/source/MaterialXGraphEditor/CMakeLists.txt @@ -60,6 +60,9 @@ set(GLFW_BUILD_TESTS OFF) set(GLFW_BUILD_DOCS OFF) set(GLFW_INSTALL OFF) +# Explicitly build GLFW with static libraries, independent of the broader build settings. +set(BUILD_SHARED_LIBS OFF) + add_subdirectory(External/Glfw) if(MSVC)