From 6b948f6718d3f8a63f212771329f69fa55cf460e Mon Sep 17 00:00:00 2001 From: Lorenzo Torres Date: Wed, 6 Aug 2025 15:32:32 +0200 Subject: [PATCH] Implemented transformations --- assets/shaders/shader.vert | 7 +- src/math.zig | 218 +++++++++++++++++++++++++++++++++---- src/renderer/Camera.zig | 2 +- src/renderer/Renderer.zig | 5 + src/renderer/vulkan.zig | 46 +++++++- src/sideros.zig | 2 +- 6 files changed, 250 insertions(+), 30 deletions(-) diff --git a/assets/shaders/shader.vert b/assets/shaders/shader.vert index 851a79b..9193aba 100644 --- a/assets/shaders/shader.vert +++ b/assets/shaders/shader.vert @@ -12,13 +12,18 @@ layout (binding = 1) uniform ViewUniform { mat4 view; } view; +layout (binding = 4) uniform TransformUniform { + mat4 translation; + mat4 scale; +} transform; + layout(location = 2) out vec3 Normal; layout(location = 3) out vec3 FragPos; layout(location = 4) out vec2 TexCoords; void main() { + mat4 transformation = transform.translation * transform.scale; vec4 out_vec = proj.proj * view.view * vec4(vertPos, 1.0); - //vec4 out_vec = proj.proj * vec4(vertPos, 1.0); FragPos = vec3(vec4(vertPos, 1.0)); Normal = normal; TexCoords = uv; diff --git a/src/math.zig b/src/math.zig index 93854a0..cf80c8d 100644 --- a/src/math.zig +++ b/src/math.zig @@ -3,23 +3,60 @@ pub const tan = std.math.tan; pub const cos = std.math.cos; pub const sin = std.math.sin; pub const rad = std.math.degreesToRadians; +pub const sqrt = std.math.sqrt; + +pub const Axis = struct { + pub const x: [3]f32 = .{1.0, 0.0, 0.0}; + pub const y: [3]f32 = .{0.0, 1.0, 0.0}; + pub const z: [3]f32 = .{0.0, 0.0, 1.0}; +}; + +pub const Transform = struct { + translation: Matrix, + scale: Matrix, + rotation: Quaternion, + + pub fn init(position: [3]f32, scale: [3]f32, rotation: [3]f32) Transform { + var translation = Matrix.identity(); + translation.translate(position); + + return .{ + .translation = translation, + .rotation = Quaternion.fromEulerAngles(rotation), + .scale = Matrix.scale(scale), + }; + } + + pub fn translate(self: *Transform, delta: [3]f32) void { + self.translation.translate(delta); + } + + pub fn rotate(self: *Transform, angle: f32, axis: [3]f32) void { + const delta = Quaternion.fromAxisAngle(axis, angle); + self.rotation = self.rotation.mul(delta); + self.rotation = self.rotation.normalize(); + } +}; pub const Matrix = struct { - rows: [4]@Vector(4, f32), + rows: [4][4]f32, - pub fn lookAt(eye: @Vector(3, f32), target: @Vector(3, f32), arbitrary_up: @Vector(3, f32)) Matrix { - const forward = normalize(target - eye); - const right = normalize(cross(forward, arbitrary_up)); + pub fn lookAt(eye: [3]f32, target: [3]f32, arbitrary_up: [3]f32) Matrix { + const t: @Vector(3, f32) = target; + const e: @Vector(3, f32) = eye; + const u: @Vector(3, f32) = arbitrary_up; + const forward = normalize(t - e); + const right = normalize(cross(forward, u)); const up = cross(right, forward); - const view = [_]@Vector(4, f32){ - @Vector(4, f32){ right[0], up[0], -forward[0], 0.0 }, - @Vector(4, f32){ right[1], up[1], -forward[1], 0.0 }, - @Vector(4, f32){ right[2], up[2], -forward[2], 0.0 }, - @Vector(4, f32){ -dot(eye, right), -dot(eye, up), -dot(eye, forward), 1.0 }, + const view = [4][4]f32{ + [4]f32{ right[0], up[0], -forward[0], 0.0 }, + [4]f32{ right[1], up[1], -forward[1], 0.0 }, + [4]f32{ right[2], up[2], -forward[2], 0.0 }, + [4]f32{ -dot(e, right), -dot(e, up), -dot(e, forward), 1.0 }, }; - return Matrix{ + return .{ .rows = view, }; } @@ -31,28 +68,161 @@ pub const Matrix = struct { const a = near / (far - near); const b = far * a; - const projection = [_]@Vector(4, f32){ - @Vector(4, f32){ x, 0.0, 0.0, 0.0 }, - @Vector(4, f32){ 0.0, y, 0.0, 0.0 }, - @Vector(4, f32){ 0.0, 0.0, a, b }, - @Vector(4, f32){ 0.0, 0.0, 1.0, 0.0 }, + const projection = [4][4]f32{ + [4]f32{ x, 0.0, 0.0, 0.0 }, + [4]f32{ 0.0, y, 0.0, 0.0 }, + [4]f32{ 0.0, 0.0, a, b }, + [4]f32{ 0.0, 0.0, 1.0, 0.0 }, }; - return Matrix{ + return .{ .rows = projection, }; } - pub fn identity() Matrix { - const view = [_]@Vector(4, f32){ - @Vector(4, f32){ 1.0, 0.0, 0.0, 0.0 }, - @Vector(4, f32){ 0.0, 1.0, 0.0, 0.0 }, - @Vector(4, f32){ 0.0, 0.0, 1.0, 0.0 }, - @Vector(4, f32){ 0.0, 0.0, 0.0, 1.0 }, + pub inline fn identity() Matrix { + return .{ + .rows = .{ + [4]f32{ 1.0, 0.0, 0.0, 0.0 }, + [4]f32{ 0.0, 1.0, 0.0, 0.0 }, + [4]f32{ 0.0, 0.0, 1.0, 0.0 }, + [4]f32{ 0.0, 0.0, 0.0, 1.0 }, + }, + }; + } + + pub fn mul(a: Matrix, b: Matrix) Matrix { + var result = [4][4]f32{ + [4]f32{ 0.0, 0.0, 0.0, 0.0 }, + [4]f32{ 0.0, 0.0, 0.0, 0.0 }, + [4]f32{ 0.0, 0.0, 0.0, 0.0 }, + [4]f32{ 0.0, 0.0, 0.0, 0.0 }, }; - return Matrix{ - .rows = view, + for (0..4) |i| { + for (0..4) |j| { + for (0..4) |k| { + result[i][j] += a.rows[i][k] * b.rows[k][j]; + } + } + } + + return .{ + .rows = result, + }; + } + + pub inline fn translate(a: *Matrix, pos: [3]f32) void { + a.rows[3][0] += pos[0]; + a.rows[3][1] += pos[1]; + a.rows[3][2] += pos[2]; + } + + pub inline fn scale(s: [3]f32) Matrix { + return .{ + .rows = [4][4]f32{ + [4]f32{ s[0], 0.0, 0.0, 0.0 }, + [4]f32{ 0.0, s[1], 0.0, 0.0 }, + [4]f32{ 0.0, 0.0, s[2], 0.0 }, + [4]f32{ 0.0, 0.0, 0.0, 1.0 }, + }, + }; + } + + pub fn transform(pos: [3]f32, s: [3]f32) Matrix { + var translation = Matrix.identity(); + translation.translate(pos); + + return translation.mul(Matrix.scale(s)); + } +}; + +const Quaternion = struct { + w: f32, + x: f32, + y: f32, + z: f32, + + pub const identity: Quaternion = .{ .w = 1.0, .x = 0.0, .y = 0.0, .z = 0.0 }; + + pub fn fromAxisAngle(axis: [3]f32, angle: f32) Quaternion { + const half_angle = angle / 2.0; + const s = sin(half_angle); + + return .{ + .w = cos(half_angle), + .x = axis[0] * s, + .y = axis[1] * s, + .z = axis[3] * s, + }; + } + + fn fromEulerAngles(rotation: [3]f32) Quaternion { + const pitch = rotation[0]; + const yaw = rotation[1]; + const roll = rotation[2]; + + const half_pitch = pitch / 2.0; + const half_yaw = yaw / 2.0; + const half_roll = roll / 2.0; + + const sin_pitch = sin(half_pitch); + const cos_pitch = cos(half_pitch); + const sin_yaw = sin(half_yaw); + const cos_yaw = cos(half_yaw); + const sin_roll = sin(half_roll); + const cos_roll = cos(half_roll); + + return .{ + .w = cos_yaw * cos_pitch * cos_roll + sin_yaw * sin_pitch * sin_roll, + .x = cos_yaw * sin_pitch * cos_roll + sin_yaw * cos_pitch * sin_roll, + .y = sin_yaw * cos_pitch * cos_roll - cos_yaw * sin_pitch * sin_roll, + .z = cos_yaw * cos_pitch * sin_roll - sin_yaw * sin_pitch * cos_roll, + }; + } + + + inline fn mul(a: Quaternion, b: Quaternion) Quaternion { + return .{ + .w = a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z, + .x = a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y, + .y = a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x, + .z = a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w, + }; + } + + fn normalize(q: Quaternion) Quaternion { + const mag = sqrt(q.w*q.w + q.x*q.x + q.y*q.y + q.z*q.z); + return Quaternion{ + .w = q.w / mag, + .x = q.x / mag, + .y = q.y / mag, + .z = q.z / mag, + }; + } + + fn matrix(q: Quaternion) Matrix { + const x2 = q.x + q.x; + const y2 = q.y + q.y; + const z2 = q.z + q.z; + + const xx = q.x * x2; + const yy = q.y * y2; + const zz = q.z * z2; + const xy = q.x * y2; + const xz = q.x * z2; + const yz = q.y * z2; + const wx = q.w * x2; + const wy = q.w * y2; + const wz = q.w * z2; + + return .{ + .rows = .{ + .{ 1.0 - (yy + zz), xy - wz, xz + wy, 0.0 }, + .{ xy + wz, 1.0 - (xx + zz), yz - wx, 0.0 }, + .{ xz - wy, yz + wx, 1.0 - (xx + yy), 0.0 }, + .{ 0.0, 0.0, 0.0, 1.0 }, + } }; } }; diff --git a/src/renderer/Camera.zig b/src/renderer/Camera.zig index 18d06b1..e5da6de 100644 --- a/src/renderer/Camera.zig +++ b/src/renderer/Camera.zig @@ -18,7 +18,7 @@ up: @Vector(3, f32) = .{ 0.0, 1.0, 0.0 }, speed: f32 = 2.5, pub fn getProjection(width: usize, height: usize) math.Matrix { - return math.Matrix.perspective(math.rad(45.0), (@as(f32, @floatFromInt(width)) / @as(f32, @floatFromInt(height))), 0.1, 100.0); + return math.Matrix.perspective(math.rad(40.0), (@as(f32, @floatFromInt(width)) / @as(f32, @floatFromInt(height))), 0.1, 100.0); } pub fn getView(self: Camera) math.Matrix { diff --git a/src/renderer/Renderer.zig b/src/renderer/Renderer.zig index 9d26a5c..12ce2e1 100644 --- a/src/renderer/Renderer.zig +++ b/src/renderer/Renderer.zig @@ -19,6 +19,7 @@ graphics_pipeline: vk.GraphicsPipeline(2), current_frame: u32, vertex_buffer: vk.Buffer, index_buffer: vk.Buffer, +transform: math.Transform, pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_handle: vk.c.VkSurfaceKHR) !Renderer { const instance: vk.Instance = .{ .handle = instance_handle }; @@ -67,6 +68,7 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand // TODO: Why are we storing the buffer and not the Mesh? .vertex_buffer = triangle.vertex_buffer, .index_buffer = triangle.index_buffer, + .transform = math.Transform.init(.{0.0, 0.0, 0.0}, .{1.0, 1.0, 1.0}, .{0.0, 0.0, 0.0}), }; } @@ -94,6 +96,9 @@ pub fn render(pool: *ecs.Pool) anyerror!void { view_pos[1] = camera.position[1]; view_pos[2] = camera.position[2]; + const transform_memory = renderer.graphics_pipeline.transform_memory; + @memcpy(transform_memory[0..@sizeOf(math.Transform)], std.mem.asBytes(&renderer.transform)); + try renderer.device.waitFence(renderer.current_frame); const image = try renderer.swapchain.nextImage(renderer.device, renderer.current_frame); try renderer.device.resetCommand(renderer.current_frame); diff --git a/src/renderer/vulkan.zig b/src/renderer/vulkan.zig index 8bcf90f..5dd1212 100644 --- a/src/renderer/vulkan.zig +++ b/src/renderer/vulkan.zig @@ -237,6 +237,7 @@ pub fn GraphicsPipeline(comptime n: usize) type { light_buffer: Buffer, view_buffer: Buffer, view_memory: [*c]u8, + transform_memory: [*c]u8, view_pos_memory: [*c]u8, texture_sampler: Sampler, diffuse_sampler: Sampler, @@ -356,6 +357,13 @@ pub fn GraphicsPipeline(comptime n: usize) type { .stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT, }; + const transform_binding = c.VkDescriptorSetLayoutBinding{ + .binding = 4, + .descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = c.VK_SHADER_STAGE_VERTEX_BIT, + }; + const light_binding = c.VkDescriptorSetLayoutBinding{ .binding = 2, .descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, @@ -384,7 +392,7 @@ pub fn GraphicsPipeline(comptime n: usize) type { .stageFlags = c.VK_SHADER_STAGE_FRAGMENT_BIT, }; - const bindings = [_]c.VkDescriptorSetLayoutBinding{projection_binding, view_binding, light_binding, view_pos_binding}; + const bindings = [_]c.VkDescriptorSetLayoutBinding{projection_binding, view_binding, transform_binding, light_binding, view_pos_binding}; const texture_bindings = [_]c.VkDescriptorSetLayoutBinding{texture_sampler_binding, diffuse_sampler_binding}; var descriptor_set_layout: c.VkDescriptorSetLayout = undefined; @@ -392,7 +400,7 @@ pub fn GraphicsPipeline(comptime n: usize) type { const descriptor_set_layout_info = c.VkDescriptorSetLayoutCreateInfo{ .sType = c.VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, - .bindingCount = 4, + .bindingCount = 5, .pBindings = bindings[0..].ptr, }; @@ -444,7 +452,7 @@ pub fn GraphicsPipeline(comptime n: usize) type { const size = c.VkDescriptorPoolSize{ .type = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = 4, + .descriptorCount = 5, }; const sampler_size = c.VkDescriptorPoolSize{ @@ -540,6 +548,37 @@ pub fn GraphicsPipeline(comptime n: usize) type { c.vkUpdateDescriptorSets(device.handle, 1, &write_view_descriptor_set, 0, null); + const transform_buffer = try device.createBuffer(BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, BufferFlags{ .device_local = true }, @sizeOf(math.Transform)); + + var transform_data: [*c]u8 = undefined; + + try mapError(c.vkMapMemory( + device.handle, + transform_buffer.memory, + 0, + transform_buffer.size, + 0, + @ptrCast(&transform_data), + )); + + const transform_descriptor_buffer_info = c.VkDescriptorBufferInfo{ + .buffer = transform_buffer.handle, + .offset = 0, + .range = transform_buffer.size, + }; + + const write_transform_descriptor_set = c.VkWriteDescriptorSet{ + .sType = c.VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptor_set, + .dstBinding = 4, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = c.VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &transform_descriptor_buffer_info, + }; + + c.vkUpdateDescriptorSets(device.handle, 1, &write_transform_descriptor_set, 0, null); + const light_buffer = try device.createBuffer(BufferUsage{ .uniform_buffer = true, .transfer_dst = true }, BufferFlags{ .device_local = true }, @sizeOf([3]f32)); var light_data: [*c]u8 = undefined; @@ -616,6 +655,7 @@ pub fn GraphicsPipeline(comptime n: usize) type { .view_memory = view_data, .light_buffer = light_buffer, .view_pos_memory = view_pos_data, + .transform_memory = transform_data, .texture_sampler = try Sampler.init(device), .diffuse_sampler = try Sampler.init(device), .textures = std.ArrayList(c.VkDescriptorSet).init(allocator), diff --git a/src/sideros.zig b/src/sideros.zig index 44f334b..6537a4a 100644 --- a/src/sideros.zig +++ b/src/sideros.zig @@ -15,7 +15,7 @@ var renderer: Renderer = undefined; export fn sideros_init(init: api.GameInit) callconv(.c) void { pool = ecs.Pool.init(allocator, .{ .camera = .{ - .position = .{ 30.0, 30.0, 30.0 }, + .position = .{ 0.0, 0.0, 40.0 }, .target = .{ 0.0, 0.0, 0.0 }, }, .renderer = undefined,