-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
383 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
const std = @import("std"); | ||
const pi = std.math.pi; | ||
const Allocator = std.mem.Allocator; | ||
|
||
const Tuple = @import("../raytracer/tuple.zig").Tuple; | ||
const Matrix = @import("../raytracer/matrix.zig").Matrix; | ||
const Color = @import("../raytracer/color.zig").Color; | ||
const Material = @import("../raytracer/material.zig").Material; | ||
const Shape = @import("../raytracer/shapes/shape.zig").Shape; | ||
const Light = @import("../raytracer/light.zig").Light; | ||
const World = @import("../raytracer/world.zig").World; | ||
const Camera = @import("../raytracer/camera.zig").Camera; | ||
const Pattern = @import("../raytracer/patterns/pattern.zig").Pattern; | ||
|
||
fn hexagonCorner(comptime T: type) !Shape(T) { | ||
var corner = Shape(T).sphere(); | ||
try corner.setTransform( | ||
Matrix(T, 4) | ||
.identity() | ||
.scale(0.25, 0.25, 0.25) | ||
.translate(0.0, 0.0, -1.0) | ||
); | ||
|
||
return corner; | ||
} | ||
|
||
fn hexagonEdge(comptime T: type) !Shape(T) { | ||
var edge = Shape(T).cylinder(); | ||
edge.variant.cylinder.min = 0.0; | ||
edge.variant.cylinder.max = 1.0; | ||
|
||
try edge.setTransform( | ||
Matrix(T, 4) | ||
.identity() | ||
.scale(0.25, 1.0, 0.25) | ||
.rotateZ(-pi / 2.0) | ||
.rotateY(-pi / 6.0) | ||
.translate(0.0, 0.0, -1.0) | ||
); | ||
|
||
return edge; | ||
} | ||
|
||
fn hexagonSide(comptime T: type, allocator: Allocator) !*Shape(T) { | ||
var side = try allocator.create(Shape(T)); | ||
side.* = Shape(T).group(allocator); | ||
|
||
var corner = try allocator.create(Shape(T)); | ||
corner.* = try hexagonCorner(T); | ||
|
||
var edge = try allocator.create(Shape(T)); | ||
edge.* = try hexagonEdge(T); | ||
|
||
try side.addChild(corner); | ||
try side.addChild(edge); | ||
|
||
return side; | ||
} | ||
|
||
fn hexagon(comptime T: type, allocator: Allocator) !*Shape(T) { | ||
var hex = try allocator.create(Shape(T)); | ||
hex.* = Shape(T).group(allocator); | ||
|
||
for (0..6) |n| { | ||
var side = try hexagonSide(T, allocator); | ||
try side.setTransform(Matrix(T, 4).identity().rotateY(@as(T, @floatFromInt(n)) * pi / 3.0)); | ||
|
||
try hex.addChild(side); | ||
} | ||
|
||
return hex; | ||
} | ||
|
||
|
||
pub fn renderHexagon() !void { | ||
// Use an arena for the (few) allocations needed to make the hexagon so | ||
// that we don't have to track them down one-by-one to free them. | ||
var arena = std.heap.ArenaAllocator.init(std.heap.raw_c_allocator); | ||
defer arena.deinit(); | ||
var hex = try hexagon(f64, arena.allocator()); | ||
|
||
const allocator = std.heap.raw_c_allocator; | ||
|
||
var world = World(f64).new(allocator); | ||
defer world.destroy(); | ||
|
||
try world.objects.append(hex.*); | ||
|
||
try world.lights.append(Light(f64).pointLight( | ||
Tuple(f64).point(2.0, 10.0, -5.0), Color(f64).new(0.9, 0.9, 0.9) | ||
)); | ||
|
||
var camera = Camera(f64).new(500, 500, 0.45); | ||
try camera.setTransform( | ||
Matrix(f64, 4).viewTransform( | ||
Tuple(f64).point(0.0, 3.0, -5.0), Tuple(f64).point(0.0, 0.0, 0.0), Tuple(f64).vec3(0.0, 1.0, 0.0) | ||
) | ||
); | ||
|
||
const canvas = try camera.render(allocator, world); | ||
defer canvas.destroy(); | ||
|
||
const ppm = try canvas.ppm(allocator); | ||
defer allocator.free(ppm); | ||
|
||
const file = try std.fs.cwd().createFile( | ||
"images/hexagon.ppm", | ||
.{ .read = true }, | ||
); | ||
defer file.close(); | ||
|
||
_ = try file.writeAll(ppm); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
const std = @import("std"); | ||
const testing = std.testing; | ||
const Allocator = std.mem.Allocator; | ||
const ArrayList = std.ArrayList; | ||
|
||
const Tuple = @import("../tuple.zig").Tuple; | ||
const Matrix = @import("../matrix.zig").Matrix; | ||
const Ray = @import("../ray.zig").Ray; | ||
|
||
const shape = @import("shape.zig"); | ||
const Intersection = shape.Intersection; | ||
const Intersections = shape.Intersections; | ||
const sortIntersections = shape.sortIntersections; | ||
const Shape = shape.Shape; | ||
|
||
/// A group of objects, backed by floats of type `T`. | ||
pub fn Group(comptime T: type) type { | ||
return struct { | ||
const Self = @This(); | ||
|
||
children: ArrayList(*Shape(T)), | ||
|
||
pub fn destroy(self: Self) void { | ||
self.children.deinit(); | ||
} | ||
|
||
pub fn localIntersect( | ||
self: Self, allocator: Allocator, super: *const Shape(T), ray: Ray(T) | ||
) !Intersections(T) { | ||
_ = super; | ||
|
||
var all = Intersections(T).init(allocator); | ||
|
||
for (self.children.items) |child| { | ||
const xs: Intersections(T) = try child.intersect(allocator, ray); | ||
defer xs.deinit(); | ||
|
||
try all.appendSlice(xs.items); | ||
} | ||
|
||
sortIntersections(T, all.items); | ||
|
||
return all; | ||
} | ||
|
||
pub fn localNormalAt(self: Self, super: Shape(T), point: Tuple(T)) Tuple(T) { | ||
_ = self; | ||
_ = super; | ||
_ = point; | ||
|
||
// TODO: can this be a compile error with duck typing? | ||
|
||
@panic("`localNormalAt` not implemented for groups"); | ||
} | ||
}; | ||
} | ||
|
||
test "Creating a new group" { | ||
const allocator = testing.allocator; | ||
|
||
const g = Shape(f32).group(allocator); | ||
defer g.variant.group.destroy(); | ||
|
||
try testing.expectEqual(g._transform, Matrix(f32, 4).identity()); | ||
try testing.expectEqual(g.variant.group.children.items.len, 0); | ||
} | ||
|
||
test "Adding a child to a group" { | ||
const allocator = testing.allocator; | ||
|
||
var g = Shape(f32).group(allocator); | ||
defer g.variant.group.destroy(); | ||
|
||
var s = Shape(f32).testShape(); | ||
|
||
try g.addChild(&s); | ||
|
||
try testing.expectEqual(g.variant.group.children.items.len, 1); | ||
try testing.expectEqual(g.variant.group.children.items[0], &s); | ||
try testing.expectEqual(s.parent, &g); | ||
} | ||
|
||
test "Intersecting a ray with an empty group" { | ||
const allocator = testing.allocator; | ||
|
||
var g = Shape(f32).group(allocator); | ||
defer g.variant.group.destroy(); | ||
|
||
const r = Ray(f32).new(Tuple(f32).point(0.0, 0.0, 0.0), Tuple(f32).vec3(0.0, 0.0, 1.0)); | ||
|
||
const xs = try g.intersect(allocator, r); | ||
defer xs.deinit(); | ||
|
||
try testing.expectEqual(xs.items.len, 0); | ||
} | ||
|
||
test "Intersecting a ray with an nonempty group" { | ||
const allocator = testing.allocator; | ||
|
||
var g = Shape(f32).group(allocator); | ||
defer g.variant.group.destroy(); | ||
|
||
var s1 = Shape(f32).sphere(); | ||
var s2 = Shape(f32).sphere(); | ||
try s2.setTransform(Matrix(f32, 4).identity().translate(0.0, 0.0, -3.0)); | ||
var s3 = Shape(f32).sphere(); | ||
try s3.setTransform(Matrix(f32, 4).identity().translate(5.0, 0.0, 0.0)); | ||
|
||
try g.addChild(&s1); | ||
try g.addChild(&s2); | ||
try g.addChild(&s3); | ||
|
||
const r = Ray(f32).new(Tuple(f32).point(0.0, 0.0, -5.0), Tuple(f32).vec3(0.0, 0.0, 1.0)); | ||
|
||
const xs = try g.intersect(allocator, r); | ||
defer xs.deinit(); | ||
|
||
try testing.expectEqual(xs.items.len, 4); | ||
|
||
try testing.expectEqual(xs.items[0].object, &s2); | ||
try testing.expectEqual(xs.items[1].object, &s2); | ||
try testing.expectEqual(xs.items[2].object, &s1); | ||
try testing.expectEqual(xs.items[3].object, &s1); | ||
} | ||
|
||
test "Intersecting a transformed group" { | ||
const allocator = testing.allocator; | ||
|
||
var g = Shape(f32).group(allocator); | ||
defer g.variant.group.destroy(); | ||
|
||
try g.setTransform(Matrix(f32, 4).identity().scale(2.0, 2.0, 2.0)); | ||
|
||
var s = Shape(f32).sphere(); | ||
try s.setTransform(Matrix(f32, 4).identity().translate(5.0, 0.0, 0.0)); | ||
|
||
try g.addChild(&s); | ||
|
||
const r = Ray(f32).new(Tuple(f32).point(10.0, 0.0, -10.0), Tuple(f32).vec3(0.0, 0.0, 1.0)); | ||
|
||
const xs = try g.intersect(allocator, r); | ||
defer xs.deinit(); | ||
|
||
try testing.expectEqual(xs.items.len, 2); | ||
} |
Oops, something went wrong.