diff --git a/src/main.zig b/src/main.zig index a6f641a..8731ce0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -2,10 +2,18 @@ const std = @import("std"); const core = @import("mach-core"); const gpu = core.gpu; +const zm = @import("zmath"); +const vec = zm.f32x4; +const Mat = zm.Mat; + pub const App = @This(); +app_timer: core.Timer, title_timer: core.Timer, + pipeline: *gpu.RenderPipeline, +mvp_uniform_buffer: *gpu.Buffer, +mvp_bind_group: *gpu.BindGroup, pub fn init(app: *App) !void { try core.init(.{}); @@ -32,13 +40,32 @@ pub fn init(app: *App) !void { .fragment = &fragment, }; + app.app_timer = try core.Timer.start(); app.title_timer = try core.Timer.start(); + app.pipeline = core.device.createRenderPipeline(&pipeline_descriptor); + + app.mvp_uniform_buffer = core.device.createBuffer(&.{ + .usage = .{ .copy_dst = true, .uniform = true }, + .size = @sizeOf(zm.Mat), + .mapped_at_creation = .false, + }); + + app.mvp_bind_group = core.device.createBindGroup( + &gpu.BindGroup.Descriptor.init(.{ + .layout = app.pipeline.getBindGroupLayout(0), + .entries = &.{ + gpu.BindGroup.Entry.buffer(0, app.mvp_uniform_buffer, 0, @sizeOf(zm.Mat)), + }, + }), + ); } pub fn deinit(app: *App) void { defer core.deinit(); defer app.pipeline.release(); + defer app.mvp_uniform_buffer.release(); + defer app.mvp_bind_group.release(); } pub fn update(app: *App) !bool { @@ -50,6 +77,24 @@ pub fn update(app: *App) !bool { } } + // Set up a view matrix from the camera transform. + // This moves everything to be relative to the camera. + // TODO: Actually implement camera transform instead of hardcoding a look-at matrix. + // const view_matrix = zm.inverse(app.camera_transform); + const time = app.app_timer.read(); + const x = @cos(time * std.math.tau / 10); + const y = @sin(time * std.math.tau / 10); + const view_matrix = zm.lookAtLh(vec(x, y, -2, 1), vec(0, 0, 0, 1), vec(0, 1, 0, 1)); + + // Set up a projection matrix using the size of the window. + // The perspective projection will make things further away appear smaller. + const width: f32 = @floatFromInt(core.descriptor.width); + const height: f32 = @floatFromInt(core.descriptor.height); + const field_of_view = std.math.degreesToRadians(f32, 45.0); + const proj_matrix = zm.perspectiveFovLh(field_of_view, width / height, 0.1, 10); + + const view_proj_matrix = zm.mul(view_matrix, proj_matrix); + // Get back buffer texture to render to. const back_buffer_view = core.swap_chain.getCurrentTextureView().?; defer back_buffer_view.release(); @@ -70,15 +115,18 @@ pub fn update(app: *App) !bool { const encoder = core.device.createCommandEncoder(null); defer encoder.release(); + encoder.writeBuffer(app.mvp_uniform_buffer, 0, &[_]zm.Mat{zm.transpose(view_proj_matrix)}); + { const pass = encoder.beginRenderPass(&render_pass_info); defer pass.release(); defer pass.end(); pass.setPipeline(app.pipeline); + pass.setBindGroup(0, app.mvp_bind_group, &.{}); - // Draw a triangle with the help of a specialized shader. - pass.draw(3, 1, 0, 0); + // Draw three triangles with the help of a specialized shader. + pass.draw(9, 1, 0, 0); } // Finish recording commands, creating a `WGPUCommandBuffer`. diff --git a/src/shader.wgsl b/src/shader.wgsl index f34d2e2..34bf84e 100644 --- a/src/shader.wgsl +++ b/src/shader.wgsl @@ -1,14 +1,44 @@ +@group(0) @binding(0) var mvp_matrix: mat4x4; + +struct VertexOutput { + @builtin(position) position: vec4, + @location(0) color: vec4, +}; + +struct FragmentOutput { + @location(0) pixel_color: vec4 +}; + @vertex fn vertex_main( @builtin(vertex_index) index: u32 -) -> @builtin(position) vec4 { - let pos = array, 3>( - vec2( 0.0, 0.5), - vec2(-0.5, -0.5), - vec2( 0.5, -0.5) +) -> VertexOutput { + let color = array, 3>( + vec3(1.0, 0.0, 0.0), + vec3(0.0, 1.0, 0.0), + vec3(0.0, 0.0, 1.0), ); - return vec4(pos[index], 0.0, 1.0); + let pos = array, 9>( + vec2(-1.0, 0.5 + 0.25), + vec2(-1.5, -0.5 + 0.25), + vec2(-0.5, -0.5 + 0.25), + + vec2( 0.0, 0.5 - 0.25), + vec2(-0.5, -0.5 - 0.25), + vec2( 0.5, -0.5 - 0.25), + + vec2( 1.0, 0.5), + vec2( 0.5, -0.5), + vec2( 1.5, -0.5), + ); + + var out: VertexOutput; + out.position = vec4(pos[index], 0.0, 1.0) * mvp_matrix; + out.color = vec4(color[index / 3], 1.0); + return out; } -@fragment fn frag_main() -> @location(0) vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); +@fragment fn frag_main(in: VertexOutput) -> FragmentOutput { + var out: FragmentOutput; + out.pixel_color = in.color; + return out; }