diff options
Diffstat (limited to 'src/rtw/texture.zig')
| -rw-r--r-- | src/rtw/texture.zig | 48 |
1 files changed, 48 insertions, 0 deletions
diff --git a/src/rtw/texture.zig b/src/rtw/texture.zig index ae24265..95cbef4 100644 --- a/src/rtw/texture.zig +++ b/src/rtw/texture.zig @@ -1,4 +1,5 @@ const std = @import("std"); +const Image = @import("zigimg").Image; const Color = @import("vec.zig").Color; const Vec3 = @import("vec.zig").Vec3; @@ -10,12 +11,14 @@ const TextureTag = enum { solid, checker, noise, + image, }; pub const Texture = union(TextureTag) { solid: SolidTexture, checker: CheckerTexture, noise: NoiseTexture, + image: ImageTexture, pub fn makeSolid(color: Color) Texture { return .{ .solid = .{ .color = color } }; @@ -33,11 +36,16 @@ pub const Texture = union(TextureTag) { return .{ .noise = try NoiseTexture.init(allocator, rng, scale) }; } + pub fn makeImage(allocator: std.mem.Allocator, file_path: []const u8) !Texture { + return .{ .image = try ImageTexture.init(allocator, file_path) }; + } + pub fn value(tx: Texture, u: f64, v: f64, p: Vec3) Color { return switch (tx) { TextureTag.solid => |solidTx| solidTx.value(u, v, p), TextureTag.checker => |checkerTx| checkerTx.value(u, v, p), TextureTag.noise => |noiseTx| noiseTx.value(u, v, p), + TextureTag.image => |imageTx| imageTx.value(u, v, p), }; } }; @@ -102,3 +110,43 @@ pub const NoiseTexture = struct { return rgb(1, 1, 1).mul(0.5 * (1.0 + @sin(tx.scale * p.z + 10.0 * tx.perlin.turb(p, 7)))); } }; + +pub const ImageTexture = struct { + image: Image, + + fn init(allocator: std.mem.Allocator, file_path: []const u8) !ImageTexture { + const image = try Image.fromFilePath(allocator, file_path); + return .{ + .image = image, + }; + } + + fn deinit(tx: ImageTexture) void { + tx.image.deinit(); + } + + fn value(tx: ImageTexture, u: f64, v: f64, p: Vec3) Color { + _ = p; + // Clamp input texture coordinates to [0, 1] x [1, 0] + const u_ = std.math.clamp(u, 0.0, 1.0); + const v_ = 1.0 - std.math.clamp(v, 0.0, 1.0); // Flip v to image coordinates + const i = @floatToInt(usize, u_ * @intToFloat(f64, tx.image.width)); + const j = @floatToInt(usize, v_ * @intToFloat(f64, tx.image.height)); + // Clamp integer mapping, since actual coordinates should be less than 1.0 + const i_ = @min(i, tx.image.width - 1); + const j_ = @min(j, tx.image.width - 1); + const color_scale = 1.0 / 255.0; + const pixels = tx.image.pixels.asBytes(); + const offset = j_ * tx.image.width * 4 + i_ * 4; + const r = @intToFloat(f64, pixels[offset + 0]); + const g = @intToFloat(f64, pixels[offset + 1]); + const b = @intToFloat(f64, pixels[offset + 2]); + const a = @intToFloat(f64, pixels[offset + 3]); + if (a == 0) { + // Ocean + return rgb(0, 0, 1.0); + } else { + return rgb(color_scale * r, color_scale * g, color_scale * b); + } + } +}; |
