def __init__(self, plane, terrain, DEBUG=False, LIMIT=None): self.time_it = BL_TOOLS.TimeIt() self.DEBUG=DEBUG self.LIMIT=LIMIT self.plane = plane self.terrain = terrain self.obj_s = bpy.data.objects[plane] self.mesh_s = self.obj_s.data self.obj_t = bpy.data.objects[terrain] r = BL_TOOLS.plane_to_vertex(self.plane, calc_centers=True) self.plane_points = r['points'] self.plane_edges = r['edges'] self.raycast_points = BL_TOOLS.get_raycast(self.plane, self.terrain, vertices = self.plane_points, DEBUG=self.DEBUG, LIMIT=self.LIMIT) self.bm = bmesh.new() self.bm.from_mesh(self.obj_t.data) self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() self.time_it.stop() print("[t] __init__(): ", self.time_it)
def do_the_raycast(self): r = BL_TOOLS.plane_to_vertex(self.plane, calc_centers=True) self.plane_points = r['points'] self.plane_edges = r['edges'] self.raycast_points = BL_TOOLS.get_raycast(self.plane, self.terrain, vertices=self.plane_points, DEBUG=self.DEBUG, LIMIT=self.LIMIT)
def delete_faces_from_plane(self): "this deletes the faces and the edges for a plane, and leaves only the verts, ready to work with raycast" DEBUG = False self.time_it = BL_TOOLS.TimeIt() bm = bmesh.new() bm.from_mesh(self.obj_s.data) bm.verts.ensure_lookup_table() bm.edges.ensure_lookup_table() bm.faces.ensure_lookup_table() bmesh.ops.delete(bm, geom=list(bm.verts) + list(bm.edges) + list(bm.faces), context='EDGES_FACES') bpy.context.view_layer.update() bm.calc_loop_triangles() bm.to_mesh(self.obj_s.data) self.obj_s.data.update() bm.free() self.time_it.stop() print("[t] add_geometry(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return (("INFO", "Done"))
def fill_holes(self): DEBUG = False self.time_it = BL_TOOLS.TimeIt() BL_DEBUG.clear_marks() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() boundary_edges = [edge for edge in self.bm.edges if edge.is_boundary] print("%d boundary edge founds" % len(boundary_edges)) self.update_bm() self.time_it.stop() print("[t] add_geometry(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def __init__(self, plane, terrain, DEBUG=False, LIMIT=None, delay_raycast=False): self.time_it = BL_TOOLS.TimeIt() self.DEBUG = DEBUG self.LIMIT = LIMIT self.plane = plane self.terrain = terrain self.obj_s = bpy.data.objects[plane] self.mesh_s = self.obj_s.data self.obj_t = bpy.data.objects[terrain] if not delay_raycast: self.do_the_raycast() self.time_it.stop() print("[t] __init__(): ", self.time_it)
def do_holes(self): "get the plane, generate the info to raycast in the grid, select the matching faces" self.time_it = BL_TOOLS.TimeIt() EXTEND_SELECTION = True TRIANGULATE_FACES = False # True SUBDIVIDE_INNER_FACES = False EXTEND_DELETE_SELECTION = False SUBDIVIDE_INNER_EDGES = True CALCULATE_NEAR_FACES = True MIN_FACE_DIST = 3 BL_DEBUG.clear_marks() # at this point XD point_data has # terrain_up/down, face_index, point, location) self.unselect_all() self.selected_faces = [] # select faces tracked for point in self.raycast_points: terrain_down, index, point, location = point self.bm.faces[index].select = True if self.bm.faces[index] not in self.selected_faces: self.selected_faces.append(self.bm.faces[index]) if EXTEND_SELECTION: ret_geom = bmesh.ops.region_extend(self.bm, geom=self.selected_faces, use_faces=True, use_face_step=False, use_contract=False) for f in ret_geom['geom']: if isinstance(f, bmesh.types.BMFace): f.select = True self.selected_faces.append(f) if SUBDIVIDE_INNER_FACES: # subdivide the inner faces #el_faces = [f for f in self.bm.faces if f.select] face_edges = [] for f in self.selected_faces: for e in f.edges: face_edges.append(e.index) face_edges = list(set(face_edges)) face_edges_geom = [self.bm.edges[i] for i in face_edges] ret = bmesh.ops.subdivide_edges(self.bm, edges=face_edges_geom, cuts=2, use_grid_fill=True) for f in ret['geom']: if isinstance(f, bmesh.types.BMFace): f.select = True self.selected_faces.append(f) if TRIANGULATE_FACES: #triangulate geom = [f for f in self.bm.faces if f.select] ret_trig = bmesh.ops.triangulate(self.bm, faces=geom) # select the new created triangles for f in ret_trig['faces']: f.select = True self.selected_faces.append(f) # 0. at this point, faces are triangulated, and selected ############################################################## # # 1. delete the faces recalculate again the faces # this version only deletes the nearest faces from the # road. Maybe it's a little narrow # ############################################################## self.update_bm() #self.bm.verts.ensure_lookup_table() #self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # # I have to CHANGE to OBJECT in order to keep working on that # and update the structure. After change, return to edit and # keep working, it works. # # after triangulate, or adding more faces, there's a need of recalculate again the affected # faces to make the hole self.raycast_points = BL_TOOLS.get_raycast(self.plane, self.terrain, vertices = self.plane_points, DEBUG=self.DEBUG, LIMIT=self.LIMIT) # at this point XD point_data has # terrain_up/down, face_index, point, location) BL_DEBUG.clear_marks() faces_to_delete = {} for item in self.raycast_points: terrain_down, index, point, location = item #print(terrain_down,index, point, location) #BL_DEBUG.set_mark( obj_t.matrix_world @ location.xyz, kind="PLAIN_AXES" ) if index not in faces_to_delete.keys(): faces_to_delete[index] = self.bm.faces[index] if EXTEND_DELETE_SELECTION: for face in self.bm.faces: face.select = False for f in faces_to_delete.keys(): self.bm.faces[f].select = True #sel_verts = [v for v in self.bm.verts if v.select] #sel_edges = [e for e in self.bm.edges if e.select] selected_faces_local = [f for f in self.bm.faces if f.select] geom = selected_faces_local ret_geom = bmesh.ops.region_extend(self.bm, geom=selected_faces_local, use_faces=True, use_face_step=False, use_contract=False) for f in ret_geom['geom']: if isinstance(f, bmesh.types.BMFace): faces_to_delete[f.index] = self.bm.faces[f.index] # delete the result faces faces_to_delete = list(faces_to_delete.values()) deleted_geom_edges = [] for f in faces_to_delete: deleted_geom_edges += [e for e in f.edges] bmesh.ops.delete(self.bm, geom=faces_to_delete, context='FACES_KEEP_BOUNDARY') # FACES_ONLY # remove deleted faces from the selected container #temp_faces = [f for f in self.selected_faces if f.is_valid] #self.selected_faces = temp_faces # this creates a subdivision of the inner vertex. if SUBDIVIDE_INNER_EDGES: # subdivide existing edges keep_edges = [] for edge in deleted_geom_edges: if edge.is_valid: keep_edges.append(edge) self.inner_edges = [] print("Inner edges: %d" % (len(keep_edges)*2)) geom_created = bmesh.ops.subdivide_edges(self.bm, edges=keep_edges, cuts=1, use_grid_fill=True) for item in geom_created['geom_split']: if isinstance(item, bmesh.types.BMVert): pass #item.select = True #print(item.index) #self.inner_edges.append(item) if isinstance(item, bmesh.types.BMEdge): #item.select = True #print(item.index) self.inner_edges.append(item) if isinstance(item, bmesh.types.BMFace): #item.select = True print(item.index) item.select = True self.selected_faces.append(item) print("Inner edges Subdivide: %d" % (len(self.inner_edges)*2) ) self.update_bm() # create a KD tree for faces. add the center, and get only faces that are "near" in radious if CALCULATE_NEAR_FACES: valid_faces = [face for face in self.selected_faces if face.is_valid] kd = kdtree.KDTree(len(valid_faces)) for face in valid_faces: kd.insert(face.calc_center_median(), face.index) kd.balance() faces_to_delete = {} verts_to_delete = {} edges_to_delete = {} self.bm.faces.ensure_lookup_table() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() for item in self.raycast_points: terrain_down, index, point, location = item local_faces = [] local_point = self.plane_to_terrain_point(point.co) for (co, index, dist) in kd.find_range(local_point, 200): #print("face_local", index) local_faces.append(self.bm.faces[index]) for f in local_faces: # check for deleted faces! if f.is_valid and f.index not in faces_to_delete.keys(): #center = BL_FLATTEN.DummyVector(f.calc_center_median()) for fv in f.verts: #for fv in [center]: vpw = self.obj_s.matrix_world @ point.co vtw = self.obj_t.matrix_world @ fv.co dist = (vpw.xy - vtw.xy).length if dist < MIN_FACE_DIST: #print("Face too near: ",f.index,dist) faces_to_delete[f.index] = self.bm.faces[f.index] for e in f.edges: edges_to_delete[e.index] = self.bm.edges[e.index] for v in f.verts: verts_to_delete[v.index] = self.bm.verts[v.index] faces_to_delete = list(faces_to_delete.values()) verts_to_delete = list(verts_to_delete.values()) edges_to_delete = list(edges_to_delete.values()) # FACES_ONLY bmesh.ops.delete(self.bm, geom=verts_to_delete + edges_to_delete + faces_to_delete, context='FACES') self.update_bm() # now, just select the faces that are not select, but are in the edges. # also select all the vertex in the keep edges that are not selected by default. for e in self.inner_edges: if not e.is_valid: continue for v in e.verts: if not v.is_valid: continue v.select = True for f in v.link_faces: f.select = True if f not in self.selected_faces: self.selected_faces.append(f) # select vertex that are in a boundary (internal) self.selected_vertex = [] for e in self.bm.edges: if e.is_valid and e.is_boundary: for v in e.verts: self.selected_vertex.append(v) ############################################################## # # End # update the meshes # ############################################################## self.time_it.stop() self.update_bm() print("[t] do_holes(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def add_geometry_edge(self): "try to match near edges" def edge_median(edge): v0,v1 = edge.verts median = v0.co + ((v0.co-v1.co)/2.0) return(median) def nearest_points(kd, edge, v0, side, Nitems=7): nearest = [] for (co, index, dist) in kd.find_n(v0, Nitems): # calculate normals A = self.obj_t.matrix_world @ edge[0].co B = self.obj_t.matrix_world @ edge[1].co C = self.obj_t.matrix_world @ co normal_vec = Vector(B-A).cross(Vector(C-A)) normal_rl = normal_vec.z if normal_rl < 0.0 and side.upper() == 'RIGHT': nearest.append( (co, index, dist) ) if normal_rl > 0.0 and side.upper() == 'LEFT': nearest.append( (co, index, dist) ) return(nearest) def nearest_edge(kd, edge, side, Nitems=7): nearest = [] median = edge_median(edge) for (co, index, dist) in kd.find_n(self.obj_t.matrix_world @ median, Nitems): # calculate normals A = self.obj_t.matrix_world @ edge.verts[0].co B = self.obj_t.matrix_world @ edge.verts[1].co C = self.obj_t.matrix_world @ co normal_vec = Vector(B-A).cross(Vector(C-A)) normal_rl = normal_vec.z if normal_rl < 0.0 and side.upper() == 'RIGHT': nearest.append( (co, index, dist) ) if normal_rl > 0.0 and side.upper() == 'LEFT': nearest.append( (co, index, dist) ) return(nearest) DEBUG = False self.time_it = BL_TOOLS.TimeIt() BL_DEBUG.clear_marks() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # create a kd tree with the median edge positions. median_edges = [] for edge in self.inner_edges: if edge.is_valid and edge.is_boundary: median = edge_median(edge) median_edges.append( (median, edge.index) ) kd = kdtree.KDTree(len(median_edges)) for median,idx in median_edges: kd.insert(median, idx) kd.balance() # iterate my vertex, and find the nearest point my vertex side_keys = { 0: 'right', 1: 'left', 'right': 0, 'left': 1} count = 0 for plane in self.mesh_vertex: print("plane", plane) nearest = {} # calculate the nearest point for each point, for each side for i in range(len(plane)): if i == 0: #right ARROWS->CUBES kind = 'ARROWS' else: #left AXES->SPHERES kind = 'PLAIN_AXES' #nearest[side_keys[i]] = [ # nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][0].co, side_keys[i]), # nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][1].co, side_keys[i]) #] # create the working edge edge = [ plane[i][0], plane[i][1] ] ret_geom = bmesh.ops.contextual_create(self.bm, geom= edge) self.bm.edges.index_update() self.update_bm() for f in ret_geom['edges']: if isinstance(f, bmesh.types.BMEdge): edge = f median = edge_median(edge) BL_DEBUG.set_mark(self.obj_t.matrix_world @ median,kind='CUBE') ## mesh edge n_edge = nearest_edge(kd, edge, side_keys[i]) # build the face, rebuild the tree co,idx,_ = n_edge[0] self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() BL_DEBUG.set_mark(self.obj_t.matrix_world @ co,kind='SPHERE') ## found edge #ret_geom = bmesh.ops.contextual_create(self.bm, geom= [edge, self.bm.edges[idx]]) self.bm.edges.index_update() self.update_bm() # remove the edge from the table. edges_tmp = [] for edge in self.inner_edges: if edge.is_valid and edge.index != idx: edges_tmp.append(edge) self.inner_edges = edges_tmp median_edges = [] for edge in self.inner_edges: if edge.is_valid and edge.is_boundary: median = edge_median(edge) median_edges.append( (median, edge.index) ) kd = kdtree.KDTree(len(median_edges)) for median,idx in median_edges: kd.insert(median, idx) kd.balance() # do just ones count +=1 if count >=1: break # update the index of the new created verts. WTF ??? self.update_bm() self.time_it.stop() print("[t] add_geometry_edges(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def add_geometry(self): DEBUG = False self.time_it = BL_TOOLS.TimeIt() BL_DEBUG.clear_marks() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # create a kd tree with the vertex positions vertex = [] for v in self.selected_vertex: if v.is_valid: vertex.append(v.index) # remove dupes vertex = list(set(vertex)) kd = kdtree.KDTree(len(vertex)) for v in vertex: ##BL_DEBUG.set_mark(self.bm.verts[v].co,kind="CUBE",scale=0.2) kd.insert(self.bm.verts[v].co, v) kd.balance() def nearest_points(kd, edge, v0, side, Nitems=7): nearest = [] for (co, index, dist) in kd.find_n(v0, Nitems): # calculate normals A = self.obj_t.matrix_world @ edge[0].co B = self.obj_t.matrix_world @ edge[1].co C = self.obj_t.matrix_world @ co normal_vec = Vector(B-A).cross(Vector(C-A)) normal_rl = normal_vec.z if normal_rl < 0.0 and side.upper() == 'RIGHT': nearest.append( (co, index, dist) ) if normal_rl > 0.0 and side.upper() == 'LEFT': nearest.append( (co, index, dist) ) return(nearest) # iterate my vertex, and find the nearest point my vertex side_keys = { 0: 'right', 1: 'left', 'right': 0, 'left': 1} count = 0 B = C = None for plane in self.mesh_vertex: print(plane) nearest = {} # calculate the nearest point for each point, for each side for i in range(len(plane)): if i == 0: #right ARROWS->CUBES kind = 'ARROWS' else: #left AXES->SPHERES kind = 'PLAIN_AXES' #BL_DEBUG.set_mark(self.obj_t.matrix_world @ plane[i][0].co,kind=kind) #BL_DEBUG.set_mark(self.obj_t.matrix_world @ plane[i][1].co,kind=kind) nearest[side_keys[i]] = [ nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][0].co, side_keys[i]), nearest_points(kd, plane[i], self.obj_t.matrix_world @ plane[i][1].co, side_keys[i]) ] A = plane[0][1] D = plane[0][0] # lower point if not C: _,C,_ = nearest['right'][0][0] _,B,_ = nearest['right'][1][0] if B == C: _,B,_ = nearest['right'][1][1] else: C = B _,B,_ = nearest['right'][1][0] if B == C: _,B,_ = nearest['right'][1][1] print(A,B,C,D) # if not B in used_points.keys(): used_points[B] = 1 # if not C in used_points.keys(): used_points[C] = 1 # if B in used_points.keys() and used_points[B] < 2: # used_points[B] += 1 # else: # B = None # if C in used_points.keys() and used_points[C] < 2: # used_points[C] += 1 # else: # C = None # first point: nearest from top, and in nearest of bottom one. a = nearest['right'][0] print("r0-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="ARROWS") a = nearest['right'][1] print("r1-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="ARROWS") a = nearest['left'][0] print("l0-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="PLAIN_AXES") a = nearest['left'][1] print("l1-") for i in a: co,idx,dist = i print(co,idx,dist) DEBUG and BL_DEBUG.set_mark(co,kind="PLAIN_AXES") ## right side # top_vertex_list = [] # for _,idx,_ in nearest['right'][1]: # top_vertex_list.append(idx) # bottom_vertex_list = [] # for _,idx,_ in nearest['right'][0]: # bottom_vertex_list.append(idx) # # default, first item # # bottom # pB = None # get_next = False # _,pA,_ = nearest['right'][0][0] # for data in nearest['right'][0]: # co,idx,dist = data # if get_next: # pA = idx # break # if idx in top_vertex_list and not pB: # pB = pA # get_next = True # if not pB: # _,pB,_ = nearest['right'][1][0] # for data in nearest['right'][1]: # co,idx,dist = data # pB = idx # break if B and C: pBv = self.bm.verts[B] pCv = self.bm.verts[C] DEBUG and BL_DEBUG.set_mark(pBv.co,kind="CUBE") DEBUG and BL_DEBUG.set_mark(pCv.co,kind="CUBE") bmesh.ops.contextual_create(self.bm, geom=[A, pBv, pCv, D]) self.bm.faces.index_update() # ## left side # top_vertex_list = [] # for i in nearest['left'][1]: # co,idx,dist = i # top_vertex_list.append(idx) # bottom_vertex_list = [] # for i in nearest['left'][0]: # co,idx,dist = i # bottom_vertex_list.append(idx) # # default, first item # # bottom # pD = None # get_next = False # _,pC,_ = nearest['left'][0][0] # for data in nearest['left'][0]: # co,idx,dist = data # if get_next: # pC = idx # break # if idx in top_vertex_list and not pD: # pD = pC # get_next = True # if not pD: # _,pD,_ = nearest['left'][1][0] # for data in nearest['left'][1]: # co,idx,dist = data # pD = idx # break # pCv = self.bm.verts[pC] # pDv = self.bm.verts[pD] # DEBUG and BL_DEBUG.set_mark(pCv.co,kind="SPHERE") # DEBUG and BL_DEBUG.set_mark(pDv.co,kind="SPHERE") # if plane[0][1] != pBv and pBv != pAv and pAv != plane[0][0]: # bmesh.ops.contextual_create(self.bm, geom=[plane[0][0], pAv, pBv, plane[0][1]]) # self.bm.faces.index_update() # if plane[1][1] != pCv and pCv != pDv and pDv != plane[1][0]: # bmesh.ops.contextual_create(self.bm, geom=[plane[1][1], plane[1][0], pDv, pCv]) # self.bm.faces.index_update() # do just ones count +=1 if count > 5: break # update the index of the new created verts. WTF ??? self.update_bm() self.time_it.stop() print("[t] add_geometry(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))
def project_vertex(self): self.time_it = BL_TOOLS.TimeIt() BL_DEBUG.clear_marks() self.bm.verts.ensure_lookup_table() self.bm.edges.ensure_lookup_table() self.bm.faces.ensure_lookup_table() # here, I can flatten or move the thing #for f in bm.faces: # if f.select: # for v in f.verts: # bm.verts[v.index].co[2] += 5 self.mesh_vertex = [] verts = [] pc = 0 for edge in self.plane_edges: e_1 = edge[0] e_2 = edge[1] #verts += [v.index for v in e_1] + [v.index for v in e_2] #print(e_1,e_2) mesh_e1 = [] for e in e_1: local_point = self.plane_to_terrain_point(e.co) #set_mark( obj_s.matrix_world @ obj_s.data.vertices[v_idx].co, kind='PLAIN_AXES', scale=0.2 ) #set_mark( obj_t.matrix_world @ local_point, scale=0.2 ) vnew = self.bm.verts.new( local_point ) mesh_e1.append(vnew) mesh_e2 = [] for e in e_2: local_point = self.plane_to_terrain_point(e.co) #set_mark( obj_s.matrix_world @ obj_s.data.vertices[v_idx].co, kind='PLAIN_AXES', scale=0.2 ) #set_mark( obj_t.matrix_world @ local_point, scale=0.2 ) vnew = self.bm.verts.new( local_point ) mesh_e2.append(vnew) self.mesh_vertex.append( [ mesh_e1, mesh_e2 ] ) if pc == 0 or pc == len(self.plane_edges)-1: if pc == 0: # use first edge_cap= [ mesh_e1[0], mesh_e2[0] ] # edge from right to left else: # use last edge_cap= [ mesh_e1[1], mesh_e2[1] ] # edge from right to left bmesh.ops.contextual_create(self.bm, geom= edge_cap) self.bm.edges.index_update() pc+=1 # update the index of the new created verts. WTF ??? self.update_bm() self.time_it.stop() print("[t] add_geometry(): ", self.time_it) #return(("ERROR", "Can't found terrain below the curve")) return(("INFO", "Done"))