node_shader_utils: several fixes, improvements and cleanups.
Fix broken behavior in case of texcoords mapping (we do need texcoords node in all cases, then, even for UV coords...). Use nodes by default when generating new write-allowed wrapper around a material. Do not try to find nodes in existing tree all the time, do it only once, even for lazy-initialized nodes (through accessors). Fix ugly spacing in property accessors (since it looks like some people do not like a single 'block' with both getters, setters and prop definition, at least use one sep line everywhere (and two sep lines to separate properties)...
This commit is contained in:
@@ -43,6 +43,7 @@ def rgb_to_rgba(rgb):
|
|||||||
def rgba_to_rgb(rgba):
|
def rgba_to_rgb(rgba):
|
||||||
return Color((rgba[0], rgba[1], rgba[2]))
|
return Color((rgba[0], rgba[1], rgba[2]))
|
||||||
|
|
||||||
|
|
||||||
class ShaderWrapper():
|
class ShaderWrapper():
|
||||||
"""
|
"""
|
||||||
Base class with minimal common ground for all types of shader interfaces we may want/need to implement.
|
Base class with minimal common ground for all types of shader interfaces we may want/need to implement.
|
||||||
@@ -90,9 +91,10 @@ class ShaderWrapper():
|
|||||||
dst_node.width = min(dst_node.width, self._col_size - 20)
|
dst_node.width = min(dst_node.width, self._col_size - 20)
|
||||||
return loc
|
return loc
|
||||||
|
|
||||||
def __init__(self, material, is_readonly=True):
|
def __init__(self, material, is_readonly=True, use_nodes=True):
|
||||||
self.is_readonly = is_readonly
|
self.is_readonly = is_readonly
|
||||||
self.material = material
|
self.material = material
|
||||||
|
self.use_nodes = use_nodes
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
def update(self): # Should be re-implemented by children classes...
|
def update(self): # Should be re-implemented by children classes...
|
||||||
@@ -101,6 +103,7 @@ class ShaderWrapper():
|
|||||||
self._textures = {}
|
self._textures = {}
|
||||||
self._grid_locations = set()
|
self._grid_locations = set()
|
||||||
|
|
||||||
|
|
||||||
def use_nodes_get(self):
|
def use_nodes_get(self):
|
||||||
return self.material.use_nodes
|
return self.material.use_nodes
|
||||||
|
|
||||||
@@ -108,17 +111,22 @@ class ShaderWrapper():
|
|||||||
def use_nodes_set(self, val):
|
def use_nodes_set(self, val):
|
||||||
self.material.use_nodes = val
|
self.material.use_nodes = val
|
||||||
self.update()
|
self.update()
|
||||||
|
|
||||||
use_nodes = property(use_nodes_get, use_nodes_set)
|
use_nodes = property(use_nodes_get, use_nodes_set)
|
||||||
|
|
||||||
|
|
||||||
def node_texcoords_get(self):
|
def node_texcoords_get(self):
|
||||||
if not self.use_nodes:
|
if not self.use_nodes:
|
||||||
return None
|
return None
|
||||||
if self._node_texcoords is None:
|
if self._node_texcoords is ...:
|
||||||
|
# Running only once, trying to find a valid texcoords node.
|
||||||
for n in self.material.node_tree.nodes:
|
for n in self.material.node_tree.nodes:
|
||||||
if n.bl_idname == 'ShaderNodeTexCoord':
|
if n.bl_idname == 'ShaderNodeTexCoord':
|
||||||
self._node_texcoords = n
|
self._node_texcoords = n
|
||||||
self._grid_to_location(0, 0, ref_node=n)
|
self._grid_to_location(0, 0, ref_node=n)
|
||||||
break
|
break
|
||||||
|
if self._node_texcoords is ...:
|
||||||
|
self._node_texcoords = None
|
||||||
if self._node_texcoords is None and not self.is_readonly:
|
if self._node_texcoords is None and not self.is_readonly:
|
||||||
tree = self.material.node_tree
|
tree = self.material.node_tree
|
||||||
nodes = tree.nodes
|
nodes = tree.nodes
|
||||||
@@ -129,6 +137,7 @@ class ShaderWrapper():
|
|||||||
self._grid_to_location(-5, 1, dst_node=node_texcoords)
|
self._grid_to_location(-5, 1, dst_node=node_texcoords)
|
||||||
self._node_texcoords = node_texcoords
|
self._node_texcoords = node_texcoords
|
||||||
return self._node_texcoords
|
return self._node_texcoords
|
||||||
|
|
||||||
node_texcoords = property(node_texcoords_get)
|
node_texcoords = property(node_texcoords_get)
|
||||||
|
|
||||||
|
|
||||||
@@ -154,8 +163,9 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
|
|
||||||
NODES_LIST = ShaderWrapper.NODES_LIST + NODES_LIST
|
NODES_LIST = ShaderWrapper.NODES_LIST + NODES_LIST
|
||||||
|
|
||||||
def __init__(self, material, is_readonly=True):
|
def __init__(self, material, is_readonly=True, use_nodes=True):
|
||||||
super(PrincipledBSDFWrapper, self).__init__(material, is_readonly)
|
super(PrincipledBSDFWrapper, self).__init__(material, is_readonly, use_nodes)
|
||||||
|
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
super(PrincipledBSDFWrapper, self).update()
|
super(PrincipledBSDFWrapper, self).update()
|
||||||
@@ -211,31 +221,42 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Normal Map, lazy initialization...
|
# Normal Map, lazy initialization...
|
||||||
self._node_normalmap = None
|
self._node_normalmap = ...
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Tex Coords, lazy initialization...
|
# Tex Coords, lazy initialization...
|
||||||
self._node_texcoords = None
|
self._node_texcoords = ...
|
||||||
|
|
||||||
|
|
||||||
def node_normalmap_get(self):
|
def node_normalmap_get(self):
|
||||||
if not self.use_nodes:
|
if not self.use_nodes:
|
||||||
return None
|
return None
|
||||||
if self._node_normalmap is None and self.node_principled_bsdf is not None:
|
if self.node_principled_bsdf is not None:
|
||||||
node_principled = self.node_principled_bsdf
|
node_principled = self.node_principled_bsdf
|
||||||
|
if self._node_normalmap is ...:
|
||||||
|
# Running only once, trying to find a valid normalmap node.
|
||||||
if node_principled.inputs["Normal"].is_linked:
|
if node_principled.inputs["Normal"].is_linked:
|
||||||
node_normalmap = node_principled.inputs["Normal"].links[0].from_node
|
node_normalmap = node_principled.inputs["Normal"].links[0].from_node
|
||||||
if node_normalmap.bl_idname == 'ShaderNodeNormalMap':
|
if node_normalmap.bl_idname == 'ShaderNodeNormalMap':
|
||||||
self._node_normalmap = node_normalmap
|
self._node_normalmap = node_normalmap
|
||||||
self._grid_to_location(0, 0, ref_node=node_normalmap)
|
self._grid_to_location(0, 0, ref_node=node_normalmap)
|
||||||
|
if self._node_normalmap is ...:
|
||||||
|
self._node_normalmap = None
|
||||||
if self._node_normalmap is None and not self.is_readonly:
|
if self._node_normalmap is None and not self.is_readonly:
|
||||||
|
tree = self.material.node_tree
|
||||||
|
nodes = tree.nodes
|
||||||
|
links = tree.links
|
||||||
|
|
||||||
node_normalmap = nodes.new(type='ShaderNodeNormalMap')
|
node_normalmap = nodes.new(type='ShaderNodeNormalMap')
|
||||||
node_normalmap.label = "Normal/Map"
|
node_normalmap.label = "Normal/Map"
|
||||||
self._grid_to_location(-1, -2, dst_node=node_normalmap, ref_node=node_principled)
|
self._grid_to_location(-1, -2, dst_node=node_normalmap, ref_node=node_principled)
|
||||||
# Link
|
# Link
|
||||||
links.new(node_normalmap.outputs["Normal"], node_principled.inputs["Normal"])
|
links.new(node_normalmap.outputs["Normal"], node_principled.inputs["Normal"])
|
||||||
return self._node_normalmap
|
return self._node_normalmap
|
||||||
|
|
||||||
node_normalmap = property(node_normalmap_get)
|
node_normalmap = property(node_normalmap_get)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Base Color.
|
# Base Color.
|
||||||
|
|
||||||
@@ -249,8 +270,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.material.diffuse_color = color
|
self.material.diffuse_color = color
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["Base Color"].default_value = rgb_to_rgba(color)
|
self.node_principled_bsdf.inputs["Base Color"].default_value = rgb_to_rgba(color)
|
||||||
|
|
||||||
base_color = property(base_color_get, base_color_set)
|
base_color = property(base_color_get, base_color_set)
|
||||||
|
|
||||||
|
|
||||||
def base_color_texture_get(self):
|
def base_color_texture_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
return None
|
return None
|
||||||
@@ -259,8 +282,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.node_principled_bsdf.inputs["Base Color"],
|
self.node_principled_bsdf.inputs["Base Color"],
|
||||||
grid_row_diff=1,
|
grid_row_diff=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
base_color_texture = property(base_color_texture_get)
|
base_color_texture = property(base_color_texture_get)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Specular.
|
# Specular.
|
||||||
|
|
||||||
@@ -274,8 +299,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.material.specular_intensity = value
|
self.material.specular_intensity = value
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["Specular"].default_value = value
|
self.node_principled_bsdf.inputs["Specular"].default_value = value
|
||||||
|
|
||||||
specular = property(specular_get, specular_set)
|
specular = property(specular_get, specular_set)
|
||||||
|
|
||||||
|
|
||||||
def specular_tint_get(self):
|
def specular_tint_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -285,19 +312,24 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
def specular_tint_set(self, value):
|
def specular_tint_set(self, value):
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["Specular Tint"].default_value = rgb_to_rgba(value)
|
self.node_principled_bsdf.inputs["Specular Tint"].default_value = rgb_to_rgba(value)
|
||||||
|
|
||||||
specular_tint = property(specular_tint_get, specular_tint_set)
|
specular_tint = property(specular_tint_get, specular_tint_set)
|
||||||
|
|
||||||
|
|
||||||
# Will only be used as gray-scale one...
|
# Will only be used as gray-scale one...
|
||||||
def specular_texture_get(self):
|
def specular_texture_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
|
print("NO NODES!")
|
||||||
return None
|
return None
|
||||||
return ShaderImageTextureWrapper(
|
return ShaderImageTextureWrapper(
|
||||||
self, self.node_principled_bsdf,
|
self, self.node_principled_bsdf,
|
||||||
self.node_principled_bsdf.inputs["Specular"],
|
self.node_principled_bsdf.inputs["Specular"],
|
||||||
grid_row_diff=0,
|
grid_row_diff=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
specular_texture = property(specular_texture_get)
|
specular_texture = property(specular_texture_get)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Roughness (also sort of inverse of specular hardness...).
|
# Roughness (also sort of inverse of specular hardness...).
|
||||||
|
|
||||||
@@ -311,8 +343,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.material.roughness = value
|
self.material.roughness = value
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["Roughness"].default_value = value
|
self.node_principled_bsdf.inputs["Roughness"].default_value = value
|
||||||
|
|
||||||
roughness = property(roughness_get, roughness_set)
|
roughness = property(roughness_get, roughness_set)
|
||||||
|
|
||||||
|
|
||||||
# Will only be used as gray-scale one...
|
# Will only be used as gray-scale one...
|
||||||
def roughness_texture_get(self):
|
def roughness_texture_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
@@ -322,8 +356,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.node_principled_bsdf.inputs["Roughness"],
|
self.node_principled_bsdf.inputs["Roughness"],
|
||||||
grid_row_diff=0,
|
grid_row_diff=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
roughness_texture = property(roughness_texture_get)
|
roughness_texture = property(roughness_texture_get)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Metallic (a.k.a reflection, mirror).
|
# Metallic (a.k.a reflection, mirror).
|
||||||
|
|
||||||
@@ -337,8 +373,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.material.metallic = value
|
self.material.metallic = value
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["Metallic"].default_value = value
|
self.node_principled_bsdf.inputs["Metallic"].default_value = value
|
||||||
|
|
||||||
metallic = property(metallic_get, metallic_set)
|
metallic = property(metallic_get, metallic_set)
|
||||||
|
|
||||||
|
|
||||||
# Will only be used as gray-scale one...
|
# Will only be used as gray-scale one...
|
||||||
def metallic_texture_get(self):
|
def metallic_texture_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
@@ -348,8 +386,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.node_principled_bsdf.inputs["Metallic"],
|
self.node_principled_bsdf.inputs["Metallic"],
|
||||||
grid_row_diff=0,
|
grid_row_diff=0,
|
||||||
)
|
)
|
||||||
|
|
||||||
metallic_texture = property(metallic_texture_get)
|
metallic_texture = property(metallic_texture_get)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Transparency settings.
|
# Transparency settings.
|
||||||
|
|
||||||
@@ -362,8 +402,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
def ior_set(self, value):
|
def ior_set(self, value):
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["IOR"].default_value = value
|
self.node_principled_bsdf.inputs["IOR"].default_value = value
|
||||||
|
|
||||||
ior = property(ior_get, ior_set)
|
ior = property(ior_get, ior_set)
|
||||||
|
|
||||||
|
|
||||||
# Will only be used as gray-scale one...
|
# Will only be used as gray-scale one...
|
||||||
def ior_texture_get(self):
|
def ior_texture_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
@@ -373,8 +415,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.node_principled_bsdf.inputs["IOR"],
|
self.node_principled_bsdf.inputs["IOR"],
|
||||||
grid_row_diff=-1,
|
grid_row_diff=-1,
|
||||||
)
|
)
|
||||||
|
|
||||||
ior_texture = property(ior_texture_get)
|
ior_texture = property(ior_texture_get)
|
||||||
|
|
||||||
|
|
||||||
def transmission_get(self):
|
def transmission_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
return 0.0
|
return 0.0
|
||||||
@@ -384,8 +428,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
def transmission_set(self, value):
|
def transmission_set(self, value):
|
||||||
if self.use_nodes and self.node_principled_bsdf is not None:
|
if self.use_nodes and self.node_principled_bsdf is not None:
|
||||||
self.node_principled_bsdf.inputs["Transmission"].default_value = value
|
self.node_principled_bsdf.inputs["Transmission"].default_value = value
|
||||||
|
|
||||||
transmission = property(transmission_get, transmission_set)
|
transmission = property(transmission_get, transmission_set)
|
||||||
|
|
||||||
|
|
||||||
# Will only be used as gray-scale one...
|
# Will only be used as gray-scale one...
|
||||||
def transmission_texture_get(self):
|
def transmission_texture_get(self):
|
||||||
if not self.use_nodes or self.node_principled_bsdf is None:
|
if not self.use_nodes or self.node_principled_bsdf is None:
|
||||||
@@ -395,8 +441,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.node_principled_bsdf.inputs["Transmission"],
|
self.node_principled_bsdf.inputs["Transmission"],
|
||||||
grid_row_diff=-1,
|
grid_row_diff=-1,
|
||||||
)
|
)
|
||||||
|
|
||||||
transmission_texture = property(transmission_texture_get)
|
transmission_texture = property(transmission_texture_get)
|
||||||
|
|
||||||
|
|
||||||
# TODO: Do we need more complex handling for alpha (allowing masking and such)?
|
# TODO: Do we need more complex handling for alpha (allowing masking and such)?
|
||||||
# Would need extra mixing nodes onto Base Color maybe, or even its own shading chain...
|
# Would need extra mixing nodes onto Base Color maybe, or even its own shading chain...
|
||||||
|
|
||||||
@@ -412,8 +460,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
def normalmap_strength_set(self, value):
|
def normalmap_strength_set(self, value):
|
||||||
if self.use_nodes and self.node_normalmap is not None:
|
if self.use_nodes and self.node_normalmap is not None:
|
||||||
self.node_normalmap.inputs["Strength"].default_value = value
|
self.node_normalmap.inputs["Strength"].default_value = value
|
||||||
|
|
||||||
normalmap_strength = property(normalmap_strength_get, normalmap_strength_set)
|
normalmap_strength = property(normalmap_strength_get, normalmap_strength_set)
|
||||||
|
|
||||||
|
|
||||||
def normalmap_texture_get(self):
|
def normalmap_texture_get(self):
|
||||||
if not self.use_nodes or self.node_normalmap is None:
|
if not self.use_nodes or self.node_normalmap is None:
|
||||||
return None
|
return None
|
||||||
@@ -422,9 +472,11 @@ class PrincipledBSDFWrapper(ShaderWrapper):
|
|||||||
self.node_normalmap.inputs["Color"],
|
self.node_normalmap.inputs["Color"],
|
||||||
grid_row_diff=-2,
|
grid_row_diff=-2,
|
||||||
)
|
)
|
||||||
|
|
||||||
normalmap_texture = property(normalmap_texture_get)
|
normalmap_texture = property(normalmap_texture_get)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ShaderImageTextureWrapper():
|
class ShaderImageTextureWrapper():
|
||||||
"""
|
"""
|
||||||
Generic 'image texture'-like wrapper, handling image node, some mapping (texture coordinates transformations),
|
Generic 'image texture'-like wrapper, handling image node, some mapping (texture coordinates transformations),
|
||||||
@@ -465,15 +517,17 @@ class ShaderImageTextureWrapper():
|
|||||||
self.grid_row_diff = grid_row_diff
|
self.grid_row_diff = grid_row_diff
|
||||||
self.use_alpha = use_alpha
|
self.use_alpha = use_alpha
|
||||||
|
|
||||||
self._node_image = None
|
self._node_image = ...
|
||||||
self._node_mapping = None
|
self._node_mapping = ...
|
||||||
|
|
||||||
tree = node_dst.id_data
|
tree = node_dst.id_data
|
||||||
nodes = tree.nodes
|
nodes = tree.nodes
|
||||||
links = tree.links
|
links = tree.links
|
||||||
|
|
||||||
if socket_dst.is_linked:
|
if socket_dst.is_linked:
|
||||||
self._node_image = socket_dst.links[0].from_node
|
from_node = socket_dst.links[0].from_node
|
||||||
|
if from_node.bl_idname == 'ShaderNodeTexImage':
|
||||||
|
self._node_image = from_node
|
||||||
|
|
||||||
if self.node_image is not None:
|
if self.node_image is not None:
|
||||||
socket_dst = self.node_image.inputs["Vector"]
|
socket_dst = self.node_image.inputs["Vector"]
|
||||||
@@ -482,16 +536,67 @@ class ShaderImageTextureWrapper():
|
|||||||
if from_node.bl_idname == 'ShaderNodeMapping':
|
if from_node.bl_idname == 'ShaderNodeMapping':
|
||||||
self._node_mapping = from_node
|
self._node_mapping = from_node
|
||||||
|
|
||||||
|
|
||||||
|
def copy_from(self, tex):
|
||||||
|
# Avoid generating any node in source texture.
|
||||||
|
is_readonly_back = tex.is_readonly
|
||||||
|
tex.is_readonly = True
|
||||||
|
|
||||||
|
if tex.node_image is not None:
|
||||||
|
self.image = tex.image
|
||||||
|
self.projection = tex.projection
|
||||||
|
self.texcoords = tex.texcoords
|
||||||
|
self.copy_mapping_from(tex)
|
||||||
|
|
||||||
|
tex.is_readonly = is_readonly_back
|
||||||
|
|
||||||
|
|
||||||
|
def copy_mapping_from(self, tex):
|
||||||
|
# Avoid generating any node in source texture.
|
||||||
|
is_readonly_back = tex.is_readonly
|
||||||
|
tex.is_readonly = True
|
||||||
|
|
||||||
|
if tex.node_mapping is None: # Used to actually remove mapping node.
|
||||||
|
if self.has_mapping_node():
|
||||||
|
# We assume node_image can never be None in that case...
|
||||||
|
# Find potential existing link into image's Vector input.
|
||||||
|
socket_dst = socket_src = None
|
||||||
|
if self.node_mapping.inputs["Vector"].is_linked:
|
||||||
|
socket_dst = self.node_image.inputs["Vector"]
|
||||||
|
socket_src = self.node_mapping.inputs["Vector"].links[0].from_socket
|
||||||
|
|
||||||
|
tree = self.owner_shader.material.node_tree
|
||||||
|
tree.nodes.remove(self.node_mapping)
|
||||||
|
self._node_mapping = None
|
||||||
|
|
||||||
|
# If previously existing, re-link texcoords -> image
|
||||||
|
if socket_src is not None:
|
||||||
|
tree.links.new(socket_src, socket_dst)
|
||||||
|
elif self.node_mapping is not None:
|
||||||
|
self.translation = tex.translation
|
||||||
|
self.rotation = tex.rotation
|
||||||
|
self.scale = tex.scale
|
||||||
|
self.use_min = tex.use_min
|
||||||
|
self.use_max = tex.use_max
|
||||||
|
self.min = tex.min
|
||||||
|
self.max = tex.max
|
||||||
|
|
||||||
|
tex.is_readonly = is_readonly_back
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Image.
|
# Image.
|
||||||
|
|
||||||
def node_image_get(self):
|
def node_image_get(self):
|
||||||
if self._node_image is None:
|
if self._node_image is ...:
|
||||||
|
# Running only once, trying to find a valid image node.
|
||||||
if self.socket_dst.is_linked:
|
if self.socket_dst.is_linked:
|
||||||
node_image = self.socket_dst.links[0].from_node
|
node_image = self.socket_dst.links[0].from_node
|
||||||
if node_image.bl_idname == 'ShaderNodeTexImage':
|
if node_image.bl_idname == 'ShaderNodeTexImage':
|
||||||
self._node_image = node_image
|
self._node_image = node_image
|
||||||
self.owner_shader._grid_to_location(0, 0, ref_node=node_image)
|
self.owner_shader._grid_to_location(0, 0, ref_node=node_image)
|
||||||
|
if self._node_image is ...:
|
||||||
|
self._node_image = None
|
||||||
if self._node_image is None and not self.is_readonly:
|
if self._node_image is None and not self.is_readonly:
|
||||||
tree = self.owner_shader.material.node_tree
|
tree = self.owner_shader.material.node_tree
|
||||||
|
|
||||||
@@ -502,27 +607,33 @@ class ShaderImageTextureWrapper():
|
|||||||
|
|
||||||
self._node_image = node_image
|
self._node_image = node_image
|
||||||
return self._node_image
|
return self._node_image
|
||||||
|
|
||||||
node_image = property(node_image_get)
|
node_image = property(node_image_get)
|
||||||
|
|
||||||
|
|
||||||
def image_get(self):
|
def image_get(self):
|
||||||
return self.node_image.image if self.node_image is not None else None
|
return self.node_image.image if self.node_image is not None else None
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def image_set(self, image):
|
def image_set(self, image):
|
||||||
self.node_image.image = image
|
self.node_image.image = image
|
||||||
|
|
||||||
image = property(image_get, image_set)
|
image = property(image_get, image_set)
|
||||||
|
|
||||||
|
|
||||||
def projection_get(self):
|
def projection_get(self):
|
||||||
return self.node_image.projection if self.node_image is not None else 'FLAT'
|
return self.node_image.projection if self.node_image is not None else 'FLAT'
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def projection_set(self, projection):
|
def projection_set(self, projection):
|
||||||
self.node_image.projection = projection
|
self.node_image.projection = projection
|
||||||
|
|
||||||
projection = property(projection_get, projection_set)
|
projection = property(projection_get, projection_set)
|
||||||
|
|
||||||
|
|
||||||
def texcoords_get(self):
|
def texcoords_get(self):
|
||||||
if self.node_image is not None:
|
if self.node_image is not None:
|
||||||
socket = (self.node_mapping if self._node_mapping is not None else self.node_image).inputs["Vector"]
|
socket = (self.node_mapping if self.has_mapping_node() else self.node_image).inputs["Vector"]
|
||||||
if socket.is_linked:
|
if socket.is_linked:
|
||||||
return socket.links[0].from_socket.name
|
return socket.links[0].from_socket.name
|
||||||
return 'UV'
|
return 'UV'
|
||||||
@@ -530,20 +641,27 @@ class ShaderImageTextureWrapper():
|
|||||||
@_set_check
|
@_set_check
|
||||||
def texcoords_set(self, texcoords):
|
def texcoords_set(self, texcoords):
|
||||||
# Image texture node already defaults to UVs, no extra node needed.
|
# Image texture node already defaults to UVs, no extra node needed.
|
||||||
if texcoords == 'UV':
|
# ONLY in case we do not have any texcoords mapping!!!
|
||||||
|
if texcoords == 'UV' and not self.has_mapping_node():
|
||||||
return
|
return
|
||||||
tree = self.node_image.id_data
|
tree = self.node_image.id_data
|
||||||
links = tree.links
|
links = tree.links
|
||||||
node_dst = self.node_mapping if self._node_mapping is not None else self.node_image
|
node_dst = self.node_mapping if self.has_mapping_node() else self.node_image
|
||||||
socket_src = self.owner_shader.node_texcoords.outputs[texcoords]
|
socket_src = self.owner_shader.node_texcoords.outputs[texcoords]
|
||||||
links.new(socket_src, node_dst.inputs["Vector"])
|
links.new(socket_src, node_dst.inputs["Vector"])
|
||||||
|
|
||||||
texcoords = property(texcoords_get, texcoords_set)
|
texcoords = property(texcoords_get, texcoords_set)
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------
|
# --------------------------------------------------------------------
|
||||||
# Mapping.
|
# Mapping.
|
||||||
|
|
||||||
|
def has_mapping_node(self):
|
||||||
|
return self._node_mapping not in {None, ...}
|
||||||
|
|
||||||
def node_mapping_get(self):
|
def node_mapping_get(self):
|
||||||
if self._node_mapping is None:
|
if self._node_mapping is ...:
|
||||||
|
# Running only once, trying to find a valid mapping node.
|
||||||
if self.node_image is None:
|
if self.node_image is None:
|
||||||
return None
|
return None
|
||||||
if self.node_image.inputs["Vector"].is_linked:
|
if self.node_image.inputs["Vector"].is_linked:
|
||||||
@@ -551,78 +669,96 @@ class ShaderImageTextureWrapper():
|
|||||||
if node_mapping.bl_idname == 'ShaderNodeMapping':
|
if node_mapping.bl_idname == 'ShaderNodeMapping':
|
||||||
self._node_mapping = node_mapping
|
self._node_mapping = node_mapping
|
||||||
self.owner_shader._grid_to_location(0, 0 + self.grid_row_diff, ref_node=node_mapping)
|
self.owner_shader._grid_to_location(0, 0 + self.grid_row_diff, ref_node=node_mapping)
|
||||||
|
if self._node_mapping is ...:
|
||||||
|
self._node_mapping = None
|
||||||
if self._node_mapping is None and not self.is_readonly:
|
if self._node_mapping is None and not self.is_readonly:
|
||||||
# Find potential existing link into image's Vector input.
|
# Find potential existing link into image's Vector input.
|
||||||
socket_dst = self.node_image.inputs["Vector"]
|
socket_dst = self.node_image.inputs["Vector"]
|
||||||
socket_src = socket_dst.links[0].from_socket if socket_dst.is_linked else None
|
# If not already existing, we need to create texcoords -> mapping link (from UV).
|
||||||
|
socket_src = (socket_dst.links[0].from_socket if socket_dst.is_linked
|
||||||
|
else self.owner_shader.node_texcoords.outputs['UV'])
|
||||||
|
|
||||||
tree = self.owner_shader.material.node_tree
|
tree = self.owner_shader.material.node_tree
|
||||||
node_mapping = tree.nodes.new(type='ShaderNodeMapping')
|
node_mapping = tree.nodes.new(type='ShaderNodeMapping')
|
||||||
node_mapping.vector_type = 'TEXTURE'
|
node_mapping.vector_type = 'TEXTURE'
|
||||||
self.owner_shader._grid_to_location(-1, 0, dst_node=node_mapping, ref_node=self.node_image)
|
self.owner_shader._grid_to_location(-1, 0, dst_node=node_mapping, ref_node=self.node_image)
|
||||||
|
|
||||||
# link mapping -> image node
|
# Link mapping -> image node.
|
||||||
tree.links.new(node_mapping.outputs["Vector"], socket_dst)
|
tree.links.new(node_mapping.outputs["Vector"], socket_dst)
|
||||||
# And if already existing, re-link texcoords -> mapping
|
# Link texcoords -> mapping.
|
||||||
if socket_src is not None:
|
|
||||||
tree.links.new(socket_src, node_mapping.inputs["Vector"])
|
tree.links.new(socket_src, node_mapping.inputs["Vector"])
|
||||||
|
|
||||||
self._node_mapping = node_mapping
|
self._node_mapping = node_mapping
|
||||||
return self._node_mapping
|
return self._node_mapping
|
||||||
|
|
||||||
node_mapping = property(node_mapping_get)
|
node_mapping = property(node_mapping_get)
|
||||||
|
|
||||||
|
|
||||||
def translation_get(self):
|
def translation_get(self):
|
||||||
return self.node_mapping.translation if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
return self.node_mapping.translation if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def translation_set(self, translation):
|
def translation_set(self, translation):
|
||||||
self.node_mapping.translation = translation
|
self.node_mapping.translation = translation
|
||||||
|
|
||||||
translation = property(translation_get, translation_set)
|
translation = property(translation_get, translation_set)
|
||||||
|
|
||||||
|
|
||||||
def rotation_get(self):
|
def rotation_get(self):
|
||||||
return self.node_mapping.rotation if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
return self.node_mapping.rotation if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def rotation_set(self, rotation):
|
def rotation_set(self, rotation):
|
||||||
self.node_mapping.rotation = rotation
|
self.node_mapping.rotation = rotation
|
||||||
|
|
||||||
rotation = property(rotation_get, rotation_set)
|
rotation = property(rotation_get, rotation_set)
|
||||||
|
|
||||||
|
|
||||||
def scale_get(self):
|
def scale_get(self):
|
||||||
return self.node_mapping.scale if self.node_mapping is not None else Vector((1.0, 1.0, 1.0))
|
return self.node_mapping.scale if self.node_mapping is not None else Vector((1.0, 1.0, 1.0))
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def scale_set(self, scale):
|
def scale_set(self, scale):
|
||||||
self.node_mapping.scale = scale
|
self.node_mapping.scale = scale
|
||||||
|
|
||||||
scale = property(scale_get, scale_set)
|
scale = property(scale_get, scale_set)
|
||||||
|
|
||||||
|
|
||||||
def use_min_get(self):
|
def use_min_get(self):
|
||||||
return self.node_mapping.use_min if self_mapping.node is not None else False
|
return self.node_mapping.use_min if self_mapping.node is not None else False
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def use_min_set(self, use_min):
|
def use_min_set(self, use_min):
|
||||||
self.node_mapping.use_min = use_min
|
self.node_mapping.use_min = use_min
|
||||||
|
|
||||||
use_min = property(use_min_get, use_min_set)
|
use_min = property(use_min_get, use_min_set)
|
||||||
|
|
||||||
|
|
||||||
def use_max_get(self):
|
def use_max_get(self):
|
||||||
return self.node_mapping.use_max if self_mapping.node is not None else False
|
return self.node_mapping.use_max if self_mapping.node is not None else False
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def use_max_set(self, use_max):
|
def use_max_set(self, use_max):
|
||||||
self.node_mapping.use_max = use_max
|
self.node_mapping.use_max = use_max
|
||||||
|
|
||||||
use_max = property(use_max_get, use_max_set)
|
use_max = property(use_max_get, use_max_set)
|
||||||
|
|
||||||
|
|
||||||
def min_get(self):
|
def min_get(self):
|
||||||
return self.node_mapping.min if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
return self.node_mapping.min if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def min_set(self, min):
|
def min_set(self, min):
|
||||||
self.node_mapping.min = min
|
self.node_mapping.min = min
|
||||||
|
|
||||||
min = property(min_get, min_set)
|
min = property(min_get, min_set)
|
||||||
|
|
||||||
|
|
||||||
def max_get(self):
|
def max_get(self):
|
||||||
return self.node_mapping.max if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
return self.node_mapping.max if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
|
||||||
|
|
||||||
@_set_check
|
@_set_check
|
||||||
def max_set(self, max):
|
def max_set(self, max):
|
||||||
self.node_mapping.max = max
|
self.node_mapping.max = max
|
||||||
|
|
||||||
max = property(max_get, max_set)
|
max = property(max_get, max_set)
|
||||||
|
|||||||
Reference in New Issue
Block a user