From af331fff97878d536d5f90c3c126a7a4b248fddb Mon Sep 17 00:00:00 2001 From: "georgiy.m.markelov@gmail.com" Date: Thu, 16 Mar 2023 14:54:45 +0300 Subject: [PATCH 1/2] BLEN-350: Fix error with Add shader node --- materialx/bl_nodes/node_parser.py | 12 ++- materialx/bl_nodes/output.py | 2 +- materialx/bl_nodes/shader.py | 93 ++++++++++++++++++------ materialx/bl_nodes/texture.py | 4 +- materialx/bl_nodes/vector.py | 2 +- materialx/nodes/__init__.py | 8 +- materialx/nodes/generate_node_classes.py | 2 +- 7 files changed, 88 insertions(+), 35 deletions(-) diff --git a/materialx/bl_nodes/node_parser.py b/materialx/bl_nodes/node_parser.py index 1f7513759..c8ab2a0d2 100644 --- a/materialx/bl_nodes/node_parser.py +++ b/materialx/bl_nodes/node_parser.py @@ -32,13 +32,13 @@ class Id: class NodeItem: """This class is a wrapper used for doing operations on MaterialX nodes, floats, and tuples""" - def __init__(self, id: Id, ng: [mx.Document, mx.NodeGraph], data: [tuple, float, mx.Node]): + def __init__(self, id: Id, ng: [mx.Document, mx.NodeGraph], data: [tuple, float, mx.Node], prefix=''): self.id = id self.nodegraph = ng self.data = data self.nodedef = None if isinstance(data, mx.Node): - MxNode_cls, _ = get_mx_node_cls(data) + MxNode_cls, _ = get_mx_node_cls(data, prefix) self.nodedef = MxNode_cls.get_nodedef(self.type) def node_item(self, value): @@ -370,10 +370,14 @@ class NodeParser: return self.get_input_default(in_key) - def create_node(self, node_name, nd_type, inputs=None): + def create_node(self, node_name, nd_type, *, prefix='', inputs=None): nodegraph = utils.get_nodegraph_by_path(self.doc, self.nodegraph_path, True) node = nodegraph.addNode(node_name, f"{node_name}_{self.id()}", nd_type) - node_item = NodeItem(self.id, nodegraph, node) + node_item = NodeItem(self.id, nodegraph, node, prefix) + + mx_type = node_item.nodedef.getType() + if mx_type != nd_type: + node.setType(mx_type) if inputs: node_item.set_inputs(inputs) diff --git a/materialx/bl_nodes/output.py b/materialx/bl_nodes/output.py index fc43a3daa..dead86320 100644 --- a/materialx/bl_nodes/output.py +++ b/materialx/bl_nodes/output.py @@ -23,7 +23,7 @@ class ShaderNodeOutputMaterial(NodeParser): return None - result = self.create_node('surfacematerial', 'material', { + result = self.create_node('surfacematerial', 'material', inputs={ 'surfaceshader': surface, }) diff --git a/materialx/bl_nodes/shader.py b/materialx/bl_nodes/shader.py index 1214a2847..f3a47e825 100644 --- a/materialx/bl_nodes/shader.py +++ b/materialx/bl_nodes/shader.py @@ -89,7 +89,7 @@ class ShaderNodeBsdfPrincipled(NodeParser): tangent = self.get_input_link('Tangent') # CREATING STANDARD SURFACE - result = self.create_node('standard_surface', 'surfaceshader', { + result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF', inputs={ 'base': 1.0, 'base_color': base_color, 'diffuse_roughness': roughness, @@ -162,7 +162,7 @@ class ShaderNodeBsdfDiffuse(NodeParser): # Also tried burley_diffuse_bsdf and oren_nayar_diffuse_bsdf here, but Blender crashes with them # CREATING STANDARD SURFACE - result = self.create_node('standard_surface', 'surfaceshader', { + result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF', inputs={ 'base_color': color, 'diffuse_roughness': 1.0 - roughness, 'normal': normal, @@ -179,7 +179,7 @@ class ShaderNodeBsdfGlass(NodeParser): normal = self.get_input_link('Normal') # CREATING STANDARD SURFACE - result = self.create_node('standard_surface', 'surfaceshader', { + result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF', inputs={ 'base': 0.0, 'normal': normal, 'specular': 1.0, @@ -200,7 +200,7 @@ class ShaderNodeEmission(NodeParser): nodegraph_path = "" def export(self): - result = self.create_node('standard_surface', 'surfaceshader') + result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF') color = self.get_input_value('Color') strength = self.get_input_value('Strength') @@ -222,23 +222,48 @@ class ShaderNodeMixShader(NodeParser): shader1 = self.get_input_link(1) shader2 = self.get_input_link(2) + mix = None + if shader1 is None and shader2 is None: return None - if shader1 is None: - return shader2 - if shader2 is None: - return shader1 - result = self.create_node('STD_mix', 'surfaceshader', { - 'fg': shader1, - 'bg': shader2, - 'mix': factor + mix = self.create_node('mix', shader1.getType().lower(), prefix='PBR', inputs={ + 'fg': shader1, + 'mix': factor + }) + + if shader1 is None: + mix = self.create_node('mix', shader2.getType().lower(), prefix='PBR', inputs={ + 'bg': shader2, + 'mix': factor + }) + + if shader1 is not None and shader2 is not None: + shader1_type = shader1.getType().lower() + shader2_type = shader2.getType().lower() + + if shader1_type != shader2_type: + log.warn(f'Types of input shaders must be the same.' + f' First shader type: {shader1_type}, second shader type: {shader2_type}') + + return None + + mix = self.create_node('mix', shader1_type, prefix='PBR', inputs={ + 'fg': shader1, + 'bg': shader2, + 'mix': factor + }) + + if not mix: + return None + + result = self.create_node('surface', 'surfaceshader', prefix='PBR', inputs={ + 'bsdf': mix, + 'opacity': 1.0 }) - log.warn(f"Known issue: node doesn't work correctly with {result.nodedef.getName()}", self.material, self.node) - return result @@ -249,20 +274,42 @@ class ShaderNodeAddShader(NodeParser): shader1 = self.get_input_link(0) shader2 = self.get_input_link(1) + add = None + if shader1 is None and shader2 is None: return None - if shader1 is None: - return shader2 - if shader2 is None: - return shader1 + add = self.create_node('add', shader1.getType().lower(), prefix='PBR', inputs={ + 'in1': shader1 + }) - result = self.create_node('STD_add', 'surfaceshader', { - 'in1': shader1, - 'in2': shader2 + if shader1 is None: + add = self.create_node('add', shader2.getType().lower(), prefix='PBR', inputs={ + 'in2': shader2 + }) + + if shader1 is not None and shader2 is not None: + shader1_type = shader1.getType().lower() + shader2_type = shader2.getType().lower() + + if shader1_type != shader2_type: + log.warn(f'Types of input shaders must be the same.' + f' First shader type: {shader1_type}, second shader type: {shader2_type}') + + return None + + add = self.create_node('add', shader1_type, prefix='PBR', inputs={ + 'in1': shader1, + 'in2': shader2 + }) + + if not add: + return None + + result = self.create_node('surface', 'surfaceshader', prefix='PBR', inputs={ + add.nodedef.getType().lower(): add, + 'opacity': 1.0 }) - log.warn(f"Known issue: node doesn't work correctly with {result.nodedef.getName()}", self.material, self.node) - return result diff --git a/materialx/bl_nodes/texture.py b/materialx/bl_nodes/texture.py index eeda9d524..46e607178 100644 --- a/materialx/bl_nodes/texture.py +++ b/materialx/bl_nodes/texture.py @@ -22,9 +22,9 @@ class ShaderNodeTexImage(NodeParser): return image_error_result # TODO use Vector input for UV - uv = self.create_node('texcoord', 'vector2', {}) + uv = self.create_node('texcoord', 'vector2') - result = self.create_node('image', self.out_type, { + result = self.create_node('image', self.out_type, inputs={ 'file': img_path, 'texcoord': uv, }) diff --git a/materialx/bl_nodes/vector.py b/materialx/bl_nodes/vector.py index 3c2e125f3..0ceac5aed 100644 --- a/materialx/bl_nodes/vector.py +++ b/materialx/bl_nodes/vector.py @@ -23,7 +23,7 @@ class ShaderNodeNormalMap(NodeParser): log.warn("Ignoring unsupported UV Map", space, self.node, self.material, "No UV Map will be used") - result = self.create_node('normalmap', 'vector3', { + result = self.create_node('normalmap', 'vector3', inputs={ 'in': color , 'scale': strength, 'space': space.lower(), diff --git a/materialx/nodes/__init__.py b/materialx/nodes/__init__.py index 23e8c7a8e..1c7deb95c 100644 --- a/materialx/nodes/__init__.py +++ b/materialx/nodes/__init__.py @@ -47,17 +47,19 @@ def unregister(): unregister_sockets() -def get_mx_node_cls(mx_node): +def get_mx_node_cls(mx_node, prefix=''): node_name = mx_node.getCategory() - suffix = f'_{node_name}' + if prefix: + suffix = prefix + suffix classes = tuple(cls for cls in mx_node_classes if cls.__name__.endswith(suffix)) + if not classes: raise KeyError(f"Unable to find MxNode class for {mx_node}") def params_set(node, out_type): return {f"in_{p.getName()}:{p.getType()}" for p in node.getActiveInputs()} | \ - {out_type} + {out_type.lower()} node_params_set = params_set(mx_node, mx_node.getType()) diff --git a/materialx/nodes/generate_node_classes.py b/materialx/nodes/generate_node_classes.py index 01fe613f2..f3e897c3d 100644 --- a/materialx/nodes/generate_node_classes.py +++ b/materialx/nodes/generate_node_classes.py @@ -314,7 +314,7 @@ def generate_basic_classes(): gen_code_dir.mkdir(exist_ok=True) files = [ - ('PBR', "PBR", utils.MX_LIBS_DIR / "bxdf/standard_surface.mtlx"), + ('BXDF', "PBR", utils.MX_LIBS_DIR / "bxdf/standard_surface.mtlx"), ('USD', "USD", utils.MX_LIBS_DIR / "bxdf/usd_preview_surface.mtlx"), ('STD', None, utils.MX_LIBS_DIR / "stdlib/stdlib_defs.mtlx"), ('PBR', "PBR", utils.MX_LIBS_DIR / "pbrlib/pbrlib_defs.mtlx"), -- 2.30.2 From 2542da4b3e329456dc6f987ea2f8f17082ad8f68 Mon Sep 17 00:00:00 2001 From: "georgiy.m.markelov@gmail.com" Date: Thu, 16 Mar 2023 17:05:10 +0300 Subject: [PATCH 2/2] added function get_node_type --- materialx/bl_nodes/shader.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/materialx/bl_nodes/shader.py b/materialx/bl_nodes/shader.py index f3a47e825..219d32135 100644 --- a/materialx/bl_nodes/shader.py +++ b/materialx/bl_nodes/shader.py @@ -31,6 +31,10 @@ def enabled(val): return True +def get_node_type(node): + return node.getType() if isinstance(node, mx.Node) else node.nodedef.getType() + + class ShaderNodeBsdfPrincipled(NodeParser): nodegraph_path = "" @@ -228,29 +232,27 @@ class ShaderNodeMixShader(NodeParser): return None if shader2 is None: - - mix = self.create_node('mix', shader1.getType().lower(), prefix='PBR', inputs={ + mix = self.create_node('mix', get_node_type(shader1).lower(), prefix='PBR', inputs={ 'fg': shader1, 'mix': factor }) if shader1 is None: - mix = self.create_node('mix', shader2.getType().lower(), prefix='PBR', inputs={ + mix = self.create_node('mix', get_node_type(shader2).lower(), prefix='PBR', inputs={ 'bg': shader2, 'mix': factor }) if shader1 is not None and shader2 is not None: - shader1_type = shader1.getType().lower() - shader2_type = shader2.getType().lower() - + shader1_type = get_node_type(shader1) + shader2_type = get_node_type(shader2) if shader1_type != shader2_type: log.warn(f'Types of input shaders must be the same.' f' First shader type: {shader1_type}, second shader type: {shader2_type}') return None - mix = self.create_node('mix', shader1_type, prefix='PBR', inputs={ + mix = self.create_node('mix', shader1_type.lower(), prefix='PBR', inputs={ 'fg': shader1, 'bg': shader2, 'mix': factor @@ -260,7 +262,7 @@ class ShaderNodeMixShader(NodeParser): return None result = self.create_node('surface', 'surfaceshader', prefix='PBR', inputs={ - 'bsdf': mix, + mix.nodedef.getType().lower(): mix, 'opacity': 1.0 }) @@ -280,26 +282,25 @@ class ShaderNodeAddShader(NodeParser): return None if shader2 is None: - add = self.create_node('add', shader1.getType().lower(), prefix='PBR', inputs={ + add = self.create_node('add', get_node_type(shader1).lower(), prefix='PBR', inputs={ 'in1': shader1 }) if shader1 is None: - add = self.create_node('add', shader2.getType().lower(), prefix='PBR', inputs={ + add = self.create_node('add', get_node_type(shader2).lower(), prefix='PBR', inputs={ 'in2': shader2 }) if shader1 is not None and shader2 is not None: - shader1_type = shader1.getType().lower() - shader2_type = shader2.getType().lower() - + shader1_type = get_node_type(shader1) + shader2_type = get_node_type(shader2) if shader1_type != shader2_type: log.warn(f'Types of input shaders must be the same.' f' First shader type: {shader1_type}, second shader type: {shader2_type}') return None - add = self.create_node('add', shader1_type, prefix='PBR', inputs={ + add = self.create_node('add', shader1_type.lower(), prefix='PBR', inputs={ 'in1': shader1, 'in2': shader2 }) -- 2.30.2