Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Montagne <montagne29@wanadoo.fr>2017-05-25 19:57:13 +0300
committerBastien Montagne <montagne29@wanadoo.fr>2017-05-27 14:07:43 +0300
commitc441b2fd05e383da6b071b5efdd627c2f445ade7 (patch)
tree42e088bdc2b3d747d32aaf23cd4281475dbcd2b9 /modules/cycles_shader_compat.py
parenta188e4756e086224592f13b0eabce263db67b6be (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.py540
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")