package halfedge import "core:hash/xxhash" import lg "core:math/linalg" import "core:mem" import "core:slice" import "libs:tracy" 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, extent: 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 { tracy.Zone() 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 min_pos, max_pos: Vec3 = max(f32), min(f32) for pos, i in vertices { verts[i].pos = pos verts[i].edge = -1 min_pos = lg.min(pos, min_pos) max_pos = lg.max(pos, max_pos) } if len(vertices) == 0 { min_pos, max_pos = 0, 0 } mesh.center = (max_pos + min_pos) * 0.5 mesh.extent = (max_pos - min_pos) * 0.5 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 :: #force_inline 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 :: #force_inline proc(mesh: Half_Edge_Mesh, edge: Half_Edge) -> (dir: Vec3) { a, b := get_edge_points(mesh, edge) return b - a } get_edge_direction_normalized :: #force_inline 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 } Face_Triangles_Iterator :: struct { edge_it: Edge_Iterator, first_vert: Vec3, idx: int, } iterator_face_triangles :: proc( mesh: Half_Edge_Mesh, face: Face_Index, ) -> Face_Triangles_Iterator { edge_it := iterator_face_edges(mesh, face) edge, _, ok := iterate_next_edge(&edge_it) assert(ok) first_vert := mesh.vertices[edge.origin].pos return Face_Triangles_Iterator{edge_it = edge_it, first_vert = first_vert} } Tri :: [3]Vec3 iterate_next_triangle :: proc(it: ^Face_Triangles_Iterator) -> (tri: Tri, tri_idx: int, ok: bool) { edge, _, has_next_edge := iterate_next_edge(&it.edge_it) tri_idx = it.idx if has_next_edge { p1, p2 := get_edge_points(it.edge_it.mesh, edge) it.idx += 1 return Tri{it.first_vert, p1, p2}, tri_idx, true } else { return {}, tri_idx, false } } 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) { tracy.Zone() 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 original_extent := mesh.extent mesh.extent = lg.abs(original_extent.xxx * mat[0].xyz) mesh.extent += lg.abs(original_extent.yyy * mat[1].xyz) mesh.extent += lg.abs(original_extent.zzz * mat[2].xyz) 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 } hash :: proc(mesh: Half_Edge_Mesh) -> u32 { state: xxhash.XXH32_state xxhash.XXH32_reset_state(&state) _ = xxhash.XXH32_update(&state, mem.any_to_bytes(mesh.center)) _ = xxhash.XXH32_update(&state, slice.to_bytes(mesh.vertices)) _ = xxhash.XXH32_update(&state, slice.to_bytes(mesh.faces)) _ = xxhash.XXH32_update(&state, slice.to_bytes(mesh.edges)) return xxhash.XXH32_digest(&state) }