diff --git a/build.zig.zon b/build.zig.zon index ce773a9..ff6e125 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -17,8 +17,8 @@ .hash = "1220753b2f9e7c4f3fb6cdc8fc275cc9073904ea7032a1f5596aa776a857de69f72c", }, .zigimg = .{ - .url = "git+https://github.com/MidlightStudio/zigimg#29d34702995a7e84e097004e87ede905cdb674dd", - .hash = "12206b669a4712ff9c06d4111a650f298d7597dd39f9d4853858d7f298c819353a65", + .url = "git+https://github.com/MidlightStudio/zigimg#637974e2d31dcdbc33f1e9cc8ffb2e46abd2e215", + .hash = "122012026c3a65ff1d4acba3b3fe80785f7cee9c6b4cdaff7ed0fbf23b0a6c803989", }, }, .paths = .{ diff --git a/src/hittable.zig b/src/hittable.zig index ba6d849..de11c10 100644 --- a/src/hittable.zig +++ b/src/hittable.zig @@ -25,35 +25,31 @@ pub const HitRecord = struct { }; pub const Hittable = union(enum) { - sphere: Sphere, - bvh_node: BVH, + sphere: struct {Sphere, []const u8}, - pub fn sphere(s: Sphere) Hittable { + pub fn sphere(name: []const u8, s: Sphere) Hittable { // std.log.info("created sphere with mat: {}", .{s.mat}); - return .{ .sphere = s }; + return .{ .sphere = .{ s, name }}; } - pub fn bvh(b: BVH) Hittable { - return .{ .bvh_node = b }; + pub fn boundingBox(self: *Hittable) AABB { + switch (self.*) { + .sphere => |*s| return s[0].boundingBox(), + } } - pub fn boundingBox(self: *Hittable) AABB { + pub fn getName(self: *Hittable) []const u8 { switch (self.*) { - .sphere => |*s| return s.boundingBox(), - .bvh_node => |*s| return s.boundingBox(), + .sphere => |*s| return s[1], } } pub fn hit(self: *Hittable, r: *Ray, ray_t: IntervalF32) ?HitRecord { switch (self.*) { .sphere => |*s| { - std.log.debug("try to hit Sphere: {}", .{s}); + // std.log.debug("try to hit Sphere: {}", .{s}); // std.log.info("hitting sphere with mat: {}", .{s.mat}); - return s.hit(r, ray_t); - }, - .bvh_node => |*s| { - // std.log.debug("try to hit BVH", .{}); - return s.hit(r, ray_t); + return s[0].hit(r, ray_t); }, } diff --git a/src/hittable/BVH.zig b/src/hittable/BVH.zig index c65aebe..601dadd 100644 --- a/src/hittable/BVH.zig +++ b/src/hittable/BVH.zig @@ -10,88 +10,155 @@ const util = @import("../util.zig"); pub const BVH = @This(); -objects: *hittable.HittableList, -left: *Hittable, -right: *Hittable, -bbox: AABB, - -pub fn initL(objects: *hittable.HittableList) BVH { - std.log.info("starting to create BVH", .{}); - return BVH.init(objects, 0, objects.list.items.len); -} +const Node = struct { + left: ?*Node = null, + right: ?*Node = null, + bbox: AABB = AABB{}, + hittable: ?Hittable = null, + + // pub fn add(self: *Node, allocator: std.mem.Allocator, object: *Hittable) void {} + // + pub fn init( + self: *Node, + allocator: std.mem.Allocator, + objects: []hittable.Hittable, + // start: usize, + // end: usize, + ) !void { + for (0..objects.len) |idx| { + self.bbox = AABB.initAB(&self.bbox, &objects[idx].boundingBox()); + } + + const axis = self.bbox.longestAxis(); + const object_span = objects.len; + + if (object_span == 1) { + self.hittable = objects[0]; + // std.log.info("Node.hittable = .{?}", .{self.hittable}); + return; + } + + var left = try allocator.create(Node); + var right = try allocator.create(Node); + + // if (object_span == 2) { + // try left.init(allocator, objects, start, start + 1); + // try right.init(allocator, objects, start + 1, start + 2); + // } else + if (object_span >= 2) { + // std.log.debug("Node.init axis={} start={} end={}", .{ axis, start, end }); + if (axis == 0) { + // break :blk&boxXCompare; + std.mem.sort(Hittable, objects, .{}, boxXCompare); + } else if (axis == 1) { + // break :blk &boxYCompare; + std.mem.sort(Hittable, objects, .{}, boxYCompare); + } else { + // break :blk &boxZCompare; + std.mem.sort(Hittable, objects, .{}, boxZCompare); + } + // std.mem.sort(Hittable, list, null, comparator); + + const mid = object_span / 2; + try left.init(allocator, objects[0..mid]); + try right.init(allocator, objects[mid..]); + } + + self.left = left; + self.right = right; + + // std.log.info("Node created", .{}); + } + + pub fn deinit(self: *Node, allocator: std.mem.Allocator) void { + if (self.left) |l| { + l.deinit(allocator); + allocator.destroy(l); + } + if (self.right) |r| { + r.deinit(allocator); + allocator.destroy(r); + } + } + + pub fn hit(self: *Node, r: *Ray, ray_t: IntervalF32) ?HitRecord { + if (!self.bbox.hit(r, ray_t)) { + return null; + } -pub fn init(objects: *hittable.HittableList, start: usize, end: usize) BVH { - const list = objects.list.items; - var bbox = AABB{}; - for (start..end) |idx| { - bbox = AABB.initAB(&bbox, &list[idx].boundingBox()); + if (self.hittable) |object| { + return @constCast(&object).hit(r, ray_t); + } + + if (self.left) |left| { + if (left.hit(r, ray_t)) |rec| { + return rec; + } + } + + if (self.right) |right| { + if (right.hit(r, ray_t)) |rec| { + return rec; + } + } + + return null; } - const axis = bbox.longestAxis(); - - // const comparator = blk: { - // if (axis == 0) { - // break :blk &boxXCompare; - // } else if (axis == 1) { - // break :blk &boxYCompare; - // } - // break :blk &boxZCompare; - // }; - - const object_span = end - start; - - var left = &list[start]; - var right = &list[start]; - if (object_span == 2) { - left = &list[start]; - right = &list[start + 1]; - } else if (object_span > 2) { - std.log.debug("BVH.init axis={} start={} end={}", .{axis, start, end}); - if (axis == 0) { - // break :blk&boxXCompare; - std.mem.sort(Hittable, list, .{}, boxXCompare); - } else if (axis == 1) { - // break :blk &boxYCompare; - std.mem.sort(Hittable, list, .{}, boxYCompare); - } else { - // break :blk &boxZCompare; - std.mem.sort(Hittable, list, .{}, boxZCompare); + pub fn print(self: *Node, depth: usize, side: u8) void { + for (0..depth) |_| std.debug.print(" ", .{}); + + if (side == 1) { + std.debug.print("Left = ", .{}); + } else if (side >= 2) { + std.debug.print("Right = ", .{}); } - // std.mem.sort(Hittable, list, null, comparator); - const mid = start + object_span / 2; - left = @constCast(&Hittable.bvh(BVH.init(objects, start, mid))); - right = @constCast(&Hittable.bvh(BVH.init(objects, mid, end))); + const has_hit = if (self.hittable) |h| @constCast(&h).getName() else "Ast"; + std.debug.print("Node hittable={s}\n", .{has_hit}); + + if (self.left) |left| left.print(depth + 1, 1); + if (self.right) |right| right.print(depth + 1, 2); } +}; + +allocator: std.mem.Allocator, +// objects: hittable.HittableList, +root: Node, - std.log.info("BVH created", .{}); +pub fn init(allocator: std.mem.Allocator, objects: hittable.HittableList) !BVH { + std.log.info("Creating BVH Tree with {} objects", .{objects.list.items.len}); + // return BVH.init(objects, 0, objects.list.items.len); + var root = Node{}; + try root.init(allocator, objects.list.items); + defer @constCast(&objects).deinit(); + + root.print(0, 0); return .{ - .objects = objects, - .left = left, - .right = right, - .bbox = bbox, + .allocator = allocator, + // .objects = objects, + .root = root, }; } -pub fn hit(self: *BVH, r: *Ray, ray_t: IntervalF32) ?HitRecord { - if (!self.bbox.hit(r, ray_t)) { - return null; - } +pub fn deinit(self: *BVH) void { + self.root.deinit(self.allocator); + // self.objects.deinit(); +} - if (self.left.hit(r, ray_t)) |rec| return rec; - if (self.right.hit(r, ray_t)) |rec| return rec; - return null; +pub fn hit(self: *BVH, r: *Ray, ray_t: IntervalF32) ?HitRecord { + return self.root.hit(r, ray_t); } pub fn boundingBox(self: *BVH) AABB { - return self.bbox; + return self.root.bbox; } fn boxCompare(a: *Hittable, b: *Hittable, axis_index: i32) bool { const a_axis_interval = a.boundingBox().axisInterval(axis_index); const b_axis_interval = b.boundingBox().axisInterval(axis_index); - return a_axis_interval.min < b_axis_interval.min; + return a_axis_interval.min > b_axis_interval.min; } fn boxXCompare(_: @TypeOf(.{}), a: Hittable, b: Hittable) bool { diff --git a/src/rayray.zig b/src/rayray.zig index 4b6b5ff..6f56577 100644 --- a/src/rayray.zig +++ b/src/rayray.zig @@ -25,7 +25,7 @@ pub const Raytracer = struct { thread_pool: *std.Thread.Pool, camera: Camera, - world: hittable.HittableList, + world: hittable.BVH, pub fn init(allocator: std.mem.Allocator, world: hittable.HittableList, camera_opts: Camera.Options) !Self { var thread_pool = try allocator.create(std.Thread.Pool); @@ -35,7 +35,7 @@ pub const Raytracer = struct { .allocator = allocator, .thread_pool = thread_pool, .camera = try Camera.init(allocator, camera_opts), - .world = world, + .world = try hittable.BVH.init(allocator, world), }; } diff --git a/src/scences/in_one_weekend.zig b/src/scences/in_one_weekend.zig index f391479..9b2e206 100644 --- a/src/scences/in_one_weekend.zig +++ b/src/scences/in_one_weekend.zig @@ -17,7 +17,7 @@ pub fn scene(allocator: std.mem.Allocator) !@This() { const material_ground = try allocator.create(Material); material_ground.* = Material.lambertian(zm.f32x4(0.5, 0.5, 0.5, 1.0)); - try world.add(Hittable.sphere(Sphere{ .center = zm.f32x4(0, -1000, 0, 0), .radius = 1000, .mat = material_ground })); + try world.add(Hittable.sphere("Ground", Sphere{ .center = zm.f32x4(0, -1000, 0, 0), .radius = 1000, .mat = material_ground })); var a: isize = -11; while (a < 11) : (a += 1) { @@ -38,17 +38,17 @@ pub fn scene(allocator: std.mem.Allocator) !@This() { // diffuse const albedo = rayray.util.randomVec3() * rayray.util.randomVec3() + zm.f32x4(0, 0, 0, 1); material.* = Material.lambertian(albedo); - try world.add(Hittable.sphere(Sphere{ .center = center, .radius = 0.2, .mat = material })); + try world.add(Hittable.sphere("Lambertian", Sphere{ .center = center, .radius = 0.2, .mat = material })); } else if (choose_mat < 0.95) { // metal const albedo = rayray.util.randomVec3M(0.5, 1) + zm.f32x4(0, 0, 0, 1); const fuzz = rayray.util.randomF32M(0, 0.5); material.* = Material.metal(albedo, fuzz); - try world.add(Hittable.sphere(Sphere{ .center = center, .radius = 0.2, .mat = material })); + try world.add(Hittable.sphere("Metal", Sphere{ .center = center, .radius = 0.2, .mat = material })); } else { // glass material.* = Material.dielectric(1.5); - try world.add(Hittable.sphere(Sphere{ .center = center, .radius = 0.2, .mat = material })); + try world.add(Hittable.sphere("Dielectric", Sphere{ .center = center, .radius = 0.2, .mat = material })); } } } @@ -56,19 +56,17 @@ pub fn scene(allocator: std.mem.Allocator) !@This() { const material1 = try allocator.create(Material); material1.* = Material.dielectric(1.5); - try world.add(Hittable.sphere(Sphere{ .center = zm.f32x4(0, 1, 0, 0), .radius = 1, .mat = material1 })); + try world.add(Hittable.sphere("One", Sphere{ .center = zm.f32x4(0, 1, 0, 0), .radius = 1, .mat = material1 })); const material2 = try allocator.create(Material); material2.* = Material.lambertian(zm.f32x4(0.4, 0.2, 0.1, 1)); - try world.add(Hittable.sphere(Sphere{ .center = zm.f32x4(-4, 1, 0, 0), .radius = 1, .mat = material2 })); + try world.add(Hittable.sphere("Two", Sphere{ .center = zm.f32x4(-4, 1, 0, 0), .radius = 1, .mat = material2 })); const material3 = try allocator.create(Material); material3.* = Material.metal(zm.f32x4(0.7, 0.6, 0.5, 1), 0); - try world.add(Hittable.sphere(Sphere{ .center = zm.f32x4(4, 1, 0, 0), .radius = 1, .mat = material3 })); + try world.add(Hittable.sphere("Three", Sphere{ .center = zm.f32x4(4, 1, 0, 0), .radius = 1, .mat = material3 })); - var world2 = HittableList.init(allocator); - try world2.add(Hittable.bvh(BVH.initL(&world))); - return .{ .allocator = allocator, .world = world2 }; + return .{ .allocator = allocator, .world = world }; } pub fn deinit(self: *@This()) void { diff --git a/src/tracer.zig b/src/tracer.zig index 3a6db92..d718363 100644 --- a/src/tracer.zig +++ b/src/tracer.zig @@ -18,12 +18,12 @@ const log = std.log.scoped(.tracer); pub const Context = struct { cam: *Camera, - world: *hittable.HittableList, + world: *hittable.BVH, height: IntervalUsize, width: IntervalUsize, }; -pub fn rayColor(r: *Ray, world: *hittable.HittableList, depth: usize) zm.Vec { +pub fn rayColor(r: *Ray, world: *hittable.BVH, depth: usize) zm.Vec { if (depth <= 0) return zm.f32x4(0, 0, 0, 1.0); if (world.hit(r, .{ .min = 0.001, .max = std.math.inf(f32) })) |rec| {