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 := base_index + 0, base_index + 1, 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 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, ok: bool, ) #optional_ok { if edge.twin < 0 { return {}, false } twin := mesh.edges[edge.twin] face = mesh.faces[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 } iterate_next_edge :: proc(it: ^Edge_Iterator) -> (edge: Half_Edge, ok: bool) { if it.current_edge == it.first_edge { if !it.past_first { it.past_first = true } else { return {}, false } } edge = it.mesh.edges[it.current_edge] ok = true it.current_edge = edge.next return }