"""Build the image material Shader node groups.""" import bpy from anyimage.common.object import ( DEPTH_MATERIAL_NODE_GROUP_NAME, IMAGE_MATERIAL_NODE_GROUP_NAME, ) BUMP_BASE_DISTANCE = 0.001 MATERIAL_DISPLACEMENT_ATTRIBUTE = "o_material_displacement" BACKGROUND_DEPTH_ATTRIBUTE = "o_background_depth" DEPTH_SCALE_ATTRIBUTE = "o_depth_scale" def build_material_layer_group(depth_plane=False): group_name = ( DEPTH_MATERIAL_NODE_GROUP_NAME if depth_plane else IMAGE_MATERIAL_NODE_GROUP_NAME ) existing = bpy.data.node_groups.get(group_name) if existing is not None: return existing group = bpy.data.node_groups.new(group_name, "ShaderNodeTree") interface = group.interface input_sockets = [ ("Color", "NodeSocketColor"), ("Alpha", "NodeSocketFloat"), ("Bump Scale", "NodeSocketFloat"), ] normal_group = not depth_plane if normal_group: input_sockets.insert(2, ("Normal", "NodeSocketColor")) input_sockets.insert(3, ("Normal Scale", "NodeSocketFloat")) input_sockets.insert(4, ("Object Space", "NodeSocketBool")) if depth_plane: input_sockets.insert(2, ("Depth", "NodeSocketFloat")) for name, socket_type in input_sockets: socket = interface.new_socket( name=name, in_out="INPUT", socket_type=socket_type, ) if name == "Alpha": socket.default_value = 1.0 socket.min_value = 0.0 socket.max_value = 1.0 socket.subtype = "FACTOR" elif name == "Bump Scale": socket.default_value = 0.0 socket.min_value = -10000.0 socket.max_value = 10000.0 elif name == "Normal": socket.default_value = (0.5, 0.5, 1.0, 1.0) elif name == "Normal Scale": socket.default_value = 1.0 socket.min_value = 0.0 socket.max_value = 10.0 elif name == "Object Space": socket.default_value = False output_sockets = [ ("Color", "NodeSocketColor"), ("Roughness", "NodeSocketFloat"), ("Alpha", "NodeSocketFloat"), ("Normal", "NodeSocketVector"), ] if depth_plane: output_sockets.append(("Displacement", "NodeSocketVector")) for name, socket_type in output_sockets: interface.new_socket( name=name, in_out="OUTPUT", socket_type=socket_type, ) nodes = group.nodes links = group.links group_input = nodes.new("NodeGroupInput") group_input.location = (-500.0, 100.0) group_output = nodes.new("NodeGroupOutput") group_output.location = (300.0, 100.0) roughness_curve = nodes.new("ShaderNodeFloatCurve") roughness_curve.mapping.initialize() curve_points = roughness_curve.mapping.curves[0].points curve_points[0].location = (0.0, 0.5) curve_points[-1].location = (1.0, 0.1) roughness_curve.mapping.update() roughness_curve.location = (-250.0, 350.0) bump = nodes.new("ShaderNodeBump") bump.location = (100.0, 0.0) if normal_group: tangent_normal = nodes.new("ShaderNodeNormalMap") tangent_normal.space = "TANGENT" tangent_normal.location = (-500.0, -100.0) object_normal = nodes.new("ShaderNodeNormalMap") object_normal.space = "OBJECT" object_normal.location = (-500.0, -350.0) choose_normal = nodes.new("ShaderNodeMix") choose_normal.data_type = "VECTOR" choose_normal.location = (-150.0, -150.0) for normal_map in (tangent_normal, object_normal): links.new(group_input.outputs["Normal"], normal_map.inputs["Color"]) links.new( group_input.outputs["Normal Scale"], normal_map.inputs["Strength"], ) links.new(group_input.outputs["Object Space"], choose_normal.inputs[0]) links.new(tangent_normal.outputs["Normal"], choose_normal.inputs[4]) links.new(object_normal.outputs["Normal"], choose_normal.inputs[5]) links.new(choose_normal.outputs["Result"], bump.inputs["Normal"]) displacement = None processed_alpha = group_input.outputs["Alpha"] bump_depth = group_input.outputs["Color"] if depth_plane: displacement = nodes.new("ShaderNodeDisplacement") displacement.inputs["Midlevel"].default_value = 0.0 displacement.inputs["Scale"].default_value = 1.0 group_input.location = (-1200.0, 100.0) group_output.location = (1050.0, 100.0) roughness_curve.location = (-300.0, 450.0) bump.location = (750.0, 50.0) displacement.location = (750.0, -300.0) background_attribute = nodes.new("ShaderNodeAttribute") background_attribute.attribute_type = "GEOMETRY" background_attribute.attribute_name = BACKGROUND_DEPTH_ATTRIBUTE background_attribute.location = (-950.0, -500.0) material_attribute = nodes.new("ShaderNodeAttribute") material_attribute.attribute_type = "GEOMETRY" material_attribute.attribute_name = MATERIAL_DISPLACEMENT_ATTRIBUTE material_attribute.location = (-50.0, -650.0) strength_attribute = nodes.new("ShaderNodeAttribute") strength_attribute.attribute_type = "GEOMETRY" strength_attribute.attribute_name = DEPTH_SCALE_ATTRIBUTE strength_attribute.location = (200.0, -650.0) foreground_mask = nodes.new("ShaderNodeMath") foreground_mask.operation = "GREATER_THAN" foreground_mask.inputs[1].default_value = 0.5 foreground_mask.location = (-950.0, -250.0) clamp_foreground = nodes.new("ShaderNodeMath") clamp_foreground.operation = "MAXIMUM" clamp_foreground.location = (-650.0, -400.0) masked_depth = nodes.new("ShaderNodeMix") masked_depth.data_type = "FLOAT" masked_depth.location = (-350.0, -350.0) enabled_depth = nodes.new("ShaderNodeMath") enabled_depth.operation = "MULTIPLY" enabled_depth.location = (250.0, -350.0) scaled_depth = nodes.new("ShaderNodeMath") scaled_depth.operation = "MULTIPLY" scaled_depth.location = (450.0, -350.0) links.new(group_input.outputs["Alpha"], foreground_mask.inputs[0]) links.new(group_input.outputs["Depth"], clamp_foreground.inputs[0]) links.new(background_attribute.outputs["Fac"], clamp_foreground.inputs[1]) links.new(foreground_mask.outputs[0], masked_depth.inputs[0]) links.new(background_attribute.outputs["Fac"], masked_depth.inputs[2]) links.new(clamp_foreground.outputs[0], masked_depth.inputs[3]) links.new(masked_depth.outputs["Result"], enabled_depth.inputs[0]) links.new(material_attribute.outputs["Fac"], enabled_depth.inputs[1]) links.new(enabled_depth.outputs[0], scaled_depth.inputs[0]) links.new(strength_attribute.outputs["Fac"], scaled_depth.inputs[1]) processed_alpha = foreground_mask.outputs[0] processed_depth = scaled_depth.outputs[0] links.new(group_input.outputs["Color"], group_output.inputs["Color"]) links.new(group_input.outputs["Color"], roughness_curve.inputs["Value"]) links.new( roughness_curve.outputs["Value"], group_output.inputs["Roughness"], ) links.new(processed_alpha, group_output.inputs["Alpha"]) links.new(bump_depth, bump.inputs["Height"]) bump_distance = nodes.new("ShaderNodeMath") bump_distance.operation = "MULTIPLY" bump_distance.inputs[1].default_value = BUMP_BASE_DISTANCE bump_distance.location = (500.0, 100.0) if depth_plane else (-250.0, 150.0) links.new(group_input.outputs["Bump Scale"], bump_distance.inputs[0]) links.new(bump_distance.outputs[0], bump.inputs["Distance"]) links.new(bump.outputs["Normal"], group_output.inputs["Normal"]) if displacement is not None: links.new(processed_depth, displacement.inputs["Height"]) links.new( displacement.outputs["Displacement"], group_output.inputs["Displacement"], ) group.color_tag = "SHADER" group.description = ( "Prepare image depth material outputs." if depth_plane else f"Prepare {group_name} material outputs." ) group.use_fake_user = True return group