def fill(vertlist, offset=0, normals=True): if normals: vertlist.reverse() faces = geometry.tessellate_polygon([vertlist]) if offset != 0: for i, f in enumerate(faces): faces[i] = (f[0] + offset, f[1] + offset, f[2] + offset) return faces
def from_faces(cls, faces): """BMFaceのリストからLoopTrisを生成する。""" looptris_tmp = [] for face in faces: loops = face.loops # tri_indices = geom.tessellation([loop.vert.co for loop in loops]) # geom.tessellationのパッチを消したので tri_indices = geom.tessellate_polygon( [[loop.vert.co for loop in loops]]) tris = [tuple([loops[i] for i in i3]) for i3 in tri_indices] looptris_tmp.extend(tris) return cls(looptris_tmp, True)
def tessellate(cls, polyline, correct=False): """三角形に分割する :param polyline: BMLoopかVectorのリスト :param correct: ほぼ直線になっている三点に貼られている面を置換する。 :return: ソート済みのインデックスの二次元リストを返す。 :rtype: list[(int, int, int)] """ if not polyline or len(polyline) < 3: return is_loop = isinstance(polyline[0], bmesh.types.BMLoop) if len(polyline) == 3: tri_indices = [(0, 1, 2)] elif len(polyline) == 4: if is_loop: # 元の面が4頂点だと、歪みが大きくても分割してくれない # 1-3で分割した場合 (0, 1, 3), (1, 2, 3) d1 = polyline[0].vert.normal.dot(polyline[2].vert.normal) # 0-2で分割した場合 (0, 1, 2), (0, 2, 3) d2 = polyline[1].vert.normal.dot(polyline[3].vert.normal) # 法線の差が大きい方を採用する if d1 < d2: tri_indices = [(0, 1, 3), (1, 2, 3)] else: tri_indices = [(0, 1, 2), (0, 2, 3)] else: v1, v2, v3, v4 = polyline if (v2 - v4).length < (v1 - v3).length: tri_indices = [(0, 1, 3), (1, 2, 3)] else: tri_indices = [(0, 1, 2), (0, 2, 3)] else: if is_loop: seq = [[loop.vert.co for loop in polyline]] else: seq = [polyline] # geom.tessellate_polygon()は 2D list を要求する。 tri_indices = geom.tessellate_polygon(seq) # これの精度は不明 # 順番が無茶苦茶になっているのでソートする tri_indices = [tuple(sorted(tri)) for tri in tri_indices] tri_indices.sort(key=lambda tri: tri[0]) if correct: tri_indices = cls.correct_tessellate(polyline, tri_indices, True) return tri_indices
def drawCurve(curve, resolution, size, offset): # segments = getSegments(curve.data.splines[0]) polylines = [] for spl in curve.data.splines: segments = getSegments(spl) print("Segments:\n", segments) polylines.append(segmentsToPolyline(segments, 6, 100)) print("=" * 80, "\n", polylines) chainedPolyLines = [] for i in polylines: for j in i: chainedPolyLines.append(j) tess = tessellate_polygon(polylines) bgl.glBegin(bgl.GL_TRIANGLES) for i in tess: bgl.glVertex2f(chainedPolyLines[i[0]][0] * size + offset[0], chainedPolyLines[i[0]][1] * size + offset[1]) bgl.glVertex2f(chainedPolyLines[i[1]][0] * size + offset[0], chainedPolyLines[i[1]][1] * size + offset[1]) bgl.glVertex2f(chainedPolyLines[i[2]][0] * size + offset[0], chainedPolyLines[i[2]][1] * size + offset[1]) bgl.glEnd() """
def ngon_tessellate(from_data, indices, fix_loops=True): """ Takes a polyline of indices (fgon) and returns a list of face indicie lists. Designed to be used for importers that need indices for an fgon to create from existing verts. from_data: either a mesh, or a list/tuple of vectors. indices: a list of indices to use this list is the ordered closed polyline to fill, and can be a subset of the data given. fix_loops: If this is enabled polylines that use loops to make multiple polylines are delt with correctly. """ from mathutils.geometry import tessellate_polygon from mathutils import Vector vector_to_tuple = Vector.to_tuple if not indices: return [] def mlen(co): # manhatten length of a vector, faster then length return abs(co[0]) + abs(co[1]) + abs(co[2]) def vert_treplet(v, i): return v, vector_to_tuple(v, 6), i, mlen(v) def ed_key_mlen(v1, v2): if v1[3] > v2[3]: return v2[1], v1[1] else: return v1[1], v2[1] if not fix_loops: """ Normal single concave loop filling """ if type(from_data) in {tuple, list}: verts = [Vector(from_data[i]) for ii, i in enumerate(indices)] else: verts = [from_data.vertices[i].co for ii, i in enumerate(indices)] # same as reversed(range(1, len(verts))): for i in range(len(verts) - 1, 0, -1): if verts[i][1] == verts[i - 1][0]: verts.pop(i - 1) fill = tessellate_polygon([verts]) else: """ Seperate this loop into multiple loops be finding edges that are used twice. This is used by lightwave LWO files a lot """ if type(from_data) in {tuple, list}: verts = [vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices)] else: verts = [vert_treplet(from_data.vertices[i].co, ii) for ii, i in enumerate(indices)] edges = [(i, i - 1) for i in range(len(verts))] if edges: edges[0] = (0, len(verts) - 1) if not verts: return [] edges_used = set() edges_doubles = set() # We need to check if any edges are used twice location based. for ed in edges: edkey = ed_key_mlen(verts[ed[0]], verts[ed[1]]) if edkey in edges_used: edges_doubles.add(edkey) else: edges_used.add(edkey) # Store a list of unconnected loop segments split by double edges. # will join later loop_segments = [] v_prev = verts[0] context_loop = [v_prev] loop_segments = [context_loop] for v in verts: if v != v_prev: # Are we crossing an edge we removed? if ed_key_mlen(v, v_prev) in edges_doubles: context_loop = [v] loop_segments.append(context_loop) else: if context_loop and context_loop[-1][1] == v[1]: #raise "as" pass else: context_loop.append(v) v_prev = v # Now join loop segments def join_seg(s1, s2): if s2[-1][1] == s1[0][1]: s1, s2 = s2, s1 elif s1[-1][1] == s2[0][1]: pass else: return False # If were stuill here s1 and s2 are 2 segments in the same polyline s1.pop() # remove the last vert from s1 s1.extend(s2) # add segment 2 to segment 1 if s1[0][1] == s1[-1][1]: # remove endpoints double s1.pop() del s2[:] # Empty this segment s2 so we don't use it again. return True joining_segments = True while joining_segments: joining_segments = False segcount = len(loop_segments) for j in range(segcount - 1, -1, -1): # reversed(range(segcount)): seg_j = loop_segments[j] if seg_j: for k in range(j - 1, -1, -1): # reversed(range(j)): if not seg_j: break seg_k = loop_segments[k] if seg_k and join_seg(seg_j, seg_k): joining_segments = True loop_list = loop_segments for verts in loop_list: while verts and verts[0][1] == verts[-1][1]: verts.pop() loop_list = [verts for verts in loop_list if len(verts) > 2] # DONE DEALING WITH LOOP FIXING # vert mapping vert_map = [None] * len(indices) ii = 0 for verts in loop_list: if len(verts) > 2: for i, vert in enumerate(verts): vert_map[i + ii] = vert[2] ii += len(verts) fill = tessellate_polygon([[v[0] for v in loop] for loop in loop_list]) #draw_loops(loop_list) #raise Exception("done loop") # map to original indices fill = [[vert_map[i] for i in reversed(f)] for f in fill] if not fill: print('Warning Cannot scanfill, fallback on a triangle fan.') fill = [[0, i - 1, i] for i in range(2, len(indices))] else: # Use real scanfill. # See if its flipped the wrong way. flip = None for fi in fill: if flip is not None: break for i, vi in enumerate(fi): if vi == 0 and fi[i - 1] == 1: flip = False break elif vi == 1 and fi[i - 1] == 0: flip = True break if not flip: for i, fi in enumerate(fill): fill[i] = tuple([ii for ii in reversed(fi)]) return fill
def tesselateVecs(self, vecs): return (tuple(reversed(t)) for t in tessellate_polygon( [[v for v in vecs]] ) )
def tesselatePoly(self, vecs, poly): return (tuple(poly[i] for i in t) for t in tessellate_polygon([[vecs[v] for v in poly]]))
def correct_vectors(cls, vecs): """ 要パッチ vecs: 単一面の外周を構成するベクトルのリスト。 """ # tesstris = geom.tessellation(vecs) tesstris = geom.tessellate_polygon([vecs]) if len(vecs) <= 3: return tesstris tesstris = list(tesstris) removed_tris = set() new_tris = [] corrected_num = 0 for tri in tesstris: if tri in removed_tris: continue i1, i2, i3 = tri # v3 <- v2 <- v1 if i1 + 1 == i2 and i2 + 1 == i3: v1, v2, v3 = vecs[i1], vecs[i2], vecs[i3] elif i2 + 1 == i3 and i3 + 1 == i1: v1, v2, v3 = vecs[i2], vecs[i3], vecs[i1] elif i3 + 1 == i1 and i1 + 1 == i2: v1, v2, v3 = vecs[i3], vecs[i1], vecs[i2] else: continue vec1 = v1 - v2 vec2 = v3 - v2 angle = vec1.angle(vec2, 0.0) if angle >= cls.CORRECT_ANGLE_THRESHOLD: # i1とi3を持つ面を探す for link_tri in tesstris: if link_tri != tri: if i1 in link_tri and i3 in link_tri: break else: link_tri = None if link_tri: i4 = link_tri[link_tri.index(i1) - 1] vec3 = vecs[i4] - v2 if vec3.length > cls.CORRECT_DIST_THRESHOLD: tri1 = (i1, i2, i4) tri2 = (i2, i3, i4) new_tris.extend((tri1, tri2)) removed_tris.add(tri) removed_tris.add(link_tri) corrected_num += 1 for tri in removed_tris: tesstris.remove(tri) for tri in new_tris: tesstris.append(tri) # print('Corrected {0} faces.'.format(corrected_num)) return tesstris
def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Furthermore, primitives are split up, if the indices range is exceeded. Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) use_tangents = False if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console( 'WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.' ) # material_map = {} # # Gathering position, normal and tex_coords. # no_material_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: no_material_attributes[TANGENT_ATTRIBUTE] = [] # # Directory of materials with its primitive. # no_material_primitives = { MATERIAL_ID: '', INDICES_ID: [], ATTRIBUTES_ID: no_material_attributes } material_name_to_primitives = {'': no_material_primitives} # vertex_index_to_new_indices = {} material_map[''] = vertex_index_to_new_indices # # Create primitive for each material. # for blender_material in blender_mesh.materials: if blender_material is None: continue attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: attributes[TANGENT_ATTRIBUTE] = [] primitive = { MATERIAL_ID: blender_material.name, INDICES_ID: [], ATTRIBUTES_ID: attributes } material_name_to_primitives[blender_material.name] = primitive # vertex_index_to_new_indices = {} material_map[blender_material.name] = vertex_index_to_new_indices tex_coord_max = 0 if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) # vertex_colors = {} color_index = 0 for vertex_color in blender_mesh.vertex_colors: vertex_color_name = COLOR_PREFIX + str(color_index) vertex_colors[vertex_color_name] = vertex_color color_index += 1 if color_index >= GLTF_MAX_COLORS: break color_max = color_index # bone_max = 0 for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index bones_count = len(blender_mesh.vertices[vertex_index].groups) if bones_count > 0: if bones_count % 4 == 0: bones_count -= 1 bone_max = max(bone_max, bones_count // 4 + 1) # morph_max = 0 blender_shape_keys = [] if blender_mesh.shape_keys is not None: morph_max = len(blender_mesh.shape_keys.key_blocks) - 1 for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key != blender_shape_key.relative_key: blender_shape_keys.append( ShapeKey( blender_shape_key, blender_shape_key.normals_vertex_get( ), # calculate vertex normals for this shape key blender_shape_key.normals_polygon_get()) ) # calculate polygon normals for this shape key # # Convert polygon to primitive indices and eliminate invalid ones. Assign to material. # for blender_polygon in blender_mesh.polygons: export_color = True # if blender_polygon.material_index < 0 or blender_polygon.material_index >= len(blender_mesh.materials) or \ blender_mesh.materials[blender_polygon.material_index] is None: primitive = material_name_to_primitives[''] vertex_index_to_new_indices = material_map[''] else: primitive = material_name_to_primitives[blender_mesh.materials[ blender_polygon.material_index].name] vertex_index_to_new_indices = material_map[blender_mesh.materials[ blender_polygon.material_index].name] # attributes = primitive[ATTRIBUTES_ID] face_normal = blender_polygon.normal face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) if use_tangents: for loop_index in blender_polygon.loop_indices: temp_vertex = blender_mesh.loops[loop_index] face_tangent += temp_vertex.tangent face_bitangent += temp_vertex.bitangent face_tangent.normalize() face_bitangent.normalize() # indices = primitive[INDICES_ID] loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline, )) for triangle in triangles: loop_index_list.append( blender_polygon.loop_indices[triangle[0]]) loop_index_list.append( blender_polygon.loop_indices[triangle[2]]) loop_index_list.append( blender_polygon.loop_indices[triangle[1]]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index if vertex_index_to_new_indices.get(vertex_index) is None: vertex_index_to_new_indices[vertex_index] = [] # v = None n = None t = None b = None uvs = [] colors = [] joints = [] weights = [] target_positions = [] target_normals = [] target_tangents = [] vertex = blender_mesh.vertices[vertex_index] v = convert_swizzle_location(vertex.co, export_settings) if blender_polygon.use_smooth: n = convert_swizzle_location(vertex.normal, export_settings) if use_tangents: t = convert_swizzle_tangent( blender_mesh.loops[loop_index].tangent, export_settings) b = convert_swizzle_location( blender_mesh.loops[loop_index].bitangent, export_settings) else: n = convert_swizzle_location(face_normal, export_settings) if use_tangents: t = convert_swizzle_tangent(face_tangent, export_settings) b = convert_swizzle_location(face_bitangent, export_settings) if use_tangents: tv = Vector((t[0], t[1], t[2])) bv = Vector((b[0], b[1], b[2])) nv = Vector((n[0], n[1], n[2])) if (nv.cross(tv)).dot(bv) < 0.0: t[3] = -1.0 if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[ loop_index].uv uvs.append([uv.x, 1.0 - uv.y]) # if color_max > 0 and export_color: for color_index in range(0, color_max): color_name = COLOR_PREFIX + str(color_index) color = vertex_colors[color_name].data[loop_index].color colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), 1.0 ]) # bone_count = 0 if blender_vertex_groups is not None and vertex.groups is not None and len( vertex.groups) > 0 and export_settings[ gltf2_blender_export_keys.SKINS]: joint = [] weight = [] vertex_groups = vertex.groups if not export_settings['gltf_all_vertex_influences']: # sort groups by weight descending vertex_groups = sorted(vertex.groups, key=attrgetter('weight'), reverse=True) for group_element in vertex_groups: if len(joint) == 4: bone_count += 1 joints.append(joint) weights.append(weight) joint = [] weight = [] # joint_weight = group_element.weight if joint_weight <= 0.0: continue # vertex_group_index = group_element.group vertex_group_name = blender_vertex_groups[ vertex_group_index].name joint_index = None if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: armature = modifiers_dict["ARMATURE"].object skin = gltf2_blender_gather_skins.gather_skin( armature, export_settings) for index, j in enumerate(skin.joints): if j.name == vertex_group_name: joint_index = index break # if joint_index is not None: joint.append(joint_index) weight.append(joint_weight) if len(joint) > 0: bone_count += 1 for fill in range(0, 4 - len(joint)): joint.append(0) weight.append(0.0) joints.append(joint) weights.append(weight) for fill in range(0, bone_max - bone_count): joints.append([0, 0, 0, 0]) weights.append([0.0, 0.0, 0.0, 0.0]) # if morph_max > 0 and export_settings[ gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): blender_shape_key = blender_shape_keys[morph_index] v_morph = convert_swizzle_location( blender_shape_key.shape_key.data[vertex_index].co, export_settings) # Store delta. v_morph -= v target_positions.append(v_morph) # n_morph = None if blender_polygon.use_smooth: temp_normals = blender_shape_key.vertex_normals n_morph = (temp_normals[vertex_index * 3 + 0], temp_normals[vertex_index * 3 + 1], temp_normals[vertex_index * 3 + 2]) else: temp_normals = blender_shape_key.polygon_normals n_morph = (temp_normals[blender_polygon.index * 3 + 0], temp_normals[blender_polygon.index * 3 + 1], temp_normals[blender_polygon.index * 3 + 2]) n_morph = convert_swizzle_location(n_morph, export_settings) # Store delta. n_morph -= n target_normals.append(n_morph) # if use_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector((t[0], t[1], t[2])) t_morph.rotate(rotation) target_tangents.append(t_morph) # # create = True for current_new_index in vertex_index_to_new_indices[vertex_index]: found = True for i in range(0, 3): if attributes[POSITION_ATTRIBUTE][current_new_index * 3 + i] != v[i]: found = False break if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 + i] != n[i]: found = False break if use_tangents: for i in range(0, 4): if attributes[TANGENT_ATTRIBUTE][current_new_index * 4 + i] != t[i]: found = False break if not found: continue for tex_coord_index in range(0, tex_coord_max): uv = uvs[tex_coord_index] tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) for i in range(0, 2): if attributes[tex_coord_id][current_new_index * 2 + i] != uv[i]: found = False break if export_color: for color_index in range(0, color_max): color = colors[color_index] color_id = COLOR_PREFIX + str(color_index) for i in range(0, 3): # Alpha is always 1.0 - see above. current_color = attributes[color_id][ current_new_index * 4 + i] if color_srgb_to_scene_linear( current_color) != color[i]: found = False break if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint = joints[bone_index] weight = weights[bone_index] joint_id = JOINTS_PREFIX + str(bone_index) weight_id = WEIGHTS_PREFIX + str(bone_index) for i in range(0, 4): if attributes[joint_id][current_new_index * 4 + i] != joint[i]: found = False break if attributes[weight_id][current_new_index * 4 + i] != weight[i]: found = False break if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position = target_positions[morph_index] target_normal = target_normals[morph_index] if use_tangents: target_tangent = target_tangents[morph_index] target_position_id = MORPH_POSITION_PREFIX + str( morph_index) target_normal_id = MORPH_NORMAL_PREFIX + str( morph_index) target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) for i in range(0, 3): if attributes[target_position_id][ current_new_index * 3 + i] != target_position[i]: found = False break if attributes[target_normal_id][ current_new_index * 3 + i] != target_normal[i]: found = False break if use_tangents: if attributes[target_tangent_id][ current_new_index * 3 + i] != target_tangent[i]: found = False break if found: indices.append(current_new_index) create = False break if not create: continue new_index = 0 if primitive.get('max_index') is not None: new_index = primitive['max_index'] + 1 primitive['max_index'] = new_index vertex_index_to_new_indices[vertex_index].append(new_index) # # indices.append(new_index) # attributes[POSITION_ATTRIBUTE].extend(v) attributes[NORMAL_ATTRIBUTE].extend(n) if use_tangents: attributes[TANGENT_ATTRIBUTE].extend(t) if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) if attributes.get(tex_coord_id) is None: attributes[tex_coord_id] = [] attributes[tex_coord_id].extend(uvs[tex_coord_index]) if export_color: for color_index in range(0, color_max): color_id = COLOR_PREFIX + str(color_index) if attributes.get(color_id) is None: attributes[color_id] = [] attributes[color_id].extend(colors[color_index]) if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint_id = JOINTS_PREFIX + str(bone_index) if attributes.get(joint_id) is None: attributes[joint_id] = [] attributes[joint_id].extend(joints[bone_index]) weight_id = WEIGHTS_PREFIX + str(bone_index) if attributes.get(weight_id) is None: attributes[weight_id] = [] attributes[weight_id].extend(weights[bone_index]) if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position_id = MORPH_POSITION_PREFIX + str( morph_index) if attributes.get(target_position_id) is None: attributes[target_position_id] = [] attributes[target_position_id].extend( target_positions[morph_index]) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) if attributes.get(target_normal_id) is None: attributes[target_normal_id] = [] attributes[target_normal_id].extend( target_normals[morph_index]) if use_tangents: target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) if attributes.get(target_tangent_id) is None: attributes[target_tangent_id] = [] attributes[target_tangent_id].extend( target_tangents[morph_index]) # # Add primitive plus split them if needed. # result_primitives = [] for material_name, primitive in material_name_to_primitives.items(): export_color = True # indices = primitive[INDICES_ID] if len(indices) == 0: continue position = primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE] normal = primitive[ATTRIBUTES_ID][NORMAL_ATTRIBUTE] if use_tangents: tangent = primitive[ATTRIBUTES_ID][TANGENT_ATTRIBUTE] tex_coords = [] for tex_coord_index in range(0, tex_coord_max): tex_coords.append(primitive[ATTRIBUTES_ID][TEXCOORD_PREFIX + str(tex_coord_index)]) colors = [] if export_color: for color_index in range(0, color_max): tex_coords.append(primitive[ATTRIBUTES_ID][COLOR_PREFIX + str(color_index)]) joints = [] weights = [] if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joints.append(primitive[ATTRIBUTES_ID][JOINTS_PREFIX + str(bone_index)]) weights.append(primitive[ATTRIBUTES_ID][WEIGHTS_PREFIX + str(bone_index)]) target_positions = [] target_normals = [] target_tangents = [] if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_positions.append( primitive[ATTRIBUTES_ID][MORPH_POSITION_PREFIX + str(morph_index)]) target_normals.append( primitive[ATTRIBUTES_ID][MORPH_NORMAL_PREFIX + str(morph_index)]) if use_tangents: target_tangents.append( primitive[ATTRIBUTES_ID][MORPH_TANGENT_PREFIX + str(morph_index)]) # count = len(indices) if count == 0: continue max_index = max(indices) # # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed. # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index. # https://github.com/KhronosGroup/glTF/issues/1142 # https://github.com/KhronosGroup/glTF/pull/1476/files range_indices = 65535 # if max_index >= range_indices: # # Splitting result_primitives. # # At start, all indices are pending. pending_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: pending_attributes[TANGENT_ATTRIBUTE] = [] pending_primitive = { MATERIAL_ID: material_name, INDICES_ID: [], ATTRIBUTES_ID: pending_attributes } pending_primitive[INDICES_ID].extend(indices) pending_attributes[POSITION_ATTRIBUTE].extend(position) pending_attributes[NORMAL_ATTRIBUTE].extend(normal) if use_tangents: pending_attributes[TANGENT_ATTRIBUTE].extend(tangent) tex_coord_index = 0 for tex_coord in tex_coords: pending_attributes[TEXCOORD_PREFIX + str(tex_coord_index)] = tex_coord tex_coord_index += 1 if export_color: color_index = 0 for color in colors: pending_attributes[COLOR_PREFIX + str(color_index)] = color color_index += 1 if export_settings[gltf2_blender_export_keys.SKINS]: joint_index = 0 for joint in joints: pending_attributes[JOINTS_PREFIX + str(joint_index)] = joint joint_index += 1 weight_index = 0 for weight in weights: pending_attributes[WEIGHTS_PREFIX + str(weight_index)] = weight weight_index += 1 if export_settings[gltf2_blender_export_keys.MORPH]: morph_index = 0 for target_position in target_positions: pending_attributes[MORPH_POSITION_PREFIX + str(morph_index)] = target_position morph_index += 1 morph_index = 0 for target_normal in target_normals: pending_attributes[MORPH_NORMAL_PREFIX + str(morph_index)] = target_normal morph_index += 1 if use_tangents: morph_index = 0 for target_tangent in target_tangents: pending_attributes[MORPH_TANGENT_PREFIX + str(morph_index)] = target_tangent morph_index += 1 pending_indices = pending_primitive[INDICES_ID] # Continue until all are processed. while len(pending_indices) > 0: process_indices = pending_primitive[INDICES_ID] max_index = max(process_indices) pending_indices = [] # # all_local_indices = [] for i in range(0, (max_index // range_indices) + 1): all_local_indices.append([]) # # # For all faces ... for face_index in range(0, len(process_indices), 3): written = False face_min_index = min(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) face_max_index = max(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) # ... check if it can be but in a range of maximum indices. for i in range(0, (max_index // range_indices) + 1): offset = i * range_indices # Yes, so store the primitive with its indices. if face_min_index >= offset and face_max_index < offset + range_indices: all_local_indices[i].extend([ process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2] ]) written = True break # If not written, the triangle face has indices from different ranges. if not written: pending_indices.extend([ process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2] ]) # Only add result_primitives, which do have indices in it. for local_indices in all_local_indices: if len(local_indices) > 0: current_primitive = extract_primitive_floor( pending_primitive, local_indices, use_tangents) result_primitives.append(current_primitive) print_console( 'DEBUG', 'Adding primitive with splitting. Indices: ' + str(len(current_primitive[INDICES_ID])) + ' Vertices: ' + str( len(current_primitive[ATTRIBUTES_ID] [POSITION_ATTRIBUTE]) // 3)) # Process primitive faces having indices in several ranges. if len(pending_indices) > 0: pending_primitive = extract_primitive_pack( pending_primitive, pending_indices, use_tangents) print_console( 'DEBUG', 'Creating temporary primitive for splitting') else: # # No splitting needed. # result_primitives.append(primitive) print_console( 'DEBUG', 'Adding primitive without splitting. Indices: ' + str(len(primitive[INDICES_ID])) + ' Vertices: ' + str(len(primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3)) print_console('INFO', 'Primitives created: ' + str(len(result_primitives))) return result_primitives
def tessellate_uvs(uvs): return tessellate_polygon([[Vector(uv) for uv in uvs]])
def tesselateVecs(self, vecs): return (tuple(reversed(t)) for t in tessellate_polygon([[v for v in vecs]]))
def build_objects(object_layers, object_surfs, object_tags, object_name, add_subd_mod, skel_to_arm): """Using the gathered data, create the objects.""" ob_dict= {} # Used for the parenting setup. print("Adding %d Materials" % len(object_surfs)) for surf_key in object_surfs: surf_data= object_surfs[surf_key] surf_data.bl_mat= bpy.data.materials.new(surf_data.name) surf_data.bl_mat.diffuse_color= (surf_data.colr[:]) surf_data.bl_mat.diffuse_intensity= surf_data.diff surf_data.bl_mat.emit= surf_data.lumi surf_data.bl_mat.specular_intensity= surf_data.spec if surf_data.refl != 0.0: surf_data.bl_mat.raytrace_mirror.use= True surf_data.bl_mat.raytrace_mirror.reflect_factor= surf_data.refl surf_data.bl_mat.raytrace_mirror.gloss_factor= 1.0-surf_data.rblr if surf_data.tran != 0.0: surf_data.bl_mat.use_transparency= True surf_data.bl_mat.transparency_method= 'RAYTRACE' surf_data.bl_mat.alpha= 1.0 - surf_data.tran surf_data.bl_mat.raytrace_transparency.ior= surf_data.rind surf_data.bl_mat.raytrace_transparency.gloss_factor= 1.0 - surf_data.tblr surf_data.bl_mat.translucency= surf_data.trnl surf_data.bl_mat.specular_hardness= int(4*((10*surf_data.glos)*(10*surf_data.glos)))+4 # The Gloss is as close as possible given the differences. # Single layer objects use the object file's name instead. if len(object_layers) and object_layers[-1].name == 'Layer 1': object_layers[-1].name= object_name print("Building '%s' Object" % object_name) else: print("Building %d Objects" % len(object_layers)) # Before adding any meshes or armatures go into Object mode. if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') for layer_data in object_layers: me= bpy.data.meshes.new(layer_data.name) me.vertices.add(len(layer_data.pnts)) me.tessfaces.add(len(layer_data.pols)) # for vi in range(len(layer_data.pnts)): # me.vertices[vi].co= layer_data.pnts[vi] # faster, would be faster again to use an array me.vertices.foreach_set("co", [axis for co in layer_data.pnts for axis in co]) ngons= {} # To keep the FaceIdx consistent, handle NGons later. edges= [] # Holds the FaceIdx of the 2-point polys. for fi, fpol in enumerate(layer_data.pols): fpol.reverse() # Reversing gives correct normal directions # PointID 0 in the last element causes Blender to think it's un-used. if fpol[-1] == 0: fpol.insert(0, fpol[-1]) del fpol[-1] vlen= len(fpol) if vlen == 3 or vlen == 4: for i in range(vlen): me.tessfaces[fi].vertices_raw[i]= fpol[i] elif vlen == 2: edges.append(fi) elif vlen != 1: ngons[fi]= fpol # Deal with them later ob= bpy.data.objects.new(layer_data.name, me) bpy.context.scene.objects.link(ob) ob_dict[layer_data.index]= [ob, layer_data.parent_index] # Move the object so the pivot is in the right place. ob.location= layer_data.pivot # Create the Material Slots and assign the MatIndex to the correct faces. mat_slot= 0 for surf_key in layer_data.surf_tags: if object_tags[surf_key] in object_surfs: me.materials.append(object_surfs[object_tags[surf_key]].bl_mat) for fi in layer_data.surf_tags[surf_key]: me.tessfaces[fi].material_index= mat_slot me.tessfaces[fi].use_smooth= object_surfs[object_tags[surf_key]].smooth mat_slot+=1 # Create the Vertex Groups (LW's Weight Maps). if len(layer_data.wmaps) > 0: print("Adding %d Vertex Groups" % len(layer_data.wmaps)) for wmap_key in layer_data.wmaps: vgroup= ob.vertex_groups.new() vgroup.name= wmap_key wlist= layer_data.wmaps[wmap_key] for pvp in wlist: vgroup.add((pvp[0], ), pvp[1], 'REPLACE') # Create the Shape Keys (LW's Endomorphs). if len(layer_data.morphs) > 0: print("Adding %d Shapes Keys" % len(layer_data.morphs)) ob.shape_key_add('Basis') # Got to have a Base Shape. for morph_key in layer_data.morphs: skey= ob.shape_key_add(morph_key) dlist= layer_data.morphs[morph_key] for pdp in dlist: me.shape_keys.key_blocks[skey.name].data[pdp[0]].co= [pdp[1], pdp[2], pdp[3]] # Create the Vertex Color maps. if len(layer_data.colmaps) > 0: print("Adding %d Vertex Color Maps" % len(layer_data.colmaps)) for cmap_key in layer_data.colmaps: map_pack= create_mappack(layer_data, cmap_key, "COLOR") me.vertex_colors.new(cmap_key) vcol= me.tessface_vertex_colors[-1] if not vcol or not vcol.data: break for fi in map_pack: if fi > len(vcol.data): continue face= map_pack[fi] colf= vcol.data[fi] if len(face) > 2: colf.color1= face[0] colf.color2= face[1] colf.color3= face[2] if len(face) == 4: colf.color4= face[3] # Create the UV Maps. if len(layer_data.uvmaps) > 0: print("Adding %d UV Textures" % len(layer_data.uvmaps)) for uvmap_key in layer_data.uvmaps: map_pack= create_mappack(layer_data, uvmap_key, "UV") me.uv_textures.new(name=uvmap_key) uvm= me.tessface_uv_textures[-1] if not uvm or not uvm.data: break for fi in map_pack: if fi > len(uvm.data): continue face= map_pack[fi] uvf= uvm.data[fi] if len(face) > 2: uvf.uv1= face[0] uvf.uv2= face[1] uvf.uv3= face[2] if len(face) == 4: uvf.uv4= face[3] # Now add the NGons. if len(ngons) > 0: for ng_key in ngons: face_offset= len(me.tessfaces) ng= ngons[ng_key] v_locs= [] for vi in range(len(ng)): v_locs.append(mathutils.Vector(layer_data.pnts[ngons[ng_key][vi]])) tris= tessellate_polygon([v_locs]) me.tessfaces.add(len(tris)) for tri in tris: face= me.tessfaces[face_offset] face.vertices_raw[0]= ng[tri[0]] face.vertices_raw[1]= ng[tri[1]] face.vertices_raw[2]= ng[tri[2]] face.material_index= me.tessfaces[ng_key].material_index face.use_smooth= me.tessfaces[ng_key].use_smooth face_offset+= 1 # FaceIDs are no longer a concern, so now update the mesh. has_edges= len(edges) > 0 or len(layer_data.edge_weights) > 0 me.update(calc_edges=has_edges) # Add the edges. edge_offset= len(me.edges) me.edges.add(len(edges)) for edge_fi in edges: me.edges[edge_offset].vertices[0]= layer_data.pols[edge_fi][0] me.edges[edge_offset].vertices[1]= layer_data.pols[edge_fi][1] edge_offset+= 1 # Apply the Edge Weighting. if len(layer_data.edge_weights) > 0: for edge in me.edges: edge_sa= "{0} {1}".format(edge.vertices[0], edge.vertices[1]) edge_sb= "{0} {1}".format(edge.vertices[1], edge.vertices[0]) if edge_sa in layer_data.edge_weights: edge.crease= layer_data.edge_weights[edge_sa] elif edge_sb in layer_data.edge_weights: edge.crease= layer_data.edge_weights[edge_sb] # Unfortunately we can't exlude certain faces from the subdivision. if layer_data.has_subds and add_subd_mod: ob.modifiers.new(name="Subsurf", type='SUBSURF') # Should we build an armature from the embedded rig? if len(layer_data.bones) > 0 and skel_to_arm: bpy.ops.object.armature_add() arm_object= bpy.context.active_object arm_object.name= "ARM_" + layer_data.name arm_object.data.name= arm_object.name arm_object.location= layer_data.pivot bpy.ops.object.mode_set(mode='EDIT') build_armature(layer_data, arm_object.data.edit_bones) bpy.ops.object.mode_set(mode='OBJECT') # Clear out the dictionaries for this layer. layer_data.bone_names.clear() layer_data.bone_rolls.clear() layer_data.wmaps.clear() layer_data.colmaps.clear() layer_data.uvmaps.clear() layer_data.morphs.clear() layer_data.surf_tags.clear() # We may have some invalid mesh data, See: [#27916] # keep this last! print("validating mesh: %r..." % me.name) me.validate(verbose=1) print("done!") # With the objects made, setup the parents and re-adjust the locations. for ob_key in ob_dict: if ob_dict[ob_key][1] != -1 and ob_dict[ob_key][1] in ob_dict: parent_ob = ob_dict[ob_dict[ob_key][1]] ob_dict[ob_key][0].parent= parent_ob[0] ob_dict[ob_key][0].location-= parent_ob[0].location bpy.context.scene.update() print("Done Importing LWO File")
def tesselatePoly(self, vecs, poly): return (tuple(poly[i] for i in t) for t in tessellate_polygon( [[vecs[v] for v in poly]] ) )
def build_objects(object_layers, object_surfs, object_tags, object_name, add_subd_mod, skel_to_arm, use_existing_materials): """Using the gathered data, create the objects.""" ob_dict= {} # Used for the parenting setup. print("Adding %d Materials" % len(object_surfs)) for surf_key in object_surfs: surf_data= object_surfs[surf_key] surf_data.bl_mat = bpy.data.materials.get(surf_data.name) if use_existing_materials else None if surf_data.bl_mat is None: surf_data.bl_mat= bpy.data.materials.new(surf_data.name) surf_data.bl_mat.diffuse_color= (surf_data.colr[:]) surf_data.bl_mat.diffuse_intensity= surf_data.diff surf_data.bl_mat.emit= surf_data.lumi surf_data.bl_mat.specular_intensity= surf_data.spec if surf_data.refl != 0.0: surf_data.bl_mat.raytrace_mirror.use= True surf_data.bl_mat.raytrace_mirror.reflect_factor= surf_data.refl surf_data.bl_mat.raytrace_mirror.gloss_factor= 1.0-surf_data.rblr if surf_data.tran != 0.0: surf_data.bl_mat.use_transparency= True surf_data.bl_mat.transparency_method= 'RAYTRACE' surf_data.bl_mat.alpha= 1.0 - surf_data.tran surf_data.bl_mat.raytrace_transparency.ior= surf_data.rind surf_data.bl_mat.raytrace_transparency.gloss_factor= 1.0 - surf_data.tblr surf_data.bl_mat.translucency= surf_data.trnl surf_data.bl_mat.specular_hardness= int(4*((10*surf_data.glos)*(10*surf_data.glos)))+4 # The Gloss is as close as possible given the differences. # Single layer objects use the object file's name instead. if len(object_layers) and object_layers[-1].name == 'Layer 1': object_layers[-1].name= object_name print("Building '%s' Object" % object_name) else: print("Building %d Objects" % len(object_layers)) # Before adding any meshes or armatures go into Object mode. if bpy.ops.object.mode_set.poll(): bpy.ops.object.mode_set(mode='OBJECT') for layer_data in object_layers: me= bpy.data.meshes.new(layer_data.name) me.vertices.add(len(layer_data.pnts)) me.tessfaces.add(len(layer_data.pols)) # for vi in range(len(layer_data.pnts)): # me.vertices[vi].co= layer_data.pnts[vi] # faster, would be faster again to use an array me.vertices.foreach_set("co", [axis for co in layer_data.pnts for axis in co]) ngons= {} # To keep the FaceIdx consistent, handle NGons later. edges= [] # Holds the FaceIdx of the 2-point polys. for fi, fpol in enumerate(layer_data.pols): fpol.reverse() # Reversing gives correct normal directions # PointID 0 in the last element causes Blender to think it's un-used. if fpol[-1] == 0: fpol.insert(0, fpol[-1]) del fpol[-1] vlen= len(fpol) if vlen == 3 or vlen == 4: for i in range(vlen): me.tessfaces[fi].vertices_raw[i]= fpol[i] elif vlen == 2: edges.append(fi) elif vlen != 1: ngons[fi]= fpol # Deal with them later ob= bpy.data.objects.new(layer_data.name, me) bpy.context.collection.objects.link(ob) ob_dict[layer_data.index]= [ob, layer_data.parent_index] # Move the object so the pivot is in the right place. ob.location= layer_data.pivot # Create the Material Slots and assign the MatIndex to the correct faces. mat_slot= 0 for surf_key in layer_data.surf_tags: if object_tags[surf_key] in object_surfs: me.materials.append(object_surfs[object_tags[surf_key]].bl_mat) for fi in layer_data.surf_tags[surf_key]: me.tessfaces[fi].material_index= mat_slot me.tessfaces[fi].use_smooth= object_surfs[object_tags[surf_key]].smooth mat_slot+=1 # Create the Vertex Groups (LW's Weight Maps). if len(layer_data.wmaps) > 0: print("Adding %d Vertex Groups" % len(layer_data.wmaps)) for wmap_key in layer_data.wmaps: vgroup= ob.vertex_groups.new() vgroup.name= wmap_key wlist= layer_data.wmaps[wmap_key] for pvp in wlist: vgroup.add((pvp[0], ), pvp[1], 'REPLACE') # Create the Shape Keys (LW's Endomorphs). if len(layer_data.morphs) > 0: print("Adding %d Shapes Keys" % len(layer_data.morphs)) ob.shape_key_add('Basis') # Got to have a Base Shape. for morph_key in layer_data.morphs: skey= ob.shape_key_add(morph_key) dlist= layer_data.morphs[morph_key] for pdp in dlist: me.shape_keys.key_blocks[skey.name].data[pdp[0]].co= [pdp[1], pdp[2], pdp[3]] # Create the Vertex Color maps. if len(layer_data.colmaps) > 0: print("Adding %d Vertex Color Maps" % len(layer_data.colmaps)) for cmap_key in layer_data.colmaps: map_pack= create_mappack(layer_data, cmap_key, "COLOR") me.vertex_colors.new(cmap_key) vcol= me.tessface_vertex_colors[-1] if not vcol or not vcol.data: break for fi in map_pack: if fi > len(vcol.data): continue face= map_pack[fi] colf= vcol.data[fi] if len(face) > 2: colf.color1= face[0] colf.color2= face[1] colf.color3= face[2] if len(face) == 4: colf.color4= face[3] # Create the UV Maps. if len(layer_data.uvmaps) > 0: print("Adding %d UV Textures" % len(layer_data.uvmaps)) for uvmap_key in layer_data.uvmaps: map_pack= create_mappack(layer_data, uvmap_key, "UV") me.uv_textures.new(name=uvmap_key) uvm= me.tessface_uv_textures[-1] if not uvm or not uvm.data: break for fi in map_pack: if fi > len(uvm.data): continue face= map_pack[fi] uvf= uvm.data[fi] if len(face) > 2: uvf.uv1= face[0] uvf.uv2= face[1] uvf.uv3= face[2] if len(face) == 4: uvf.uv4= face[3] # Now add the NGons. if len(ngons) > 0: for ng_key in ngons: face_offset= len(me.tessfaces) ng= ngons[ng_key] v_locs= [] for vi in range(len(ng)): v_locs.append(mathutils.Vector(layer_data.pnts[ngons[ng_key][vi]])) tris= tessellate_polygon([v_locs]) me.tessfaces.add(len(tris)) for tri in tris: face= me.tessfaces[face_offset] face.vertices_raw[0]= ng[tri[0]] face.vertices_raw[1]= ng[tri[1]] face.vertices_raw[2]= ng[tri[2]] face.material_index= me.tessfaces[ng_key].material_index face.use_smooth= me.tessfaces[ng_key].use_smooth face_offset+= 1 # FaceIDs are no longer a concern, so now update the mesh. has_edges= len(edges) > 0 or len(layer_data.edge_weights) > 0 me.update(calc_edges=has_edges) # Add the edges. edge_offset= len(me.edges) me.edges.add(len(edges)) for edge_fi in edges: me.edges[edge_offset].vertices[0]= layer_data.pols[edge_fi][0] me.edges[edge_offset].vertices[1]= layer_data.pols[edge_fi][1] edge_offset+= 1 # Apply the Edge Weighting. if len(layer_data.edge_weights) > 0: for edge in me.edges: edge_sa= "{0} {1}".format(edge.vertices[0], edge.vertices[1]) edge_sb= "{0} {1}".format(edge.vertices[1], edge.vertices[0]) if edge_sa in layer_data.edge_weights: edge.crease= layer_data.edge_weights[edge_sa] elif edge_sb in layer_data.edge_weights: edge.crease= layer_data.edge_weights[edge_sb] # Unfortunately we can't exlude certain faces from the subdivision. if layer_data.has_subds and add_subd_mod: ob.modifiers.new(name="Subsurf", type='SUBSURF') # Should we build an armature from the embedded rig? if len(layer_data.bones) > 0 and skel_to_arm: bpy.ops.object.armature_add() arm_object= bpy.context.active_object arm_object.name= "ARM_" + layer_data.name arm_object.data.name= arm_object.name arm_object.location= layer_data.pivot bpy.ops.object.mode_set(mode='EDIT') build_armature(layer_data, arm_object.data.edit_bones) bpy.ops.object.mode_set(mode='OBJECT') # Clear out the dictionaries for this layer. layer_data.bone_names.clear() layer_data.bone_rolls.clear() layer_data.wmaps.clear() layer_data.colmaps.clear() layer_data.uvmaps.clear() layer_data.morphs.clear() layer_data.surf_tags.clear() # We may have some invalid mesh data, See: [#27916] # keep this last! print("validating mesh: %r..." % me.name) me.validate(verbose=1) print("done!") # With the objects made, setup the parents and re-adjust the locations. for ob_key in ob_dict: if ob_dict[ob_key][1] != -1 and ob_dict[ob_key][1] in ob_dict: parent_ob = ob_dict[ob_dict[ob_key][1]] ob_dict[ob_key][0].parent= parent_ob[0] ob_dict[ob_key][0].location-= parent_ob[0].location bpy.context.scene.update() print("Done Importing LWO File")
def face_random_points_ngons(num_points, tessfaces): from random import random import mathutils from mathutils.geometry import area_tri, tessellate_polygon # Split all quads into 2 tris, tris remain unchanged tri_faces = [] for f in tessfaces: tris = [] verts = f.id_data.vertices fv = f.vertices[:] if len(fv) == 3: tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co)) elif len(fv) == 4: tris.append((verts[fv[0]].co, verts[fv[1]].co, verts[fv[2]].co)) tris.append((verts[fv[0]].co, verts[fv[3]].co, verts[fv[2]].co)) else: fvngon = [v.co for v in verts] tris.extend([[fvngon[i] for i in tess] for tess in tessellate_polygon([fvngon])]) tri_faces.append(tris) # For each face, generate the required number of random points sampled_points = [None] * (num_points * len(tessfaces)) # sampled points need to be vectorized as npo below for i, tf, npo in enumerate(zip(tri_faces,num_points)): for k in range(npo): # If this is a quad, we need to weight its 2 tris by their area if len(tf) == 2: area1 = area_tri(*tf[0]) area2 = area_tri(*tf[1]) area_tot = area1 + area2 area1 = area1 / area_tot area2 = area2 / area_tot vecs = tf[0 if (random() < area1) else 1] elif len(tf) == 1: vecs = tf[0] else: areas = [area_tri(*tface) for tface in tf] area_tot = sum(areas) areas = [(area / area_tot) for area in areas] # vecs = tf[0 if (random() < area1) else 1] ??? u1 = random() u2 = random() u_tot = u1 + u2 if u_tot > 1: u1 = 1.0 - u1 u2 = 1.0 - u2 side1 = vecs[1] - vecs[0] side2 = vecs[2] - vecs[0] p = vecs[0] + u1 * side1 + u2 * side2 sampled_points[npo * i + k] = p return sampled_points
def extract_primitives(glTF, blender_mesh, blender_object, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Furthermore, primitives are split up, if the indices range is exceeded. Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) if blender_mesh.has_custom_normals: # Custom normals are all (0, 0, 0) until calling calc_normals_split() or calc_tangents(). blender_mesh.calc_normals_split() use_tangents = False if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console( 'WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.' ) # material_map = {} # # Gathering position, normal and tex_coords. # no_material_attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: no_material_attributes[TANGENT_ATTRIBUTE] = [] # # Directory of materials with its primitive. # no_material_primitives = { MATERIAL_ID: 0, INDICES_ID: [], ATTRIBUTES_ID: no_material_attributes } material_idx_to_primitives = {0: no_material_primitives} # vertex_index_to_new_indices = {} material_map[0] = vertex_index_to_new_indices # # Create primitive for each material. # for (mat_idx, _) in enumerate(blender_mesh.materials): attributes = {POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: []} if use_tangents: attributes[TANGENT_ATTRIBUTE] = [] primitive = { MATERIAL_ID: mat_idx, INDICES_ID: [], ATTRIBUTES_ID: attributes } material_idx_to_primitives[mat_idx] = primitive # vertex_index_to_new_indices = {} material_map[mat_idx] = vertex_index_to_new_indices tex_coord_max = 0 if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) # vertex_colors = {} color_index = 0 for vertex_color in blender_mesh.vertex_colors: vertex_color_name = COLOR_PREFIX + str(color_index) vertex_colors[vertex_color_name] = vertex_color color_index += 1 if color_index >= GLTF_MAX_COLORS: break color_max = color_index # bone_max = 0 for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index bones_count = len(blender_mesh.vertices[vertex_index].groups) if bones_count > 0: if bones_count % 4 == 0: bones_count -= 1 bone_max = max(bone_max, bones_count // 4 + 1) # morph_max = 0 blender_shape_keys = [] if blender_mesh.shape_keys is not None: for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key != blender_shape_key.relative_key: if blender_shape_key.mute is False: morph_max += 1 blender_shape_keys.append( ShapeKey( blender_shape_key, blender_shape_key.normals_vertex_get( ), # calculate vertex normals for this shape key blender_shape_key.normals_polygon_get()) ) # calculate polygon normals for this shape key armature = None if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: modifier = modifiers_dict["ARMATURE"] armature = modifier.object # # Convert polygon to primitive indices and eliminate invalid ones. Assign to material. # for blender_polygon in blender_mesh.polygons: export_color = True # if not blender_polygon.material_index in material_idx_to_primitives: primitive = material_idx_to_primitives[0] vertex_index_to_new_indices = material_map[0] else: primitive = material_idx_to_primitives[ blender_polygon.material_index] vertex_index_to_new_indices = material_map[ blender_polygon.material_index] # attributes = primitive[ATTRIBUTES_ID] face_normal = blender_polygon.normal face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) if use_tangents: for loop_index in blender_polygon.loop_indices: temp_vertex = blender_mesh.loops[loop_index] face_tangent += temp_vertex.tangent face_bitangent += temp_vertex.bitangent face_tangent.normalize() face_bitangent.normalize() # indices = primitive[INDICES_ID] loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline, )) for triangle in triangles: if bpy.app.version < (2, 81, 15): # tessellate_polygon winding-order is reversed in old versions of Blender # See https://developer.blender.org/T70594 triangle = (triangle[0], triangle[2], triangle[1]) for triangle_index in triangle: loop_index_list.append( blender_polygon.loop_indices[triangle_index]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index if vertex_index_to_new_indices.get(vertex_index) is None: vertex_index_to_new_indices[vertex_index] = [] # v = None n = None t = None b = None uvs = [] colors = [] joints = [] weights = [] target_positions = [] target_normals = [] target_tangents = [] vertex = blender_mesh.vertices[vertex_index] v = convert_swizzle_location(vertex.co, armature, blender_object, export_settings) if blender_polygon.use_smooth or blender_mesh.use_auto_smooth: if blender_mesh.has_custom_normals: n = convert_swizzle_normal_and_tangent( blender_mesh.loops[loop_index].normal, armature, blender_object, export_settings) else: n = convert_swizzle_normal_and_tangent( vertex.normal, armature, blender_object, export_settings) if use_tangents: t = convert_swizzle_tangent( blender_mesh.loops[loop_index].tangent, armature, blender_object, export_settings) b = convert_swizzle_location( blender_mesh.loops[loop_index].bitangent, armature, blender_object, export_settings) else: n = convert_swizzle_normal_and_tangent(face_normal, armature, blender_object, export_settings) if use_tangents: t = convert_swizzle_tangent(face_tangent, armature, blender_object, export_settings) b = convert_swizzle_location(face_bitangent, armature, blender_object, export_settings) if use_tangents: tv = Vector((t[0], t[1], t[2])) bv = Vector((b[0], b[1], b[2])) nv = Vector((n[0], n[1], n[2])) if (nv.cross(tv)).dot(bv) < 0.0: t[3] = -1.0 if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[ loop_index].uv uvs.append([uv.x, 1.0 - uv.y]) # if color_max > 0 and export_color: for color_index in range(0, color_max): color_name = COLOR_PREFIX + str(color_index) color = vertex_colors[color_name].data[loop_index].color if len(color) == 3: colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), 1.0 ]) else: colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), color[3] ]) # bone_count = 0 if blender_vertex_groups is not None and vertex.groups is not None and len( vertex.groups) > 0 and export_settings[ gltf2_blender_export_keys.SKINS]: joint = [] weight = [] vertex_groups = vertex.groups if not export_settings['gltf_all_vertex_influences']: # sort groups by weight descending vertex_groups = sorted(vertex.groups, key=attrgetter('weight'), reverse=True) for group_element in vertex_groups: if len(joint) == 4: bone_count += 1 joints.append(joint) weights.append(weight) joint = [] weight = [] # joint_weight = group_element.weight if joint_weight <= 0.0: continue # vertex_group_index = group_element.group vertex_group_name = blender_vertex_groups[ vertex_group_index].name joint_index = None if armature: skin = gltf2_blender_gather_skins.gather_skin( armature, export_settings) for index, j in enumerate(skin.joints): if j.name == vertex_group_name: joint_index = index break # if joint_index is not None: joint.append(joint_index) weight.append(joint_weight) if len(joint) > 0: bone_count += 1 for fill in range(0, 4 - len(joint)): joint.append(0) weight.append(0.0) joints.append(joint) weights.append(weight) for fill in range(0, bone_max - bone_count): joints.append([0, 0, 0, 0]) weights.append([0.0, 0.0, 0.0, 0.0]) # if morph_max > 0 and export_settings[ gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): blender_shape_key = blender_shape_keys[morph_index] v_morph = convert_swizzle_location( blender_shape_key.shape_key.data[vertex_index].co, armature, blender_object, export_settings) # Store delta. v_morph -= v target_positions.append(v_morph) # n_morph = None if blender_polygon.use_smooth: temp_normals = blender_shape_key.vertex_normals n_morph = (temp_normals[vertex_index * 3 + 0], temp_normals[vertex_index * 3 + 1], temp_normals[vertex_index * 3 + 2]) else: temp_normals = blender_shape_key.polygon_normals n_morph = (temp_normals[blender_polygon.index * 3 + 0], temp_normals[blender_polygon.index * 3 + 1], temp_normals[blender_polygon.index * 3 + 2]) n_morph = convert_swizzle_normal_and_tangent( Vector(n_morph), armature, blender_object, export_settings) # Store delta. n_morph -= n target_normals.append(n_morph) # if use_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector((t[0], t[1], t[2])) t_morph.rotate(rotation) target_tangents.append(t_morph) # # create = True for current_new_index in vertex_index_to_new_indices[vertex_index]: found = True for i in range(0, 3): if attributes[POSITION_ATTRIBUTE][current_new_index * 3 + i] != v[i]: found = False break if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 + i] != n[i]: found = False break if use_tangents: for i in range(0, 4): if attributes[TANGENT_ATTRIBUTE][current_new_index * 4 + i] != t[i]: found = False break if not found: continue for tex_coord_index in range(0, tex_coord_max): uv = uvs[tex_coord_index] tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) for i in range(0, 2): if attributes[tex_coord_id][current_new_index * 2 + i] != uv[i]: found = False break if export_color: for color_index in range(0, color_max): color = colors[color_index] color_id = COLOR_PREFIX + str(color_index) for i in range(0, 3): # Alpha is always 1.0 - see above. current_color = attributes[color_id][ current_new_index * 4 + i] if color_srgb_to_scene_linear( current_color) != color[i]: found = False break if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint = joints[bone_index] weight = weights[bone_index] joint_id = JOINTS_PREFIX + str(bone_index) weight_id = WEIGHTS_PREFIX + str(bone_index) for i in range(0, 4): if attributes[joint_id][current_new_index * 4 + i] != joint[i]: found = False break if attributes[weight_id][current_new_index * 4 + i] != weight[i]: found = False break if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position = target_positions[morph_index] target_normal = target_normals[morph_index] if use_tangents: target_tangent = target_tangents[morph_index] target_position_id = MORPH_POSITION_PREFIX + str( morph_index) target_normal_id = MORPH_NORMAL_PREFIX + str( morph_index) target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) for i in range(0, 3): if attributes[target_position_id][ current_new_index * 3 + i] != target_position[i]: found = False break if attributes[target_normal_id][ current_new_index * 3 + i] != target_normal[i]: found = False break if use_tangents: if attributes[target_tangent_id][ current_new_index * 3 + i] != target_tangent[i]: found = False break if found: indices.append(current_new_index) create = False break if not create: continue new_index = 0 if primitive.get('max_index') is not None: new_index = primitive['max_index'] + 1 primitive['max_index'] = new_index vertex_index_to_new_indices[vertex_index].append(new_index) # # indices.append(new_index) # attributes[POSITION_ATTRIBUTE].extend(v) attributes[NORMAL_ATTRIBUTE].extend(n) if use_tangents: attributes[TANGENT_ATTRIBUTE].extend(t) if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) if attributes.get(tex_coord_id) is None: attributes[tex_coord_id] = [] attributes[tex_coord_id].extend(uvs[tex_coord_index]) if export_color: for color_index in range(0, color_max): color_id = COLOR_PREFIX + str(color_index) if attributes.get(color_id) is None: attributes[color_id] = [] attributes[color_id].extend(colors[color_index]) if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint_id = JOINTS_PREFIX + str(bone_index) if attributes.get(joint_id) is None: attributes[joint_id] = [] attributes[joint_id].extend(joints[bone_index]) weight_id = WEIGHTS_PREFIX + str(bone_index) if attributes.get(weight_id) is None: attributes[weight_id] = [] attributes[weight_id].extend(weights[bone_index]) if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position_id = MORPH_POSITION_PREFIX + str( morph_index) if attributes.get(target_position_id) is None: attributes[target_position_id] = [] attributes[target_position_id].extend( target_positions[morph_index]) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) if attributes.get(target_normal_id) is None: attributes[target_normal_id] = [] attributes[target_normal_id].extend( target_normals[morph_index]) if use_tangents: target_tangent_id = MORPH_TANGENT_PREFIX + str( morph_index) if attributes.get(target_tangent_id) is None: attributes[target_tangent_id] = [] attributes[target_tangent_id].extend( target_tangents[morph_index]) # # Add non-empty primitives # result_primitives = [ primitive for primitive in material_idx_to_primitives.values() if len(primitive[INDICES_ID]) != 0 ] print_console('INFO', 'Primitives created: ' + str(len(result_primitives))) return result_primitives
def tessellate_uvs(uvs): return tessellate_polygon([uvs])
def draw_manipulator(self, context): glEnable(GL_BLEND) glEnable(GL_LINE_SMOOTH) locations_2d = self.locations_2d locations_2d_scaled = [] for v in locations_2d: origin = 0, 0 point = v[0], v[1] value = get_preferences().fidget_manimulator_scale px, py = scale(origin, point, value) locations_2d_scaled.append(Vector((px, py))) locations_2d = locations_2d_scaled locations_2d_rotated = [] for v in locations_2d: origin = 0, 0 point = v[0], v[1] angle = math.radians(get_preferences().fidget_manimulator_rotation) px, py = rotate(origin, point, angle) locations_2d_rotated.append(Vector((px, py))) locations_2d = locations_2d_rotated location_2d_2 = [] location_2d_3 = [] for v in locations_2d: origin = 0, 0 point = v[0], v[1] angle = math.radians(120) px, py = rotate(origin, point, angle) location_2d_2.append(Vector((px, py))) for v in locations_2d: origin = 0, 0 point = v[0], v[1] angle = math.radians(-120) px, py = rotate(origin, point, angle) location_2d_3.append(Vector((px, py))) for v in locations_2d: v[0] = v[0] + self.center[0] v[1] = v[1] + self.center[1] for v in location_2d_2: v[0] = v[0] + self.center[0] v[1] = v[1] + self.center[1] for v in location_2d_3: v[0] = v[0] + self.center[0] v[1] = v[1] + self.center[1] triangles = tessellate_polygon([locations_2d]) triangles2 = tessellate_polygon([location_2d_2]) triangles3 = tessellate_polygon([location_2d_3]) if inside_polygon(self.mouse_pos[0], self.mouse_pos[1], locations_2d): if get_preferences().mode == "MODE1": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button1_color_hover) elif get_preferences().mode == "MODE2": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button1_color_mode2_hover) elif get_preferences().mode == "MODE3": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button1_color_mode3_hover) glColor4f(bgR, bgG, bgB, bgA) self.button_top = True else: if get_preferences().mode == "MODE1": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button1_color) elif get_preferences().mode == "MODE2": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button1_color_mode2) elif get_preferences().mode == "MODE3": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button1_color_mode3) glColor4f(bgR, bgG, bgB, bgA) self.button_top = False glBegin(GL_TRIANGLES) for tri in triangles: for v_id in tri: v = locations_2d[v_id] glVertex2f(v[0], v[1]) glEnd() if get_preferences().fidget_enable_outline: bgR, bgG, bgB, bgA = set_color(get_preferences().fidget_outline) glColor4f(bgR, bgG, bgB, bgA) glLineWidth(get_preferences().fidget_outline_width) glBegin(GL_LINE_LOOP) for loc_2d in locations_2d: glVertex2f(loc_2d[0], loc_2d[1]) glEnd() if inside_polygon(self.mouse_pos[0], self.mouse_pos[1], location_2d_2): if get_preferences().mode == "MODE1": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button2_color_hover) elif get_preferences().mode == "MODE2": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button2_color_mode2_hover) elif get_preferences().mode == "MODE3": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button2_color_mode3_hover) glColor4f(bgR, bgG, bgB, bgA) self.button_left = True else: if get_preferences().mode == "MODE1": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button2_color) elif get_preferences().mode == "MODE2": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button2_color_mode2) elif get_preferences().mode == "MODE3": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button2_color_mode3) glColor4f(bgR, bgG, bgB, bgA) self.button_left = False glBegin(GL_TRIANGLES) for tri in triangles2: for v_id in tri: v = location_2d_2[v_id] glVertex2f(v[0], v[1]) glEnd() if get_preferences().fidget_enable_outline: bgR, bgG, bgB, bgA = set_color(get_preferences().fidget_outline) glLineWidth(get_preferences().fidget_outline_width) glColor4f(bgR, bgG, bgB, bgA) glBegin(GL_LINE_LOOP) for loc_2d in location_2d_2: glVertex2f(loc_2d[0], loc_2d[1]) glEnd() if inside_polygon(self.mouse_pos[0], self.mouse_pos[1], location_2d_3): if get_preferences().mode == "MODE1": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button3_color_hover) elif get_preferences().mode == "MODE2": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button3_color_mode2_hover) elif get_preferences().mode == "MODE3": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button3_color_mode3_hover) glColor4f(bgR, bgG, bgB, bgA) self.button_right = True else: if get_preferences().mode == "MODE1": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button3_color) elif get_preferences().mode == "MODE2": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button3_color_mode2) elif get_preferences().mode == "MODE3": bgR, bgG, bgB, bgA = set_color( get_preferences().fidget_button3_color_mode3) glColor4f(bgR, bgG, bgB, bgA) self.button_right = False glBegin(GL_TRIANGLES) for tri in triangles3: for v_id in tri: v = location_2d_3[v_id] glVertex2f(v[0], v[1]) glEnd() if get_preferences().fidget_enable_outline: bgR, bgG, bgB, bgA = set_color(get_preferences().fidget_outline) glLineWidth(get_preferences().fidget_outline_width) glColor4f(bgR, bgG, bgB, bgA) glBegin(GL_LINE_LOOP) for loc_2d in location_2d_3: glVertex2f(loc_2d[0], loc_2d[1]) glEnd() glDisable(GL_LINE_SMOOTH) glDisable(GL_BLEND)
def test_empty(self): self.assertEqual([], geometry.tessellate_polygon([]))
def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Furthermore, primitives are split up, if the indices range is exceeded. Finally, triangles are also split up/duplicated, if face normals are used instead of vertex normals. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) use_tangents = False if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console('WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.') # material_map = {} # # Gathering position, normal and tex_coords. # no_material_attributes = { POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: [] } if use_tangents: no_material_attributes[TANGENT_ATTRIBUTE] = [] # # Directory of materials with its primitive. # no_material_primitives = { MATERIAL_ID: '', INDICES_ID: [], ATTRIBUTES_ID: no_material_attributes } material_name_to_primitives = {'': no_material_primitives} # vertex_index_to_new_indices = {} material_map[''] = vertex_index_to_new_indices # # Create primitive for each material. # for blender_material in blender_mesh.materials: if blender_material is None: continue attributes = { POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: [] } if use_tangents: attributes[TANGENT_ATTRIBUTE] = [] primitive = { MATERIAL_ID: blender_material.name, INDICES_ID: [], ATTRIBUTES_ID: attributes } material_name_to_primitives[blender_material.name] = primitive # vertex_index_to_new_indices = {} material_map[blender_material.name] = vertex_index_to_new_indices tex_coord_max = 0 if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) # vertex_colors = {} color_index = 0 for vertex_color in blender_mesh.vertex_colors: vertex_color_name = COLOR_PREFIX + str(color_index) vertex_colors[vertex_color_name] = vertex_color color_index += 1 if color_index >= GLTF_MAX_COLORS: break color_max = color_index # bone_max = 0 for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index bones_count = len(blender_mesh.vertices[vertex_index].groups) if bones_count > 0: if bones_count % 4 == 0: bones_count -= 1 bone_max = max(bone_max, bones_count // 4 + 1) # morph_max = 0 blender_shape_keys = [] if blender_mesh.shape_keys is not None: morph_max = len(blender_mesh.shape_keys.key_blocks) - 1 for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key != blender_shape_key.relative_key: blender_shape_keys.append(ShapeKey( blender_shape_key, blender_shape_key.normals_vertex_get(), # calculate vertex normals for this shape key blender_shape_key.normals_polygon_get())) # calculate polygon normals for this shape key # # Convert polygon to primitive indices and eliminate invalid ones. Assign to material. # for blender_polygon in blender_mesh.polygons: export_color = True # if blender_polygon.material_index < 0 or blender_polygon.material_index >= len(blender_mesh.materials) or \ blender_mesh.materials[blender_polygon.material_index] is None: primitive = material_name_to_primitives[''] vertex_index_to_new_indices = material_map[''] else: primitive = material_name_to_primitives[blender_mesh.materials[blender_polygon.material_index].name] vertex_index_to_new_indices = material_map[blender_mesh.materials[blender_polygon.material_index].name] # attributes = primitive[ATTRIBUTES_ID] face_normal = blender_polygon.normal face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) if use_tangents: for loop_index in blender_polygon.loop_indices: temp_vertex = blender_mesh.loops[loop_index] face_tangent += temp_vertex.tangent face_bitangent += temp_vertex.bitangent face_tangent.normalize() face_bitangent.normalize() # indices = primitive[INDICES_ID] loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline,)) for triangle in triangles: loop_index_list.append(blender_polygon.loop_indices[triangle[0]]) loop_index_list.append(blender_polygon.loop_indices[triangle[2]]) loop_index_list.append(blender_polygon.loop_indices[triangle[1]]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index if vertex_index_to_new_indices.get(vertex_index) is None: vertex_index_to_new_indices[vertex_index] = [] # v = None n = None t = None b = None uvs = [] colors = [] joints = [] weights = [] target_positions = [] target_normals = [] target_tangents = [] vertex = blender_mesh.vertices[vertex_index] v = convert_swizzle_location(vertex.co, export_settings) if blender_polygon.use_smooth: if blender_mesh.has_custom_normals: n = convert_swizzle_location(blender_mesh.loops[loop_index].normal, export_settings) else: n = convert_swizzle_location(vertex.normal, export_settings) if use_tangents: t = convert_swizzle_tangent(blender_mesh.loops[loop_index].tangent, export_settings) b = convert_swizzle_location(blender_mesh.loops[loop_index].bitangent, export_settings) else: n = convert_swizzle_location(face_normal, export_settings) if use_tangents: t = convert_swizzle_tangent(face_tangent, export_settings) b = convert_swizzle_location(face_bitangent, export_settings) if use_tangents: tv = Vector((t[0], t[1], t[2])) bv = Vector((b[0], b[1], b[2])) nv = Vector((n[0], n[1], n[2])) if (nv.cross(tv)).dot(bv) < 0.0: t[3] = -1.0 if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[loop_index].uv uvs.append([uv.x, 1.0 - uv.y]) # if color_max > 0 and export_color: for color_index in range(0, color_max): color_name = COLOR_PREFIX + str(color_index) color = vertex_colors[color_name].data[loop_index].color colors.append([ color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), 1.0 ]) # bone_count = 0 if blender_vertex_groups is not None and vertex.groups is not None and len(vertex.groups) > 0 and export_settings[gltf2_blender_export_keys.SKINS]: joint = [] weight = [] vertex_groups = vertex.groups if not export_settings['gltf_all_vertex_influences']: # sort groups by weight descending vertex_groups = sorted(vertex.groups, key=attrgetter('weight'), reverse=True) for group_element in vertex_groups: if len(joint) == 4: bone_count += 1 joints.append(joint) weights.append(weight) joint = [] weight = [] # joint_weight = group_element.weight if joint_weight <= 0.0: continue # vertex_group_index = group_element.group vertex_group_name = blender_vertex_groups[vertex_group_index].name joint_index = None if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: modifier = modifiers_dict["ARMATURE"] armature = modifier.object if armature: skin = gltf2_blender_gather_skins.gather_skin(armature, modifier.id_data, export_settings) for index, j in enumerate(skin.joints): if j.name == vertex_group_name: joint_index = index break # if joint_index is not None: joint.append(joint_index) weight.append(joint_weight) if len(joint) > 0: bone_count += 1 for fill in range(0, 4 - len(joint)): joint.append(0) weight.append(0.0) joints.append(joint) weights.append(weight) for fill in range(0, bone_max - bone_count): joints.append([0, 0, 0, 0]) weights.append([0.0, 0.0, 0.0, 0.0]) # if morph_max > 0 and export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): blender_shape_key = blender_shape_keys[morph_index] v_morph = convert_swizzle_location(blender_shape_key.shape_key.data[vertex_index].co, export_settings) # Store delta. v_morph -= v target_positions.append(v_morph) # n_morph = None if blender_polygon.use_smooth: temp_normals = blender_shape_key.vertex_normals n_morph = (temp_normals[vertex_index * 3 + 0], temp_normals[vertex_index * 3 + 1], temp_normals[vertex_index * 3 + 2]) else: temp_normals = blender_shape_key.polygon_normals n_morph = ( temp_normals[blender_polygon.index * 3 + 0], temp_normals[blender_polygon.index * 3 + 1], temp_normals[blender_polygon.index * 3 + 2]) n_morph = convert_swizzle_location(n_morph, export_settings) # Store delta. n_morph -= n target_normals.append(n_morph) # if use_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector((t[0], t[1], t[2])) t_morph.rotate(rotation) target_tangents.append(t_morph) # # create = True for current_new_index in vertex_index_to_new_indices[vertex_index]: found = True for i in range(0, 3): if attributes[POSITION_ATTRIBUTE][current_new_index * 3 + i] != v[i]: found = False break if attributes[NORMAL_ATTRIBUTE][current_new_index * 3 + i] != n[i]: found = False break if use_tangents: for i in range(0, 4): if attributes[TANGENT_ATTRIBUTE][current_new_index * 4 + i] != t[i]: found = False break if not found: continue for tex_coord_index in range(0, tex_coord_max): uv = uvs[tex_coord_index] tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) for i in range(0, 2): if attributes[tex_coord_id][current_new_index * 2 + i] != uv[i]: found = False break if export_color: for color_index in range(0, color_max): color = colors[color_index] color_id = COLOR_PREFIX + str(color_index) for i in range(0, 3): # Alpha is always 1.0 - see above. current_color = attributes[color_id][current_new_index * 4 + i] if color_srgb_to_scene_linear(current_color) != color[i]: found = False break if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint = joints[bone_index] weight = weights[bone_index] joint_id = JOINTS_PREFIX + str(bone_index) weight_id = WEIGHTS_PREFIX + str(bone_index) for i in range(0, 4): if attributes[joint_id][current_new_index * 4 + i] != joint[i]: found = False break if attributes[weight_id][current_new_index * 4 + i] != weight[i]: found = False break if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position = target_positions[morph_index] target_normal = target_normals[morph_index] if use_tangents: target_tangent = target_tangents[morph_index] target_position_id = MORPH_POSITION_PREFIX + str(morph_index) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) target_tangent_id = MORPH_TANGENT_PREFIX + str(morph_index) for i in range(0, 3): if attributes[target_position_id][current_new_index * 3 + i] != target_position[i]: found = False break if attributes[target_normal_id][current_new_index * 3 + i] != target_normal[i]: found = False break if use_tangents: if attributes[target_tangent_id][current_new_index * 3 + i] != target_tangent[i]: found = False break if found: indices.append(current_new_index) create = False break if not create: continue new_index = 0 if primitive.get('max_index') is not None: new_index = primitive['max_index'] + 1 primitive['max_index'] = new_index vertex_index_to_new_indices[vertex_index].append(new_index) # # indices.append(new_index) # attributes[POSITION_ATTRIBUTE].extend(v) attributes[NORMAL_ATTRIBUTE].extend(n) if use_tangents: attributes[TANGENT_ATTRIBUTE].extend(t) if blender_mesh.uv_layers.active: for tex_coord_index in range(0, tex_coord_max): tex_coord_id = TEXCOORD_PREFIX + str(tex_coord_index) if attributes.get(tex_coord_id) is None: attributes[tex_coord_id] = [] attributes[tex_coord_id].extend(uvs[tex_coord_index]) if export_color: for color_index in range(0, color_max): color_id = COLOR_PREFIX + str(color_index) if attributes.get(color_id) is None: attributes[color_id] = [] attributes[color_id].extend(colors[color_index]) if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joint_id = JOINTS_PREFIX + str(bone_index) if attributes.get(joint_id) is None: attributes[joint_id] = [] attributes[joint_id].extend(joints[bone_index]) weight_id = WEIGHTS_PREFIX + str(bone_index) if attributes.get(weight_id) is None: attributes[weight_id] = [] attributes[weight_id].extend(weights[bone_index]) if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_position_id = MORPH_POSITION_PREFIX + str(morph_index) if attributes.get(target_position_id) is None: attributes[target_position_id] = [] attributes[target_position_id].extend(target_positions[morph_index]) target_normal_id = MORPH_NORMAL_PREFIX + str(morph_index) if attributes.get(target_normal_id) is None: attributes[target_normal_id] = [] attributes[target_normal_id].extend(target_normals[morph_index]) if use_tangents: target_tangent_id = MORPH_TANGENT_PREFIX + str(morph_index) if attributes.get(target_tangent_id) is None: attributes[target_tangent_id] = [] attributes[target_tangent_id].extend(target_tangents[morph_index]) # # Add primitive plus split them if needed. # result_primitives = [] for material_name, primitive in material_name_to_primitives.items(): export_color = True # indices = primitive[INDICES_ID] if len(indices) == 0: continue position = primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE] normal = primitive[ATTRIBUTES_ID][NORMAL_ATTRIBUTE] if use_tangents: tangent = primitive[ATTRIBUTES_ID][TANGENT_ATTRIBUTE] tex_coords = [] for tex_coord_index in range(0, tex_coord_max): tex_coords.append(primitive[ATTRIBUTES_ID][TEXCOORD_PREFIX + str(tex_coord_index)]) colors = [] if export_color: for color_index in range(0, color_max): tex_coords.append(primitive[ATTRIBUTES_ID][COLOR_PREFIX + str(color_index)]) joints = [] weights = [] if export_settings[gltf2_blender_export_keys.SKINS]: for bone_index in range(0, bone_max): joints.append(primitive[ATTRIBUTES_ID][JOINTS_PREFIX + str(bone_index)]) weights.append(primitive[ATTRIBUTES_ID][WEIGHTS_PREFIX + str(bone_index)]) target_positions = [] target_normals = [] target_tangents = [] if export_settings[gltf2_blender_export_keys.MORPH]: for morph_index in range(0, morph_max): target_positions.append(primitive[ATTRIBUTES_ID][MORPH_POSITION_PREFIX + str(morph_index)]) target_normals.append(primitive[ATTRIBUTES_ID][MORPH_NORMAL_PREFIX + str(morph_index)]) if use_tangents: target_tangents.append(primitive[ATTRIBUTES_ID][MORPH_TANGENT_PREFIX + str(morph_index)]) # count = len(indices) if count == 0: continue max_index = max(indices) # # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed. # Specifically, the value 65535 (in UINT16) cannot be used as a vertex index. # https://github.com/KhronosGroup/glTF/issues/1142 # https://github.com/KhronosGroup/glTF/pull/1476/files range_indices = 65535 # if max_index >= range_indices: # # Splitting result_primitives. # # At start, all indices are pending. pending_attributes = { POSITION_ATTRIBUTE: [], NORMAL_ATTRIBUTE: [] } if use_tangents: pending_attributes[TANGENT_ATTRIBUTE] = [] pending_primitive = { MATERIAL_ID: material_name, INDICES_ID: [], ATTRIBUTES_ID: pending_attributes } pending_primitive[INDICES_ID].extend(indices) pending_attributes[POSITION_ATTRIBUTE].extend(position) pending_attributes[NORMAL_ATTRIBUTE].extend(normal) if use_tangents: pending_attributes[TANGENT_ATTRIBUTE].extend(tangent) tex_coord_index = 0 for tex_coord in tex_coords: pending_attributes[TEXCOORD_PREFIX + str(tex_coord_index)] = tex_coord tex_coord_index += 1 if export_color: color_index = 0 for color in colors: pending_attributes[COLOR_PREFIX + str(color_index)] = color color_index += 1 if export_settings[gltf2_blender_export_keys.SKINS]: joint_index = 0 for joint in joints: pending_attributes[JOINTS_PREFIX + str(joint_index)] = joint joint_index += 1 weight_index = 0 for weight in weights: pending_attributes[WEIGHTS_PREFIX + str(weight_index)] = weight weight_index += 1 if export_settings[gltf2_blender_export_keys.MORPH]: morph_index = 0 for target_position in target_positions: pending_attributes[MORPH_POSITION_PREFIX + str(morph_index)] = target_position morph_index += 1 morph_index = 0 for target_normal in target_normals: pending_attributes[MORPH_NORMAL_PREFIX + str(morph_index)] = target_normal morph_index += 1 if use_tangents: morph_index = 0 for target_tangent in target_tangents: pending_attributes[MORPH_TANGENT_PREFIX + str(morph_index)] = target_tangent morph_index += 1 pending_indices = pending_primitive[INDICES_ID] # Continue until all are processed. while len(pending_indices) > 0: process_indices = pending_primitive[INDICES_ID] max_index = max(process_indices) pending_indices = [] # # all_local_indices = [] for i in range(0, (max_index // range_indices) + 1): all_local_indices.append([]) # # # For all faces ... for face_index in range(0, len(process_indices), 3): written = False face_min_index = min(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) face_max_index = max(process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]) # ... check if it can be but in a range of maximum indices. for i in range(0, (max_index // range_indices) + 1): offset = i * range_indices # Yes, so store the primitive with its indices. if face_min_index >= offset and face_max_index < offset + range_indices: all_local_indices[i].extend( [process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]]) written = True break # If not written, the triangle face has indices from different ranges. if not written: pending_indices.extend([process_indices[face_index + 0], process_indices[face_index + 1], process_indices[face_index + 2]]) # Only add result_primitives, which do have indices in it. for local_indices in all_local_indices: if len(local_indices) > 0: current_primitive = extract_primitive_floor(pending_primitive, local_indices, use_tangents) result_primitives.append(current_primitive) print_console('DEBUG', 'Adding primitive with splitting. Indices: ' + str( len(current_primitive[INDICES_ID])) + ' Vertices: ' + str( len(current_primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3)) # Process primitive faces having indices in several ranges. if len(pending_indices) > 0: pending_primitive = extract_primitive_pack(pending_primitive, pending_indices, use_tangents) print_console('DEBUG', 'Creating temporary primitive for splitting') else: # # No splitting needed. # result_primitives.append(primitive) print_console('DEBUG', 'Adding primitive without splitting. Indices: ' + str( len(primitive[INDICES_ID])) + ' Vertices: ' + str( len(primitive[ATTRIBUTES_ID][POSITION_ATTRIBUTE]) // 3)) print_console('INFO', 'Primitives created: ' + str(len(result_primitives))) return result_primitives
def modal(self, context, event): self.mouse_x = event.mouse_region_x self.mouse_y = event.mouse_region_y self.region = context.region if context.space_data is not None: self.region_3d = context.space_data.region_3d self.updateSelectionModes(context) # self.flag_redraw -> one more redraw needed at the end of modals # (which block this very one) if self.flag and not self.flag_redraw: self.region.tag_redraw() self.flag_redraw = True elif self.flag_redraw: self.flag_redraw = False self.flag = False if (self.region_3d is not None and context.mode == 'EDIT_MESH' and self.running): coords = (self.mouse_x, self.mouse_y) self.ray_origin = region_2d_to_origin_3d( self.region, self.region_3d, coords) self.ray_direction = region_2d_to_vector_3d( self.region, self.region_3d, coords)*RAY_MAX obj = context.object matrix_world = obj.matrix_world if self.bmesh is None or not self.bmesh.is_valid: self.bmesh = from_edit_mesh(obj.data) df = self.draw_face de = self.draw_edges dv = self.draw_verts found = False if self.select_verts: min_dist_sqr = None point_vert = None for vert in self.bmesh.verts: if not vert.hide: point = matrix_world*vert.co point_2d = self.location_3d_to_region_2d(point) dist_sqr = self.testVert2D(point_2d) if dist_sqr is not None: if (min_dist_sqr is None or min_dist_sqr > dist_sqr): min_dist_sqr = dist_sqr point_vert = point if point_vert is None or min_dist_sqr > DIST_MAX_SQR: #???? self.draw_face = None self.draw_edges = [] self.draw_verts = [] else: found = True self.draw_face = None self.draw_edges = [] self.draw_verts = [point_vert] if self.select_edges and not found: min_dist_sqr = None points_edge = None for edge in self.bmesh.edges: if not edge.hide: points = [matrix_world*v.co for v in edge.verts] points_2d = [self.location_3d_to_region_2d(point) for point in points] dist_sqr = self.testEdge2D(points_2d) if dist_sqr is not None: if (min_dist_sqr is None or min_dist_sqr > dist_sqr): min_dist_sqr = dist_sqr points_edge = points if points_edge is None or min_dist_sqr > DIST_MAX_SQR: #???? self.draw_face = None self.draw_edges = [] self.draw_verts = [] else: found = True self.draw_face = None self.draw_edges = [points_edge] self.draw_verts = points_edge if self.select_faces and not found: min_dist = None points_face = None for face in self.bmesh.faces: if not face.hide: points = [matrix_world*v.co for v in face.verts] points_2d = [self.location_3d_to_region_2d(point) for point in points] tess_face = tessellate_polygon((points,)) possible_tris = [] for tri in tess_face: if self.testTri2D([points_2d[i] for i in tri]): possible_tris.append(tri) for tri in possible_tris: dist = self.testTri3D([points[i] for i in tri]) if dist is not None: if min_dist is None or min_dist > dist: min_dist = dist points_face = points break if points_face is None: self.draw_face = None self.draw_edges = [] # XXX TEMP!!! self.draw_verts = [] # XXX else: self.draw_face = points_face self.draw_edges = [(points_face[i - 1], v) for i, v in enumerate(points_face)] self.draw_verts = points_face if (df != self.draw_face or de != self.draw_edges or dv != self.draw_verts): self.region.tag_redraw() # forced redraw -> not another modal # -> no need for a new one self.flag_redraw = True
def ngon_tessellate(from_data, indices, fix_loops=True): """ Takes a polyline of indices (fgon) and returns a list of face index lists. Designed to be used for importers that need indices for an fgon to create from existing verts. :arg from_data: either a mesh, or a list/tuple of vectors. :type from_data: list or :class:`bpy.types.Mesh` :arg indices: a list of indices to use this list is the ordered closed polyline to fill, and can be a subset of the data given. :type indices: list :arg fix_loops: If this is enabled polylines that use loops to make multiple polylines are delt with correctly. :type fix_loops: bool """ from mathutils.geometry import tessellate_polygon from mathutils import Vector vector_to_tuple = Vector.to_tuple if not indices: return [] def mlen(co): # manhatten length of a vector, faster then length return abs(co[0]) + abs(co[1]) + abs(co[2]) def vert_treplet(v, i): return v, vector_to_tuple(v, 6), i, mlen(v) def ed_key_mlen(v1, v2): if v1[3] > v2[3]: return v2[1], v1[1] else: return v1[1], v2[1] if not fix_loops: """ Normal single concave loop filling """ if type(from_data) in {tuple, list}: verts = [Vector(from_data[i]) for ii, i in enumerate(indices)] else: verts = [from_data.vertices[i].co for ii, i in enumerate(indices)] # same as reversed(range(1, len(verts))): for i in range(len(verts) - 1, 0, -1): if verts[i][1] == verts[i - 1][0]: verts.pop(i - 1) fill = tessellate_polygon([verts]) else: """ Seperate this loop into multiple loops be finding edges that are used twice. This is used by lightwave LWO files a lot """ if type(from_data) in {tuple, list}: verts = [ vert_treplet(Vector(from_data[i]), ii) for ii, i in enumerate(indices) ] else: verts = [ vert_treplet(from_data.vertices[i].co, ii) for ii, i in enumerate(indices) ] edges = [(i, i - 1) for i in range(len(verts))] if edges: edges[0] = (0, len(verts) - 1) if not verts: return [] edges_used = set() edges_doubles = set() # We need to check if any edges are used twice location based. for ed in edges: edkey = ed_key_mlen(verts[ed[0]], verts[ed[1]]) if edkey in edges_used: edges_doubles.add(edkey) else: edges_used.add(edkey) # Store a list of unconnected loop segments split by double edges. # will join later loop_segments = [] v_prev = verts[0] context_loop = [v_prev] loop_segments = [context_loop] for v in verts: if v != v_prev: # Are we crossing an edge we removed? if ed_key_mlen(v, v_prev) in edges_doubles: context_loop = [v] loop_segments.append(context_loop) else: if context_loop and context_loop[-1][1] == v[1]: #raise "as" pass else: context_loop.append(v) v_prev = v # Now join loop segments def join_seg(s1, s2): if s2[-1][1] == s1[0][1]: s1, s2 = s2, s1 elif s1[-1][1] == s2[0][1]: pass else: return False # If were stuill here s1 and s2 are 2 segments in the same polyline s1.pop() # remove the last vert from s1 s1.extend(s2) # add segment 2 to segment 1 if s1[0][1] == s1[-1][1]: # remove endpoints double s1.pop() del s2[:] # Empty this segment s2 so we don't use it again. return True joining_segments = True while joining_segments: joining_segments = False segcount = len(loop_segments) for j in range(segcount - 1, -1, -1): # reversed(range(segcount)): seg_j = loop_segments[j] if seg_j: for k in range(j - 1, -1, -1): # reversed(range(j)): if not seg_j: break seg_k = loop_segments[k] if seg_k and join_seg(seg_j, seg_k): joining_segments = True loop_list = loop_segments for verts in loop_list: while verts and verts[0][1] == verts[-1][1]: verts.pop() loop_list = [verts for verts in loop_list if len(verts) > 2] # DONE DEALING WITH LOOP FIXING # vert mapping vert_map = [None] * len(indices) ii = 0 for verts in loop_list: if len(verts) > 2: for i, vert in enumerate(verts): vert_map[i + ii] = vert[2] ii += len(verts) fill = tessellate_polygon([[v[0] for v in loop] for loop in loop_list]) #draw_loops(loop_list) #raise Exception("done loop") # map to original indices fill = [[vert_map[i] for i in reversed(f)] for f in fill] if not fill: print('Warning Cannot scanfill, fallback on a triangle fan.') fill = [[0, i - 1, i] for i in range(2, len(indices))] else: # Use real scanfill. # See if its flipped the wrong way. flip = None for fi in fill: if flip is not None: break for i, vi in enumerate(fi): if vi == 0 and fi[i - 1] == 1: flip = False break elif vi == 1 and fi[i - 1] == 0: flip = True break if not flip: for i, fi in enumerate(fill): fill[i] = tuple([ii for ii in reversed(fi)]) return fill
def extract_primitives(glTF, blender_mesh, library, blender_object, blender_vertex_groups, modifiers, export_settings): """ Extract primitives from a mesh. Polygons are triangulated and sorted by material. Vertices in multiple faces get split up as necessary. """ print_console('INFO', 'Extracting primitive: ' + blender_mesh.name) # # First, decide what attributes to gather (eg. how many COLOR_n, etc.) # Also calculate normals/tangents now if necessary. # use_normals = export_settings[gltf2_blender_export_keys.NORMALS] if use_normals: if blender_mesh.has_custom_normals: # Custom normals are all (0, 0, 0) until calling calc_normals_split() or calc_tangents(). blender_mesh.calc_normals_split() use_tangents = False if use_normals and export_settings[gltf2_blender_export_keys.TANGENTS]: if blender_mesh.uv_layers.active and len(blender_mesh.uv_layers) > 0: try: blender_mesh.calc_tangents() use_tangents = True except Exception: print_console('WARNING', 'Could not calculate tangents. Please try to triangulate the mesh first.') tex_coord_max = 0 if export_settings[gltf2_blender_export_keys.TEX_COORDS]: if blender_mesh.uv_layers.active: tex_coord_max = len(blender_mesh.uv_layers) color_max = 0 if export_settings[gltf2_blender_export_keys.COLORS]: color_max = len(blender_mesh.vertex_colors) bone_max = 0 # number of JOINTS_n sets needed (1 set = 4 influences) armature = None if blender_vertex_groups and export_settings[gltf2_blender_export_keys.SKINS]: if modifiers is not None: modifiers_dict = {m.type: m for m in modifiers} if "ARMATURE" in modifiers_dict: modifier = modifiers_dict["ARMATURE"] armature = modifier.object # Skin must be ignored if the object is parented to a bone of the armature # (This creates an infinite recursive error) # So ignoring skin in that case is_child_of_arma = ( armature and blender_object and blender_object.parent_type == "BONE" and blender_object.parent.name == armature.name ) if is_child_of_arma: armature = None if armature: skin = gltf2_blender_gather_skins.gather_skin(armature, export_settings) if not skin: armature = None else: joint_name_to_index = {joint.name: index for index, joint in enumerate(skin.joints)} group_to_joint = [joint_name_to_index.get(g.name) for g in blender_vertex_groups] # Find out max number of bone influences for blender_polygon in blender_mesh.polygons: for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index groups_count = len(blender_mesh.vertices[vertex_index].groups) bones_count = (groups_count + 3) // 4 bone_max = max(bone_max, bones_count) use_morph_normals = use_normals and export_settings[gltf2_blender_export_keys.MORPH_NORMAL] use_morph_tangents = use_morph_normals and use_tangents and export_settings[gltf2_blender_export_keys.MORPH_TANGENT] shape_keys = [] if blender_mesh.shape_keys and export_settings[gltf2_blender_export_keys.MORPH]: for blender_shape_key in blender_mesh.shape_keys.key_blocks: if blender_shape_key == blender_shape_key.relative_key or blender_shape_key.mute: continue if use_morph_normals: vertex_normals = blender_shape_key.normals_vertex_get() polygon_normals = blender_shape_key.normals_polygon_get() else: vertex_normals = None polygon_normals = None shape_keys.append(ShapeKey( blender_shape_key, vertex_normals, polygon_normals, )) use_materials = export_settings[gltf2_blender_export_keys.MATERIALS] # # Gather the verts and indices for each primitive. # prims = {} for blender_polygon in blender_mesh.polygons: material_idx = -1 if use_materials: material_idx = blender_polygon.material_index prim = prims.get(material_idx) if not prim: prim = Prim() prims[material_idx] = prim if use_normals: face_normal = None if not (blender_polygon.use_smooth or blender_mesh.use_auto_smooth): # Calc face normal/tangents face_normal = blender_polygon.normal if use_tangents: face_tangent = Vector((0.0, 0.0, 0.0)) face_bitangent = Vector((0.0, 0.0, 0.0)) for loop_index in blender_polygon.loop_indices: loop = blender_mesh.loops[loop_index] face_tangent += loop.tangent face_bitangent += loop.bitangent face_tangent.normalize() face_bitangent.normalize() loop_index_list = [] if len(blender_polygon.loop_indices) == 3: loop_index_list.extend(blender_polygon.loop_indices) elif len(blender_polygon.loop_indices) > 3: # Triangulation of polygon. Using internal function, as non-convex polygons could exist. polyline = [] for loop_index in blender_polygon.loop_indices: vertex_index = blender_mesh.loops[loop_index].vertex_index v = blender_mesh.vertices[vertex_index].co polyline.append(Vector((v[0], v[1], v[2]))) triangles = tessellate_polygon((polyline,)) for triangle in triangles: for triangle_index in triangle: loop_index_list.append(blender_polygon.loop_indices[triangle_index]) else: continue for loop_index in loop_index_list: vertex_index = blender_mesh.loops[loop_index].vertex_index vertex = blender_mesh.vertices[vertex_index] # vert will be a tuple of all the vertex attributes. # Used as cache key in prim.verts. vert = (vertex_index,) v = vertex.co vert += ((v[0], v[1], v[2]),) if use_normals: if face_normal is None: if blender_mesh.has_custom_normals: n = blender_mesh.loops[loop_index].normal else: n = vertex.normal if use_tangents: t = blender_mesh.loops[loop_index].tangent b = blender_mesh.loops[loop_index].bitangent else: n = face_normal if use_tangents: t = face_tangent b = face_bitangent vert += ((n[0], n[1], n[2]),) if use_tangents: vert += ((t[0], t[1], t[2]),) vert += ((b[0], b[1], b[2]),) # TODO: store just bitangent_sign in vert, not whole bitangent? for tex_coord_index in range(0, tex_coord_max): uv = blender_mesh.uv_layers[tex_coord_index].data[loop_index].uv uv = (uv.x, 1.0 - uv.y) vert += (uv,) for color_index in range(0, color_max): color = blender_mesh.vertex_colors[color_index].data[loop_index].color col = ( color_srgb_to_scene_linear(color[0]), color_srgb_to_scene_linear(color[1]), color_srgb_to_scene_linear(color[2]), color[3], ) vert += (col,) if bone_max: bones = [] if vertex.groups: for group_element in vertex.groups: weight = group_element.weight if weight <= 0.0: continue try: joint = group_to_joint[group_element.group] except Exception: continue if joint is None: continue bones.append((joint, weight)) bones.sort(key=lambda x: x[1], reverse=True) bones = tuple(bones) vert += (bones,) for shape_key in shape_keys: v_morph = shape_key.shape_key.data[vertex_index].co v_morph = v_morph - v # store delta vert += ((v_morph[0], v_morph[1], v_morph[2]),) if use_morph_normals: if blender_polygon.use_smooth: normals = shape_key.vertex_normals n_morph = Vector(( normals[vertex_index * 3 + 0], normals[vertex_index * 3 + 1], normals[vertex_index * 3 + 2], )) else: normals = shape_key.polygon_normals n_morph = Vector(( normals[blender_polygon.index * 3 + 0], normals[blender_polygon.index * 3 + 1], normals[blender_polygon.index * 3 + 2], )) n_morph = n_morph - n # store delta vert += ((n_morph[0], n_morph[1], n_morph[2]),) vert_idx = prim.verts.setdefault(vert, len(prim.verts)) prim.indices.append(vert_idx) # # Put the verts into attribute arrays. # result_primitives = [] for material_idx, prim in prims.items(): if not prim.indices: continue vs = [] ns = [] ts = [] uvs = [[] for _ in range(tex_coord_max)] cols = [[] for _ in range(color_max)] joints = [[] for _ in range(bone_max)] weights = [[] for _ in range(bone_max)] vs_morph = [[] for _ in shape_keys] ns_morph = [[] for _ in shape_keys] ts_morph = [[] for _ in shape_keys] for vert in prim.verts.keys(): i = 0 i += 1 # skip over Blender mesh index v = vert[i] i += 1 v = convert_swizzle_location(v, armature, blender_object, export_settings) vs.extend(v) if use_normals: n = vert[i] i += 1 n = convert_swizzle_normal(n, armature, blender_object, export_settings) ns.extend(n) if use_tangents: t = vert[i] i += 1 t = convert_swizzle_tangent(t, armature, blender_object, export_settings) ts.extend(t) b = vert[i] i += 1 b = convert_swizzle_tangent(b, armature, blender_object, export_settings) b_sign = -1.0 if (Vector(n).cross(Vector(t))).dot(Vector(b)) < 0.0 else 1.0 ts.append(b_sign) for tex_coord_index in range(0, tex_coord_max): uv = vert[i] i += 1 uvs[tex_coord_index].extend(uv) for color_index in range(0, color_max): col = vert[i] i += 1 cols[color_index].extend(col) if bone_max: bones = vert[i] i += 1 for j in range(0, 4 * bone_max): if j < len(bones): joint, weight = bones[j] else: joint, weight = 0, 0.0 joints[j//4].append(joint) weights[j//4].append(weight) for shape_key_index in range(0, len(shape_keys)): v_morph = vert[i] i += 1 v_morph = convert_swizzle_location(v_morph, armature, blender_object, export_settings) vs_morph[shape_key_index].extend(v_morph) if use_morph_normals: n_morph = vert[i] i += 1 n_morph = convert_swizzle_normal(n_morph, armature, blender_object, export_settings) ns_morph[shape_key_index].extend(n_morph) if use_morph_tangents: rotation = n_morph.rotation_difference(n) t_morph = Vector(t) t_morph.rotate(rotation) ts_morph[shape_key_index].extend(t_morph) attributes = {} attributes['POSITION'] = vs if ns: attributes['NORMAL'] = ns if ts: attributes['TANGENT'] = ts for i, uv in enumerate(uvs): attributes['TEXCOORD_%d' % i] = uv for i, col in enumerate(cols): attributes['COLOR_%d' % i] = col for i, js in enumerate(joints): attributes['JOINTS_%d' % i] = js for i, ws in enumerate(weights): attributes['WEIGHTS_%d' % i] = ws for i, vm in enumerate(vs_morph): attributes['MORPH_POSITION_%d' % i] = vm for i, nm in enumerate(ns_morph): attributes['MORPH_NORMAL_%d' % i] = nm for i, tm in enumerate(ts_morph): attributes['MORPH_TANGENT_%d' % i] = tm result_primitives.append({ 'attributes': attributes, 'indices': prim.indices, 'material': material_idx, }) print_console('INFO', 'Primitives created: %d' % len(result_primitives)) return result_primitives