Made terrain its own struct

This commit is contained in:
Lorenzo Torres 2025-08-14 14:52:48 +02:00
parent c0b8d021d4
commit 1475fd2101
6 changed files with 93 additions and 26 deletions

View file

@ -17,6 +17,7 @@ pub const Resources = struct {
camera: *Camera,
renderer: *Renderer,
input: *Input,
terrain: rendering.Terrain,
delta_time: f32 = 0.0,
};

View file

@ -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,
};
}

70
src/rendering/Terrain.zig Normal file
View file

@ -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];
}

View file

@ -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");

View file

@ -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();

View file

@ -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| {