Files
voxel-game/src/Game.zig

950 lines
31 KiB
Zig

const Game = @This();
const std = @import("std");
const glfw = @import("zglfw");
const vk = @import("vulkan");
const math = @import("math.zig");
const Materials = @import("assets/Materials.zig");
const Textures = @import("assets/Textures.zig");
const CommandBuffer = @import("engine/CommandBuffer.zig").CommandBuffer;
const Engine = @import("engine/Engine.zig");
const GenericBuffer = @import("engine/GenericBuffer.zig").GenericBuffer;
const StagingBuffer = @import("engine/StagingBuffer.zig");
const Swapchain = @import("engine/Swapchain.zig");
const Iterator3 = math.Iterator3;
const Matrix4x4 = math.Matrix4x4;
const Quaternion = math.Quaternion;
const Vector2 = math.Vector2;
const Vector3 = math.Vector3;
const Vector4 = math.Vector4;
const PointLight = extern struct {
positionWS: [3]f32,
color: [3]f32,
pub fn init(position_ws: Vector3, color: Vector3) PointLight {
return .{
.positionWS = position_ws.asArray(),
.color = color.asArray(),
};
}
};
const DirectionalLight = extern struct {
directionWS: [3]f32,
color: [3]f32,
pub fn init(direction_ws: Vector3, color: Vector3) DirectionalLight {
return .{
.directionWS = direction_ws.asArray(),
.color = color.asArray(),
};
}
};
const GlobalUniforms = extern struct {
matrixWStoVS: [16]f32,
matrixVStoCS: [16]f32,
ambientLight: [3]f32,
pub fn init(matrix_ws_to_vs: Matrix4x4, matrix_vs_to_cs: Matrix4x4, ambient_light: Vector3) GlobalUniforms {
return .{
.matrixWStoVS = matrix_ws_to_vs.asArray(),
.matrixVStoCS = matrix_vs_to_cs.asArray(),
.ambientLight = ambient_light.asArray(),
};
}
};
const ObjectUniforms = extern struct {
matrixOStoWS: [16]f32,
matrixOStoWSNormal: [16]f32,
material: Materials.Id,
pub fn init(matrix_os_to_ws: Matrix4x4, matrix_ow_to_ws_normal: Matrix4x4, material: Materials.Id) ObjectUniforms {
return .{
.matrixOStoWS = matrix_os_to_ws.asArray(),
.matrixOStoWSNormal = matrix_ow_to_ws_normal.asArray(),
.material = material,
};
}
};
const Vertex = extern struct {
positionOS: [3]f32,
texCoord: [2]u16,
normalOS: [3]i8,
tangentOS: [4]i8,
pub fn init(position_os: Vector3, tex_coord: Vector2, normal_os: Vector3, tangent_os: Vector4) Vertex {
return .{
.positionOS = position_os.asArray(),
.texCoord = tex_coord.asArrayNorm(u16),
.normalOS = normal_os.asArrayNorm(i8),
.tangentOS = tangent_os.asArrayNorm(i8),
};
}
};
const GlobalUniformsBuffer = GenericBuffer(GlobalUniforms, void);
const PointLightBuffer = GenericBuffer(u32, PointLight);
const DirectionalLightBuffer = GenericBuffer(u32, DirectionalLight);
const ObjectUniformsBuffer = GenericBuffer(void, ObjectUniforms);
const VertexBuffer = GenericBuffer(void, Vertex);
const IndexBuffer = GenericBuffer(void, u16);
const main_vert_spv align(4) = @embedFile("shaders/main_vert.spv").*;
const main_frag_spv align(4) = @embedFile("shaders/main_frag.spv").*;
allocator: std.mem.Allocator,
engine: *Engine,
swapchain: *Swapchain,
global_descriptor_set_layout: vk.DescriptorSetLayout,
per_batch_descriptor_set_layout: vk.DescriptorSetLayout,
descriptor_pool: vk.DescriptorPool,
/// [0] GLOBAL, [1] PER BATCH
descriptor_sets: [2]vk.DescriptorSet,
pipeline_layout: vk.PipelineLayout,
pipeline: vk.Pipeline,
vertex_buffer: VertexBuffer,
index_buffer: IndexBuffer,
global_uniforms: GlobalUniformsBuffer,
global_uniforms_staging_buffer: StagingBuffer,
global_uniforms_transfer_command_buffer: CommandBuffer(.graphics, .persistent),
global_uniforms_transfer_semaphores: []vk.Semaphore,
point_lights: PointLightBuffer,
directional_lights: DirectionalLightBuffer,
object_uniforms: ObjectUniformsBuffer,
sampler: vk.Sampler,
object_count: u32,
materials: Materials,
textures: Textures,
camera_position: Vector3 = .init(0, 0, 1.62),
camera_pitch: f32 = 0,
camera_yaw: f32 = 0,
input_forwards: bool = false,
input_backwards: bool = false,
input_left: bool = false,
input_right: bool = false,
input_up: bool = false,
input_down: bool = false,
const max_textures = 1024;
const max_point_lights = 1024;
const max_directional_lights = 4;
const max_objects = 1024;
const camera_near_plane = 0.1;
const camera_vertical_fov_deg = 90.0;
const camera_half_vertical_fov_rad = 0.5 * camera_vertical_fov_deg * std.math.rad_per_deg;
const camera_mouse_sensitivity = 0.002;
const player_horizontal_speed = 11.0;
const player_vertical_speed = 7.49;
pub fn init(allocator: std.mem.Allocator, engine: *Engine, swapchain: *Swapchain) !Game {
var materials = try Materials.init(engine, allocator);
errdefer materials.deinit(engine, allocator);
var textures = try Textures.init(engine, allocator);
errdefer textures.deinit(engine, allocator);
materials.loadAll(engine, &textures, allocator);
const sampler = try engine.createSampler(.{
.mag_filter = .linear,
.min_filter = .linear,
.mipmap_mode = .linear,
.address_mode_u = .repeat,
.address_mode_v = .repeat,
.address_mode_w = .repeat,
.mip_lod_bias = 0,
.anisotropy_enable = .false,
.max_anisotropy = 0,
.compare_enable = .false,
.compare_op = .always,
.min_lod = 0,
.max_lod = vk.LOD_CLAMP_NONE,
.border_color = .float_transparent_black,
.unnormalized_coordinates = .false,
});
errdefer engine.destroySampler(sampler);
const global_descriptor_set_layout = try engine.createDescriptorSetLayout(.{
.bindings = &.{
.{
.binding = 0,
.descriptor_type = .uniform_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 1,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 2,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 3,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
.{
.binding = 4,
.descriptor_type = .sampler,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
.immutable_samplers = &.{sampler},
},
.{
.binding = 5,
.descriptor_type = .sampled_image,
.descriptor_count = max_textures,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
.flags = .{ .variable_descriptor_count_bit = true },
},
},
});
errdefer engine.destroyDescriptorSetLayout(global_descriptor_set_layout);
const per_batch_descriptor_set_layout = try engine.createDescriptorSetLayout(.{
.bindings = &.{
.{
.binding = 0,
.descriptor_type = .storage_buffer,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true, .fragment_bit = true },
},
},
});
errdefer engine.destroyDescriptorSetLayout(per_batch_descriptor_set_layout);
const pipeline_layout = try engine.createPipelineLayout(.{
.set_layouts = &.{
global_descriptor_set_layout,
per_batch_descriptor_set_layout,
},
});
errdefer engine.destroyPipelineLayout(pipeline_layout);
const vertex_shader = try engine.createShaderModule(.{ .code = &main_vert_spv });
defer engine.destroyShaderModule(vertex_shader);
const fragment_shader = try engine.createShaderModule(.{ .code = &main_frag_spv });
defer engine.destroyShaderModule(fragment_shader);
var vertex_buffer = try VertexBuffer.init(engine, .{
.usage = .vertex,
.target_queue = .graphics,
.array_capacity = 4,
});
errdefer vertex_buffer.deinit(engine);
try vertex_buffer.write(engine, .{
.elements = &.{
.init(
.init(-0.5, -0.5, 0),
.init(0, 1),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(0.5, -0.5, 0),
.init(1, 1),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(-0.5, 0.5, 0),
.init(0, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
.init(
.init(0.5, 0.5, 0),
.init(1, 0),
.init(0, 0, 1),
.init(1, 0, 0, -1),
),
},
});
var index_buffer = try IndexBuffer.init(engine, .{
.usage = .index,
.target_queue = .graphics,
.array_capacity = 6,
});
errdefer index_buffer.deinit(engine);
try index_buffer.write(engine, .{
.elements = &.{ 0, 1, 2, 2, 1, 3 },
});
var global_uniforms = try GlobalUniformsBuffer.init(engine, .{
.usage = .uniform,
.target_queue = .graphics,
});
errdefer global_uniforms.deinit(engine);
var global_uniforms_staging_buffer = try StagingBuffer.init(engine, .{
.capacity = @sizeOf(GlobalUniforms),
.target_queue = .graphics,
});
errdefer global_uniforms_staging_buffer.deinit(engine);
var global_uniforms_transfer_command_buffer: CommandBuffer(.graphics, .persistent) = try .init(engine);
errdefer global_uniforms_transfer_command_buffer.deinit(engine);
try global_uniforms_transfer_command_buffer.beginCommandBuffer(.{});
global_uniforms_transfer_command_buffer.copyBuffer(
global_uniforms_staging_buffer.buffer,
global_uniforms.buffer,
&.{
.{
.src_offset = 0,
.dst_offset = 0,
.size = @sizeOf(GlobalUniforms),
},
},
);
try global_uniforms_transfer_command_buffer.endCommandBuffer();
const global_uniforms_transfer_semaphores = blk: {
var semaphores: std.ArrayList(vk.Semaphore) = try .initCapacity(allocator, swapchain.swapchain_images.len);
errdefer semaphores.deinit(allocator);
errdefer for (semaphores.items) |x| engine.destroySemaphore(x);
for (0..swapchain.swapchain_images.len) |_| {
semaphores.appendAssumeCapacity(try engine.createSemaphore());
}
break :blk try semaphores.toOwnedSlice(allocator);
};
errdefer {
for (global_uniforms_transfer_semaphores) |semaphore| {
engine.destroySemaphore(semaphore);
}
allocator.free(global_uniforms_transfer_semaphores);
}
const global_uniforms_transfer_semaphore = try engine.createSemaphore();
errdefer engine.destroySemaphore(global_uniforms_transfer_semaphore);
var point_lights = try PointLightBuffer.init(engine, .{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_point_lights,
});
errdefer point_lights.deinit(engine);
var directional_lights = try DirectionalLightBuffer.init(engine, .{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_directional_lights,
});
errdefer directional_lights.deinit(engine);
var object_uniforms = try ObjectUniformsBuffer.init(engine, .{
.usage = .storage,
.target_queue = .graphics,
.array_capacity = max_objects,
});
errdefer object_uniforms.deinit(engine);
const pipeline = try engine.createGraphicsPipeline(.{
.stages = &.{
.{
.stage = .{ .vertex_bit = true },
.module = vertex_shader,
.name = "main",
},
.{
.stage = .{ .fragment_bit = true },
.module = fragment_shader,
.name = "main",
},
},
.vertex_input_state = .{
.vertex_binding_descriptions = &.{
.{
.binding = 0,
.stride = @sizeOf(Vertex),
.input_rate = .vertex,
},
},
.vertex_attribute_descriptions = &.{
.{
.location = 0,
.binding = 0,
.format = .r32g32b32_sfloat,
.offset = @offsetOf(Vertex, "positionOS"),
},
.{
.location = 1,
.binding = 0,
.format = .r16g16_unorm,
.offset = @offsetOf(Vertex, "texCoord"),
},
.{
.location = 2,
.binding = 0,
.format = .r8g8b8_snorm,
.offset = @offsetOf(Vertex, "normalOS"),
},
.{
.location = 3,
.binding = 0,
.format = .r8g8b8a8_snorm,
.offset = @offsetOf(Vertex, "tangentOS"),
},
},
},
.input_assembly_state = .{
.topology = .triangle_list,
.primitive_restart_enable = .false,
},
.viewport_state = .{
.viewports = &.{undefined}, // dynamic
.scissors = &.{undefined}, // dynamic
},
.rasterization_state = .{
.depth_clamp_enable = .false,
.rasterizer_discard_enable = .false,
.polygon_mode = .fill,
.cull_mode = .{ .back_bit = true },
.front_face = .counter_clockwise,
.depth_bias_enable = .false,
.depth_bias_constant_factor = 0,
.depth_bias_clamp = 0,
.depth_bias_slope_factor = 0,
.line_width = 1,
},
.multisample_state = .{
.rasterization_samples = .{ .@"1_bit" = true },
.sample_shading_enable = .false,
.min_sample_shading = 1,
.alpha_to_coverage_enable = .false,
.alpha_to_one_enable = .false,
},
.depth_stencil_state = .{
.depth_test_enable = .true,
.depth_write_enable = .true,
.depth_compare_op = .greater,
.depth_bounds_test_enable = .false,
.stencil_test_enable = .false,
.front = undefined,
.back = undefined,
.min_depth_bounds = 0,
.max_depth_bounds = 1,
},
.color_blend_state = .{
.logic_op_enable = .false,
.logic_op = .copy,
.attachments = &.{
.{
.blend_enable = .false,
.src_color_blend_factor = .one,
.dst_color_blend_factor = .zero,
.color_blend_op = .add,
.src_alpha_blend_factor = .one,
.dst_alpha_blend_factor = .zero,
.alpha_blend_op = .add,
.color_write_mask = .{
.r_bit = true,
.g_bit = true,
.b_bit = true,
.a_bit = true,
},
},
},
.blend_constants = .{ 0, 0, 0, 0 },
},
.dynamic_state = .{
.dynamic_states = &.{ .viewport, .scissor },
},
.layout = pipeline_layout,
.render_pass = swapchain.render_pass,
.subpass = 0,
});
errdefer engine.destroyPipeline(pipeline);
const descriptor_pool = try engine.createDescriptorPool(.{
.max_sets = 2,
.pool_sizes = &.{
.{
.type = .sampler,
.descriptor_count = 1,
},
.{
.type = .sampled_image,
.descriptor_count = max_textures,
},
.{
.type = .uniform_buffer,
.descriptor_count = 1,
},
.{
.type = .storage_buffer,
.descriptor_count = 4,
},
},
});
errdefer engine.destroyDescriptorPool(descriptor_pool);
// [0] GLOBAL, [1] PER BATCH
var descriptor_sets: [2]vk.DescriptorSet = undefined;
try engine.allocateDescriptorSets(.{
.descriptor_pool = descriptor_pool,
.set_layouts = &.{ global_descriptor_set_layout, per_batch_descriptor_set_layout },
.variable_descriptor_counts = &.{ @intCast(textures.textures.items.len), 0 },
}, &descriptor_sets);
const descriptor_images = try allocator.alloc(vk.DescriptorImageInfo, textures.textures.items.len);
for (textures.textures.items, descriptor_images) |texture, *info| {
info.* = .{
.sampler = .null_handle,
.image_view = texture.image_view,
.image_layout = .shader_read_only_optimal,
};
}
defer allocator.free(descriptor_images);
try engine.updateDescriptorSets(.{
.writes = &.{
.{
.dst_set = descriptor_sets[0],
.dst_binding = 0,
.dst_array_element = 0,
.descriptor_type = .uniform_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = global_uniforms.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
.{
.dst_set = descriptor_sets[0],
.dst_binding = 1,
.dst_array_element = 0,
.descriptor_type = .storage_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = point_lights.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
.{
.dst_set = descriptor_sets[0],
.dst_binding = 2,
.dst_array_element = 0,
.descriptor_type = .storage_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = directional_lights.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
.{
.dst_set = descriptor_sets[0],
.dst_binding = 3,
.dst_array_element = 0,
.descriptor_type = .storage_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = materials.material_buffer.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
.{
.dst_set = descriptor_sets[0],
.dst_binding = 5,
.dst_array_element = 0,
.descriptor_type = .sampled_image,
.descriptor_infos = .{ .image = descriptor_images },
},
.{
.dst_set = descriptor_sets[1],
.dst_binding = 0,
.dst_array_element = 0,
.descriptor_type = .storage_buffer,
.descriptor_infos = .{
.buffer = &.{
.{
.buffer = object_uniforms.buffer,
.offset = 0,
.range = vk.WHOLE_SIZE,
},
},
},
},
},
});
const point_lights_data: []const PointLight = &.{
.{
.positionWS = .{ 0, 0, 0.5 },
.color = .{ 1, 1, 1 },
},
.{
.positionWS = .{ -5, 5, 0.5 },
.color = .{ 1, 0, 0 },
},
.{
.positionWS = .{ 5, 5, 0.5 },
.color = .{ 0, 0, 1 },
},
.{
.positionWS = .{ -5, -5, 0.5 },
.color = .{ 0, 1, 0 },
},
.{
.positionWS = .{ 5, -5, 0.5 },
.color = .{ 1, 1, 0 },
},
};
try point_lights.write(engine, .{
.header = @intCast(point_lights_data.len),
.elements = point_lights_data,
});
const directional_lights_data: []const DirectionalLight = &.{
.{
.directionWS = .{ 0, 0, -1 },
.color = .{ 0.3, 0.3, 0.3 },
},
};
try directional_lights.write(engine, .{
.header = @intCast(directional_lights_data.len),
.elements = directional_lights_data,
});
const object_count: u32 = blk: {
var objects: std.ArrayList(ObjectUniforms) = try .initCapacity(allocator, 578);
defer objects.deinit(allocator);
var it = Iterator3.init(.init(-8, -8, 0), .init(8, 8, 2), .init(1, 1, 2));
while (it.next()) |pos| {
const material: Materials.Id = @enumFromInt(engine.random.uintLessThan(u16, @intFromEnum(materials.next_id)));
const matrix_os_to_ws = Matrix4x4.initTranslation(pos);
const matrix_os_to_ws_normal = Matrix4x4.inverseTransposeAffine(matrix_os_to_ws);
objects.appendAssumeCapacity(.{
.matrixOStoWS = matrix_os_to_ws.asArray(),
.matrixOStoWSNormal = matrix_os_to_ws_normal.asArray(),
.material = material,
});
}
try object_uniforms.write(engine, .{ .elements = objects.items });
break :blk @intCast(objects.items.len);
};
return .{
.allocator = allocator,
.engine = engine,
.swapchain = swapchain,
.global_descriptor_set_layout = global_descriptor_set_layout,
.per_batch_descriptor_set_layout = per_batch_descriptor_set_layout,
.descriptor_pool = descriptor_pool,
.descriptor_sets = descriptor_sets,
.pipeline_layout = pipeline_layout,
.pipeline = pipeline,
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
.global_uniforms = global_uniforms,
.global_uniforms_staging_buffer = global_uniforms_staging_buffer,
.global_uniforms_transfer_command_buffer = global_uniforms_transfer_command_buffer,
.global_uniforms_transfer_semaphores = global_uniforms_transfer_semaphores,
.point_lights = point_lights,
.directional_lights = directional_lights,
.object_uniforms = object_uniforms,
.sampler = sampler,
.object_count = object_count,
.materials = materials,
.textures = textures,
};
}
pub fn deinit(self: *Game) void {
std.log.debug("Deinitializing {*}", .{self});
self.vertex_buffer.deinit(self.engine);
self.index_buffer.deinit(self.engine);
self.global_uniforms.deinit(self.engine);
self.global_uniforms_staging_buffer.deinit(self.engine);
self.global_uniforms_transfer_command_buffer.deinit(self.engine);
self.point_lights.deinit(self.engine);
self.directional_lights.deinit(self.engine);
self.object_uniforms.deinit(self.engine);
for (self.global_uniforms_transfer_semaphores) |semaphore| {
self.engine.destroySemaphore(semaphore);
}
self.allocator.free(self.global_uniforms_transfer_semaphores);
self.engine.destroyDescriptorPool(self.descriptor_pool);
self.engine.destroySampler(self.sampler);
self.engine.destroyPipeline(self.pipeline);
self.engine.destroyPipelineLayout(self.pipeline_layout);
self.engine.destroyDescriptorSetLayout(self.per_batch_descriptor_set_layout);
self.engine.destroyDescriptorSetLayout(self.global_descriptor_set_layout);
self.textures.deinit(self.engine, self.allocator);
self.materials.deinit(self.engine, self.allocator);
self.* = undefined;
}
pub fn update(self: *Game, dt: f32) void {
const camera_d = Vector2
.init(
@as(f32, @floatFromInt(@intFromBool(self.input_right))) - @as(f32, @floatFromInt(@intFromBool(self.input_left))),
@as(f32, @floatFromInt(@intFromBool(self.input_forwards))) - @as(f32, @floatFromInt(@intFromBool(self.input_backwards))),
)
.rotate(self.camera_yaw)
.mulScalar(player_horizontal_speed)
.asVector3(player_vertical_speed * (@as(f32, @floatFromInt(@intFromBool(self.input_up))) - @as(f32, @floatFromInt(@intFromBool(self.input_down)))))
.mulScalar(dt);
self.camera_position = Vector3.add(self.camera_position, camera_d);
const framebuffer_size = Vector2.init(
@floatFromInt(self.swapchain.extent.width),
@floatFromInt(self.swapchain.extent.height),
);
const camera_rotation = Quaternion.mulQuaternion(
.initRotationXY(self.camera_yaw),
.initRotationYZ(self.camera_pitch),
);
const camera_aspect_ratio = framebuffer_size.getX() / framebuffer_size.getY();
const camera_yscale = 1.0 / @tan(camera_half_vertical_fov_rad);
const camera_xscale = camera_yscale / camera_aspect_ratio;
const matrix_ws_to_vs = Matrix4x4.mulMatrix(
Matrix4x4.initRotation(camera_rotation.conjugate()),
Matrix4x4.initTranslation(self.camera_position.negate()),
);
// zig fmt: off
const matrix_vs_to_cs = Matrix4x4.init(
camera_xscale, 0, 0, 0,
0, 0, 0, 1,
0, -camera_yscale, 0, 0,
0, 0, camera_near_plane, 0,
);
// zig fmt: on
const ambient_light = Vector3.init(0.2, 0.2, 0.2);
const global_uniforms_data: GlobalUniforms = .{
.matrixWStoVS = matrix_ws_to_vs.asArray(),
.matrixVStoCS = matrix_vs_to_cs.asArray(),
.ambientLight = ambient_light.asArray(),
};
const staging_memory = self.global_uniforms_staging_buffer.map(self.engine) catch |err| {
std.log.err("Failed to map global uniforms staging buffer: {s}", .{@errorName(err)});
@panic("Frame update failed");
};
@memcpy(staging_memory, std.mem.asBytes(&global_uniforms_data));
self.global_uniforms_staging_buffer.unmap(self.engine);
self.global_uniforms_transfer_command_buffer.submit(self.engine, .{
.signal_semaphores = &.{self.global_uniforms_transfer_semaphores[self.swapchain.image_index]},
}) catch |err| {
std.log.err("Failed to submit global uniforms transfer: {s}", .{@errorName(err)});
@panic("Frame update failed");
};
self.render() catch |err| {
std.log.err("Failed to render: {s}", .{@errorName(err)});
@panic("Frame update failed");
};
}
pub fn onKeyDown(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void {
const no_mods = @as(c_int, @bitCast(mods)) == 0;
if (key_code == .escape and no_mods) {
self.engine.mode.surface.window.setShouldClose(true);
}
if (key_code == .w) {
self.input_forwards = true;
self.input_backwards = false;
}
if (key_code == .s) {
self.input_backwards = true;
self.input_forwards = false;
}
if (key_code == .a) {
self.input_left = true;
self.input_right = false;
}
if (key_code == .d) {
self.input_right = true;
self.input_left = false;
}
if (key_code == .space) {
self.input_up = true;
}
if (key_code == .left_shift) {
self.input_down = true;
}
}
pub fn onKeyUp(self: *Game, key_code: glfw.Key, mods: glfw.Mods) void {
_ = mods;
if (key_code == .w) {
self.input_forwards = false;
}
if (key_code == .s) {
self.input_backwards = false;
}
if (key_code == .a) {
self.input_left = false;
}
if (key_code == .d) {
self.input_right = false;
}
if (key_code == .space) {
self.input_up = false;
}
if (key_code == .left_shift) {
self.input_down = false;
}
}
pub fn onMouseMove(self: *Game, dx: f32, dy: f32) void {
self.camera_pitch -= dy * camera_mouse_sensitivity;
self.camera_yaw -= dx * camera_mouse_sensitivity;
self.camera_pitch = std.math.clamp(self.camera_pitch, -0.5 * std.math.pi, 0.5 * std.math.pi);
self.camera_yaw = @mod(self.camera_yaw, 2 * std.math.pi);
}
pub fn onMouseDown(self: *Game, button: glfw.MouseButton, mods: glfw.Mods) void {
_ = self;
_ = button;
_ = mods;
}
pub fn onMouseUp(self: *Game, button: glfw.MouseButton, mods: glfw.Mods) void {
_ = self;
_ = button;
_ = mods;
}
fn render(self: *Game) !void {
const extent = self.swapchain.extent;
const command_buffer: CommandBuffer(.graphics, .transient) = try .init(self.engine);
// NOTE Do not free command buffer yet
try command_buffer.beginCommandBuffer(.{});
{
command_buffer.beginRenderPass(.{
.render_pass = self.swapchain.render_pass,
.framebuffer = self.swapchain.swapchain_images[self.swapchain.image_index].framebuffer,
.render_area = .{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
},
.clear_values = &.{
.{
.color = .{
.float_32 = .{ 0, 0, 0, 0 },
},
},
.{
.depth_stencil = .{
.depth = 0,
.stencil = 0,
},
},
},
}, .@"inline");
defer command_buffer.endRenderPass();
command_buffer.setViewport(0, &.{
.{
.x = 0,
.y = 0,
.width = @floatFromInt(extent.width),
.height = @floatFromInt(extent.height),
.min_depth = 0,
.max_depth = 1,
},
});
command_buffer.setScissor(0, &.{
.{
.offset = .{ .x = 0, .y = 0 },
.extent = extent,
},
});
command_buffer.bindPipeline(.graphics, self.pipeline);
try command_buffer.bindVertexBuffers(0, &.{
.{
.buffer = self.vertex_buffer.buffer,
.offset = 0,
},
});
command_buffer.bindIndexBuffer(self.index_buffer.buffer, 0, .uint16);
command_buffer.bindDescriptorSets(.graphics, self.pipeline_layout, 0, &self.descriptor_sets, &.{});
command_buffer.drawIndexed(.{
.index_count = self.index_buffer.array_capacity,
.instance_count = self.object_count,
});
}
try command_buffer.endCommandBuffer();
const res = try self.swapchain.present(self.engine, .{
.command_buffer = command_buffer,
.wait_semaphores = &.{
.{
.semaphore = self.global_uniforms_transfer_semaphores[self.swapchain.image_index],
.stage_flags = .{ .vertex_shader_bit = true },
},
},
});
_ = res;
}