package halfedge import lg "core:math/linalg" Vec3 :: [3]f32 Vertex :: struct { pos: Vec3, edge: Edge_Index, } Face :: struct { edge: Edge_Index, normal: Vec3, } Vertex_Index :: distinct u16 Face_Index :: distinct u16 Edge_Index :: distinct i16 Half_Edge :: struct { origin: Vertex_Index, twin: Edge_Index, face: Face_Index, next: Edge_Index, prev: Edge_Index, } Half_Edge_Mesh :: struct { center: Vec3, vertices: []Vertex, faces: []Face, edges: []Half_Edge, } mesh_from_vertex_index_list :: proc( vertices: []Vec3, indices: []u16, vertices_per_face: int = 3, allocator := context.allocator, ) -> Half_Edge_Mesh { assert(vertices_per_face >= 3) num_faces := len(indices) / vertices_per_face mesh: Half_Edge_Mesh verts := make([]Vertex, len(vertices), allocator) faces := make([]Face, num_faces, allocator) edges := make([]Half_Edge, len(indices), allocator) mesh.vertices = verts mesh.faces = faces mesh.edges = edges mesh_center_avg_factor := 1.0 / f32(len(vertices)) for pos, i in vertices { verts[i].pos = pos verts[i].edge = -1 mesh.center += pos * mesh_center_avg_factor } temp_edges: map[[2]u16]Edge_Index = make_map(map[[2]u16]Edge_Index, context.temp_allocator) for f in 0 ..< num_faces { base_index := f * vertices_per_face i1, i2, i3 := indices[base_index + 0], indices[base_index + 1], indices[base_index + 2] v1, v2, v3 := vertices[i1], vertices[i2], vertices[i3] // Assuming ccw winding normal := lg.normalize0(lg.cross(v2 - v1, v3 - v1)) dist := lg.dot(v1, normal) faces[f].normal = normal for i in 0 ..< vertices_per_face { e := base_index + i index := indices[e] // check that point lies on the same plane if i > 2 { point_dist := lg.dot(normal, vertices[index]) assert(abs(dist - point_dist) < 0.00001) } if verts[index].edge == -1 { verts[index].edge = Edge_Index(e) } if i == 0 { faces[f].edge = Edge_Index(e) } next_edge := f * vertices_per_face + ((i + 1) % vertices_per_face) prev_edge := f * vertices_per_face + ((i - 1) % vertices_per_face) edges[e] = { origin = Vertex_Index(index), twin = -1, face = Face_Index(f), next = Edge_Index(next_edge), prev = Edge_Index(prev_edge), } next_index := indices[next_edge] stable_idx := [2]u16{min(index, next_index), max(index, next_index)} twin, ok := temp_edges[stable_idx] if ok { edges[e].twin = twin edges[twin].twin = Edge_Index(e) } else { temp_edges[stable_idx] = Edge_Index(e) } } } return mesh } get_edge_points :: proc(mesh: Half_Edge_Mesh, edge: Half_Edge) -> (a: Vec3, b: Vec3) { a = mesh.vertices[edge.origin].pos b = mesh.vertices[mesh.edges[edge.next].origin].pos return } get_edge_direction_normalized :: proc(mesh: Half_Edge_Mesh, edge: Half_Edge) -> (dir: Vec3) { a, b := get_edge_points(mesh, edge) return lg.normalize0(b - a) } get_adjacent_face :: proc( mesh: Half_Edge_Mesh, edge: Half_Edge, ) -> ( face: Face, face_idx: Face_Index, ok: bool, ) { if edge.twin < 0 { return {}, 0, false } twin := mesh.edges[edge.twin] face = mesh.faces[twin.face] face_idx = twin.face ok = true return } Edge_Iterator :: struct { mesh: Half_Edge_Mesh, first_edge: Edge_Index, current_edge: Edge_Index, past_first: bool, } iterator_face_edges :: proc(mesh: Half_Edge_Mesh, face: Face_Index) -> (it: Edge_Iterator) { it.mesh = mesh it.first_edge = mesh.faces[face].edge it.current_edge = it.first_edge return } iterator_reset_edges :: proc(it: ^Edge_Iterator) { it.current_edge = it.first_edge it.past_first = false } iterate_next_edge :: proc( it: ^Edge_Iterator, ) -> ( edge: Half_Edge, edge_idx: Edge_Index, ok: bool, ) { if it.current_edge == it.first_edge { if !it.past_first { it.past_first = true } else { return {}, -1, false } } edge = it.mesh.edges[it.current_edge] edge_idx = it.current_edge ok = true it.current_edge = edge.next return } Vec4 :: [4]f32 copy_mesh :: proc( mesh: Half_Edge_Mesh, allocator := context.allocator, ) -> ( result: Half_Edge_Mesh, ) { result.center = mesh.center result.vertices = make([]Vertex, len(mesh.vertices), allocator) result.faces = make([]Face, len(mesh.faces), allocator) result.edges = make([]Half_Edge, len(mesh.edges), allocator) copy(result.vertices, mesh.vertices) copy(result.faces, mesh.faces) copy(result.edges, mesh.edges) return } transform_mesh :: proc(mesh: ^Half_Edge_Mesh, mat: lg.Matrix4f32) { mesh_center_avg_factor := 1.0 / f32(len(mesh.vertices)) new_center: Vec3 for i in 0 ..< len(mesh.vertices) { vert := &mesh.vertices[i] p := vert.pos vert.pos = (mat * Vec4{p.x, p.y, p.z, 1}).xyz new_center += vert.pos * mesh_center_avg_factor } mesh.center = new_center for i in 0 ..< len(mesh.faces) { n := &mesh.faces[i].normal mesh.faces[i].normal = lg.normalize0((mat * Vec4{n.x, n.y, n.z, 0}).xyz) } } get_face_centroid :: proc(mesh: Half_Edge_Mesh, face_idx: Face_Index) -> Vec3 { center: Vec3 it := iterator_face_edges(mesh, face_idx) num_verts := 0 for edge in iterate_next_edge(&it) { num_verts += 1 center += mesh.vertices[edge.origin].pos } center /= f32(num_verts) return center } Vertex_Edge_Iterator :: struct { mesh: Half_Edge_Mesh, vert: Vertex_Index, first_edge_idx: Edge_Index, edge_idx: Edge_Index, iteration: i32, } // Iterates over all edges that have vert_idx as the origin iterator_vertex_edges :: proc( mesh: Half_Edge_Mesh, vert_idx: Vertex_Index, ) -> ( it: Vertex_Edge_Iterator, ) { it.mesh = mesh it.vert = vert_idx it.first_edge_idx = mesh.vertices[vert_idx].edge it.edge_idx = it.first_edge_idx return } iterate_next_vertex_edge :: proc( it: ^Vertex_Edge_Iterator, ) -> ( edge: Half_Edge, edge_idx: Edge_Index, ok: bool, ) { if it.edge_idx != -1 && (it.edge_idx != it.first_edge_idx || it.iteration == 0) { edge = it.mesh.edges[it.edge_idx] edge_idx = it.edge_idx ok = true twin := edge.twin if twin >= 0 { it.edge_idx = it.mesh.edges[twin].next } else { it.edge_idx = -1 } it.iteration += 1 } else { edge_idx = -1 } return }