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