diff --git a/src/ecs/entities.zig b/src/ecs/entities.zig index da52611..db66fc4 100644 --- a/src/ecs/entities.zig +++ b/src/ecs/entities.zig @@ -17,6 +17,7 @@ pub const Resources = struct { camera: *Camera, renderer: *Renderer, input: *Input, + terrain: rendering.Terrain, delta_time: f32 = 0.0, }; diff --git a/src/rendering/Renderer.zig b/src/rendering/Renderer.zig index 59a4896..4a543f3 100644 --- a/src/rendering/Renderer.zig +++ b/src/rendering/Renderer.zig @@ -22,8 +22,6 @@ mesh: Mesh, transforms: std.ArrayList(math.Transform), previous_time: std.time.Instant, current_image: usize, -terrain_vertex: vk.Buffer, -terrain_index: vk.Buffer, pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_handle: vk.c.VkSurfaceKHR) !Self { const instance: vk.Instance = .{ .handle = instance_handle }; @@ -49,27 +47,7 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand const terrain_fragment_shader = try device.initShader("terrain_frag"); defer device.deinitShader(terrain_fragment_shader); - var terrain_pipeline = try vk.TerrainPipeline.init(graphics_pipeline, terrain_vertex_shader, terrain_fragment_shader); - - const perlin: math.PerlinNoise = .{ .seed = 54321 }; - const heightmap = try allocator.alloc(u32, 700 * 700); - defer allocator.free(heightmap); - for (0..700) |x| { - for (0..700) |y| { - const scale = 0.01; - var pixel = (perlin.fbm(@as(f64, @floatFromInt(x)) * scale, @as(f64, @floatFromInt(y)) * scale, 8, 3, 0.5) * 3); - pixel = std.math.pow(f64, pixel, 1.2); - const gray: u32 = @intFromFloat(pixel * 255); - const color: u32 = (255 << 24) | (gray << 16) | (gray << 8) | gray; - - heightmap[x*700 + y] = color; - } - } - - const terrain_vertex, const terrain_index = try Mesh.terrain(allocator, device, 700, 700, 10); - const heightmap_texture = try Texture.fromBytes(@alignCast(@ptrCast(heightmap)), device, 700, 700); - //const heightmap_texture = try Texture.init("assets/textures/heightmap.png", device); - try terrain_pipeline.setHeightmap(device, heightmap_texture); + const terrain_pipeline = try vk.TerrainPipeline.init(graphics_pipeline, terrain_vertex_shader, terrain_fragment_shader); const texture = try Texture.init("assets/textures/container.png", device); const diffuse = try Texture.init("assets/textures/container_specular.png", device); @@ -116,8 +94,6 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand .previous_time = try std.time.Instant.now(), .mesh = mesh, .current_image = undefined, - .terrain_vertex = terrain_vertex, - .terrain_index = terrain_index, }; } diff --git a/src/rendering/Terrain.zig b/src/rendering/Terrain.zig new file mode 100644 index 0000000..44f919a --- /dev/null +++ b/src/rendering/Terrain.zig @@ -0,0 +1,70 @@ +const std = @import("std"); +const vk = @import("vulkan.zig"); +const Mesh = @import("Mesh.zig"); +const math = @import("math"); +const Self = @This(); + +pub const Generator = struct { + octaves: u32, + lacunarity: f64, + gain: f64, + scale: f64 = 0.01, + multiplier: f64, + exponent: f64 = 1.0, + + width: usize, + height: usize, + seed: u64, + resolution: f32 = 1.0, +}; + +heightmap: []f64, +width: usize, +height: usize, +seed: u64, + +texture: vk.Texture, +vertex_buffer: vk.Buffer, +index_buffer: vk.Buffer, + +pub fn init(allocator: std.mem.Allocator, device: vk.Device, generator: Generator) !Self { + const perlin: math.PerlinNoise = .{ .seed = generator.seed }; + const heightmap = try allocator.alloc(f64, generator.width * generator.height); + const heightmap_data = try allocator.alloc(u32, generator.width * generator.height); + defer allocator.free(heightmap_data); + for (0..generator.width) |x| { + for (0..generator.height) |y| { + var pixel = (perlin.fbm(@as(f64, @floatFromInt(x)) * generator.scale, @as(f64, @floatFromInt(y)) * generator.scale, generator.octaves, generator.lacunarity, generator.gain) * generator.multiplier); + pixel = std.math.pow(f64, pixel, generator.exponent); + const gray: u32 = @intFromFloat(pixel * 255); + const color: u32 = (255 << 24) | (gray << 16) | (gray << 8) | gray; + + heightmap[x*generator.width + y] = pixel; + heightmap_data[x*generator.width + y] = color; + } + } + + const vertex_buffer, const index_buffer = try Mesh.terrain(allocator, device, generator.width, generator.height, generator.resolution); + const heightmap_texture = try vk.Texture.fromBytes(@alignCast(@ptrCast(heightmap_data)), device, generator.width, generator.height); + + return .{ + .heightmap = heightmap, + .width = generator.width, + .height = generator.height, + .seed = generator.seed, + .vertex_buffer = vertex_buffer, + .index_buffer = index_buffer, + .texture = heightmap_texture, + }; +} + +pub fn deinit(self: *Self, allocator: std.mem.Allocator, device: vk.Device) !void { + allocator.free(self.heightmap); + self.vertex_buffer.deinit(device.handle); + self.index_buffer.deinit(device.handle); + self.texture.deinit(device); +} + +pub fn getHeight(self: Self, x: usize, y: usize) f64 { + return self.heightmap[x*self.width + y]; +} diff --git a/src/rendering/rendering.zig b/src/rendering/rendering.zig index b3ccc07..8d2a931 100644 --- a/src/rendering/rendering.zig +++ b/src/rendering/rendering.zig @@ -3,3 +3,4 @@ pub const vk = @import("vulkan.zig"); pub const Camera = @import("Camera.zig"); pub const Renderer = @import("Renderer.zig"); pub const lights = @import("lights.zig"); +pub const Terrain = @import("Terrain.zig"); diff --git a/src/sideros.zig b/src/sideros.zig index 54d2660..cc55757 100644 --- a/src/sideros.zig +++ b/src/sideros.zig @@ -136,6 +136,7 @@ export fn sideros_init(init: api.GameInit) callconv(.c) void { .camera = &camera, .renderer = undefined, .input = &input, + .terrain = undefined, }; ecs.hooks.init(allocator) catch @panic("TODO: handle this"); @@ -143,6 +144,23 @@ export fn sideros_init(init: api.GameInit) callconv(.c) void { pool = ecs.Pool.init(allocator, &resources) catch @panic("TODO: Gracefully handle error"); // TODO(ernesto): I think this @ptrCast are unavoidable but maybe not? renderer = Renderer.init(allocator, @ptrCast(init.instance), @ptrCast(init.surface)) catch @panic("TODO: Gracefully handle error"); + + resources.terrain = rendering.Terrain.init(allocator, renderer.device, .{ + .octaves = 8, + .lacunarity = 3.0, + .gain = 0.5, + .scale = 0.01, + .multiplier = 3.0, + .exponent = 1.2, + + .width = 700, + .height = 700, + .seed = 12345678, + .resolution = 10.0, + }) catch @panic("TODO: handle this"); + + renderer.terrain_pipeline.setHeightmap(renderer.device, resources.terrain.texture) catch @panic("TODO: handle this"); + pool.addSystemGroup(&[_]ecs.System{systems.render, systems.moveCamera}, true) catch @panic("TODO: Gracefuly handle error"); pool.resources.renderer = &renderer; pool.tick(); diff --git a/src/systems.zig b/src/systems.zig index d245735..8b708f4 100644 --- a/src/systems.zig +++ b/src/systems.zig @@ -6,6 +6,7 @@ const Input = ecs.Input; pub fn render(pool: *ecs.Pool) anyerror!void { var renderer = pool.resources.renderer; const camera = pool.resources.camera; + const terrain = pool.resources.terrain; const now = try std.time.Instant.now(); const delta_time: f32 = @as(f32, @floatFromInt(now.since(renderer.previous_time))) / @as(f32, 1_000_000_000.0); @@ -21,7 +22,7 @@ pub fn render(pool: *ecs.Pool) anyerror!void { renderer.setLightCount(2); try renderer.beginTerrain(); - renderer.device.drawTerrain(@as(u32, @intCast(renderer.terrain_index.size/@sizeOf(u32))), renderer.current_frame, renderer.terrain_vertex, renderer.terrain_index); + renderer.device.drawTerrain(@as(u32, @intCast(terrain.index_buffer.size/@sizeOf(u32))), renderer.current_frame, terrain.vertex_buffer, terrain.index_buffer); try renderer.beginGraphics(); for (renderer.transforms.items, 0..) |transform, i| {