gutter_runner/game/halfedge/halfedge.odin

338 lines
7.1 KiB
Odin

package halfedge
import "core:hash/xxhash"
import lg "core:math/linalg"
import "core:mem"
import "core:slice"
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 {
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
}
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
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)
}