+ face_count = len(self.faces)
+ for i in range(face_count):
+ f = self.faces[i]
+ nverts = len(f.vertices)
+ if nverts==3:
+ continue
+
+ # Calculate normals at each vertex of the face
+ edge_vecs = []
+ for j in range(nverts):
+ edge_vecs.append(f.vertices[(j+1)%nverts].co-f.vertices[j].co)
+
+ normals = []
+ for j in range(nverts):
+ normals.append(edge_vecs[j-1].cross(edge_vecs[j]).normalized())
+
+ # Check which diagonal results in a flatter triangulation
+ flatness1 = normals[0].dot(normals[2])
+ flatness2 = normals[1].dot(normals[3])
+ cut_index = 1 if flatness1>flatness2 else 0
+
+ nf = Face(f)
+ nf.index = len(self.faces)
+ self.faces.append(nf)
+
+ ne = Edge(None)
+ ne.index = len(self.edges)
+ self.edges.append(ne)
+
+ nf.vertices = [f.vertices[cut_index], f.vertices[2], f.vertices[3]]
+ nf.loop_indices = [f.loop_indices[cut_index], f.loop_indices[2], f.loop_indices[3]]
+ for v in nf.vertices:
+ v.faces.append(nf)
+
+ ne.vertices = [f.vertices[cut_index], f.vertices[2+cut_index]]
+ for v in ne.vertices:
+ v.edges.append(ne)
+ ne.key = make_edge_key(ne.vertices[0].index, ne.vertices[1].index)
+ ne.smooth = True
+
+ f.vertices[3-cut_index].faces.remove(f)
+ del f.vertices[3-cut_index]
+ f.loop_indices = [f.loop_indices[0], f.loop_indices[1], f.loop_indices[2+cut_index]]
+
+ ne.faces = [f, nf]
+ if cut_index==0:
+ nf.edges = [ne, f.edges[2], f.edges[3]]
+ f.edges = [f.edges[0], f.edges[1], ne]
+ else:
+ nf.edges = [f.edges[1], f.edges[2], ne]
+ f.edges = [f.edges[0], ne, f.edges[3]]
+ for e in nf.edges:
+ if e!=ne:
+ e.faces.remove(f)
+ e.faces.append(nf)
+
+ f.normal = normals[1-cut_index]
+ nf.normal = normals[3-cut_index]
+
+ task.set_progress(i/face_count)
+
+ def prepare_smoothing(self, task):
+ smooth_limit = -1
+ if self.smoothing=='NONE':
+ for f in self.faces:
+ f.use_smooth = False
+
+ smooth_limit = 1
+ elif self.use_auto_smooth:
+ smooth_limit = math.cos(self.auto_smooth_angle)
+
+ for e in self.edges:
+ e.check_smooth(smooth_limit)
+
+ subtask = task.task("Sharp edges", 0.7)
+ self.split_vertices(self.find_smooth_group, subtask)
+
+ if self.smoothing!='BLENDER':
+ subtask = task.task("Updating normals", 1.0)
+ self.compute_normals(subtask)
+
+ def prepare_vertex_groups(self, obj):
+ if not self.vertex_groups:
+ return
+
+ for v in self.vertices:
+ if v.groups:
+ weight_sum = sum(g.weight for g in v.groups)
+ v.groups = sorted(v.groups, key=(lambda g: g.weight), reverse=True)[:self.max_groups_per_vertex]
+ weight_scale = weight_sum/sum(g.weight for g in v.groups)
+ for g in v.groups:
+ g.weight *= weight_scale
+ while len(v.groups)<self.max_groups_per_vertex:
+ v.groups.append(VertexGroup(None))
+
+ if obj.parent and obj.parent.type=="ARMATURE":
+ armature = obj.parent.data
+ bone_indices = {b.name: i for i, b in enumerate(armature.bones)}
+ group_index_map = {i: i for i in range(len(obj.vertex_groups))}
+ for g in first_obj.vertex_groups:
+ if g.name in bone_indices:
+ group_index_map[g.index] = bone_indices[g.name]
+
+ for v in self.vertices:
+ for g in v.groups:
+ g.group = group_index_map[g.group]
+
+ def prepare_uv(self, task):
+ # Form a list of UV layers referenced by materials with the array atlas
+ # property set
+ array_uv_layers = [] #[t.uv_layer for m in self.materials if m.array_atlas for t in m.texture_slots if t and t.texture_coords=='UV']
+ array_uv_layers = [u for u in self.uv_layers if u.name in array_uv_layers]
+
+ if array_uv_layers:
+ for f in self.faces:
+ layer = 0
+ if f.material_index<len(self.materials):
+ mat = self.materials[f.material_index]
+ if mat and mat.array_atlas:
+ layer = mat.array_layer
+
+ for l in array_uv_layers:
+ for i in f.loop_indices:
+ l.uvs[i] = mathutils.Vector((*l.uvs[i], layer))
+
+ # Split by the UV layer used for tangent vectors first so connectivity
+ # remains intact for tangent vector computation
+ tangent_layer_index = -1
+ if self.tangent_vecs:
+ if self.tangent_uvtex:
+ uv_names = [u.name for u in self.uv_layers]
+ if self.tangent_uvtex in uv_names:
+ tangent_layer_index = uv_names.index(self.tangent_uvtex)
+ elif self.uv_layers[0].unit==0:
+ tangent_layer_index = 0
+
+ if tangent_layer_index<0:
+ raise Exception("Invalid configuration on mesh {}: No tangent UV layer".format(self.name))
+
+ prog_count = len(self.uv_layers)
+ if tangent_layer_index>=0:
+ prog_count += 1
+ task.set_slices(prog_count)
+
+ if tangent_layer_index>=0:
+ subtask = task.next_slice("Computing tangents")
+ self.split_vertices(self.find_uv_group, subtask, tangent_layer_index)
+ subtask = task.next_slice(self.tangent_uvtex)
+ self.compute_tangents(tangent_layer_index, subtask)
+
+ # Split by the remaining UV layers
+ for i, u in enumerate(self.uv_layers):
+ if i==tangent_layer_index:
+ continue
+
+ subtask = task.next_slice(u.name)
+ self.split_vertices(self.find_uv_group, subtask, i)
+
+ # Copy UVs from layers to vertices
+ for v in self.vertices:
+ if v.faces:
+ # All faces still connected to the vertex have the same UV value
+ f = v.faces[0]
+ i = f.get_loop_index(v)
+ v.uvs = [u.uvs[i] for u in self.uv_layers]
+ else:
+ v.uvs = [(0.0, 0.0)]*len(self.uv_layers)
+
+ def prepare_colors(self, task):
+ if not self.colors:
+ return
+
+ self.split_vertices(self.find_color_group, task)
+
+ for v in self.vertices:
+ if v.faces:
+ f = v.faces[0]
+ v.color = self.colors.colors[f.get_loop_index(v)]
+ else:
+ v.color = (1.0, 1.0, 1.0, 1.0)
+
+ def prepare_splat_weights(self, task):
+ if not self.splat_layers:
+ return
+
+ splat_weights = []
+ remainder = None
+ for s in self.splat_sources:
+ if s[0] is None:
+ splat_weights.append(remainder)
+ else:
+ index = s[1]
+ layer_values = [c[index] for c in s[0].colors]
+ if remainder:
+ splat_weights.append([v*r for v, r in zip(layer_values, remainder)])
+ remainder = [(1-v)*r for v, r in zip(layer_values, remainder)]
+ else:
+ splat_weights.append(layer_values)
+ remainder = [1-v for v in layer_values]
+
+ splat_weights = list(zip(*splat_weights))