diff options
Diffstat (limited to 'render_povray/nodes_fn.py')
-rw-r--r-- | render_povray/nodes_fn.py | 704 |
1 files changed, 704 insertions, 0 deletions
diff --git a/render_povray/nodes_fn.py b/render_povray/nodes_fn.py new file mode 100644 index 00000000..ef12032f --- /dev/null +++ b/render_povray/nodes_fn.py @@ -0,0 +1,704 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +# <pep8 compliant> + +"""Translate complex shaders to exported POV textures.""" + +import bpy + +# WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +def write_nodes(pov_mat_name, ntree, file): + """Translate Blender node trees to pov and write them to file.""" + # such function local inlined import are official guidelines + # of Blender Foundation to lighten addons footprint at startup + from os import path + from .render import string_strip_hyphen + + declare_nodes = [] + scene = bpy.context.scene + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayFinishNode" and node.outputs["Finish"].is_linked: + file.write("#declare %s = finish {\n" % pov_node_name) + emission = node.inputs["Emission"].default_value + if node.inputs["Emission"].is_linked: + pass + file.write(" emission %.4g\n" % emission) + for link in ntree.links: + if link.to_node == node: + + if link.from_node.bl_idname == "PovrayDiffuseNode": + intensity = 0 + albedo = "" + brilliance = 0 + crand = 0 + if link.from_node.inputs["Intensity"].is_linked: + pass + else: + intensity = link.from_node.inputs["Intensity"].default_value + if link.from_node.inputs["Albedo"].is_linked: + pass + else: + if link.from_node.inputs["Albedo"].default_value: + albedo = "albedo" + file.write(" diffuse %s %.4g\n" % (albedo, intensity)) + if link.from_node.inputs["Brilliance"].is_linked: + pass + else: + brilliance = link.from_node.inputs["Brilliance"].default_value + file.write(" brilliance %.4g\n" % brilliance) + if link.from_node.inputs["Crand"].is_linked: + pass + else: + crand = link.from_node.inputs["Crand"].default_value + if crand > 0: + file.write(" crand %.4g\n" % crand) + + if link.from_node.bl_idname == "PovraySubsurfaceNode": + if scene.povray.sslt_enable: + energy = 0 + r = g = b = 0 + if link.from_node.inputs["Translucency"].is_linked: + pass + else: + r, g, b, a = link.from_node.inputs["Translucency"].default_value[:] + if link.from_node.inputs["Energy"].is_linked: + pass + else: + energy = link.from_node.inputs["Energy"].default_value + file.write( + " subsurface { translucency <%.4g,%.4g,%.4g>*%s }\n" + % (r, g, b, energy) + ) + + if link.from_node.bl_idname in {"PovraySpecularNode", "PovrayPhongNode"}: + intensity = 0 + albedo = "" + roughness = 0 + metallic = 0 + phong_size = 0 + highlight = "specular" + if link.from_node.inputs["Intensity"].is_linked: + pass + else: + intensity = link.from_node.inputs["Intensity"].default_value + + if link.from_node.inputs["Albedo"].is_linked: + pass + else: + if link.from_node.inputs["Albedo"].default_value: + albedo = "albedo" + if link.from_node.bl_idname in {"PovrayPhongNode"}: + highlight = "phong" + file.write(" %s %s %.4g\n" % (highlight, albedo, intensity)) + + if link.from_node.bl_idname in {"PovraySpecularNode"}: + if link.from_node.inputs["Roughness"].is_linked: + pass + else: + roughness = link.from_node.inputs["Roughness"].default_value + file.write(" roughness %.6g\n" % roughness) + + if link.from_node.bl_idname in {"PovrayPhongNode"}: + if link.from_node.inputs["Size"].is_linked: + pass + else: + phong_size = link.from_node.inputs["Size"].default_value + file.write(" phong_size %s\n" % phong_size) + + if link.from_node.inputs["Metallic"].is_linked: + pass + else: + metallic = link.from_node.inputs["Metallic"].default_value + file.write(" metallic %.4g\n" % metallic) + + if link.from_node.bl_idname in {"PovrayMirrorNode"}: + file.write(" reflection {\n") + color = None + exponent = 0 + metallic = 0 + falloff = 0 + fresnel = "" + conserve = "" + if link.from_node.inputs["Color"].is_linked: + pass + else: + color = link.from_node.inputs["Color"].default_value[:] + file.write(" <%.4g,%.4g,%.4g>\n" % (color[0], color[1], color[2])) + + if link.from_node.inputs["Exponent"].is_linked: + pass + else: + exponent = link.from_node.inputs["Exponent"].default_value + file.write(" exponent %.4g\n" % exponent) + + if link.from_node.inputs["Falloff"].is_linked: + pass + else: + falloff = link.from_node.inputs["Falloff"].default_value + file.write(" falloff %.4g\n" % falloff) + + if link.from_node.inputs["Metallic"].is_linked: + pass + else: + metallic = link.from_node.inputs["Metallic"].default_value + file.write(" metallic %.4g" % metallic) + + if link.from_node.inputs["Fresnel"].is_linked: + pass + else: + if link.from_node.inputs["Fresnel"].default_value: + fresnel = "fresnel" + + if link.from_node.inputs["Conserve energy"].is_linked: + pass + else: + if link.from_node.inputs["Conserve energy"].default_value: + conserve = "conserve_energy" + + file.write(" %s}\n %s\n" % (fresnel, conserve)) + + if link.from_node.bl_idname == "PovrayAmbientNode": + ambient = (0, 0, 0) + if link.from_node.inputs["Ambient"].is_linked: + pass + else: + ambient = link.from_node.inputs["Ambient"].default_value[:] + file.write(" ambient <%.4g,%.4g,%.4g>\n" % ambient) + + if link.from_node.bl_idname in {"PovrayIridescenceNode"}: + file.write(" irid {\n") + amount = 0 + thickness = 0 + turbulence = 0 + if link.from_node.inputs["Amount"].is_linked: + pass + else: + amount = link.from_node.inputs["Amount"].default_value + file.write(" %.4g\n" % amount) + + if link.from_node.inputs["Thickness"].is_linked: + pass + else: + exponent = link.from_node.inputs["Thickness"].default_value + file.write(" thickness %.4g\n" % thickness) + + if link.from_node.inputs["Turbulence"].is_linked: + pass + else: + falloff = link.from_node.inputs["Turbulence"].default_value + file.write(" turbulence %.4g}\n" % turbulence) + + file.write("}\n") + + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayTransformNode" and node.outputs["Transform"].is_linked: + tx = node.inputs["Translate x"].default_value + ty = node.inputs["Translate y"].default_value + tz = node.inputs["Translate z"].default_value + rx = node.inputs["Rotate x"].default_value + ry = node.inputs["Rotate y"].default_value + rz = node.inputs["Rotate z"].default_value + sx = node.inputs["Scale x"].default_value + sy = node.inputs["Scale y"].default_value + sz = node.inputs["Scale z"].default_value + file.write( + "#declare %s = transform {\n" + " translate<%.4g,%.4g,%.4g>\n" + " rotate<%.4g,%.4g,%.4g>\n" + " scale<%.4g,%.4g,%.4g>}\n" % (pov_node_name, tx, ty, tz, rx, ry, rz, sx, sy, sz) + ) + + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayColorImageNode" and node.outputs["Pigment"].is_linked: + declare_nodes.append(node.name) + if node.image == "": + file.write("#declare %s = pigment { color rgb 0.8}\n" % pov_node_name) + else: + im = bpy.data.images[node.image] + if im.filepath and path.exists(bpy.path.abspath(im.filepath)): # (os.path) + transform = "" + for link in ntree.links: + if ( + link.from_node.bl_idname == "PovrayTransformNode" + and link.to_node == node + ): + pov_trans_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + uv = "" + if node.map_type == "uv_mapping": + uv = "uv_mapping" + filepath = bpy.path.abspath(im.filepath) + file.write("#declare %s = pigment {%s image_map {\n" % (pov_node_name, uv)) + premul = "off" + if node.premultiplied: + premul = "on" + once = "" + if node.once: + once = "once" + file.write( + ' "%s"\n gamma %.6g\n premultiplied %s\n' + % (filepath, node.inputs["Gamma"].default_value, premul) + ) + file.write(" %s\n" % once) + if node.map_type != "uv_mapping": + file.write(" map_type %s\n" % node.map_type) + file.write( + " interpolate %s\n filter all %.4g\n transmit all %.4g\n" + % ( + node.interpolate, + node.inputs["Filter"].default_value, + node.inputs["Transmit"].default_value, + ) + ) + file.write(" }\n") + file.write(" %s\n" % transform) + file.write(" }\n") + + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayImagePatternNode" and node.outputs["Pattern"].is_linked: + declare_nodes.append(node.name) + if node.image != "": + im = bpy.data.images[node.image] + if im.filepath and path.exists(bpy.path.abspath(im.filepath)): + transform = "" + for link in ntree.links: + if ( + link.from_node.bl_idname == "PovrayTransformNode" + and link.to_node == node + ): + pov_trans_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + uv = "" + if node.map_type == "uv_mapping": + uv = "uv_mapping" + filepath = bpy.path.abspath(im.filepath) + file.write("#macro %s() %s image_pattern {\n" % (pov_node_name, uv)) + premul = "off" + if node.premultiplied: + premul = "on" + once = "" + if node.once: + once = "once" + file.write( + ' "%s"\n gamma %.6g\n premultiplied %s\n' + % (filepath, node.inputs["Gamma"].default_value, premul) + ) + file.write(" %s\n" % once) + if node.map_type != "uv_mapping": + file.write(" map_type %s\n" % node.map_type) + file.write(" interpolate %s\n" % node.interpolate) + file.write(" }\n") + file.write(" %s\n" % transform) + file.write("#end\n") + + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayBumpMapNode" and node.outputs["Normal"].is_linked: + if node.image != "": + im = bpy.data.images[node.image] + if im.filepath and path.exists(bpy.path.abspath(im.filepath)): + transform = "" + for link in ntree.links: + if ( + link.from_node.bl_idname == "PovrayTransformNode" + and link.to_node == node + ): + pov_trans_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + uv = "" + if node.map_type == "uv_mapping": + uv = "uv_mapping" + filepath = bpy.path.abspath(im.filepath) + file.write("#declare %s = normal {%s bump_map {\n" % (pov_node_name, uv)) + once = "" + if node.once: + once = "once" + file.write(' "%s"\n' % filepath) + file.write(" %s\n" % once) + if node.map_type != "uv_mapping": + file.write(" map_type %s\n" % node.map_type) + bump_size = node.inputs["Normal"].default_value + if node.inputs["Normal"].is_linked: + pass + file.write( + " interpolate %s\n bump_size %.4g\n" % (node.interpolate, bump_size) + ) + file.write(" }\n") + file.write(" %s\n" % transform) + file.write(" }\n") + declare_nodes.append(node.name) + + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayPigmentNode" and node.outputs["Pigment"].is_linked: + declare_nodes.append(node.name) + r, g, b = node.inputs["Color"].default_value[:] + f = node.inputs["Filter"].default_value + t = node.inputs["Transmit"].default_value + if node.inputs["Color"].is_linked: + pass + file.write( + "#declare %s = pigment{color srgbft <%.4g,%.4g,%.4g,%.4g,%.4g>}\n" + % (pov_node_name, r, g, b, f, t) + ) + + for node in ntree.nodes: + pov_node_name = string_strip_hyphen(bpy.path.clean_name(node.name)) + "_%s" % pov_mat_name + if node.bl_idname == "PovrayTextureNode" and node.outputs["Texture"].is_linked: + declare_nodes.append(node.name) + r, g, b = node.inputs["Pigment"].default_value[:] + pov_col_name = "color rgb <%.4g,%.4g,%.4g>" % (r, g, b) + if node.inputs["Pigment"].is_linked: + for link in ntree.links: + if link.to_node == node and link.to_socket.name == "Pigment": + pov_col_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + file.write("#declare %s = texture{\n pigment{%s}\n" % (pov_node_name, pov_col_name)) + if node.inputs["Normal"].is_linked: + for link in ntree.links: + if ( + link.to_node == node + and link.to_socket.name == "Normal" + and link.from_node.name in declare_nodes + ): + pov_nor_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + file.write(" normal{%s}\n" % pov_nor_name) + if node.inputs["Finish"].is_linked: + for link in ntree.links: + if link.to_node == node and link.to_socket.name == "Finish": + pov_fin_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + + "_%s" % pov_mat_name + ) + file.write(" finish{%s}\n" % pov_fin_name) + file.write("}\n") + declare_nodes.append(node.name) + + for i in range(0, len(ntree.nodes)): + for node in ntree.nodes: + if node.bl_idname in {"ShaderNodeGroup", "ShaderTextureMapNode"}: + for output in node.outputs: + if ( + output.name == "Texture" + and output.is_linked + and (node.name not in declare_nodes) + ): + declare = True + for link in ntree.links: + if link.to_node == node and link.to_socket.name not in { + "", + "Color ramp", + "Mapping", + "Transform", + "Modifier", + }: + if link.from_node.name not in declare_nodes: + declare = False + if declare: + pov_node_name = ( + string_strip_hyphen(bpy.path.clean_name(node.name)) + + "_%s" % pov_mat_name + ) + uv = "" + warp = "" + for link in ntree.links: + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayMappingNode" + and link.from_node.warp_type != "NONE" + ): + w_type = link.from_node.warp_type + if w_type == "uv_mapping": + uv = "uv_mapping" + else: + tor = "" + if w_type == "toroidal": + tor = ( + "major_radius %.4g" + % link.from_node.warp_tor_major_radius + ) + orient = link.from_node.warp_orientation + exp = link.from_node.warp_dist_exp + warp = "warp{%s orientation %s dist_exp %.4g %s}" % ( + w_type, + orient, + exp, + tor, + ) + if link.from_node.warp_type == "planar": + warp = "warp{%s %s %.4g}" % (w_type, orient, exp) + if link.from_node.warp_type == "cubic": + warp = "warp{%s}" % w_type + file.write("#declare %s = texture {%s\n" % (pov_node_name, uv)) + pattern = node.inputs[0].default_value + advanced = "" + if node.inputs[0].is_linked: + for link in ntree.links: + if ( + link.to_node == node + and link.from_node.bl_idname == "ShaderPatternNode" + ): + # ------------ advanced ------------------------- # + lfn = link.from_node + pattern = lfn.pattern + if pattern == "agate": + advanced = "agate_turb %.4g" % lfn.agate_turb + if pattern == "crackle": + advanced = "form <%.4g,%.4g,%.4g>" % ( + lfn.crackle_form_x, + lfn.crackle_form_y, + lfn.crackle_form_z, + ) + advanced += " metric %.4g" % lfn.crackle_metric + if lfn.crackle_solid: + advanced += " solid" + if pattern in {"spiral1", "spiral2"}: + advanced = "%.4g" % lfn.spiral_arms + if pattern in {"tiling"}: + advanced = "%.4g" % lfn.tiling_number + if pattern in {"gradient"}: + advanced = "%s" % lfn.gradient_orient + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayImagePatternNode" + ): + pov_macro_name = ( + string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + + "_%s" % pov_mat_name + ) + pattern = "%s()" % pov_macro_name + file.write(" %s %s %s\n" % (pattern, advanced, warp)) + + repeat = "" + for link in ntree.links: + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayMultiplyNode" + ): + if link.from_node.amount_x > 1: + repeat += "warp{repeat %.4g * x}" % link.from_node.amount_x + if link.from_node.amount_y > 1: + repeat += " warp{repeat %.4g * y}" % link.from_node.amount_y + if link.from_node.amount_z > 1: + repeat += " warp{repeat %.4g * z}" % link.from_node.amount_z + + transform = "" + for link in ntree.links: + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayTransformNode" + ): + pov_trans_name = ( + string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + + "_%s" % pov_mat_name + ) + transform = "transform {%s}" % pov_trans_name + x = 0 + y = 0 + z = 0 + d = 0 + e = 0 + f = 0 + g = 0 + h = 0 + modifier = False + for link in ntree.links: + if ( + link.to_node == node + and link.from_node.bl_idname == "PovrayModifierNode" + ): + modifier = True + if link.from_node.inputs["Turb X"].is_linked: + pass + else: + x = link.from_node.inputs["Turb X"].default_value + + if link.from_node.inputs["Turb Y"].is_linked: + pass + else: + y = link.from_node.inputs["Turb Y"].default_value + + if link.from_node.inputs["Turb Z"].is_linked: + pass + else: + z = link.from_node.inputs["Turb Z"].default_value + + if link.from_node.inputs["Octaves"].is_linked: + pass + else: + d = link.from_node.inputs["Octaves"].default_value + + if link.from_node.inputs["Lambda"].is_linked: + pass + else: + e = link.from_node.inputs["Lambda"].default_value + + if link.from_node.inputs["Omega"].is_linked: + pass + else: + f = link.from_node.inputs["Omega"].default_value + + if link.from_node.inputs["Frequency"].is_linked: + pass + else: + g = link.from_node.inputs["Frequency"].default_value + + if link.from_node.inputs["Phase"].is_linked: + pass + else: + h = link.from_node.inputs["Phase"].default_value + + turb = "turbulence <%.4g,%.4g,%.4g>" % (x, y, z) + octv = "octaves %s" % d + lmbd = "lambda %.4g" % e + omg = "omega %.4g" % f + freq = "frequency %.4g" % g + pha = "phase %.4g" % h + + file.write("\n") + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write(" texture_map {\n") + if node.inputs["Color ramp"].is_linked: + for link in ntree.links: + if ( + link.to_node == node + and link.from_node.bl_idname == "ShaderNodeValToRGB" + ): + els = link.from_node.color_ramp.elements + n = -1 + for el in els: + n += 1 + pov_in_mat_name = string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + "_%s_%s" % (n, pov_mat_name) + default = True + for ilink in ntree.links: + if ( + ilink.to_node == node + and ilink.to_socket.name == str(n) + ): + default = False + pov_in_mat_name = ( + string_strip_hyphen( + bpy.path.clean_name( + ilink.from_node.name + ) + ) + + "_%s" % pov_mat_name + ) + if default: + r, g, b, a = el.color[:] + file.write( + " #declare %s = texture{" + "pigment{" + "color srgbt <%.4g,%.4g,%.4g,%.4g>}};\n" + % (pov_in_mat_name, r, g, b, 1 - a) + ) + file.write( + " [%s %s]\n" % (el.position, pov_in_mat_name) + ) + else: + els = [[0, 0, 0, 0], [1, 1, 1, 1]] + for t in range(0, 2): + pov_in_mat_name = string_strip_hyphen( + bpy.path.clean_name(link.from_node.name) + ) + "_%s_%s" % (t, pov_mat_name) + default = True + for ilink in ntree.links: + if ilink.to_node == node and ilink.to_socket.name == str(t): + default = False + pov_in_mat_name = ( + string_strip_hyphen( + bpy.path.clean_name(ilink.from_node.name) + ) + + "_%s" % pov_mat_name + ) + if default: + r, g, b = els[t][1], els[t][2], els[t][3] + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write( + " #declare %s = texture{pigment{color rgb <%.4g,%.4g,%.4g>}};\n" + % (pov_in_mat_name, r, g, b) + ) + else: + file.write( + " texture{pigment{color rgb <%.4g,%.4g,%.4g>}}\n" + % (r, g, b) + ) + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write(" [%s %s]\n" % (els[t][0], pov_in_mat_name)) + else: + if not default: + file.write(" texture{%s}\n" % pov_in_mat_name) + if pattern not in { + "checker", + "hexagon", + "square", + "triangular", + "brick", + }: + file.write("}\n") + if pattern == "brick": + file.write( + "brick_size <%.4g, %.4g, %.4g> mortar %.4g \n" + % ( + node.brick_size_x, + node.brick_size_y, + node.brick_size_z, + node.brick_mortar, + ) + ) + file.write(" %s %s" % (repeat, transform)) + if modifier: + file.write( + " %s %s %s %s %s %s" % (turb, octv, lmbd, omg, freq, pha) + ) + file.write("}\n") + declare_nodes.append(node.name) + + for link in ntree.links: + if link.to_node.bl_idname == "PovrayOutputNode" and link.from_node.name in declare_nodes: + pov_mat_node_name = ( + string_strip_hyphen(bpy.path.clean_name(link.from_node.name)) + "_%s" % pov_mat_name + ) + file.write("#declare %s = %s\n" % (pov_mat_name, pov_mat_node_name)) |