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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'release/scripts/modules')
-rw-r--r--release/scripts/modules/bpy_extras/mesh_utils.py190
-rw-r--r--release/scripts/modules/bpy_extras/node_shader_utils.py214
-rw-r--r--release/scripts/modules/bpy_types.py36
-rw-r--r--release/scripts/modules/rna_keymap_ui.py8
-rw-r--r--release/scripts/modules/rna_prop_ui.py2
5 files changed, 243 insertions, 207 deletions
diff --git a/release/scripts/modules/bpy_extras/mesh_utils.py b/release/scripts/modules/bpy_extras/mesh_utils.py
index a7872daca67..a09282da2fe 100644
--- a/release/scripts/modules/bpy_extras/mesh_utils.py
+++ b/release/scripts/modules/bpy_extras/mesh_utils.py
@@ -20,13 +20,12 @@
__all__ = (
"mesh_linked_uv_islands",
- "mesh_linked_tessfaces",
+ "mesh_linked_triangles",
"edge_face_count_dict",
"edge_face_count",
- "edge_loops_from_tessfaces",
"edge_loops_from_edges",
"ngon_tessellate",
- "face_random_points",
+ "triangle_random_points",
)
@@ -90,41 +89,41 @@ def mesh_linked_uv_islands(mesh):
return poly_islands
-def mesh_linked_tessfaces(mesh):
+def mesh_linked_triangles(mesh):
"""
- Splits the mesh into connected faces, use this for separating cubes from
+ Splits the mesh into connected triangles, use this for separating cubes from
other mesh elements within 1 mesh datablock.
:arg mesh: the mesh used to group with.
:type mesh: :class:`bpy.types.Mesh`
- :return: lists of lists containing faces.
+ :return: lists of lists containing triangles.
:rtype: list
"""
# Build vert face connectivity
- vert_faces = [[] for i in range(len(mesh.vertices))]
- for f in mesh.tessfaces:
- for v in f.vertices:
- vert_faces[v].append(f)
+ vert_tris = [[] for i in range(len(mesh.vertices))]
+ for t in mesh.loop_triangles:
+ for v in t.vertices:
+ vert_tris[v].append(t)
- # sort faces into connectivity groups
- face_groups = [[f] for f in mesh.tessfaces]
- # map old, new face location
- face_mapping = list(range(len(mesh.tessfaces)))
+ # sort triangles into connectivity groups
+ tri_groups = [[t] for t in mesh.loop_triangles]
+ # map old, new tri location
+ tri_mapping = list(range(len(mesh.loop_triangles)))
- # Now clump faces iteratively
+ # Now clump triangles iteratively
ok = True
while ok:
ok = False
- for i, f in enumerate(mesh.tessfaces):
- mapped_index = face_mapping[f.index]
- mapped_group = face_groups[mapped_index]
+ for i, t in enumerate(mesh.loop_triangles):
+ mapped_index = tri_mapping[t.index]
+ mapped_group = tri_groups[mapped_index]
- for v in f.vertices:
- for nxt_f in vert_faces[v]:
- if nxt_f != f:
- nxt_mapped_index = face_mapping[nxt_f.index]
+ for v in t.vertices:
+ for nxt_t in vert_tris[v]:
+ if nxt_t != t:
+ nxt_mapped_index = tri_mapping[nxt_t.index]
# We are not a part of the same group
if mapped_index != nxt_mapped_index:
@@ -132,18 +131,18 @@ def mesh_linked_tessfaces(mesh):
# Assign mapping to this group so they
# all map to this group
- for grp_f in face_groups[nxt_mapped_index]:
- face_mapping[grp_f.index] = mapped_index
+ for grp_t in tri_groups[nxt_mapped_index]:
+ tri_mapping[grp_t.index] = mapped_index
- # Move faces into this group
- mapped_group.extend(face_groups[nxt_mapped_index])
+ # Move triangles into this group
+ mapped_group.extend(tri_groups[nxt_mapped_index])
# remove reference to the list
- face_groups[nxt_mapped_index] = None
+ tri_groups[nxt_mapped_index] = None
- # return all face groups that are not null
- # this is all the faces that are connected in their own lists.
- return [fg for fg in face_groups if fg]
+ # return all tri groups that are not null
+ # this is all the triangles that are connected in their own lists.
+ return [tg for tg in tri_groups if tg]
def edge_face_count_dict(mesh):
@@ -177,87 +176,6 @@ def edge_face_count(mesh):
return [get(edge_face_count, ed.key, 0) for ed in mesh.edges]
-def edge_loops_from_tessfaces(mesh, tessfaces=None, seams=()):
- """
- Edge loops defined by faces
-
- Takes me.tessfaces or a list of faces and returns the edge loops
- These edge loops are the edges that sit between quads, so they don't touch
- 1 quad, note: not connected will make 2 edge loops,
- both only containing 2 edges.
-
- return a list of edge key lists
- [[(0, 1), (4, 8), (3, 8)], ...]
-
- :arg mesh: the mesh used to get edge loops from.
- :type mesh: :class:`bpy.types.Mesh`
- :arg tessfaces: optional face list to only use some of the meshes faces.
- :type tessfaces: :class:`bpy.types.MeshTessFace`, sequence or or NoneType
- :return: return a list of edge vertex index lists.
- :rtype: list
- """
-
- OTHER_INDEX = 2, 3, 0, 1 # opposite face index
-
- if tessfaces is None:
- tessfaces = mesh.tessfaces
-
- edges = {}
-
- for f in tessfaces:
- if len(f.vertices) == 4:
- edge_keys = f.edge_keys
- for i, edkey in enumerate(f.edge_keys):
- edges.setdefault(edkey, []).append(edge_keys[OTHER_INDEX[i]])
-
- for edkey in seams:
- edges[edkey] = []
-
- # Collect edge loops here
- edge_loops = []
-
- for edkey, ed_adj in edges.items():
- if 0 < len(ed_adj) < 3: # 1 or 2
- # Seek the first edge
- context_loop = [edkey, ed_adj[0]]
- edge_loops.append(context_loop)
- if len(ed_adj) == 2:
- other_dir = ed_adj[1]
- else:
- other_dir = None
-
- del ed_adj[:]
-
- flipped = False
-
- while 1:
- # from knowing the last 2, look for the next.
- ed_adj = edges[context_loop[-1]]
- if len(ed_adj) != 2:
- # the original edge had 2 other edges
- if other_dir and flipped is False:
- flipped = True # only flip the list once
- context_loop.reverse()
- del ed_adj[:]
- context_loop.append(other_dir) # save 1 look-up
-
- ed_adj = edges[context_loop[-1]]
- if len(ed_adj) != 2:
- del ed_adj[:]
- break
- else:
- del ed_adj[:]
- break
-
- i = ed_adj.index(context_loop[-2])
- context_loop.append(ed_adj[not i])
-
- # Don't look at this again
- del ed_adj[:]
-
- return edge_loops
-
-
def edge_loops_from_edges(mesh, edges=None):
"""
Edge loops defined by edges
@@ -511,54 +429,42 @@ def ngon_tessellate(from_data, indices, fix_loops=True):
return fill
-def face_random_points(num_points, tessfaces):
+def triangle_random_points(num_points, loop_triangles):
"""
- Generates a list of random points over mesh tessfaces.
+ Generates a list of random points over mesh loop triangles.
- :arg num_points: the number of random points to generate on each face.
+ :arg num_points: the number of random points to generate on each triangle.
:type int:
- :arg tessfaces: list of the faces to generate points on.
- :type tessfaces: :class:`bpy.types.MeshTessFace`, sequence
- :return: list of random points over all faces.
+ :arg loop_triangles: list of the triangles to generate points on.
+ :type loop_triangles: :class:`bpy.types.MeshLoopTriangle`, sequence
+ :return: list of random points over all triangles.
:rtype: list
"""
from random import random
from mathutils.geometry import area_tri
- # Split all quads into 2 tris, tris remain unchanged
- tri_faces = []
- for f in tessfaces:
- tris = []
- verts = f.id_data.vertices
- fv = f.vertices[:]
- tris.append((verts[fv[0]].co,
- verts[fv[1]].co,
- verts[fv[2]].co,
- ))
- if len(fv) == 4:
- tris.append((verts[fv[0]].co,
- verts[fv[3]].co,
- verts[fv[2]].co,
- ))
- tri_faces.append(tris)
-
- # For each face, generate the required number of random points
- sampled_points = [None] * (num_points * len(tessfaces))
- for i, tf in enumerate(tri_faces):
+ # For each triangle, generate the required number of random points
+ sampled_points = [None] * (num_points * len(loop_triangles))
+ for i, lt in enumerate(loop_triangles):
+ # Get triangle vertex coordinates
+ verts = lt.id_data.vertices
+ ltv = lt.vertices[:]
+ tv = (verts[ltv[0]].co, verts[ltv[1]].co, verts[ltv[2]].co)
+
for k in range(num_points):
# If this is a quad, we need to weight its 2 tris by their area
- if len(tf) != 1:
- area1 = area_tri(*tf[0])
- area2 = area_tri(*tf[1])
+ if len(tv) != 1:
+ area1 = area_tri(*tv[0])
+ area2 = area_tri(*tv[1])
area_tot = area1 + area2
area1 = area1 / area_tot
area2 = area2 / area_tot
- vecs = tf[0 if (random() < area1) else 1]
+ vecs = tv[0 if (random() < area1) else 1]
else:
- vecs = tf[0]
+ vecs = tv[0]
u1 = random()
u2 = random()
diff --git a/release/scripts/modules/bpy_extras/node_shader_utils.py b/release/scripts/modules/bpy_extras/node_shader_utils.py
index 456c38921a7..e97eac0a9e6 100644
--- a/release/scripts/modules/bpy_extras/node_shader_utils.py
+++ b/release/scripts/modules/bpy_extras/node_shader_utils.py
@@ -19,7 +19,7 @@
# <pep8 compliant>
import bpy
-from mathutils import Vector
+from mathutils import Color, Vector
__all__ = (
"PrincipledBSDFWrapper",
@@ -37,6 +37,12 @@ def _set_check(func):
return func(self, *args, **kwargs)
return wrapper
+def rgb_to_rgba(rgb):
+ return list(rgb) + [1.0]
+
+def rgba_to_rgb(rgba):
+ return Color((rgba[0], rgba[1], rgba[2]))
+
class ShaderWrapper():
"""
@@ -85,9 +91,10 @@ class ShaderWrapper():
dst_node.width = min(dst_node.width, self._col_size - 20)
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.material = material
+ self.use_nodes = use_nodes
self.update()
def update(self): # Should be re-implemented by children classes...
@@ -96,6 +103,7 @@ class ShaderWrapper():
self._textures = {}
self._grid_locations = set()
+
def use_nodes_get(self):
return self.material.use_nodes
@@ -103,17 +111,22 @@ class ShaderWrapper():
def use_nodes_set(self, val):
self.material.use_nodes = val
self.update()
+
use_nodes = property(use_nodes_get, use_nodes_set)
+
def node_texcoords_get(self):
if not self.use_nodes:
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:
if n.bl_idname == 'ShaderNodeTexCoord':
self._node_texcoords = n
self._grid_to_location(0, 0, ref_node=n)
break
+ if self._node_texcoords is ...:
+ self._node_texcoords = None
if self._node_texcoords is None and not self.is_readonly:
tree = self.material.node_tree
nodes = tree.nodes
@@ -124,6 +137,7 @@ class ShaderWrapper():
self._grid_to_location(-5, 1, dst_node=node_texcoords)
self._node_texcoords = node_texcoords
return self._node_texcoords
+
node_texcoords = property(node_texcoords_get)
@@ -149,8 +163,9 @@ class PrincipledBSDFWrapper(ShaderWrapper):
NODES_LIST = ShaderWrapper.NODES_LIST + NODES_LIST
- def __init__(self, material, is_readonly=True):
- super(PrincipledBSDFWrapper, self).__init__(material, is_readonly)
+ def __init__(self, material, is_readonly=True, use_nodes=True):
+ super(PrincipledBSDFWrapper, self).__init__(material, is_readonly, use_nodes)
+
def update(self):
super(PrincipledBSDFWrapper, self).update()
@@ -206,47 +221,60 @@ class PrincipledBSDFWrapper(ShaderWrapper):
# --------------------------------------------------------------------
# Normal Map, lazy initialization...
- self._node_normalmap = None
+ self._node_normalmap = ...
# --------------------------------------------------------------------
# Tex Coords, lazy initialization...
- self._node_texcoords = None
+ self._node_texcoords = ...
+
def node_normalmap_get(self):
if not self.use_nodes:
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
- if node_principled.inputs["Normal"].is_linked:
- node_normalmap = node_principled.inputs["Normal"].links[0].from_node
- if node_normalmap.bl_idname == 'ShaderNodeNormalMap':
- self._node_normalmap = node_normalmap
- self._grid_to_location(0, 0, ref_node=node_normalmap)
+ if self._node_normalmap is ...:
+ # Running only once, trying to find a valid normalmap node.
+ if node_principled.inputs["Normal"].is_linked:
+ node_normalmap = node_principled.inputs["Normal"].links[0].from_node
+ if node_normalmap.bl_idname == 'ShaderNodeNormalMap':
+ self._node_normalmap = 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:
+ tree = self.material.node_tree
+ nodes = tree.nodes
+ links = tree.links
+
node_normalmap = nodes.new(type='ShaderNodeNormalMap')
node_normalmap.label = "Normal/Map"
self._grid_to_location(-1, -2, dst_node=node_normalmap, ref_node=node_principled)
# Link
links.new(node_normalmap.outputs["Normal"], node_principled.inputs["Normal"])
return self._node_normalmap
+
node_normalmap = property(node_normalmap_get)
+
# --------------------------------------------------------------------
- # Diffuse.
+ # Base Color.
- def diffuse_color_get(self):
+ def base_color_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
return self.material.diffuse_color
- return self.node_principled_bsdf.inputs["Base Color"].default_value
+ return rgba_to_rgb(self.node_principled_bsdf.inputs["Base Color"].default_value)
@_set_check
- def diffuse_color_set(self, color):
+ def base_color_set(self, color):
self.material.diffuse_color = color
if self.use_nodes and self.node_principled_bsdf is not None:
- self.node_principled_bsdf.inputs["Base Color"].default_value = color
- diffuse_color = property(diffuse_color_get, diffuse_color_set)
+ self.node_principled_bsdf.inputs["Base Color"].default_value = rgb_to_rgba(color)
+
+ base_color = property(base_color_get, base_color_set)
+
- def diffuse_texture_get(self):
+ def base_color_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
return None
return ShaderImageTextureWrapper(
@@ -254,7 +282,9 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.node_principled_bsdf.inputs["Base Color"],
grid_row_diff=1,
)
- diffuse_texture = property(diffuse_texture_get)
+
+ base_color_texture = property(base_color_texture_get)
+
# --------------------------------------------------------------------
# Specular.
@@ -269,30 +299,37 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.material.specular_intensity = value
if self.use_nodes and self.node_principled_bsdf is not None:
self.node_principled_bsdf.inputs["Specular"].default_value = value
+
specular = property(specular_get, specular_set)
+
def specular_tint_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
return 0.0
- return self.node_principled_bsdf.inputs["Specular Tint"].default_value
+ return rgba_to_rgb(self.node_principled_bsdf.inputs["Specular Tint"].default_value)
@_set_check
def specular_tint_set(self, value):
if self.use_nodes and self.node_principled_bsdf is not None:
- self.node_principled_bsdf.inputs["Specular Tint"].default_value = value
+ self.node_principled_bsdf.inputs["Specular Tint"].default_value = rgb_to_rgba(value)
+
specular_tint = property(specular_tint_get, specular_tint_set)
+
# Will only be used as gray-scale one...
def specular_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
+ print("NO NODES!")
return None
return ShaderImageTextureWrapper(
self, self.node_principled_bsdf,
self.node_principled_bsdf.inputs["Specular"],
grid_row_diff=0,
)
+
specular_texture = property(specular_texture_get)
+
# --------------------------------------------------------------------
# Roughness (also sort of inverse of specular hardness...).
@@ -306,8 +343,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.material.roughness = value
if self.use_nodes and self.node_principled_bsdf is not None:
self.node_principled_bsdf.inputs["Roughness"].default_value = value
+
roughness = property(roughness_get, roughness_set)
+
# Will only be used as gray-scale one...
def roughness_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
@@ -317,8 +356,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.node_principled_bsdf.inputs["Roughness"],
grid_row_diff=0,
)
+
roughness_texture = property(roughness_texture_get)
+
# --------------------------------------------------------------------
# Metallic (a.k.a reflection, mirror).
@@ -332,8 +373,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.material.metallic = value
if self.use_nodes and self.node_principled_bsdf is not None:
self.node_principled_bsdf.inputs["Metallic"].default_value = value
+
metallic = property(metallic_get, metallic_set)
+
# Will only be used as gray-scale one...
def metallic_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
@@ -343,8 +386,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.node_principled_bsdf.inputs["Metallic"],
grid_row_diff=0,
)
+
metallic_texture = property(metallic_texture_get)
+
# --------------------------------------------------------------------
# Transparency settings.
@@ -357,8 +402,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
def ior_set(self, value):
if self.use_nodes and self.node_principled_bsdf is not None:
self.node_principled_bsdf.inputs["IOR"].default_value = value
+
ior = property(ior_get, ior_set)
+
# Will only be used as gray-scale one...
def ior_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
@@ -368,8 +415,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.node_principled_bsdf.inputs["IOR"],
grid_row_diff=-1,
)
+
ior_texture = property(ior_texture_get)
+
def transmission_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
return 0.0
@@ -379,8 +428,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
def transmission_set(self, value):
if self.use_nodes and self.node_principled_bsdf is not None:
self.node_principled_bsdf.inputs["Transmission"].default_value = value
+
transmission = property(transmission_get, transmission_set)
+
# Will only be used as gray-scale one...
def transmission_texture_get(self):
if not self.use_nodes or self.node_principled_bsdf is None:
@@ -390,8 +441,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.node_principled_bsdf.inputs["Transmission"],
grid_row_diff=-1,
)
+
transmission_texture = property(transmission_texture_get)
+
# 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...
@@ -407,8 +460,10 @@ class PrincipledBSDFWrapper(ShaderWrapper):
def normalmap_strength_set(self, value):
if self.use_nodes and self.node_normalmap is not None:
self.node_normalmap.inputs["Strength"].default_value = value
+
normalmap_strength = property(normalmap_strength_get, normalmap_strength_set)
+
def normalmap_texture_get(self):
if not self.use_nodes or self.node_normalmap is None:
return None
@@ -417,9 +472,11 @@ class PrincipledBSDFWrapper(ShaderWrapper):
self.node_normalmap.inputs["Color"],
grid_row_diff=-2,
)
+
normalmap_texture = property(normalmap_texture_get)
+
class ShaderImageTextureWrapper():
"""
Generic 'image texture'-like wrapper, handling image node, some mapping (texture coordinates transformations),
@@ -460,15 +517,17 @@ class ShaderImageTextureWrapper():
self.grid_row_diff = grid_row_diff
self.use_alpha = use_alpha
- self._node_image = None
- self._node_mapping = None
+ self._node_image = ...
+ self._node_mapping = ...
tree = node_dst.id_data
nodes = tree.nodes
links = tree.links
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:
socket_dst = self.node_image.inputs["Vector"]
@@ -477,16 +536,67 @@ class ShaderImageTextureWrapper():
if from_node.bl_idname == 'ShaderNodeMapping':
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.
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:
node_image = self.socket_dst.links[0].from_node
if node_image.bl_idname == 'ShaderNodeTexImage':
self._node_image = 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:
tree = self.owner_shader.material.node_tree
@@ -497,45 +607,61 @@ class ShaderImageTextureWrapper():
self._node_image = node_image
return self._node_image
+
node_image = property(node_image_get)
+
def image_get(self):
return self.node_image.image if self.node_image is not None else None
@_set_check
def image_set(self, image):
self.node_image.image = image
+
image = property(image_get, image_set)
+
def projection_get(self):
return self.node_image.projection if self.node_image is not None else 'FLAT'
@_set_check
def projection_set(self, projection):
self.node_image.projection = projection
+
projection = property(projection_get, projection_set)
+
def texcoords_get(self):
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:
return socket.links[0].from_socket.name
return 'UV'
@_set_check
def texcoords_set(self, texcoords):
+ # Image texture node already defaults to UVs, no extra node needed.
+ # ONLY in case we do not have any texcoords mapping!!!
+ if texcoords == 'UV' and not self.has_mapping_node():
+ return
tree = self.node_image.id_data
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]
links.new(socket_src, node_dst.inputs["Vector"])
+
texcoords = property(texcoords_get, texcoords_set)
+
# --------------------------------------------------------------------
# Mapping.
+ def has_mapping_node(self):
+ return self._node_mapping not in {None, ...}
+
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:
return None
if self.node_image.inputs["Vector"].is_linked:
@@ -543,78 +669,96 @@ class ShaderImageTextureWrapper():
if node_mapping.bl_idname == 'ShaderNodeMapping':
self._node_mapping = 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:
# Find potential existing link into image's Vector input.
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
node_mapping = tree.nodes.new(type='ShaderNodeMapping')
node_mapping.vector_type = 'TEXTURE'
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)
- # And if already existing, re-link texcoords -> mapping
- if socket_src is not None:
- tree.links.new(socket_src, node_mapping.inputs["Vector"])
+ # Link texcoords -> mapping.
+ tree.links.new(socket_src, node_mapping.inputs["Vector"])
self._node_mapping = node_mapping
return self._node_mapping
+
node_mapping = property(node_mapping_get)
+
def translation_get(self):
return self.node_mapping.translation if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
@_set_check
def translation_set(self, translation):
self.node_mapping.translation = translation
+
translation = property(translation_get, translation_set)
+
def rotation_get(self):
return self.node_mapping.rotation if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
@_set_check
def rotation_set(self, rotation):
self.node_mapping.rotation = rotation
+
rotation = property(rotation_get, rotation_set)
+
def scale_get(self):
return self.node_mapping.scale if self.node_mapping is not None else Vector((1.0, 1.0, 1.0))
@_set_check
def scale_set(self, scale):
self.node_mapping.scale = scale
+
scale = property(scale_get, scale_set)
+
def use_min_get(self):
return self.node_mapping.use_min if self_mapping.node is not None else False
@_set_check
def use_min_set(self, use_min):
self.node_mapping.use_min = use_min
+
use_min = property(use_min_get, use_min_set)
+
def use_max_get(self):
return self.node_mapping.use_max if self_mapping.node is not None else False
@_set_check
def use_max_set(self, use_max):
self.node_mapping.use_max = use_max
+
use_max = property(use_max_get, use_max_set)
+
def min_get(self):
return self.node_mapping.min if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
@_set_check
def min_set(self, min):
self.node_mapping.min = min
+
min = property(min_get, min_set)
+
def max_get(self):
return self.node_mapping.max if self.node_mapping is not None else Vector((0.0, 0.0, 0.0))
@_set_check
def max_set(self, max):
self.node_mapping.max = max
+
max = property(max_get, max_set)
diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py
index 52dce466800..9fe45d223f5 100644
--- a/release/scripts/modules/bpy_types.py
+++ b/release/scripts/modules/bpy_types.py
@@ -468,7 +468,7 @@ class MeshEdge(StructRNA):
return ord_ind(*tuple(self.vertices))
-class MeshTessFace(StructRNA):
+class MeshLoopTriangle(StructRNA):
__slots__ = ()
@property
@@ -476,32 +476,18 @@ class MeshTessFace(StructRNA):
"""The midpoint of the face."""
face_verts = self.vertices[:]
mesh_verts = self.id_data.vertices
- if len(face_verts) == 3:
- return (mesh_verts[face_verts[0]].co +
- mesh_verts[face_verts[1]].co +
- mesh_verts[face_verts[2]].co
- ) / 3.0
- else:
- return (mesh_verts[face_verts[0]].co +
- mesh_verts[face_verts[1]].co +
- mesh_verts[face_verts[2]].co +
- mesh_verts[face_verts[3]].co
- ) / 4.0
+ return (mesh_verts[face_verts[0]].co +
+ mesh_verts[face_verts[1]].co +
+ mesh_verts[face_verts[2]].co
+ ) / 3.0
@property
def edge_keys(self):
verts = self.vertices[:]
- if len(verts) == 3:
- return (ord_ind(verts[0], verts[1]),
- ord_ind(verts[1], verts[2]),
- ord_ind(verts[2], verts[0]),
- )
- else:
- return (ord_ind(verts[0], verts[1]),
- ord_ind(verts[1], verts[2]),
- ord_ind(verts[2], verts[3]),
- ord_ind(verts[3], verts[0]),
- )
+ return (ord_ind(verts[0], verts[1]),
+ ord_ind(verts[1], verts[2]),
+ ord_ind(verts[2], verts[0]),
+ )
class MeshPolygon(StructRNA):
@@ -887,7 +873,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
props.menu_idname = self.bl_idname
if add_operator:
- props = row.operator(add_operator, text="", icon='ZOOMOUT')
+ props = row.operator(add_operator, text="", icon='REMOVE')
props.name = name
props.remove_name = True
@@ -901,7 +887,7 @@ class Menu(StructRNA, _GenericUI, metaclass=RNAMeta):
sub.emboss = 'NORMAL'
sub.prop(wm, "preset_name", text="")
- props = row.operator(add_operator, text="", icon='ZOOMIN')
+ props = row.operator(add_operator, text="", icon='ADD')
props.name = wm.preset_name
def draw_preset(self, context):
diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py
index 2581d7782f1..752db7fd5ac 100644
--- a/release/scripts/modules/rna_keymap_ui.py
+++ b/release/scripts/modules/rna_keymap_ui.py
@@ -105,7 +105,7 @@ def draw_km(display_keymaps, kc, km, children, layout, level):
subcol = _indented_layout(col, kmi_level)
subcol = subcol.split(factor=0.2).column()
subcol.operator("wm.keyitem_add", text="Add New", text_ctxt=i18n_contexts.id_windowmanager,
- icon='ZOOMIN')
+ icon='ADD')
col.separator()
@@ -350,7 +350,7 @@ def draw_filtered(display_keymaps, filter_type, filter_text, layout):
# "Add New" at end of keymap item list
col = _indented_layout(layout, 1)
subcol = col.split(factor=0.2).column()
- subcol.operator("wm.keyitem_add", text="Add New", icon='ZOOMIN')
+ subcol.operator("wm.keyitem_add", text="Add New", icon='ADD')
return True
@@ -380,8 +380,8 @@ def draw_keymaps(context, layout):
if not text:
text = "Blender (default)"
row.menu("USERPREF_MT_keyconfigs", text=text)
- row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMIN')
- row.operator("wm.keyconfig_preset_add", text="", icon='ZOOMOUT').remove_active = True
+ row.operator("wm.keyconfig_preset_add", text="", icon='ADD')
+ row.operator("wm.keyconfig_preset_add", text="", icon='REMOVE').remove_active = True
# layout.context_pointer_set("keyconfig", wm.keyconfigs.active)
# row.operator("wm.keyconfig_remove", text="", icon='X')
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py
index c987609bafa..fc17cc60c6c 100644
--- a/release/scripts/modules/rna_prop_ui.py
+++ b/release/scripts/modules/rna_prop_ui.py
@@ -188,7 +188,7 @@ def draw(layout, context, context_member, property_type, use_edit=True):
if not is_rna:
props = row.operator("wm.properties_edit", text="Edit")
assign_props(props, val_draw, key)
- props = row.operator("wm.properties_remove", text="", icon='ZOOMOUT')
+ props = row.operator("wm.properties_remove", text="", icon='REMOVE')
assign_props(props, val_draw, key)
else:
row.label(text="API Defined")