diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2017-05-25 19:57:13 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2017-05-27 14:07:43 +0300 |
commit | c441b2fd05e383da6b071b5efdd627c2f445ade7 (patch) | |
tree | 42e088bdc2b3d747d32aaf23cd4281475dbcd2b9 /modules/cycles_shader_compat.py | |
parent | a188e4756e086224592f13b0eabce263db67b6be (diff) |
Move FBX importer's Cycles material 'convertor' to modules.
That way other addons may use it as well.
Diffstat (limited to 'modules/cycles_shader_compat.py')
-rw-r--r-- | modules/cycles_shader_compat.py | 540 |
1 files changed, 540 insertions, 0 deletions
diff --git a/modules/cycles_shader_compat.py b/modules/cycles_shader_compat.py new file mode 100644 index 00000000..e0adbea7 --- /dev/null +++ b/modules/cycles_shader_compat.py @@ -0,0 +1,540 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# ##### END GPL LICENSE BLOCK ##### + +# <pep8 compliant> + +import bpy + +__all__ = ( + "CyclesShaderWrapper", + ) + + +class CyclesShaderWrapper(): + """ + Hard coded shader setup. + Suitable for importers, adds basic: + diffuse/spec/alpha/normal/bump/reflect. + """ + __slots__ = ( + "material", + + "node_out", + "node_mix_shader_spec", + "node_mix_shader_alpha", + "node_mix_shader_refl", + + "node_bsdf_alpha", + "node_bsdf_diff", + "node_bsdf_spec", + "node_bsdf_refl", + + "node_mix_color_alpha", + "node_mix_color_diff", + "node_mix_color_spec", + "node_mix_color_hard", + "node_mix_color_refl", + "node_mix_color_bump", + + "node_normalmap", + "node_texcoords", + + "node_image_alpha", + "node_image_diff", + "node_image_spec", + "node_image_hard", + "node_image_refl", + "node_image_bump", + "node_image_normalmap", + ) + + _col_size = 200 + _row_size = 220 + + def __init__(self, material): + + COLOR_WHITE = 1.0, 1.0, 1.0, 1.0 + COLOR_BLACK = 0.0, 0.0, 0.0, 1.0 + + self.material = material + self.material.use_nodes = True + + tree = self.material.node_tree + + nodes = tree.nodes + links = tree.links + nodes.clear() + + # ---- + # Add shaders + node = nodes.new(type='ShaderNodeOutputMaterial') + node.label = "Material Out" + node.location = self._grid_location(6, 4) + self.node_out = node + del node + + node = nodes.new(type='ShaderNodeAddShader') + node.label = "Shader Add Refl" + node.location = self._grid_location(5, 4) + self.node_mix_shader_refl = node + del node + # Link + links.new(self.node_mix_shader_refl.outputs["Shader"], + self.node_out.inputs["Surface"]) + + node = nodes.new(type='ShaderNodeAddShader') + node.label = "Shader Add Spec" + node.location = self._grid_location(4, 4) + self.node_mix_shader_spec = node + del node + # Link + links.new(self.node_mix_shader_spec.outputs["Shader"], + self.node_mix_shader_refl.inputs[0]) + + # -------------------------------------------------------------------- + # Reflection + node = nodes.new(type='ShaderNodeBsdfRefraction') + node.label = "Refl BSDF" + node.location = self._grid_location(6, 1) + node.mute = True # unmute on use + self.node_bsdf_refl = node + del node + # Link + links.new(self.node_bsdf_refl.outputs["BSDF"], + self.node_mix_shader_refl.inputs[1]) + + # Mix Refl Color + node = nodes.new(type='ShaderNodeMixRGB') + node.label = "Mix Color/Refl" + node.location = self._grid_location(5, 1) + node.blend_type = 'MULTIPLY' + node.inputs["Fac"].default_value = 1.0 + # reverse of most other mix nodes + node.inputs["Color1"].default_value = COLOR_WHITE # color + node.inputs["Color2"].default_value = COLOR_BLACK # factor + self.node_mix_color_refl = node + del node + # Link + links.new(self.node_mix_color_refl.outputs["Color"], + self.node_bsdf_refl.inputs["Color"]) + + # -------------------------------------------------------------------- + # Alpha + + # ---- + # Mix shader + node = nodes.new(type='ShaderNodeMixShader') + node.label = "Shader Mix Alpha" + node.location = self._grid_location(3, 4) + node.inputs["Fac"].default_value = 1.0 # no alpha by default + self.node_mix_shader_alpha = node + del node + # Link + links.new(self.node_mix_shader_alpha.outputs["Shader"], + self.node_mix_shader_spec.inputs[0]) + + # Alpha BSDF + node = nodes.new(type='ShaderNodeBsdfTransparent') + node.label = "Alpha BSDF" + node.location = self._grid_location(2, 4) + node.mute = True # unmute on use + self.node_bsdf_alpha = node + del node + # Link + links.new(self.node_bsdf_alpha.outputs["BSDF"], + self.node_mix_shader_alpha.inputs[1]) # first 'Shader' + + # Mix Alpha Color + node = nodes.new(type='ShaderNodeMixRGB') + node.label = "Mix Color/Alpha" + node.location = self._grid_location(1, 5) + node.blend_type = 'MULTIPLY' + node.inputs["Fac"].default_value = 1.0 + node.inputs["Color1"].default_value = COLOR_WHITE + node.inputs["Color2"].default_value = COLOR_WHITE + self.node_mix_color_alpha = node + del node + # Link + links.new(self.node_mix_color_alpha.outputs["Color"], + self.node_mix_shader_alpha.inputs["Fac"]) + + # -------------------------------------------------------------------- + # Diffuse + + # Diffuse BSDF + node = nodes.new(type='ShaderNodeBsdfDiffuse') + node.label = "Diff BSDF" + node.location = self._grid_location(2, 3) + self.node_bsdf_diff = node + del node + # Link + links.new(self.node_bsdf_diff.outputs["BSDF"], + self.node_mix_shader_alpha.inputs[2]) # first 'Shader' + + # Mix Diffuse Color + node = nodes.new(type='ShaderNodeMixRGB') + node.label = "Mix Color/Diffuse" + node.location = self._grid_location(1, 3) + node.blend_type = 'MULTIPLY' + node.inputs["Fac"].default_value = 1.0 + node.inputs["Color1"].default_value = COLOR_WHITE + node.inputs["Color2"].default_value = COLOR_WHITE + self.node_mix_color_diff = node + del node + # Link + links.new(self.node_mix_color_diff.outputs["Color"], + self.node_bsdf_diff.inputs["Color"]) + + # -------------------------------------------------------------------- + # Specular + node = nodes.new(type='ShaderNodeBsdfGlossy') + node.label = "Spec BSDF" + node.location = self._grid_location(2, 1) + node.mute = True # unmute on use + self.node_bsdf_spec = node + del node + # Link (with add shader) + links.new(self.node_bsdf_spec.outputs["BSDF"], + self.node_mix_shader_spec.inputs[1]) # second 'Shader' slot + + node = nodes.new(type='ShaderNodeMixRGB') + node.label = "Mix Color/Spec" + node.location = self._grid_location(1, 1) + node.blend_type = 'MULTIPLY' + node.inputs["Fac"].default_value = 1.0 + node.inputs["Color1"].default_value = COLOR_WHITE + node.inputs["Color2"].default_value = COLOR_BLACK + self.node_mix_color_spec = node + del node + # Link + links.new(self.node_mix_color_spec.outputs["Color"], + self.node_bsdf_spec.inputs["Color"]) + + node = nodes.new(type='ShaderNodeMixRGB') + node.label = "Mix Color/Hardness" + node.location = self._grid_location(1, 0) + node.blend_type = 'MULTIPLY' + node.inputs["Fac"].default_value = 1.0 + node.inputs["Color1"].default_value = COLOR_WHITE + node.inputs["Color2"].default_value = COLOR_WHITE + self.node_mix_color_hard = node + del node + # Link + links.new(self.node_mix_color_hard.outputs["Color"], + self.node_bsdf_spec.inputs["Roughness"]) + + # -------------------------------------------------------------------- + # Normal Map + node = nodes.new(type='ShaderNodeNormalMap') + node.label = "Normal/Map" + node.location = self._grid_location(1, 2) + node.mute = True # unmute on use + self.node_normalmap = node + del node + + # Link (with diff shader) + socket_src = self.node_normalmap.outputs["Normal"] + links.new(socket_src, + self.node_bsdf_diff.inputs["Normal"]) + # Link (with spec shader) + links.new(socket_src, + self.node_bsdf_spec.inputs["Normal"]) + # Link (with refl shader) + links.new(socket_src, + self.node_bsdf_refl.inputs["Normal"]) + del socket_src + + # -------------------------------------------------------------------- + # Bump Map + # Mix Refl Color + node = nodes.new(type='ShaderNodeMixRGB') + node.label = "Bump/Map" + node.location = self._grid_location(5, 3) + node.mute = True # unmute on use + node.blend_type = 'MULTIPLY' + node.inputs["Fac"].default_value = 1.0 + # reverse of most other mix nodes + node.inputs["Color1"].default_value = COLOR_WHITE # color + node.inputs["Color2"].default_value = COLOR_BLACK # factor + self.node_mix_color_bump = node + del node + # Link + links.new(self.node_mix_color_bump.outputs["Color"], + self.node_out.inputs["Displacement"]) + + # -------------------------------------------------------------------- + # Tex Coords + node = nodes.new(type='ShaderNodeTexCoord') + node.label = "Texture Coords" + node.location = self._grid_location(-3, 3) + self.node_texcoords = node + del node + # no links, only use when needed! + + @staticmethod + def _image_create_helper(image, node_dst, sockets_dst, use_alpha=False): + tree = node_dst.id_data + nodes = tree.nodes + links = tree.links + + node = nodes.new(type='ShaderNodeTexImage') + node.image = image + node.location = node_dst.location + node.location.x -= CyclesShaderWrapper._col_size + for socket in sockets_dst: + links.new(node.outputs["Alpha" if use_alpha else "Color"], + socket) + return node + + @staticmethod + def _mapping_create_helper(node_dst, socket_src, + translation, rotation, scale, clamp): + tree = node_dst.id_data + nodes = tree.nodes + links = tree.links + + # in most cases: + # (socket_src == self.node_texcoords.outputs['UV']) + + node_map = None + + # find an existing mapping node (allows multiple calls) + if node_dst.inputs["Vector"].links: + node_map = node_dst.inputs["Vector"].links[0].from_node + + if node_map is None: + node_map = nodes.new(type='ShaderNodeMapping') + node_map.vector_type = 'TEXTURE' + node_map.location = node_dst.location + node_map.location.x -= CyclesShaderWrapper._col_size + + node_map.width = 160.0 + + # link mapping -> image node + links.new(node_map.outputs["Vector"], + node_dst.inputs["Vector"]) + + # link coord -> mapping + links.new(socket_src, + node_map.inputs["Vector"]) + + if translation is not None: + node_map.translation = translation + if scale is not None: + node_map.scale = scale + if rotation is not None: + node_map.rotation = rotation + if clamp is not None: + # awkward conversion UV clamping to minmax + node_map.min = (0.0, 0.0, 0.0) + node_map.max = (1.0, 1.0, 1.0) + + if clamp in {(False, False), (True, True)}: + node_map.use_min = node_map.use_max = clamp[0] + else: + node_map.use_min = node_map.use_max = True + # use bool as index + node_map.min[not clamp[0]] = -1000000000.0 + node_map.max[not clamp[0]] = 1000000000.0 + + return node_map + + # note, all ***_mapping_set() functions currenly work the same way + # (only with different image arg), could generalize. + + @staticmethod + def _grid_location(x, y): + return (x * CyclesShaderWrapper._col_size, + y * CyclesShaderWrapper._row_size) + + def diffuse_color_set(self, color): + self.node_mix_color_diff.inputs["Color1"].default_value[0:3] = color + + def diffuse_image_set(self, image): + node = self.node_mix_color_diff + self.node_image_diff = ( + self._image_create_helper(image, node, (node.inputs["Color2"],))) + + def diffuse_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_diff, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def specular_color_set(self, color): + self.node_bsdf_spec.mute = max(color) <= 0.0 + self.node_mix_color_spec.inputs["Color1"].default_value[0:3] = color + + def specular_image_set(self, image): + node = self.node_mix_color_spec + self.node_image_spec = ( + self._image_create_helper(image, node, (node.inputs["Color2"],))) + + def specular_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_spec, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def hardness_value_set(self, value): + node = self.node_mix_color_hard + node.inputs["Color1"].default_value = (value,) * 4 + + def hardness_image_set(self, image): + node = self.node_mix_color_hard + self.node_image_hard = ( + self._image_create_helper(image, node, (node.inputs["Color2"],))) + + def hardness_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_hard, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def reflect_color_set(self, color): + node = self.node_mix_color_refl + node.inputs["Color1"].default_value[0:3] = color + + def reflect_factor_set(self, value): + # XXX, conflicts with image + self.node_bsdf_refl.mute = value <= 0.0 + node = self.node_mix_color_refl + node.inputs["Color2"].default_value = (value,) * 4 + + def reflect_image_set(self, image): + self.node_bsdf_refl.mute = False + node = self.node_mix_color_refl + self.node_image_refl = ( + self._image_create_helper(image, node, (node.inputs["Color2"],))) + + def reflect_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_refl, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def alpha_value_set(self, value): + self.node_bsdf_alpha.mute &= (value >= 1.0) + node = self.node_mix_color_alpha + node.inputs["Color1"].default_value = (value,) * 4 + + def alpha_image_set(self, image): + self.node_bsdf_alpha.mute = False + node = self.node_mix_color_alpha + # note: use_alpha may need to be configurable + # its not always the case that alpha channels use the image alpha + # a grayscale image may also be used. + self.node_image_alpha = ( + self._image_create_helper(image, node, (node.inputs["Color2"],), use_alpha=True)) + + def alpha_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_alpha, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def alpha_image_set_from_diffuse(self): + # XXX, remove? + tree = self.node_mix_color_diff.id_data + links = tree.links + + self.node_bsdf_alpha.mute = False + node_image = self.node_image_diff + node = self.node_mix_color_alpha + if 1: + links.new(node_image.outputs["Alpha"], + node.inputs["Color2"]) + else: + self.alpha_image_set(node_image.image) + self.node_image_alpha.label = "Image Texture_ALPHA" + + def normal_factor_set(self, value): + node = self.node_normalmap + node.inputs["Strength"].default_value = value + + def normal_image_set(self, image): + self.node_normalmap.mute = False + node = self.node_normalmap + self.node_image_normalmap = ( + self._image_create_helper(image, node, (node.inputs["Color"],))) + self.node_image_normalmap.color_space = 'NONE' + + def normal_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_normalmap, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def bump_factor_set(self, value): + node = self.node_mix_color_bump + node.mute = (value <= 0.0) + node.inputs["Color1"].default_value = (value,) * 4 + + def bump_image_set(self, image): + node = self.node_mix_color_bump + self.node_image_bump = ( + self._image_create_helper(image, node, (node.inputs["Color2"],))) + + def bump_mapping_set(self, coords='UV', + translation=None, rotation=None, scale=None, clamp=None): + return self._mapping_create_helper( + self.node_image_bump, self.node_texcoords.outputs[coords], translation, rotation, scale, clamp) + + def mapping_set_from_diffuse(self, + specular=True, + hardness=True, + reflect=True, + alpha=True, + normal=True, + bump=True): + """ + Set all mapping based on diffuse + (sometimes we want to assume default mapping follows diffuse). + """ + # get mapping from diffuse + if not hasattr(self, "node_image_diff"): + return + + links = self.node_image_diff.inputs["Vector"].links + if not links: + return + + mapping_out_socket = links[0].from_socket + + tree = self.material.node_tree + links = tree.links + + def node_image_mapping_apply(node_image_attr): + # ensure strings are valid attrs + assert(node_image_attr in self.__slots__) + + node_image = getattr(self, node_image_attr, None) + + if node_image is not None: + node_image_input_socket = node_image.inputs["Vector"] + # don't overwrite existing sockets + if not node_image_input_socket.links: + links.new(mapping_out_socket, + node_image_input_socket) + + if specular: + node_image_mapping_apply("node_image_spec") + if hardness: + node_image_mapping_apply("node_image_hard") + if reflect: + node_image_mapping_apply("node_image_refl") + if alpha: + node_image_mapping_apply("node_image_alpha") + if normal: + node_image_mapping_apply("node_image_normalmap") + if bump: + node_image_mapping_apply("node_image_bump") |