diff --git a/data/shaders/basic.frag b/data/shaders/basic.frag index 65a1710..ec61ee5 100644 --- a/data/shaders/basic.frag +++ b/data/shaders/basic.frag @@ -1,14 +1,10 @@ #version 450 -layout(location = 0) in float vertexIndex; -layout(location = 1) in float depth; +layout(location = 0) in vec2 inUV; layout(location = 0) out vec4 fragColor; +layout(set = 2, binding = 0) uniform sampler2D texture_sampler; + void main() { - fragColor = vec4( - depth, - cos(vertexIndex * 0.5) * 0.25 + 0.75, - sin(vertexIndex * 0.5) * 0.25 + 0.75, - 1.0 - ); + fragColor = texture(texture_sampler, inUV); } diff --git a/data/shaders/basic.vert b/data/shaders/basic.vert index a79ca11..00fe354 100644 --- a/data/shaders/basic.vert +++ b/data/shaders/basic.vert @@ -1,8 +1,8 @@ #version 450 layout(location = 0) in vec3 inCoord; -layout(location = 0) out float vertexIndex; -layout(location = 1) out float depth; +layout(location = 1) in vec2 inUV; +layout(location = 0) out vec2 outUV; layout(set = 1, binding = 0) uniform Camera{ mat4 transform; @@ -11,10 +11,7 @@ layout(set = 1, binding = 1) uniform Object{ mat4 transform; } object; - void main() { - vertexIndex = gl_VertexIndex; - vec4 outPos = vec4(inCoord, 1.0) * object.transform * camera.transform; - depth = outPos.z / outPos.w; - gl_Position = outPos; + gl_Position = vec4(inCoord, 1.0) * object.transform * camera.transform; + outUV = inUV; } diff --git a/src/debug_scene.zig b/src/debug_scene.zig index af10455..fe6f1d2 100644 --- a/src/debug_scene.zig +++ b/src/debug_scene.zig @@ -8,50 +8,62 @@ const Graphics = @import("graphics.zig"); const Game = @import("game.zig"); const CUBE_MESH_DATA = [_]f32{ - -0.5, 0.5, -0.5, - 0.5, 0.5, -0.5, - -0.5, -0.5, -0.5, - 0.5, -0.5, -0.5, - -0.5, -0.5, -0.5, - 0.5, 0.5, -0.5, - 0.5, 0.5, -0.5, - 0.5, 0.5, 0.5, - 0.5, -0.5, -0.5, - 0.5, -0.5, 0.5, - 0.5, -0.5, -0.5, - 0.5, 0.5, 0.5, - 0.5, 0.5, 0.5, - -0.5, 0.5, 0.5, - 0.5, -0.5, 0.5, - -0.5, -0.5, 0.5, - 0.5, -0.5, 0.5, - -0.5, 0.5, 0.5, - -0.5, 0.5, 0.5, - -0.5, 0.5, -0.5, - -0.5, -0.5, 0.5, - -0.5, -0.5, -0.5, - -0.5, -0.5, 0.5, - -0.5, 0.5, -0.5, - -0.5, 0.5, 0.5, - 0.5, 0.5, 0.5, - -0.5, 0.5, -0.5, - 0.5, 0.5, -0.5, - -0.5, 0.5, -0.5, - 0.5, 0.5, 0.5, - -0.5, -0.5, -0.5, - 0.5, -0.5, -0.5, - -0.5, -0.5, 0.5, - 0.5, -0.5, 0.5, - -0.5, -0.5, 0.5, - 0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, -0.5, 0.0, 0.0, + -0.5, -0.5, -0.5, 0.0, 0.0, + 0.5, -0.5, -0.5, 0.0, 0.0, + -0.5, -0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, -0.5, 0.0, 0.0, + 0.5, -0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, 0.5, 0.0, 0.0, + 0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, 0.5, 0.0, 0.0, + -0.5, -0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, -0.5, 0.0, 0.0, + -0.5, -0.5, 0.5, 0.0, 0.0, + -0.5, -0.5, -0.5, 0.0, 0.0, + -0.5, -0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, -0.5, 0.0, 0.0, + -0.5, 0.5, 0.5, 0.0, 0.0, + 0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, 0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, -0.5, 0.0, 0.0, + -0.5, 0.5, -0.5, 0.0, 0.0, + 0.5, 0.5, 0.5, 0.0, 0.0, + -0.5, -0.5, -0.5, 0.0, 0.0, + 0.5, -0.5, -0.5, 0.0, 0.0, + -0.5, -0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, 0.5, 0.0, 0.0, + -0.5, -0.5, 0.5, 0.0, 0.0, + 0.5, -0.5, -0.5, 0.0, 0.0, }; const PLANE_MESH_DATA = [_]f32{ - -0.5, -0.5, 0, - 0.5, 0.5, 0, - -0.5, 0.5, 0, - 0.5, 0.5, 0, - -0.5, -0.5, 0, - 0.5, -0.5, 0, + -0.5, -0.5, 0, 0.0, 1.0, + 0.5, 0.5, 0, 1.0, 0.0, + -0.5, 0.5, 0, 0.0, 0.0, + 0.5, 0.5, 0, 1.0, 0.0, + -0.5, -0.5, 0, 0.0, 1.0, + 0.5, -0.5, 0, 1.0, 1.0, +}; +// const TEXTURE_DATA = [_]u8{ +// 255, 0, 0, 255, +// 0, 255, 0, 255, +// 0, 0, 255, 255, +// 0, 0, 0, 255, +// }; +const TEXTURE_DATA = [_]u8{ + 255, 0, 0, 255, + 0, 255, 0, 255, + 0, 0, 255, 255, + 0, 0, 0, 255, }; pub const WorldTime = struct { @@ -72,6 +84,7 @@ pub const PlayerState = union(enum) { pub const Player = struct { mesh: Graphics.Mesh, + texture: Graphics.Texture, transform: Transform, position: @Vector(2, i32), state: PlayerState, @@ -83,12 +96,14 @@ pub const Player = struct { pub const Environment = struct { mesh: Graphics.Mesh, + texture: Graphics.Texture, transform: Transform, }; pub fn init(controller: *Controller, graphics: *Graphics) !void { controller.addResource(Player{ .mesh = try graphics.loadMesh(@ptrCast(&CUBE_MESH_DATA)), + .texture = try graphics.loadTexture(2, 2, @ptrCast(&TEXTURE_DATA)), .transform = .{}, .position = .{ 0, 0 }, .state = .idle, @@ -98,6 +113,7 @@ pub fn init(controller: *Controller, graphics: *Graphics) !void { }); controller.addResource(Environment{ .mesh = try graphics.loadMesh(@ptrCast(&PLANE_MESH_DATA)), + .texture = try graphics.loadTexture(2, 2, @ptrCast(&TEXTURE_DATA)), .transform = .{ .position = .{ 0, 0, -1 }, .scale = @splat(5), @@ -259,10 +275,10 @@ pub fn draw( env.transform.rotation, Transform.rotationByAxis(.{ 0, 0, 1 }, world_time.time.unitsSince(world_time.last_time) * std.math.pi), ); - try graphics.drawMesh(env.mesh, env.transform); - try graphics.drawMesh(env.mesh, Transform{ + try graphics.drawMesh(env.mesh, env.texture, env.transform); + try graphics.drawMesh(env.mesh, env.texture, Transform{ .position = .{ 0, 0, -0.5 }, .scale = @splat(5), }); - try graphics.drawMesh(player.mesh, player.transform); + try graphics.drawMesh(player.mesh, player.texture, player.transform); } diff --git a/src/graphics.zig b/src/graphics.zig index 260a6c7..ff33928 100644 --- a/src/graphics.zig +++ b/src/graphics.zig @@ -11,6 +11,11 @@ pub const Mesh = struct { vertex_count: usize, }; +pub const Texture = struct { + texture: *sdl.GPUTexture, + sampler: *sdl.GPUSampler, +}; + window: *sdl.Window, renderer: *sdl.Renderer, device: *sdl.GPUDevice, @@ -41,7 +46,7 @@ to_resize: ?[2]u32 = null, const VERTEX_BUFFER_DEFAULT_CAPACITY = 1024; const VERTEX_BUFFER_GROWTH_MULTIPLIER = 2; const TRANSFER_BUFFER_DEFAULT_CAPACITY = 1024; -const BYTES_PER_VERTEX = 3 * 4; +const BYTES_PER_VERTEX = 5 * 4; const Self = @This(); pub fn create() GameError!Self { @@ -96,6 +101,7 @@ pub fn create() GameError!Self { .entrypoint = "main", .format = sdl.GPU_SHADERFORMAT_SPIRV, .stage = sdl.GPU_SHADERSTAGE_FRAGMENT, + .num_samplers = 1, }, ); errdefer sdl.ReleaseGPUShader(device, shader_frag); @@ -132,18 +138,25 @@ pub fn create() GameError!Self { .vertex_input_state = .{ .vertex_buffer_descriptions = &.{ .slot = 0, - // 3 Coordinates * 4 Bytes - .pitch = 3 * 4, + .pitch = 5 * 4, .input_rate = sdl.GPU_VERTEXINPUTRATE_VERTEX, }, .num_vertex_buffers = 1, - .vertex_attributes = &sdl.GPUVertexAttribute{ - .offset = 0, - .location = 0, - .format = sdl.GPU_VERTEXELEMENTFORMAT_FLOAT3, - .buffer_slot = 0, + .vertex_attributes = &[2]sdl.GPUVertexAttribute{ + sdl.GPUVertexAttribute{ + .offset = 0, + .location = 0, + .format = sdl.GPU_VERTEXELEMENTFORMAT_FLOAT3, + .buffer_slot = 0, + }, + sdl.GPUVertexAttribute{ + .offset = 3 * 4, + .location = 1, + .format = sdl.GPU_VERTEXELEMENTFORMAT_FLOAT2, + .buffer_slot = 0, + }, }, - .num_vertex_attributes = 1, + .num_vertex_attributes = 2, }, .primitive_type = sdl.GPU_PRIMITIVETYPE_TRIANGLELIST, .rasterizer_state = presets.RASTERIZER_CULL, @@ -218,6 +231,64 @@ pub fn destroy(self: *Self) void { sdl.DestroyGPUDevice(self.device); } +pub fn loadTexture(self: *Self, width: u32, height: u32, texture_bytes: []const u8) GameError!Texture { + const target_format = sdl.SDL_GetGPUSwapchainTextureFormat(self.device, self.window); + + const texture = sdl.CreateGPUTexture(self.device, &sdl.GPUTextureCreateInfo{ + .format = target_format, + .layer_count_or_depth = 1, + .width = width, + .height = height, + .num_levels = 1, + .sample_count = sdl.GPU_SAMPLECOUNT_1, + .usage = sdl.GPU_TEXTUREUSAGE_SAMPLER, + }) orelse return GameError.SdlError; + errdefer sdl.ReleaseGPUTexture(self.device, texture); + + const command_buffer = sdl.AcquireGPUCommandBuffer(self.device) orelse return GameError.SdlError; + { + errdefer _ = sdl.CancelGPUCommandBuffer(command_buffer); + + const copy_pass = sdl.BeginGPUCopyPass(command_buffer) orelse return GameError.SdlError; + defer sdl.EndGPUCopyPass(copy_pass); + + const map: [*]u8 = @ptrCast(sdl.MapGPUTransferBuffer(self.device, self.transfer_buffer, false) orelse return GameError.SdlError); + @memcpy(map, texture_bytes); + sdl.UnmapGPUTransferBuffer(self.device, self.transfer_buffer); + + sdl.UploadToGPUTexture(copy_pass, &sdl.GPUTextureTransferInfo{ + .offset = 0, + .pixels_per_row = width, + .rows_per_layer = height, + .transfer_buffer = self.transfer_buffer, + }, &sdl.GPUTextureRegion{ + .texture = texture, + .mip_level = 0, + .layer = 0, + .x = 0, + .y = 0, + .z = 0, + .w = width, + .h = height, + .d = 1, + }, false); + } + if (!sdl.SubmitGPUCommandBuffer(command_buffer)) return GameError.SdlError; + + const sampler = sdl.CreateGPUSampler(self.device, &sdl.GPUSamplerCreateInfo{ + .address_mode_u = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .address_mode_v = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .address_mode_w = sdl.GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, + .mag_filter = sdl.GPU_FILTER_NEAREST, + .min_filter = sdl.GPU_FILTER_LINEAR, + }) orelse return GameError.SdlError; + + return Texture{ + .texture = texture, + .sampler = sampler, + }; +} + pub fn loadMesh(self: *Self, mesh_bytes: []const u8) GameError!Mesh { std.debug.assert(mesh_bytes.len < self.transfer_buffer_capacity); @@ -359,10 +430,14 @@ pub fn beginDraw(self: *Self) GameError!bool { return true; } -pub fn drawMesh(self: *Self, mesh: Mesh, transform: Transform) GameError!void { +pub fn drawMesh(self: *Self, mesh: Mesh, texture: Texture, transform: Transform) GameError!void { if (self.render_pass == null) return; sdl.PushGPUVertexUniformData(self.command_buffer, 1, &transform.matrix(), 16 * 4); + sdl.BindGPUFragmentSamplers(self.render_pass, 0, &sdl.GPUTextureSamplerBinding{ + .texture = texture.texture, + .sampler = texture.sampler, + }, 1); sdl.DrawGPUPrimitives(self.render_pass, @intCast(mesh.vertex_count), 1, @intCast(mesh.vertex_start), 0); }