this repo has no description
at 99607ee3976658e1d00a96ec22bd6d7fc0b7e4b6 184 lines 5.1 kB view raw
1const std = @import("std"); 2 3const AABB = @import("AABB.zig"); 4const hittable = @import("hittable.zig"); 5const Hittable = hittable.Hittable; 6const HitRecord = hittable.HitRecord; 7const IntervalF32 = @import("interval.zig").IntervalF32; 8const Ray = @import("Ray.zig"); 9const util = @import("util.zig"); 10 11const log = std.log.scoped(.BVH); 12 13pub const BVH = @This(); 14 15const Ast = struct { 16 left: ?*Node = null, 17 right: ?*Node = null, 18 bbox: AABB = AABB{}, 19 20 pub fn hit(self: *Ast, r: *Ray, ray_t: IntervalF32) ?HitRecord { 21 // if (!self.bbox.hit(r, ray_t)) return null; 22 23 var rec: ?HitRecord = null; 24 var interval = ray_t; 25 if (self.left) |left| { 26 if (left.hit(r, interval)) |res| { 27 interval = IntervalF32.init(ray_t.min, res.t); 28 rec = res; 29 } 30 } 31 32 if (self.right) |right| { 33 if (right.hit(r, interval)) |res| { 34 return res; 35 } 36 } 37 38 return rec; 39 } 40}; 41 42const Leaf = struct { 43 objects: []Hittable, 44 bbox: AABB, 45 46 pub fn hit(self: *Leaf, r: *Ray, ray_t: IntervalF32) ?HitRecord { 47 var rec: ?HitRecord = null; 48 var interval = ray_t; 49 for (self.objects) |obj| { 50 if (obj.hit(r, interval)) |res| { 51 interval = IntervalF32.init(ray_t.min, res.t); 52 rec = res; 53 } 54 } 55 return rec; 56 } 57}; 58 59threadlocal var reached_depth: usize = 0; 60threadlocal var max_objects: usize = 0; 61 62const Node = union(enum) { 63 ast: Ast, 64 leaf: Leaf, 65 66 pub fn init( 67 self: *Node, 68 allocator: std.mem.Allocator, 69 objects: []Hittable, 70 max_depth: usize, 71 depth: usize, 72 ) !void { 73 if (reached_depth < depth) reached_depth = depth; 74 75 var ast_bbox = AABB{}; 76 for (0..objects.len) |idx| { 77 ast_bbox = AABB.initAB(&ast_bbox, &objects[idx].boundingBox()); 78 } 79 80 if (depth >= max_depth or objects.len <= 2) { 81 if (max_objects < objects.len) max_objects = objects.len; 82 self.* = .{ .leaf = .{ .objects = objects, .bbox = ast_bbox } }; 83 return; 84 } 85 86 const axis = ast_bbox.longestAxis(); 87 88 if (axis == 0) { 89 std.mem.sort(Hittable, objects, .{}, boxXCompare); 90 } else if (axis == 1) { 91 std.mem.sort(Hittable, objects, .{}, boxYCompare); 92 } else { 93 std.mem.sort(Hittable, objects, .{}, boxZCompare); 94 } 95 96 var left = try allocator.create(Node); 97 var right = try allocator.create(Node); 98 99 const mid = objects.len / 2; 100 try left.init(allocator, objects[0..mid], max_depth, depth + 1); 101 try right.init(allocator, objects[mid..], max_depth, depth + 1); 102 103 self.* = .{ .ast = .{ 104 .left = left, 105 .right = right, 106 .bbox = ast_bbox, 107 } }; 108 } 109 110 pub fn deinit(self: *Node, allocator: std.mem.Allocator) void { 111 switch (self.*) { 112 .ast => |*a| { 113 if (a.left) |left| { 114 left.deinit(allocator); 115 allocator.destroy(left); 116 } 117 if (a.right) |right| { 118 right.deinit(allocator); 119 allocator.destroy(right); 120 } 121 }, 122 .leaf => |*l| { 123 allocator.destroy(&l.objects); 124 }, 125 } 126 } 127 128 pub fn bbox(self: *Node) AABB { 129 return self.bbox; 130 } 131 132 pub inline fn hit(self: *Node, r: *Ray, ray_t: IntervalF32) ?HitRecord { 133 switch (self.*) { 134 inline else => |*n| if (n.bbox.hit(r, ray_t)) { 135 return n.hit(r, ray_t); 136 } else { 137 return null; 138 }, 139 } 140 } 141}; 142 143allocator: std.mem.Allocator, 144root: *Node, 145 146pub fn init(allocator: std.mem.Allocator, objects: hittable.HittableList, max_depth: usize) !BVH { 147 defer @constCast(&objects).deinit(); 148 log.info("Creating BVH Tree with {} objects", .{objects.list.items.len}); 149 150 const root = try allocator.create(Node); 151 try root.init(allocator, objects.list.items, max_depth, 0); 152 // const bbox = root.recomputeBbox(); 153 154 log.debug("Reached depth of: {}, max objects: {}", .{ reached_depth, max_objects }); 155 156 return .{ 157 .allocator = allocator, 158 .root = root, 159 }; 160} 161 162pub fn deinit(self: *BVH) void { 163 self.root.deinit(self.allocator); 164} 165 166pub inline fn hit(self: *BVH, r: *Ray, ray_t: IntervalF32) ?HitRecord { 167 return self.root.hit(r, ray_t); 168} 169 170inline fn boxCompare(a: *Hittable, b: *Hittable, axis_index: i32) bool { 171 return a.boundingBox().axisInterval(axis_index).min < b.boundingBox().axisInterval(axis_index).min; 172} 173 174fn boxXCompare(_: @TypeOf(.{}), a: Hittable, b: Hittable) bool { 175 return boxCompare(@constCast(&a), @constCast(&b), 0); 176} 177 178fn boxYCompare(_: @TypeOf(.{}), a: Hittable, b: Hittable) bool { 179 return boxCompare(@constCast(&a), @constCast(&b), 1); 180} 181 182fn boxZCompare(_: @TypeOf(.{}), a: Hittable, b: Hittable) bool { 183 return boxCompare(@constCast(&a), @constCast(&b), 2); 184}