diff --git a/src/Game.zig b/src/Game.zig index 8ef7246..34c55ca 100644 --- a/src/Game.zig +++ b/src/Game.zig @@ -616,6 +616,7 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain const block_grass = blocks.getFilename("Grass.json").?; const block_dirt = blocks.getFilename("Dirt.json").?; + const block_stone = blocks.getFilename("Stone.json").?; const block_bedrock = blocks.getFilename("Bedrock.json").?; while (it.next()) |chunk_coords| { @@ -636,26 +637,30 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain var it2 = Interator3(usize).init(0, 0, 0, 15, 15, 0); while (it2.next()) |pos| { const x, const y, _ = pos; - const fpos = Vector3.init( + const fpos = Vector2.init( @floatFromInt(pos[0]), @floatFromInt(pos[1]), - @floatFromInt(pos[2]), - ).add(origin); + ).add(origin.asVector2()); - const noise = std.math.clamp(math.noise2(fpos.asVector2().divScalar(30)), -1, 1); - const fheight = (0.5 * noise + 0.5) * 4 + 1; - const iheight: u32 = @intFromFloat(@round(fheight)); + const iheight = worldHeightI(fpos); chunk.blocks[0][y][x] = block_bedrock; - var i: u32 = 0; + var i: i32 = 0; while (i < iheight) : (i += 1) { const iz = i + 1; - const block = if (i + 1 == iheight) block_grass else block_dirt; - chunk.blocks[iz][y][x] = block; + const block = if (i + 1 == iheight) + block_grass + else if (i + 4 >= iheight) + block_dirt + else + block_stone; + chunk.blocks[@intCast(iz)][y][x] = block; } } } + const camera_position = Vector3.init(0, 0, @as(f32, @floatFromInt(worldHeightI(.zero))) + 0.5 + 1.62); + var chunk_it = chunks.iterator(); while (chunk_it.next()) |entry| { const x, const y, const z = entry.key_ptr.*; @@ -713,6 +718,8 @@ pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain .materials = materials, .textures = textures, .chunks = chunks, + + .camera_position = camera_position, }; } @@ -989,3 +996,19 @@ fn recreateSwapchain(self: *Game) !void { self.engine.setObjectName(semaphore.*, "S Transfer[{d}]", .{i}); } } + +fn worldHeightF(pos: Vector2) f32 { + const noise_hscale = 80; + + const noise_zcenter = 8; + const noise_zscale = 5; + + const noise = math.noise2(pos.divScalar(noise_hscale)); + const height = noise_zscale * noise + noise_zcenter; + return height; +} + +fn worldHeightI(pos: Vector2) i32 { + const iheight = worldHeightF(pos); + return @intFromFloat(@round(iheight)); +} diff --git a/src/math/noise.zig b/src/math/noise.zig index af58c6b..415e6bb 100644 --- a/src/math/noise.zig +++ b/src/math/noise.zig @@ -1,5 +1,23 @@ +const std = @import("std"); + const Vector2 = @import("Vector2.zig").Vector2; +const f32x8 = @Vector(8, f32); +const i32x8 = @Vector(8, i32); +const u32x8 = @Vector(8, u32); + +inline fn ps(value: f32) f32x8 { + return @splat(value); +} + +inline fn epi32(value: i32) i32x8 { + return @splat(value); +} + +inline fn epu32(value: u32) u32x8 { + return @splat(value); +} + const permutation = [256]u8{ 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, @@ -19,18 +37,56 @@ const permutation = [256]u8{ 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, }; -fn grad2(hash: u8, x: f32, y: f32) f32 { - const h: u3 = @truncate(hash); - const u: f32 = if (h < 4) x else y; - const v: f32 = if (h < 4) x else x; - return (if (h & 0b001 != 0) -u else u) + (if (hash & 0b010 != 0) -2 * v else 2 * v); +inline fn perm(i: i32) i32 { + @setRuntimeSafety(false); + const index: u32 = @as(u32, @bitCast(i)) & 0xFF; + return permutation[index]; } -const F2: f32 = 0.5 * (@sqrt(@as(f32, 3)) - 1); +inline fn permv(i: i32x8) i32x8 { + @setRuntimeSafety(false); + const index: u32x8 = @as(u32x8, @bitCast(i)) & epu32(0xFF); + return .{ + permutation[index[0]], + permutation[index[1]], + permutation[index[2]], + permutation[index[3]], + permutation[index[4]], + permutation[index[5]], + permutation[index[6]], + permutation[index[7]], + }; +} + +fn grad2(hash: i32, x: f32, y: f32) f32 { + const h: i32 = hash & 0b111; + const u: f32 = if (h < 4) x else y; + const v: f32 = if (h < 4) x else x; + return (if (h & 0b001 != 0) -u else u) + (if (h & 0b010 != 0) -2 * v else 2 * v); +} + +fn grad2v(hash: i32x8, x: f32x8, y: f32x8) f32x8 { + const h: i32x8 = hash & epi32(0b111); + const u: f32x8 = @select(f32, h < epi32(4), x, y); + const v: f32x8 = @select(f32, h < epi32(4), y, x); + const r1: f32x8 = @select(f32, h & epi32(0b001) != epi32(0), -u, u); + const r2: f32x8 = @select(f32, h & epi32(0b010) != epi32(0), ps(-2) * v, ps(2) * v); + return r1 + r2; +} + +const F2: f32 = (@sqrt(@as(f32, 3)) - 1) / 2; const G2: f32 = (3 - @sqrt(@as(f32, 3))) / 6; +const F2v: f32x8 = ps(F2); +const G2v: f32x8 = ps(G2); + +// NOTE No idea why this value, derived experimentally +const noise2_scale: f32 = 34.11; +const noise2v_scale: f32x8 = ps(noise2_scale); + pub fn noise2(v: Vector2) f32 { const x: f32, const y: f32 = v.asArray(); + const s: f32 = (x + y) * F2; const xs: f32 = x + s; const ys: f32 = y + s; @@ -43,23 +99,65 @@ pub fn noise2(v: Vector2) f32 { const x0: f32 = x - X0; const y0: f32 = y - Y0; - const _i1: u8, const _j1: u8 = if (x0 > y0) .{ 1, 0 } else .{ 0, 1 }; + const _i1: i32 = if (x0 > y0) 1 else 0; + const _j1: i32 = if (x0 > y0) 0 else 1; const x1: f32 = x0 - @as(f32, @floatFromInt(_i1)) + G2; const y1: f32 = y0 - @as(f32, @floatFromInt(_j1)) + G2; const x2: f32 = x0 - 1 + 2 * G2; const y2: f32 = y0 - 1 + 2 * G2; - const ii: u8 = @truncate(@as(u32, @bitCast(i))); - const jj: u8 = @truncate(@as(u32, @bitCast(j))); - const t0: f32 = 0.5 - x0 * x0 - y0 * y0; const t1: f32 = 0.5 - x1 * x1 - y1 * y1; const t2: f32 = 0.5 - x2 * x2 - y2 * y2; - const n0 = if (t0 < 0) 0 else (t0 * t0) * (t0 * t0) * grad2(permutation[ii +% permutation[jj]], x0, y0); - const n1 = if (t1 < 0) 0 else (t1 * t1) * (t1 * t1) * grad2(permutation[ii +% _i1 +% permutation[jj +% _j1]], x1, y1); - const n2 = if (t2 < 0) 0 else (t2 * t2) * (t2 * t2) * grad2(permutation[ii +% 1 +% permutation[jj +% 1]], x2, y2); + const gi0: i32 = perm(i + perm(j)); + const gi1: i32 = perm(i + _i1 + perm(j + _j1)); + const gi2: i32 = perm(i + 1 + perm(j + 1)); - return 40 * (n0 + n1 + n2); + const n0: f32 = if (t0 < 0) 0 else (t0 * t0) * (t0 * t0) * grad2(gi0, x0, y0); + const n1: f32 = if (t1 < 0) 0 else (t1 * t1) * (t1 * t1) * grad2(gi1, x1, y1); + const n2: f32 = if (t2 < 0) 0 else (t2 * t2) * (t2 * t2) * grad2(gi2, x2, y2); + + const ret: f32 = noise2_scale * (n0 + n1 + n2); + std.debug.assert(ret >= -1 and ret <= 1); + return ret; +} + +pub fn noise2v(x: f32x8, y: f32x8) f32x8 { + const s: f32x8 = (x + y) * F2v; + const xs: f32x8 = x + s; + const ys: f32x8 = y + s; + const i: i32x8 = @intFromFloat(@floor(xs)); + const j: i32x8 = @intFromFloat(@floor(ys)); + + const t: f32x8 = @as(f32x8, @floatFromInt(i + j)) * G2v; + const X0: f32x8 = @as(f32x8, @floatFromInt(i)) - t; + const Y0: f32x8 = @as(f32x8, @floatFromInt(j)) - t; + const x0: f32x8 = x - X0; + const y0: f32x8 = y - Y0; + + const _i1: i32x8 = @select(i32, x0 > y0, epi32(1), epi32(0)); + const _j1: i32x8 = @select(i32, x0 > y0, epi32(0), epi32(1)); + + const x1: f32x8 = x0 - @as(f32x8, @floatFromInt(_i1)) + G2v; + const y1: f32x8 = y0 - @as(f32x8, @floatFromInt(_j1)) + G2v; + const x2: f32x8 = x0 - ps(1) + ps(2) * G2v; + const y2: f32x8 = y0 - ps(1) + ps(2) * G2v; + + const t0: f32x8 = ps(0.5) - x0 * x0 - y0 * y0; + const t1: f32x8 = ps(0.5) - x1 * x1 - y1 * y1; + const t2: f32x8 = ps(0.5) - x2 * x2 - y2 * y2; + + const gi0: i32x8 = permv(i + permv(j)); + const gi1: i32x8 = permv(i + _i1 + permv(j + _j1)); + const gi2: i32x8 = permv(i + epi32(1) + permv(j + epi32(1))); + + const n0: f32x8 = @select(f32, t0 < ps(0), ps(0), (t0 * t0) * (t0 * t0) * grad2(gi0, x0, y0)); + const n1: f32x8 = @select(f32, t1 < ps(0), ps(0), (t1 * t1) * (t1 * t1) * grad2(gi1, x1, y1)); + const n2: f32x8 = @select(f32, t2 < ps(0), ps(0), (t2 * t2) * (t2 * t2) * grad2(gi2, x2, y2)); + + const ret: f32x8 = noise2v_scale * (n0 + n1 + n2); + std.debug.assert(@reduce(.And, ret >= ps(-1)) and @reduce(.And, ret <= ps(1))); + return ret; } diff --git a/src/math/noise_rangetest.zig b/src/math/noise_rangetest.zig new file mode 100644 index 0000000..4139fb0 --- /dev/null +++ b/src/math/noise_rangetest.zig @@ -0,0 +1,32 @@ +const std = @import("std"); + +fn noise2rangetest() void { + const f0: u32 = @bitCast(@as(f32, 1)); + const f1: u32 = @bitCast(@as(f32, 10)); + const step: u32 = (f1 - f0) / 30000; + + var max: f32 = -std.math.inf(f32); + var timer = std.time.Timer.start() catch unreachable; + + var y: u32 = f0; + while (y <= f1) : (y += step) { + const fy: f32 = @bitCast(@as(u32, @intCast(y))); + + var x: u32 = f0; + while (x <= f1) : (x += step) { + const fx: f32 = @bitCast(@as(u32, @intCast(x))); + + const noise = @import("noise.zig").noise2(.init(fx, fy)); + const noise_abs = @abs(noise); + max = @max(noise_abs, max); + + if (timer.read() > 1 * std.time.ns_per_s) { + _ = timer.lap(); + const yp: f32 = @as(f32, @floatFromInt(y - f0)) / @as(f32, @floatFromInt(f1 - f0)) * 100.0; + std.debug.print("[{d:.2}%] max: {d} | scale: {d}\n", .{ yp, max, 1 / max }); + } + } + } + + std.debug.print("max: {d} | scale: {d}\n", .{ max, 1 / max }); +}