def read_surfaces(self, ac_file, toks): surf_count = int(toks[1]) for n in range(surf_count): line = ac_file.readline() if line=='': break line = line.strip().split() if line[0] == 'SURF': surf = AcSurf(line[1], ac_file, self.import_config) if( len(surf.refs) in [3,4] ): self.surf_list.append(surf) else: if(len(surf.refs) > 4): print(surf.refs) tess = ngon_tessellate(self.vert_list, surf.refs) print(tess) for triangle in tess: new_triangle = [] for tri_index in triangle: new_triangle.append(surf.refs[tri_index]) uv_rfs = [] for ref_index in new_triangle: for i, uv_indx in enumerate(surf.uv_refs): uv_rf = surf.refs[i] if(uv_rf == ref_index): uv_rfs.append(uv_indx) subsurf = AcSurfNgon(surf.flags, surf.mat_index, new_triangle, uv_rfs, surf.import_config) self.surf_list.append(subsurf) else: TRACE("Ignoring surface (vertex-count: {0})".format(len(surf.refs)))
def get_area_and_paint(myvertices, myobj, obverts, region, rv3d): mymesh = myobj.data totarea = 0 if len(myvertices) > 3: # Tessellate the polygon if myobj.mode != 'EDIT': tris = mesh_utils.ngon_tessellate(mymesh, myvertices) else: bm = bmesh.from_edit_mesh(myobj.data) myv = [] for v in bm.verts: myv.extend([v.co]) tris = mesh_utils.ngon_tessellate(myv, myvertices) for t in tris: v1, v2, v3 = t p1 = get_point(obverts[myvertices[v1]].co, myobj) p2 = get_point(obverts[myvertices[v2]].co, myobj) p3 = get_point(obverts[myvertices[v3]].co, myobj) screen_point_p1 = get_2d_point(region, rv3d, p1) screen_point_p2 = get_2d_point(region, rv3d, p2) screen_point_p3 = get_2d_point(region, rv3d, p3) draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3) # Area area = get_triangle_area(p1, p2, p3) totarea += area elif len(myvertices) == 3: v1, v2, v3 = myvertices p1 = get_point(obverts[v1].co, myobj) p2 = get_point(obverts[v2].co, myobj) p3 = get_point(obverts[v3].co, myobj) screen_point_p1 = get_2d_point(region, rv3d, p1) screen_point_p2 = get_2d_point(region, rv3d, p2) screen_point_p3 = get_2d_point(region, rv3d, p3) draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3) # Area area = get_triangle_area(p1, p2, p3) totarea += area else: return 0.0 return totarea
def get_area_and_paint(myvertices, myobj, obverts, region, rv3d): mymesh = myobj.data totarea = 0 if len(myvertices) > 3: # Tessellate the polygon if myobj.mode != "EDIT": tris = mesh_utils.ngon_tessellate(mymesh, myvertices) else: bm = bmesh.from_edit_mesh(myobj.data) myv = [] for v in bm.verts: myv.extend([v.co]) tris = mesh_utils.ngon_tessellate(myv, myvertices) for t in tris: v1, v2, v3 = t p1 = get_point(obverts[myvertices[v1]].co, myobj) p2 = get_point(obverts[myvertices[v2]].co, myobj) p3 = get_point(obverts[myvertices[v3]].co, myobj) screen_point_p1 = get_2d_point(region, rv3d, p1) screen_point_p2 = get_2d_point(region, rv3d, p2) screen_point_p3 = get_2d_point(region, rv3d, p3) draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3) # Area area = get_triangle_area(p1, p2, p3) totarea += area elif len(myvertices) == 3: v1, v2, v3 = myvertices p1 = get_point(obverts[v1].co, myobj) p2 = get_point(obverts[v2].co, myobj) p3 = get_point(obverts[v3].co, myobj) screen_point_p1 = get_2d_point(region, rv3d, p1) screen_point_p2 = get_2d_point(region, rv3d, p2) screen_point_p3 = get_2d_point(region, rv3d, p3) draw_triangle(screen_point_p1, screen_point_p2, screen_point_p3) # Area area = get_triangle_area(p1, p2, p3) totarea += area else: return 0.0 return totarea
def calculatePolygonVolume(mesh, polygon, reference_point): if len(polygon.vertices) == 3: vcs = [mesh.vertices[polygon.vertices[i]].co for i in (0, 1, 2)] return calculateTriangleVolume(vcs, reference_point) elif len(polygon.vertices) == 4: vcs1 = [mesh.vertices[polygon.vertices[i]].co for i in (0, 1, 2)] vcs2 = [mesh.vertices[polygon.vertices[i]].co for i in (0, 2, 3)] v1 = calculateTriangleVolume(vcs1, reference_point) v2 = calculateTriangleVolume(vcs2, reference_point) return v1 + v2 else: volume = 0.0 tris = ngon_tessellate(mesh, polygon.vertices) for tri in tris: volume += calculateTriangleVolume( [mesh.vertices[i].co for i in tri], reference_point) return volume
def _construct_indexing_data( self, mesh ): ''' _construct_indexing_data return two lists (v_indices, l_indices) that contain the triangle indices of the input mesh 'mesh'. These lists can be used to build output triangle data by referring to either vertex data (co, no, ...) or loop data (uv, col, ...) listed indices will be triads of consecutive tuples of form [ vertex_data_index_0, vertex_data_index_1, vertex_data_index_2, ... vertex_data_index_n+0, vertex_data_index_n+1, vertex_data_index_n+2 ] ''' v_indices = [] l_indices = [] p_indices = [] for polygon in mesh.polygons: triangle_loop_data = list( chain( *ngon_tessellate( mesh, polygon.vertices, True ) ) ) for loop_index in ( polygon.loop_indices[i] for i in triangle_loop_data ): p_indices.append( polygon.index ) v_indices.append( mesh.loops[ loop_index ].vertex_index ) l_indices.append( loop_index ) return p_indices, v_indices, l_indices
def calculateArea(mesh, matrix=None): """ Simply sum up faces areas. """ area = 0.0 if matrix is None: for polygon in mesh.polygons: area += polygon.area else: for polygon in mesh.polygons: if len(polygon.vertices) == 3: area += calculateTriangleArea(mesh, polygon.vertices, matrix) elif len(polygon.vertices) == 4: area += calculateTriangleArea(mesh, [polygon.vertices[i] for i in (0, 1, 2)], matrix) area += calculateTriangleArea(mesh, [polygon.vertices[i] for i in (0, 2, 3)], matrix) else: tris = ngon_tessellate(mesh, polygon.vertices) for tri in tris: area += calculateTriangleArea(mesh, tri, matrix) return area
def calculatePolygonCOM(mesh, polygon, reference_point): if len(polygon.vertices) == 3: vcs = [mesh.vertices[polygon.vertices[i]].co for i in (0, 1, 2)] return calculateTriangleCOM(vcs, reference_point) elif len(polygon.vertices) == 4: vcs1 = [mesh.vertices[polygon.vertices[i]].co for i in (0, 1, 2)] vcs2 = [mesh.vertices[polygon.vertices[i]].co for i in (0, 2, 3)] v1 = calculateTriangleVolume(vcs1, reference_point) v2 = calculateTriangleVolume(vcs2, reference_point) com1 = calculateTriangleCOM(vcs1, reference_point) com2 = calculateTriangleCOM(vcs2, reference_point) return (com1 * v1 + com2 * v2) / (v1 + v2) else: couples = [] tris = ngon_tessellate(mesh, polygon.vertices) for tri in tris: vs = [mesh.vertices[i].co for i in tri] couples.append((calculateTriangleCOM(vs, reference_point), calculateTriangleVolume(vs, reference_point))) return weightedMean(couples)
def calculatePolygonCOM(mesh, polygon, reference_point): if len(polygon.vertices) == 3: vcs = [mesh.vertices[polygon.vertices[i]].co for i in (0, 1, 2)] return calculateTriangleCOM(vcs, reference_point) elif len(polygon.vertices) == 4: vcs1 = [mesh.vertices[polygon.vertices[i]].co for i in (0, 1, 2)] vcs2 = [mesh.vertices[polygon.vertices[i]].co for i in (0, 2, 3)] v1 = calculateTriangleVolume(vcs1, reference_point) v2 = calculateTriangleVolume(vcs2, reference_point) com1 = calculateTriangleCOM(vcs1, reference_point) com2 = calculateTriangleCOM(vcs2, reference_point) return (com1*v1 + com2*v2)/(v1 + v2) else: couples = [] tris = ngon_tessellate(mesh, polygon.vertices) for tri in tris: vs = [mesh.vertices[i].co for i in tri] couples.append( (calculateTriangleCOM(vs, reference_point), calculateTriangleVolume(vs, reference_point))) return weightedMean(couples)
def create_mesh(new_objects, has_ngons, use_ngons, use_edges, verts_loc, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname, ): """ Takes all the data gathered and generates a mesh, adding the new object to new_objects deals with ngons, sharp edges and assigning materials """ from bpy_extras.mesh_utils import ngon_tessellate if not has_ngons: use_ngons = False if unique_smooth_groups: sharp_edges = {} smooth_group_users = {context_smooth_group: {} for context_smooth_group in list(unique_smooth_groups.keys())} context_smooth_group_old = -1 # Split ngons into tri's fgon_edges = set() # Used for storing fgon keys if use_edges: edges = [] context_object = None # reverse loop through face indices for f_idx in range(len(faces) - 1, -1, -1): (face_vert_loc_indices, face_vert_tex_indices, context_material, context_smooth_group, context_object, ) = faces[f_idx] len_face_vert_loc_indices = len(face_vert_loc_indices) if len_face_vert_loc_indices == 1: faces.pop(f_idx) # cant add single vert faces elif not face_vert_tex_indices or len_face_vert_loc_indices == 2: # faces that have no texture coords are lines if use_edges: # generators are better in python 2.4+ but can't be used in 2.3 # edges.extend( (face_vert_loc_indices[i], face_vert_loc_indices[i+1]) for i in xrange(len_face_vert_loc_indices-1) ) edges.extend([(face_vert_loc_indices[i], face_vert_loc_indices[i + 1]) for i in range(len_face_vert_loc_indices - 1)]) faces.pop(f_idx) else: # Smooth Group if unique_smooth_groups and context_smooth_group: # Is a part of of a smooth group and is a face if context_smooth_group_old is not context_smooth_group: edge_dict = smooth_group_users[context_smooth_group] context_smooth_group_old = context_smooth_group for i in range(len_face_vert_loc_indices): i1 = face_vert_loc_indices[i] i2 = face_vert_loc_indices[i - 1] if i1 > i2: i1, i2 = i2, i1 try: edge_dict[i1, i2] += 1 except KeyError: edge_dict[i1, i2] = 1 # NGons into triangles if has_ngons and len_face_vert_loc_indices > 4: ngon_face_indices = ngon_tessellate(verts_loc, face_vert_loc_indices) faces.extend([([face_vert_loc_indices[ngon[0]], face_vert_loc_indices[ngon[1]], face_vert_loc_indices[ngon[2]], ], [face_vert_tex_indices[ngon[0]], face_vert_tex_indices[ngon[1]], face_vert_tex_indices[ngon[2]], ], context_material, context_smooth_group, context_object, ) for ngon in ngon_face_indices] ) # edges to make ngons if use_ngons: edge_users = {} for ngon in ngon_face_indices: for i in (0, 1, 2): i1 = face_vert_loc_indices[ngon[i]] i2 = face_vert_loc_indices[ngon[i - 1]] if i1 > i2: i1, i2 = i2, i1 try: edge_users[i1, i2] += 1 except KeyError: edge_users[i1, i2] = 1 for key, users in edge_users.items(): if users > 1: fgon_edges.add(key) # remove all after 3, means we dont have to pop this one. faces.pop(f_idx) # Build sharp edges if unique_smooth_groups: for edge_dict in list(smooth_group_users.values()): for key, users in list(edge_dict.items()): if users == 1: # This edge is on the boundry of a group sharp_edges[key] = None # map the material names to an index material_mapping = {name: i for i, name in enumerate(unique_materials)} # enumerate over unique_materials keys() materials = [None] * len(unique_materials) for name, index in list(material_mapping.items()): materials[index] = unique_materials[name] me = bpy.data.meshes.new(dataname.decode('utf-8', "replace")) # make sure the list isnt too big for material in materials: me.materials.append(material) me.vertices.add(len(verts_loc)) me.tessfaces.add(len(faces)) # verts_loc is a list of (x, y, z) tuples me.vertices.foreach_set("co", unpack_list(verts_loc)) # faces is a list of (vert_indices, texco_indices, ...) tuples # XXX faces should contain either 3 or 4 verts # XXX no check for valid face indices me.tessfaces.foreach_set("vertices_raw", unpack_face_list([f[0] for f in faces])) if verts_tex and me.tessfaces: me.tessface_uv_textures.new() context_material_old = -1 # avoid a dict lookup mat = 0 # rare case it may be un-initialized. me_faces = me.tessfaces for i, face in enumerate(faces): if len(face[0]) < 2: pass # raise "bad face" elif len(face[0]) == 2: if use_edges: edges.append(face[0]) else: blender_face = me.tessfaces[i] (face_vert_loc_indices, face_vert_tex_indices, context_material, context_smooth_group, context_object, ) = face if context_smooth_group: blender_face.use_smooth = True if context_material: if context_material_old is not context_material: mat = material_mapping[context_material] context_material_old = context_material blender_face.material_index = mat # blender_face.mat= mat if verts_tex: blender_tface = me.tessface_uv_textures[0].data[i] if context_material: image = unique_material_images[context_material] if image: # Can be none if the material dosnt have an image. blender_tface.image = image # BUG - Evil eekadoodle problem where faces that have vert index 0 location at 3 or 4 are shuffled. if len(face_vert_loc_indices) == 4: if face_vert_loc_indices[2] == 0 or face_vert_loc_indices[3] == 0: face_vert_tex_indices = face_vert_tex_indices[2], face_vert_tex_indices[3], face_vert_tex_indices[0], face_vert_tex_indices[1] else: # length of 3 if face_vert_loc_indices[2] == 0: face_vert_tex_indices = face_vert_tex_indices[1], face_vert_tex_indices[2], face_vert_tex_indices[0] # END EEEKADOODLE FIX # assign material, uv's and image blender_tface.uv1 = verts_tex[face_vert_tex_indices[0]] blender_tface.uv2 = verts_tex[face_vert_tex_indices[1]] blender_tface.uv3 = verts_tex[face_vert_tex_indices[2]] if len(face_vert_loc_indices) == 4: blender_tface.uv4 = verts_tex[face_vert_tex_indices[3]] # for ii, uv in enumerate(blender_face.uv): # uv.x, uv.y= verts_tex[face_vert_tex_indices[ii]] del me_faces # del ALPHA if use_edges and not edges: use_edges = False if use_edges: me.edges.add(len(edges)) # edges should be a list of (a, b) tuples me.edges.foreach_set("vertices", unpack_list(edges)) # me_edges.extend( edges ) # del me_edges # Add edge faces. # me_edges= me.edges def edges_match(e1, e2): return (e1[0] == e2[0] and e1[1] == e2[1]) or (e1[0] == e2[1] and e1[1] == e2[0]) me.validate() me.update(calc_edges=use_edges) if unique_smooth_groups and sharp_edges: import bmesh bm = bmesh.new() bm.from_mesh(me) # to avoid slow iterator lookups later / indexing verts is slow in bmesh bm_verts = bm.verts[:] for sharp_edge in sharp_edges.keys(): vert1 = bm_verts[sharp_edge[0]] vert2 = bm_verts[sharp_edge[1]] if vert1 != vert2: edge = bm.edges.get((vert1, vert2)) if edge is not None: me.edges[edge.index].use_edge_sharp = True bm.free() del bm mesh_untessellate(me, fgon_edges) # XXX slow # if unique_smooth_groups and sharp_edges: # for sharp_edge in sharp_edges.keys(): # for ed in me.edges: # if edges_match(sharp_edge, ed.vertices): # ed.use_edge_sharp = True # if unique_smooth_groups and sharp_edges: # SHARP= Mesh.EdgeFlags.SHARP # for ed in me.findEdges( sharp_edges.keys() ): # if ed is not None: # me_edges[ed].flag |= SHARP # del SHARP ob = bpy.data.objects.new(me.name, me) new_objects.append(ob) # Create the vertex groups. No need to have the flag passed here since we test for the # content of the vertex_groups. If the user selects to NOT have vertex groups saved then # the following test will never run for group_name, group_indices in vertex_groups.items(): group = ob.vertex_groups.new(group_name.decode('utf-8', "replace")) group.add(group_indices, 1.0, 'REPLACE')
def tesselatePolyNgon(self, vecs, poly, fix): return (tuple(poly[i] for i in t) for t in ngon_tessellate(vecs, poly, fix_loops=fix))
def create_mesh(new_objects, use_edges, verts_loc, verts_nor, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname, ): """ Takes all the data gathered and generates a mesh, adding the new object to new_objects deals with ngons, sharp edges and assigning materials """ if unique_smooth_groups: sharp_edges = set() smooth_group_users = {context_smooth_group: {} for context_smooth_group in unique_smooth_groups.keys()} context_smooth_group_old = -1 fgon_edges = set() # Used for storing fgon keys when we need to tesselate/untesselate them (ngons with hole). edges = [] tot_loops = 0 context_object = None # reverse loop through face indices for f_idx in range(len(faces) - 1, -1, -1): (face_vert_loc_indices, face_vert_nor_indices, face_vert_tex_indices, context_material, context_smooth_group, context_object, face_invalid_blenpoly, ) = faces[f_idx] len_face_vert_loc_indices = len(face_vert_loc_indices) if len_face_vert_loc_indices == 1: faces.pop(f_idx) # cant add single vert faces # Face with a single item in face_vert_nor_indices is actually a polyline! elif len(face_vert_nor_indices) == 1 or len_face_vert_loc_indices == 2: if use_edges: edges.extend((face_vert_loc_indices[i], face_vert_loc_indices[i + 1]) for i in range(len_face_vert_loc_indices - 1)) faces.pop(f_idx) else: # Smooth Group if unique_smooth_groups and context_smooth_group: # Is a part of of a smooth group and is a face if context_smooth_group_old is not context_smooth_group: edge_dict = smooth_group_users[context_smooth_group] context_smooth_group_old = context_smooth_group prev_vidx = face_vert_loc_indices[-1] for vidx in face_vert_loc_indices: edge_key = (prev_vidx, vidx) if (prev_vidx < vidx) else (vidx, prev_vidx) prev_vidx = vidx edge_dict[edge_key] = edge_dict.get(edge_key, 0) + 1 # NGons into triangles if face_invalid_blenpoly: from bpy_extras.mesh_utils import ngon_tessellate ngon_face_indices = ngon_tessellate(verts_loc, face_vert_loc_indices) faces.extend([([face_vert_loc_indices[ngon[0]], face_vert_loc_indices[ngon[1]], face_vert_loc_indices[ngon[2]], ], [face_vert_nor_indices[ngon[0]], face_vert_nor_indices[ngon[1]], face_vert_nor_indices[ngon[2]], ] if face_vert_nor_indices else [], [face_vert_tex_indices[ngon[0]], face_vert_tex_indices[ngon[1]], face_vert_tex_indices[ngon[2]], ] if face_vert_tex_indices else [], context_material, context_smooth_group, context_object, [], ) for ngon in ngon_face_indices] ) tot_loops += 3 * len(ngon_face_indices) # edges to make ngons edge_users = set() for ngon in ngon_face_indices: prev_vidx = face_vert_loc_indices[ngon[-1]] for ngidx in ngon: vidx = face_vert_loc_indices[ngidx] if vidx == prev_vidx: continue # broken OBJ... Just skip. edge_key = (prev_vidx, vidx) if (prev_vidx < vidx) else (vidx, prev_vidx) prev_vidx = vidx if edge_key in edge_users: fgon_edges.add(edge_key) else: edge_users.add(edge_key) faces.pop(f_idx) else: tot_loops += len_face_vert_loc_indices # Build sharp edges if unique_smooth_groups: for edge_dict in smooth_group_users.values(): for key, users in edge_dict.items(): if users == 1: # This edge is on the boundry of a group sharp_edges.add(key) # map the material names to an index material_mapping = {name: i for i, name in enumerate(unique_materials)} # enumerate over unique_materials keys() materials = [None] * len(unique_materials) for name, index in material_mapping.items(): materials[index] = unique_materials[name] me = bpy.data.meshes.new(dataname) # make sure the list isnt too big for material in materials: me.materials.append(material) me.vertices.add(len(verts_loc)) me.loops.add(tot_loops) me.polygons.add(len(faces)) # verts_loc is a list of (x, y, z) tuples me.vertices.foreach_set("co", unpack_list(verts_loc)) loops_vert_idx = [] faces_loop_start = [] faces_loop_total = [] lidx = 0 for f in faces: vidx = f[0] nbr_vidx = len(vidx) loops_vert_idx.extend(vidx) faces_loop_start.append(lidx) faces_loop_total.append(nbr_vidx) lidx += nbr_vidx me.loops.foreach_set("vertex_index", loops_vert_idx) me.polygons.foreach_set("loop_start", faces_loop_start) me.polygons.foreach_set("loop_total", faces_loop_total) if verts_nor and me.loops: # Note: we store 'temp' normals in loops, since validate() may alter final mesh, # we can only set custom lnors *after* calling it. me.create_normals_split() if verts_tex and me.polygons: me.uv_textures.new() context_material_old = -1 # avoid a dict lookup mat = 0 # rare case it may be un-initialized. for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)): if len(face[0]) < 3: raise Exception("bad face") # Shall not happen, we got rid of those earlier! (face_vert_loc_indices, face_vert_nor_indices, face_vert_tex_indices, context_material, context_smooth_group, context_object, face_invalid_blenpoly, ) = face if context_smooth_group: blen_poly.use_smooth = True if context_material: if context_material_old is not context_material: mat = material_mapping[context_material] context_material_old = context_material blen_poly.material_index = mat if verts_nor and face_vert_nor_indices: for face_noidx, lidx in zip(face_vert_nor_indices, blen_poly.loop_indices): me.loops[lidx].normal[:] = verts_nor[0 if (face_noidx is ...) else face_noidx] if verts_tex and face_vert_tex_indices: if context_material: image = unique_material_images[context_material] if image: # Can be none if the material dosnt have an image. me.uv_textures[0].data[i].image = image blen_uvs = me.uv_layers[0] for face_uvidx, lidx in zip(face_vert_tex_indices, blen_poly.loop_indices): blen_uvs.data[lidx].uv = verts_tex[0 if (face_uvidx is ...) else face_uvidx] use_edges = use_edges and bool(edges) if use_edges: me.edges.add(len(edges)) # edges should be a list of (a, b) tuples me.edges.foreach_set("vertices", unpack_list(edges)) me.validate(clean_customdata=False) # *Very* important to not remove lnors here! me.update(calc_edges=use_edges) # Un-tessellate as much as possible, in case we had to triangulate some ngons... if fgon_edges: import bmesh bm = bmesh.new() bm.from_mesh(me) verts = bm.verts[:] get = bm.edges.get edges = [get((verts[vidx1], verts[vidx2])) for vidx1, vidx2 in fgon_edges] try: bmesh.ops.dissolve_edges(bm, edges=edges, use_verts=False) except: # Possible dissolve fails for some edges, but don't fail silently in case this is a real bug. import traceback traceback.print_exc() bm.to_mesh(me) bm.free() # XXX If validate changes the geometry, this is likely to be broken... if unique_smooth_groups and sharp_edges: for e in me.edges: if e.key in sharp_edges: e.use_edge_sharp = True me.show_edge_sharp = True if verts_nor: clnors = array.array('f', [0.0] * (len(me.loops) * 3)) me.loops.foreach_get("normal", clnors) if not unique_smooth_groups: me.polygons.foreach_set("use_smooth", [True] * len(me.polygons)) me.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) me.use_auto_smooth = True me.show_edge_sharp = True ob = bpy.data.objects.new(me.name, me) new_objects.append(ob) # Create the vertex groups. No need to have the flag passed here since we test for the # content of the vertex_groups. If the user selects to NOT have vertex groups saved then # the following test will never run for group_name, group_indices in vertex_groups.items(): group = ob.vertex_groups.new(group_name.decode('utf-8', "replace")) group.add(group_indices, 1.0, 'REPLACE')
def create_mesh(new_objects, use_edges, verts_loc, verts_nor, verts_tex, faces, unique_materials, unique_material_images, unique_smooth_groups, vertex_groups, dataname, ): """ Takes all the data gathered and generates a mesh, adding the new object to new_objects deals with ngons, sharp edges and assigning materials """ if unique_smooth_groups: sharp_edges = set() smooth_group_users = {context_smooth_group: {} for context_smooth_group in unique_smooth_groups.keys()} context_smooth_group_old = -1 fgon_edges = set() # Used for storing fgon keys when we need to tesselate/untesselate them (ngons with hole). edges = [] tot_loops = 0 context_object = None # reverse loop through face indices for f_idx in range(len(faces) - 1, -1, -1): (face_vert_loc_indices, face_vert_nor_indices, face_vert_tex_indices, context_material, context_smooth_group, context_object, face_invalid_blenpoly, ) = faces[f_idx] len_face_vert_loc_indices = len(face_vert_loc_indices) if len_face_vert_loc_indices == 1: faces.pop(f_idx) # cant add single vert faces elif not face_vert_tex_indices or len_face_vert_loc_indices == 2: # faces that have no texture coords are lines if use_edges: edges.extend((face_vert_loc_indices[i], face_vert_loc_indices[i + 1]) for i in range(len_face_vert_loc_indices - 1)) faces.pop(f_idx) else: # Smooth Group if unique_smooth_groups and context_smooth_group: # Is a part of of a smooth group and is a face if context_smooth_group_old is not context_smooth_group: edge_dict = smooth_group_users[context_smooth_group] context_smooth_group_old = context_smooth_group prev_vidx = face_vert_loc_indices[-1] for vidx in face_vert_loc_indices: edge_key = (prev_vidx, vidx) if (prev_vidx < vidx) else (vidx, prev_vidx) prev_vidx = vidx edge_dict[edge_key] = edge_dict.get(edge_key, 0) + 1 # NGons into triangles if face_invalid_blenpoly: from bpy_extras.mesh_utils import ngon_tessellate ngon_face_indices = ngon_tessellate(verts_loc, face_vert_loc_indices) faces.extend([([face_vert_loc_indices[ngon[0]], face_vert_loc_indices[ngon[1]], face_vert_loc_indices[ngon[2]], ], [face_vert_nor_indices[ngon[0]], face_vert_nor_indices[ngon[1]], face_vert_nor_indices[ngon[2]], ], [face_vert_tex_indices[ngon[0]], face_vert_tex_indices[ngon[1]], face_vert_tex_indices[ngon[2]], ], context_material, context_smooth_group, context_object, [], ) for ngon in ngon_face_indices] ) tot_loops += 3 * len(ngon_face_indices) # edges to make ngons edge_users = set() for ngon in ngon_face_indices: prev_vidx = face_vert_loc_indices[ngon[-1]] for ngidx in ngon: vidx = face_vert_loc_indices[ngidx] if vidx == prev_vidx: continue # broken OBJ... Just skip. edge_key = (prev_vidx, vidx) if (prev_vidx < vidx) else (vidx, prev_vidx) prev_vidx = vidx if edge_key in edge_users: fgon_edges.add(edge_key) else: edge_users.add(edge_key) faces.pop(f_idx) else: tot_loops += len_face_vert_loc_indices # Build sharp edges if unique_smooth_groups: for edge_dict in smooth_group_users.values(): for key, users in edge_dict.items(): if users == 1: # This edge is on the boundry of a group sharp_edges.add(key) # map the material names to an index material_mapping = {name: i for i, name in enumerate(unique_materials)} # enumerate over unique_materials keys() materials = [None] * len(unique_materials) for name, index in material_mapping.items(): materials[index] = unique_materials[name] me = bpy.data.meshes.new(dataname.decode('utf-8', "replace")) # make sure the list isnt too big for material in materials: me.materials.append(material) me.vertices.add(len(verts_loc)) me.loops.add(tot_loops) me.polygons.add(len(faces)) # verts_loc is a list of (x, y, z) tuples me.vertices.foreach_set("co", unpack_list(verts_loc)) loops_vert_idx = [] faces_loop_start = [] faces_loop_total = [] lidx = 0 for f in faces: vidx = f[0] nbr_vidx = len(vidx) loops_vert_idx.extend(vidx) faces_loop_start.append(lidx) faces_loop_total.append(nbr_vidx) lidx += nbr_vidx me.loops.foreach_set("vertex_index", loops_vert_idx) me.polygons.foreach_set("loop_start", faces_loop_start) me.polygons.foreach_set("loop_total", faces_loop_total) if verts_nor and me.loops: # Note: we store 'temp' normals in loops, since validate() may alter final mesh, # we can only set custom lnors *after* calling it. me.create_normals_split() if verts_tex and me.polygons: me.uv_textures.new() context_material_old = -1 # avoid a dict lookup mat = 0 # rare case it may be un-initialized. for i, (face, blen_poly) in enumerate(zip(faces, me.polygons)): if len(face[0]) < 3: raise Exception("bad face") # Shall not happen, we got rid of those earlier! (face_vert_loc_indices, face_vert_nor_indices, face_vert_tex_indices, context_material, context_smooth_group, context_object, face_invalid_blenpoly, ) = face if context_smooth_group: blen_poly.use_smooth = True if context_material: if context_material_old is not context_material: mat = material_mapping[context_material] context_material_old = context_material blen_poly.material_index = mat if verts_nor: for face_noidx, lidx in zip(face_vert_nor_indices, blen_poly.loop_indices): me.loops[lidx].normal[:] = verts_nor[face_noidx] if verts_tex: if context_material: image = unique_material_images[context_material] if image: # Can be none if the material dosnt have an image. me.uv_textures[0].data[i].image = image blen_uvs = me.uv_layers[0] for face_uvidx, lidx in zip(face_vert_tex_indices, blen_poly.loop_indices): blen_uvs.data[lidx].uv = verts_tex[face_uvidx] use_edges = use_edges and bool(edges) if use_edges: me.edges.add(len(edges)) # edges should be a list of (a, b) tuples me.edges.foreach_set("vertices", unpack_list(edges)) me.validate(clean_customdata=False) # *Very* important to not remove lnors here! me.update(calc_edges=use_edges) # Un-tessellate as much as possible, in case we had to triangulate some ngons... if fgon_edges: import bmesh bm = bmesh.new() bm.from_mesh(me) verts = bm.verts[:] get = bm.edges.get edges = [get((verts[vidx1], verts[vidx2])) for vidx1, vidx2 in fgon_edges] try: bmesh.ops.dissolve_edges(bm, edges=edges, use_verts=False) except: # Possible dissolve fails for some edges, but don't fail silently in case this is a real bug. import traceback traceback.print_exc() bm.to_mesh(me) bm.free() # XXX If validate changes the geometry, this is likely to be broken... if unique_smooth_groups and sharp_edges: for e in me.edges: if e.key in sharp_edges: e.use_edge_sharp = True me.show_edge_sharp = True if verts_nor: clnors = array.array('f', [0.0] * (len(me.loops) * 3)) me.loops.foreach_get("normal", clnors) if not unique_smooth_groups: me.polygons.foreach_set("use_smooth", [True] * len(me.polygons)) me.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) me.use_auto_smooth = True me.show_edge_sharp = True ob = bpy.data.objects.new(me.name, me) new_objects.append(ob) # Create the vertex groups. No need to have the flag passed here since we test for the # content of the vertex_groups. If the user selects to NOT have vertex groups saved then # the following test will never run for group_name, group_indices in vertex_groups.items(): group = ob.vertex_groups.new(group_name.decode('utf-8', "replace")) group.add(group_indices, 1.0, 'REPLACE')
def create_mesh(new_objects, use_edges, verts_loc, verts_nor, verts_tex, faces, unique_smooth_groups, vertex_groups, dataname, ): """ Takes all the data gathered and generates a mesh, adding the new object to new_objects deals with ngons, sharp edges and assigning materials """ if unique_smooth_groups: sharp_edges = set() smooth_group_users = {context_smooth_group: {} for context_smooth_group in unique_smooth_groups.keys()} context_smooth_group_old = -1 fgon_edges = set() # Used for storing fgon keys when we need to tessellate/untessellate them (ngons with hole). edges = [] tot_loops = 0 context_object_key = None # reverse loop through face indices for f_idx in range(len(faces) - 1, -1, -1): (face_vert_loc_indices, face_vert_nor_indices, face_vert_tex_indices, context_smooth_group, context_object_key, face_invalid_blenpoly, ) = faces[f_idx] len_face_vert_loc_indices = len(face_vert_loc_indices) if len_face_vert_loc_indices == 1: faces.pop(f_idx) # cant add single vert faces # Face with a single item in face_vert_nor_indices is actually a polyline! elif len(face_vert_nor_indices) == 1 or len_face_vert_loc_indices == 2: if use_edges: edges.extend((face_vert_loc_indices[i], face_vert_loc_indices[i + 1]) for i in range(len_face_vert_loc_indices - 1)) faces.pop(f_idx) else: # Smooth Group if unique_smooth_groups and context_smooth_group: # Is a part of of a smooth group and is a face if context_smooth_group_old is not context_smooth_group: edge_dict = smooth_group_users[context_smooth_group] context_smooth_group_old = context_smooth_group prev_vidx = face_vert_loc_indices[-1] for vidx in face_vert_loc_indices: edge_key = (prev_vidx, vidx) if (prev_vidx < vidx) else (vidx, prev_vidx) prev_vidx = vidx edge_dict[edge_key] = edge_dict.get(edge_key, 0) + 1 # NGons into triangles if face_invalid_blenpoly: # ignore triangles with invalid indices if len(face_vert_loc_indices) > 3: from bpy_extras.mesh_utils import ngon_tessellate ngon_face_indices = ngon_tessellate(verts_loc, face_vert_loc_indices, debug_print=bpy.app.debug) faces.extend([([face_vert_loc_indices[ngon[0]], face_vert_loc_indices[ngon[1]], face_vert_loc_indices[ngon[2]], ], [face_vert_nor_indices[ngon[0]], face_vert_nor_indices[ngon[1]], face_vert_nor_indices[ngon[2]], ] if face_vert_nor_indices else [], [face_vert_tex_indices[ngon[0]], face_vert_tex_indices[ngon[1]], face_vert_tex_indices[ngon[2]], ] if face_vert_tex_indices else [], context_smooth_group, context_object_key, [], ) for ngon in ngon_face_indices] ) tot_loops += 3 * len(ngon_face_indices) # edges to make ngons if len(ngon_face_indices) > 1: edge_users = set() for ngon in ngon_face_indices: prev_vidx = face_vert_loc_indices[ngon[-1]] for ngidx in ngon: vidx = face_vert_loc_indices[ngidx] if vidx == prev_vidx: continue #broken BRK... Just skip edge_key = (prev_vidx, vidx) if (prev_vidx < vidx) else (vidx, prev_vidx) prev_vidx = vidx if edge_key in edge_users: fgon_edges.add(edge_key) else: edge_users.add(edge_key) faces.pop(f_idx) else: tot_loops += len_face_vert_loc_indices #build sharp edges if unique_smooth_groups: for edge_dict in smooth_group_users.values(): for key, users in edge_dict.items(): if users == 1: #this edge is on the boundary of a group sharp_edges.add(key) me = bpy.data.meshes.new(dataname) me.vertices.add(len(verts_loc)) me.loops.add(tot_loops) me.polygons.add(len(faces)) #verts_loc is a list of (x, y, z) tuples me.vertices.foreach_set("co", unpack_list(verts_loc)) loops_vert_idx = tuple(vidx for (face_vert_loc_indices, _, _, _, _, _) in faces for vidx in face_vert_loc_indices) faces_loop_start = [] lidx = 0 for f in faces: face_vert_loc_indices = f[0] nbr_vidx = len(face_vert_loc_indices) faces_loop_start.append(lidx) lidx += nbr_vidx faces_loop_total = tuple(len(face_vert_loc_indices) for (face_vert_loc_indices, _, _, _, _, _) in faces) me.loops.foreach_set("vertex_index", loops_vert_idx) me.polygons.foreach_set("loop_start", faces_loop_start) me.polygons.foreach_set("loop_total", faces_loop_total) faces_use_smooth = tuple(bool(context_smooth_group) for (_, _, _, context_smooth_group, _, _) in faces) me.polygons.foreach_set("use_smooth", faces_use_smooth) if verts_nor and me.loops: #note: we store 'temp' normals in loops, since validate() may alter final mesh, we can only set custom lnors *after* calling it me.create_normals_split() loops_nor = tuple(no for (_, face_vert_nor_indices, _, _, _, _) in faces for face_noidx in face_vert_nor_indices for no in verts_nor[face_noidx]) me.loops.foreach_set("normal", loops_nor) if verts_tex and me.polygons: me.uv_layers.new(do_init=False) loops_uv = tuple(uv for (_, _, face_vert_tex_indices, _, _, _) in faces for face_uvidx in face_vert_tex_indices for uv in verts_tex[face_uvidx]) me.uv_layers[0].data.foreach_set("uv", loops_uv) use_edges = use_edges and bool(edges) if use_edges: me.edges.add(len(edges)) #edges should be a list of (a, b) tuples me.edges.foreach_set("vertices", unpack_list(edges)) me.validate(clean_customdata=False) # *Very* important to not remove lnors here! me.update(calc_edges=use_edges) #un-tessellate as much as possible, in case we had to triangulate some ngons... if fgon_edges: import bmesh bm = bmesh.new() bm.from_mesh(me) verts = bm.verts[:] get = bm.edges.get edges = [get((verts[vidx1], verts[vidx2])) for vidx1, vidx2 in fgon_edges] try: bmesh.ops.dissolve_edges(bm, edges=edges, use_verts=False) except: #possible dissolve fails for some edges, but don't fail silently in case this is a real bug import traceback traceback.print_exc() bm.to_mesh(me) bm.free() # XXX If validate changes the geometry, this is likely to be broken... if unique_smooth_groups and sharp_edges: for e in me.edges: if e.key in sharp_edges: e.use_edge_sharp = True if verts_nor: clnors = array.array('f', [0.0] * (len(me.loops) * 3)) me.loops.foreach_get("normal", clnors) if not unique_smooth_groups: me.polygons.foreach_set("use_smooth", [True] * len(me.polygons)) me.normals_split_custom_set(tuple(zip(*(iter(clnors),) * 3))) me.use_auto_smooth = True ob = bpy.data.objects.new(me.name, me) new_objects.append(ob) # Create the vertex groups. No need to have the flag passed here since we test for the # content of the vertex_groups. If the user selects to NOT have vertex groups saved then # the following test will never run for group_name, group_indices in vertex_groups.items(): group = ob.vertex_groups.new(name=group_name.decode('utf-8', "replace")) group.add(group_indices, 1.0, 'REPLACE')