WIP: MaterialX addon #104594
@ -32,13 +32,13 @@ class Id:
|
|||||||
class NodeItem:
|
class NodeItem:
|
||||||
"""This class is a wrapper used for doing operations on MaterialX nodes, floats, and tuples"""
|
"""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.id = id
|
||||||
self.nodegraph = ng
|
self.nodegraph = ng
|
||||||
self.data = data
|
self.data = data
|
||||||
self.nodedef = None
|
self.nodedef = None
|
||||||
if isinstance(data, mx.Node):
|
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)
|
self.nodedef = MxNode_cls.get_nodedef(self.type)
|
||||||
|
|
||||||
def node_item(self, value):
|
def node_item(self, value):
|
||||||
@ -370,10 +370,14 @@ class NodeParser:
|
|||||||
|
|
||||||
return self.get_input_default(in_key)
|
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)
|
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 = 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:
|
if inputs:
|
||||||
node_item.set_inputs(inputs)
|
node_item.set_inputs(inputs)
|
||||||
|
@ -23,7 +23,7 @@ class ShaderNodeOutputMaterial(NodeParser):
|
|||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
result = self.create_node('surfacematerial', 'material', {
|
result = self.create_node('surfacematerial', 'material', inputs={
|
||||||
'surfaceshader': surface,
|
'surfaceshader': surface,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -31,6 +31,10 @@ def enabled(val):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_node_type(node):
|
||||||
|
return node.getType() if isinstance(node, mx.Node) else node.nodedef.getType()
|
||||||
|
|
||||||
|
|
||||||
class ShaderNodeBsdfPrincipled(NodeParser):
|
class ShaderNodeBsdfPrincipled(NodeParser):
|
||||||
nodegraph_path = ""
|
nodegraph_path = ""
|
||||||
|
|
||||||
@ -89,7 +93,7 @@ class ShaderNodeBsdfPrincipled(NodeParser):
|
|||||||
tangent = self.get_input_link('Tangent')
|
tangent = self.get_input_link('Tangent')
|
||||||
|
|
||||||
# CREATING STANDARD SURFACE
|
# CREATING STANDARD SURFACE
|
||||||
result = self.create_node('standard_surface', 'surfaceshader', {
|
result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF', inputs={
|
||||||
'base': 1.0,
|
'base': 1.0,
|
||||||
'base_color': base_color,
|
'base_color': base_color,
|
||||||
'diffuse_roughness': roughness,
|
'diffuse_roughness': roughness,
|
||||||
@ -162,7 +166,7 @@ class ShaderNodeBsdfDiffuse(NodeParser):
|
|||||||
|
|
||||||
# Also tried burley_diffuse_bsdf and oren_nayar_diffuse_bsdf here, but Blender crashes with them
|
# Also tried burley_diffuse_bsdf and oren_nayar_diffuse_bsdf here, but Blender crashes with them
|
||||||
# CREATING STANDARD SURFACE
|
# CREATING STANDARD SURFACE
|
||||||
result = self.create_node('standard_surface', 'surfaceshader', {
|
result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF', inputs={
|
||||||
'base_color': color,
|
'base_color': color,
|
||||||
'diffuse_roughness': 1.0 - roughness,
|
'diffuse_roughness': 1.0 - roughness,
|
||||||
'normal': normal,
|
'normal': normal,
|
||||||
@ -179,7 +183,7 @@ class ShaderNodeBsdfGlass(NodeParser):
|
|||||||
normal = self.get_input_link('Normal')
|
normal = self.get_input_link('Normal')
|
||||||
|
|
||||||
# CREATING STANDARD SURFACE
|
# CREATING STANDARD SURFACE
|
||||||
result = self.create_node('standard_surface', 'surfaceshader', {
|
result = self.create_node('standard_surface', 'surfaceshader', prefix='BXDF', inputs={
|
||||||
'base': 0.0,
|
'base': 0.0,
|
||||||
'normal': normal,
|
'normal': normal,
|
||||||
'specular': 1.0,
|
'specular': 1.0,
|
||||||
@ -200,7 +204,7 @@ class ShaderNodeEmission(NodeParser):
|
|||||||
nodegraph_path = ""
|
nodegraph_path = ""
|
||||||
|
|
||||||
def export(self):
|
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')
|
color = self.get_input_value('Color')
|
||||||
strength = self.get_input_value('Strength')
|
strength = self.get_input_value('Strength')
|
||||||
@ -222,22 +226,45 @@ class ShaderNodeMixShader(NodeParser):
|
|||||||
shader1 = self.get_input_link(1)
|
shader1 = self.get_input_link(1)
|
||||||
shader2 = self.get_input_link(2)
|
shader2 = self.get_input_link(2)
|
||||||
|
|
||||||
|
mix = None
|
||||||
|
|
||||||
if shader1 is None and shader2 is None:
|
if shader1 is None and shader2 is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if shader1 is None:
|
|
||||||
return shader2
|
|
||||||
|
|
||||||
if shader2 is None:
|
if shader2 is None:
|
||||||
return shader1
|
mix = self.create_node('mix', get_node_type(shader1).lower(), prefix='PBR', inputs={
|
||||||
|
'fg': shader1,
|
||||||
|
'mix': factor
|
||||||
|
})
|
||||||
|
|
||||||
result = self.create_node('STD_mix', 'surfaceshader', {
|
if shader1 is None:
|
||||||
|
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 = 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.lower(), prefix='PBR', inputs={
|
||||||
'fg': shader1,
|
'fg': shader1,
|
||||||
'bg': shader2,
|
'bg': shader2,
|
||||||
'mix': factor
|
'mix': factor
|
||||||
})
|
})
|
||||||
|
|
||||||
log.warn(f"Known issue: node doesn't work correctly with {result.nodedef.getName()}", self.material, self.node)
|
if not mix:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = self.create_node('surface', 'surfaceshader', prefix='PBR', inputs={
|
||||||
|
mix.nodedef.getType().lower(): mix,
|
||||||
|
'opacity': 1.0
|
||||||
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -249,20 +276,41 @@ class ShaderNodeAddShader(NodeParser):
|
|||||||
shader1 = self.get_input_link(0)
|
shader1 = self.get_input_link(0)
|
||||||
shader2 = self.get_input_link(1)
|
shader2 = self.get_input_link(1)
|
||||||
|
|
||||||
|
add = None
|
||||||
|
|
||||||
if shader1 is None and shader2 is None:
|
if shader1 is None and shader2 is None:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
if shader1 is None:
|
|
||||||
return shader2
|
|
||||||
|
|
||||||
if shader2 is None:
|
if shader2 is None:
|
||||||
return shader1
|
add = self.create_node('add', get_node_type(shader1).lower(), prefix='PBR', inputs={
|
||||||
|
'in1': shader1
|
||||||
|
})
|
||||||
|
|
||||||
result = self.create_node('STD_add', 'surfaceshader', {
|
if shader1 is None:
|
||||||
|
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 = 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.lower(), prefix='PBR', inputs={
|
||||||
'in1': shader1,
|
'in1': shader1,
|
||||||
'in2': shader2
|
'in2': shader2
|
||||||
})
|
})
|
||||||
|
|
||||||
log.warn(f"Known issue: node doesn't work correctly with {result.nodedef.getName()}", self.material, self.node)
|
if not add:
|
||||||
|
return None
|
||||||
|
|
||||||
|
result = self.create_node('surface', 'surfaceshader', prefix='PBR', inputs={
|
||||||
|
add.nodedef.getType().lower(): add,
|
||||||
|
'opacity': 1.0
|
||||||
|
})
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -22,9 +22,9 @@ class ShaderNodeTexImage(NodeParser):
|
|||||||
return image_error_result
|
return image_error_result
|
||||||
|
|
||||||
# TODO use Vector input for UV
|
# 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,
|
'file': img_path,
|
||||||
'texcoord': uv,
|
'texcoord': uv,
|
||||||
})
|
})
|
||||||
|
@ -23,7 +23,7 @@ class ShaderNodeNormalMap(NodeParser):
|
|||||||
log.warn("Ignoring unsupported UV Map", space, self.node, self.material,
|
log.warn("Ignoring unsupported UV Map", space, self.node, self.material,
|
||||||
"No UV Map will be used")
|
"No UV Map will be used")
|
||||||
|
|
||||||
result = self.create_node('normalmap', 'vector3', {
|
result = self.create_node('normalmap', 'vector3', inputs={
|
||||||
'in': color ,
|
'in': color ,
|
||||||
'scale': strength,
|
'scale': strength,
|
||||||
'space': space.lower(),
|
'space': space.lower(),
|
||||||
|
@ -47,17 +47,19 @@ def unregister():
|
|||||||
unregister_sockets()
|
unregister_sockets()
|
||||||
|
|
||||||
|
|
||||||
def get_mx_node_cls(mx_node):
|
def get_mx_node_cls(mx_node, prefix=''):
|
||||||
node_name = mx_node.getCategory()
|
node_name = mx_node.getCategory()
|
||||||
|
|
||||||
suffix = f'_{node_name}'
|
suffix = f'_{node_name}'
|
||||||
|
if prefix:
|
||||||
|
suffix = prefix + suffix
|
||||||
classes = tuple(cls for cls in mx_node_classes if cls.__name__.endswith(suffix))
|
classes = tuple(cls for cls in mx_node_classes if cls.__name__.endswith(suffix))
|
||||||
|
|
||||||
if not classes:
|
if not classes:
|
||||||
raise KeyError(f"Unable to find MxNode class for {mx_node}")
|
raise KeyError(f"Unable to find MxNode class for {mx_node}")
|
||||||
|
|
||||||
def params_set(node, out_type):
|
def params_set(node, out_type):
|
||||||
return {f"in_{p.getName()}:{p.getType()}" for p in node.getActiveInputs()} | \
|
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())
|
node_params_set = params_set(mx_node, mx_node.getType())
|
||||||
|
|
||||||
|
@ -314,7 +314,7 @@ def generate_basic_classes():
|
|||||||
gen_code_dir.mkdir(exist_ok=True)
|
gen_code_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
files = [
|
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"),
|
('USD', "USD", utils.MX_LIBS_DIR / "bxdf/usd_preview_surface.mtlx"),
|
||||||
('STD', None, utils.MX_LIBS_DIR / "stdlib/stdlib_defs.mtlx"),
|
('STD', None, utils.MX_LIBS_DIR / "stdlib/stdlib_defs.mtlx"),
|
||||||
('PBR', "PBR", utils.MX_LIBS_DIR / "pbrlib/pbrlib_defs.mtlx"),
|
('PBR', "PBR", utils.MX_LIBS_DIR / "pbrlib/pbrlib_defs.mtlx"),
|
||||||
|
Loading…
Reference in New Issue
Block a user