diff --git a/build.zig b/build.zig index ea039109..bd7664d5 100644 --- a/build.zig +++ b/build.zig @@ -560,7 +560,6 @@ fn buildExamples( ) !void { const Dependency = enum { assets, - model3d, freetype, zigimg, }; @@ -600,12 +599,6 @@ fn buildExamples( .optimize = optimize, })) |dep| exe.root_module.addImport("assets", dep.module("mach-example-assets")); }, - .model3d => { - if (b.lazyDependency("mach_model3d", .{ - .target = target, - .optimize = optimize, - })) |dep| exe.root_module.addImport("model3d", dep.module("mach-model3d")); - }, .freetype => { if (b.lazyDependency("mach_freetype", .{ .target = target, @@ -642,7 +635,6 @@ fn buildCoreExamples( ) !void { const Dependency = enum { zigimg, - model3d, assets, zmath, }; @@ -667,16 +659,6 @@ fn buildCoreExamples( .{ .name = "fractal-cube", .deps = &.{.zmath} }, .{ .name = "map-async", .deps = &.{.zmath} }, .{ .name = "rgb-quad", .deps = &.{.zmath} }, - .{ - .name = "pbr-basic", - .deps = &.{ .zmath, .model3d, .assets }, - .std_platform_only = true, - }, - .{ - .name = "deferred-rendering", - .deps = &.{ .zmath, .model3d, .assets }, - .std_platform_only = true, - }, .{ .name = "textured-cube", .deps = &.{ .zmath, .zigimg, .assets } }, .{ .name = "textured-quad", .deps = &.{ .zmath, .zigimg, .assets } }, .{ .name = "sprite2d", .deps = &.{ .zmath, .zigimg, .assets } }, @@ -688,13 +670,11 @@ fn buildCoreExamples( .{ .name = "boids", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "clear-color", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "cubemap", .deps = &.{ .zmath, .zigimg, .assets }, .sysgpu = true }, - .{ .name = "deferred-rendering", .deps = &.{ .zmath, .model3d, .assets }, .std_platform_only = true, .sysgpu = true }, .{ .name = "fractal-cube", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "gen-texture-light", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "image-blur", .deps = &.{ .zmath, .zigimg, .assets }, .sysgpu = true }, .{ .name = "instanced-cube", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "map-async", .deps = &.{.zmath}, .sysgpu = true }, - .{ .name = "pbr-basic", .deps = &.{ .zmath, .model3d, .assets }, .std_platform_only = true, .sysgpu = true }, .{ .name = "pixel-post-process", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "procedural-primitives", .deps = &.{.zmath}, .sysgpu = true }, .{ .name = "rotating-cube", .deps = &.{.zmath}, .sysgpu = true }, @@ -746,12 +726,6 @@ fn buildCoreExamples( .optimize = optimize, })) |dep| app.module.addImport("zigimg", dep.module("zigimg")); }, - .model3d => { - if (b.lazyDependency("mach_model3d", .{ - .target = target, - .optimize = optimize, - })) |dep| app.module.addImport("model3d", dep.module("mach-model3d")); - }, .assets => { if (b.lazyDependency("mach_example_assets", .{ .target = target, diff --git a/build.zig.zon b/build.zig.zon index d1300a68..0adb06f4 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -87,11 +87,6 @@ .hash = "1220ebfa8587cfd644995fc08e218dbb3ebd7344fb8e129ff02bc5a6d52a2325370d", .lazy = true, }, - .mach_model3d = .{ - .url = "https://pkg.machengine.org/mach-model3d/5d4dd54db2da123b38656d4574afaa4c04fd3838.tar.gz", - .hash = "1220c1c16fa17783379f773c291af3693b84744200099f1cf5c1ddb66c4fe88bcf3a", - .lazy = true, - }, .mach_opus = .{ .url = "https://pkg.machengine.org/mach-opus/58a22201881d7639339f3c029d13a00bbfe005e2.tar.gz", .hash = "1220821426e087874779f8d76a6bb74844aa3934648aad5b7331701e5f386791c51e", diff --git a/src/core/examples/deferred-rendering/fragmentDeferredRendering.wgsl b/src/core/examples/deferred-rendering/fragmentDeferredRendering.wgsl deleted file mode 100644 index 71ed702e..00000000 --- a/src/core/examples/deferred-rendering/fragmentDeferredRendering.wgsl +++ /dev/null @@ -1,83 +0,0 @@ - -@group(0) @binding(0) var gBufferNormal: texture_2d; -@group(0) @binding(1) var gBufferAlbedo: texture_2d; -@group(0) @binding(2) var gBufferDepth: texture_depth_2d; - -struct LightData { - position : vec4, - color : vec3, - radius : f32, -} -struct LightsBuffer { - lights: array, -} -@group(1) @binding(0) var lightsBuffer: LightsBuffer; - -struct Config { - numLights : u32, -} -struct Camera { - viewProjectionMatrix : mat4x4, - invViewProjectionMatrix : mat4x4, -} -@group(1) @binding(1) var config: Config; -@group(1) @binding(2) var camera: Camera; - -fn world_from_screen_coord(coord : vec2, depth_sample: f32) -> vec3 { - // reconstruct world-space position from the screen coordinate. - let posClip = vec4(coord.x * 2.0 - 1.0, (1.0 - coord.y) * 2.0 - 1.0, depth_sample, 1.0); - let posWorldW = camera.invViewProjectionMatrix * posClip; - let posWorld = posWorldW.xyz / posWorldW.www; - return posWorld; -} - -@fragment -fn main( - @builtin(position) coord : vec4 -) -> @location(0) vec4 { - var result : vec3; - - let depth = textureLoad( - gBufferDepth, - vec2(floor(coord.xy)), - 0 - ); - - // Don't light the sky. - if (depth >= 1.0) { - discard; - } - - let bufferSize = textureDimensions(gBufferDepth); - let coordUV = coord.xy / vec2(bufferSize); - let position = world_from_screen_coord(coordUV, depth); - - let normal = textureLoad( - gBufferNormal, - vec2(floor(coord.xy)), - 0 - ).xyz; - - let albedo = textureLoad( - gBufferAlbedo, - vec2(floor(coord.xy)), - 0 - ).rgb; - - for (var i = 0u; i < config.numLights; i++) { - let L = lightsBuffer.lights[i].position.xyz - position; - let distance = length(L); - if (distance > lightsBuffer.lights[i].radius) { - continue; - } - let lambert = max(dot(normal, normalize(L)), 0.0); - result += vec3( - lambert * pow(1.0 - distance / lightsBuffer.lights[i].radius, 2.0) * lightsBuffer.lights[i].color * albedo - ); - } - - // some manual ambient - result += vec3(0.2); - - return vec4(result, 1.0); -} diff --git a/src/core/examples/deferred-rendering/fragmentGBuffersDebugView.wgsl b/src/core/examples/deferred-rendering/fragmentGBuffersDebugView.wgsl deleted file mode 100644 index cedf7645..00000000 --- a/src/core/examples/deferred-rendering/fragmentGBuffersDebugView.wgsl +++ /dev/null @@ -1,44 +0,0 @@ - -@group(0) @binding(0) var gBufferNormal: texture_2d; -@group(0) @binding(1) var gBufferAlbedo: texture_2d; -@group(0) @binding(2) var gBufferDepth: texture_depth_2d; - -@group(1) @binding(0) var canvas : CanvasConstants; - -struct CanvasConstants { - size: vec2, -} - -@fragment -fn main( - @builtin(position) coord : vec4 -) -> @location(0) vec4 { - var result : vec4; - let c = coord.xy / vec2(canvas.size.x, canvas.size.y); - if (c.x < 0.33333) { - let rawDepth = textureLoad( - gBufferDepth, - vec2(floor(coord.xy)), - 0 - ); - // remap depth into something a bit more visible - let depth = (1.0 - rawDepth) * 50.0; - result = vec4(depth); - } else if (c.x < 0.66667) { - result = textureLoad( - gBufferNormal, - vec2(floor(coord.xy)), - 0 - ); - result.x = (result.x + 1.0) * 0.5; - result.y = (result.y + 1.0) * 0.5; - result.z = (result.z + 1.0) * 0.5; - } else { - result = textureLoad( - gBufferAlbedo, - vec2(floor(coord.xy)), - 0 - ); - } - return result; -} diff --git a/src/core/examples/deferred-rendering/fragmentWriteGBuffers.wgsl b/src/core/examples/deferred-rendering/fragmentWriteGBuffers.wgsl deleted file mode 100644 index 3cd2f652..00000000 --- a/src/core/examples/deferred-rendering/fragmentWriteGBuffers.wgsl +++ /dev/null @@ -1,22 +0,0 @@ -struct GBufferOutput { - @location(0) normal : vec4, - - // Textures: diffuse color, specular color, smoothness, emissive etc. could go here - @location(1) albedo : vec4, -} - -@fragment -fn main( - @location(0) fragNormal: vec3, - @location(1) fragUV : vec2 -) -> GBufferOutput { - // faking some kind of checkerboard texture - let uv = floor(30.0 * fragUV); - let c = 0.2 + 0.5 * ((uv.x + uv.y) - 2.0 * floor((uv.x + uv.y) / 2.0)); - - var output : GBufferOutput; - output.normal = vec4(fragNormal, 1.0); - output.albedo = vec4(c, c, c, 1.0); - - return output; -} diff --git a/src/core/examples/deferred-rendering/lightUpdate.wgsl b/src/core/examples/deferred-rendering/lightUpdate.wgsl deleted file mode 100644 index 2c7672dd..00000000 --- a/src/core/examples/deferred-rendering/lightUpdate.wgsl +++ /dev/null @@ -1,34 +0,0 @@ -struct LightData { - position : vec4, - color : vec3, - radius : f32, -} -struct LightsBuffer { - lights: array, -} -@group(0) @binding(0) var lightsBuffer: LightsBuffer; - -struct Config { - numLights : u32, -} -@group(0) @binding(1) var config: Config; - -struct LightExtent { - min : vec4, - max : vec4, -} -@group(0) @binding(2) var lightExtent: LightExtent; - -@compute @workgroup_size(64, 1, 1) -fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) { - var index = GlobalInvocationID.x; - if (index >= config.numLights) { - return; - } - - lightsBuffer.lights[index].position.y = lightsBuffer.lights[index].position.y - 0.5 - 0.003 * (f32(index) - 64.0 * floor(f32(index) / 64.0)); - - if (lightsBuffer.lights[index].position.y < lightExtent.min.y) { - lightsBuffer.lights[index].position.y = lightExtent.max.y; - } -} diff --git a/src/core/examples/deferred-rendering/main.zig b/src/core/examples/deferred-rendering/main.zig deleted file mode 100644 index 3c93009b..00000000 --- a/src/core/examples/deferred-rendering/main.zig +++ /dev/null @@ -1,1213 +0,0 @@ -const std = @import("std"); - -const mach = @import("mach"); -const core = mach.core; -const gpu = mach.gpu; - -const m3d = @import("model3d"); -const zm = @import("zmath"); -const assets = @import("assets"); -const VertexWriter = @import("vertex_writer.zig").VertexWriter; - -pub const App = @This(); - -const Vec2 = [2]f32; -const Vec3 = [3]f32; -const Vec4 = [4]f32; -const Mat4 = [4]Vec4; - -fn Dimensions2D(comptime T: type) type { - return struct { - width: T, - height: T, - }; -} - -const Vertex = extern struct { - position: Vec3, - normal: Vec3, - uv: Vec2, -}; - -const ViewMatrices = struct { - up_vector: zm.Vec, - origin: zm.Vec, - projection_matrix: zm.Mat, - view_proj_matrix: zm.Mat, -}; - -const TextureQuadPass = struct { - color_attachment: gpu.RenderPassColorAttachment, - descriptor: gpu.RenderPassDescriptor, -}; - -const WriteGBufferPass = struct { - color_attachments: [2]gpu.RenderPassColorAttachment, - depth_stencil_attachment: gpu.RenderPassDepthStencilAttachment, - descriptor: gpu.RenderPassDescriptor, -}; - -const RenderMode = enum(u32) { - rendering, - gbuffer_view, -}; - -const Settings = struct { - render_mode: RenderMode, - lights_count: i32, -}; - -// -// Constants -// - -const max_num_lights = 1024; -const light_data_stride = 8; -const light_extent_min = Vec3{ -50.0, -30.0, -50.0 }; -const light_extent_max = Vec3{ 50.0, 30.0, 50.0 }; -const camera_uniform_buffer_size = @sizeOf(Mat4) * 2; - -// -// Member variables -// - -const GBuffer = struct { - texture_2d_float16: *gpu.Texture, - texture_albedo: *gpu.Texture, - texture_depth: *gpu.Texture, - texture_views: [3]*gpu.TextureView, -}; - -const Lights = struct { - buffer: *gpu.Buffer, - buffer_size: u64, - extent_buffer: *gpu.Buffer, - extent_buffer_size: u64, - config_uniform_buffer: *gpu.Buffer, - config_uniform_buffer_size: u64, - buffer_bind_group: *gpu.BindGroup, - buffer_bind_group_layout: *gpu.BindGroupLayout, - buffer_compute_bind_group: *gpu.BindGroup, - buffer_compute_bind_group_layout: *gpu.BindGroupLayout, -}; - -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - -title_timer: core.Timer, -timer: core.Timer, -delta_time: f32, - -camera_rotation: f32, -vertex_buffer: *gpu.Buffer, -vertex_count: u32, -index_buffer: *gpu.Buffer, -index_count: u32, -gbuffer: GBuffer, -model_uniform_buffer: *gpu.Buffer, -camera_uniform_buffer: *gpu.Buffer, -surface_size_uniform_buffer: *gpu.Buffer, -lights: Lights, -view_matrices: ViewMatrices, - -// Bind groups -scene_uniform_bind_group: *gpu.BindGroup, -surface_size_uniform_bind_group: *gpu.BindGroup, -gbuffer_textures_bind_group: *gpu.BindGroup, - -// Bind group layouts -scene_uniform_bind_group_layout: *gpu.BindGroupLayout, -surface_size_uniform_bind_group_layout: *gpu.BindGroupLayout, -gbuffer_textures_bind_group_layout: *gpu.BindGroupLayout, - -// Pipelines -write_gbuffers_pipeline: *gpu.RenderPipeline, -gbuffers_debug_view_pipeline: *gpu.RenderPipeline, -deferred_render_pipeline: *gpu.RenderPipeline, -light_update_compute_pipeline: *gpu.ComputePipeline, - -// Pipeline layouts -write_gbuffers_pipeline_layout: *gpu.PipelineLayout, -gbuffers_debug_view_pipeline_layout: *gpu.PipelineLayout, -deferred_render_pipeline_layout: *gpu.PipelineLayout, -light_update_compute_pipeline_layout: *gpu.PipelineLayout, - -// Render pass descriptor -write_gbuffer_pass: WriteGBufferPass, -texture_quad_pass: TextureQuadPass, -settings: Settings, - -screen_dimensions: Dimensions2D(u32), -is_paused: bool, - -// -// Functions -// - -pub fn init(app: *App) !void { - try core.init(.{}); - - // This example has some frame-rate-dependent animation, so restrict frame rate to 60hz. - core.setFrameRateLimit(60); - - app.timer = try core.Timer.start(); - app.title_timer = try core.Timer.start(); - - app.camera_rotation = 0.0; - app.is_paused = false; - app.settings.render_mode = .rendering; - app.settings.lights_count = 128; - - app.screen_dimensions = Dimensions2D(u32){ - .width = core.descriptor.width, - .height = core.descriptor.height, - }; - - try app.loadMeshFromModel3d(std.heap.c_allocator, assets.stanford_dragon_m3d); - app.prepareGBufferTextureRenderTargets(); - app.prepareBindGroupLayouts(); - app.prepareRenderPipelineLayouts(); - app.prepareWriteGBuffersPipeline(); - app.prepareGBuffersDebugViewPipeline(); - app.prepareDeferredRenderPipeline(); - app.setupRenderPasses(); - app.prepareUniformBuffers(); - app.prepareComputePipelineLayout(); - app.prepareLightUpdateComputePipeline(); - app.prepareLights(); - app.prepareViewMatrices(); - app.printControls(); -} - -pub fn deinit(app: *App) void { - defer _ = gpa.deinit(); - defer core.deinit(); - - app.write_gbuffers_pipeline.release(); - app.gbuffers_debug_view_pipeline.release(); - app.deferred_render_pipeline.release(); - app.light_update_compute_pipeline.release(); - - app.write_gbuffers_pipeline_layout.release(); - app.gbuffers_debug_view_pipeline_layout.release(); - app.deferred_render_pipeline_layout.release(); - app.light_update_compute_pipeline_layout.release(); - - app.scene_uniform_bind_group.release(); - app.surface_size_uniform_bind_group.release(); - app.gbuffer_textures_bind_group.release(); - - app.lights.buffer.release(); - app.lights.extent_buffer.release(); - app.lights.config_uniform_buffer.release(); - app.lights.buffer_bind_group.release(); - app.lights.buffer_bind_group_layout.release(); - app.lights.buffer_compute_bind_group.release(); - app.lights.buffer_compute_bind_group_layout.release(); - - app.gbuffer.texture_views[0].release(); - app.gbuffer.texture_views[1].release(); - app.gbuffer.texture_views[2].release(); - - app.gbuffer.texture_2d_float16.release(); - app.gbuffer.texture_albedo.release(); - app.gbuffer.texture_depth.release(); - - app.scene_uniform_bind_group_layout.release(); - app.surface_size_uniform_bind_group_layout.release(); - app.gbuffer_textures_bind_group_layout.release(); - - app.surface_size_uniform_buffer.release(); - app.model_uniform_buffer.release(); - app.camera_uniform_buffer.release(); - app.vertex_buffer.release(); - app.index_buffer.release(); -} - -pub fn update(app: *App) !bool { - app.delta_time = app.timer.lap(); - - var iter = core.pollEvents(); - while (iter.next()) |event| { - app.updateUI(event); - switch (event) { - .framebuffer_resize => |ev| { - app.screen_dimensions.width = ev.width; - app.screen_dimensions.height = ev.height; - - // TODO: we use destroy() here instead of release() because our reference counting - // is wrong somewhere else. - app.gbuffer.texture_2d_float16.release(); - app.gbuffer.texture_albedo.release(); - app.gbuffer.texture_depth.release(); - app.gbuffer.texture_views[0].release(); - app.gbuffer.texture_views[1].release(); - app.gbuffer.texture_views[2].release(); - app.gbuffer_textures_bind_group.release(); - - app.prepareGBufferTextureRenderTargets(); - app.setupRenderPasses(); - - const bind_group_entries = [_]gpu.BindGroup.Entry{ - gpu.BindGroup.Entry.textureView(0, app.gbuffer.texture_views[0]), - gpu.BindGroup.Entry.textureView(1, app.gbuffer.texture_views[1]), - gpu.BindGroup.Entry.textureView(2, app.gbuffer.texture_views[2]), - }; - app.gbuffer_textures_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.gbuffer_textures_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - - app.prepareViewMatrices(); - }, - .close => return true, - else => {}, - } - } - - if (!app.is_paused) { - app.updateUniformBuffers(); - } - - const command = try app.buildCommandBuffer(); - const queue = core.queue; - queue.submit(&[_]*gpu.CommandBuffer{command}); - command.release(); - core.swap_chain.present(); - core.swap_chain.getCurrentTextureView().?.release(); - - // update the window title every second - if (app.title_timer.read() >= 1.0) { - app.title_timer.reset(); - try core.printTitle("Deferred Rendering [ {d}fps ] [ Input {d}hz ]", .{ - core.frameRate(), - core.inputRate(), - }); - } - - return false; -} - -fn loadMeshFromModel3d(app: *App, allocator: std.mem.Allocator, model_data: [:0]const u8) !void { - const m3d_model = m3d.load(model_data, null, null, null) orelse return error.LoadModelFailed; - - const vertex_count = m3d_model.handle.numvertex; - const vertices = m3d_model.handle.vertex[0..vertex_count]; - - const face_count = m3d_model.handle.numface; - app.index_count = (face_count * 3) + 6; - - var vertex_writer = try VertexWriter(Vertex, u16).init( - allocator, - @as(u16, @intCast(app.index_count)), - @as(u16, @intCast(vertex_count)), - @as(u16, @intCast(face_count * 3)), - ); - defer vertex_writer.deinit(allocator); - - const scale: f32 = 80.0; - const plane_xy = [2]usize{ 0, 1 }; - var extent_min = [2]f32{ std.math.floatMax(f32), std.math.floatMax(f32) }; - var extent_max = [2]f32{ std.math.floatMin(f32), std.math.floatMin(f32) }; - - var i: usize = 0; - while (i < face_count) : (i += 1) { - const face = m3d_model.handle.face[i]; - var x: usize = 0; - while (x < 3) : (x += 1) { - const vertex_index = face.vertex[x]; - const normal_index = face.normal[x]; - const position = Vec3{ - vertices[vertex_index].x * scale, - vertices[vertex_index].y * scale, - vertices[vertex_index].z * scale, - }; - extent_min[0] = @min(position[plane_xy[0]], extent_min[0]); - extent_min[1] = @min(position[plane_xy[1]], extent_min[1]); - extent_max[0] = @max(position[plane_xy[0]], extent_max[0]); - extent_max[1] = @max(position[plane_xy[1]], extent_max[1]); - const vertex = Vertex{ .position = position, .normal = .{ - vertices[normal_index].x, - vertices[normal_index].y, - vertices[normal_index].z, - }, .uv = .{ position[plane_xy[0]], position[plane_xy[1]] } }; - vertex_writer.put(vertex, @as(u16, @intCast(vertex_index))); - } - } - - const vertex_buffer = vertex_writer.vertices[0 .. vertex_writer.next_packed_index + 4]; - const index_buffer = vertex_writer.indices; - - app.vertex_count = @as(u32, @intCast(vertex_buffer.len)); - - // - // Compute UV values - // - for (vertex_buffer) |*vertex| { - vertex.uv = .{ - (vertex.uv[0] - extent_min[0]) / (extent_max[0] - extent_min[0]), - (vertex.uv[1] - extent_min[1]) / (extent_max[1] - extent_min[1]), - }; - } - - // - // Manually append ground plane to mesh - // - { - const last_vertex_index: u16 = @as(u16, @intCast(vertex_buffer.len - 4)); - const index_base = index_buffer.len - 6; - index_buffer[index_base + 0] = last_vertex_index; - index_buffer[index_base + 1] = last_vertex_index + 2; - index_buffer[index_base + 2] = last_vertex_index + 1; - index_buffer[index_base + 3] = last_vertex_index; - index_buffer[index_base + 4] = last_vertex_index + 1; - index_buffer[index_base + 5] = last_vertex_index + 3; - } - - { - const index_base = vertex_buffer.len - 4; - vertex_buffer[index_base + 0].position = .{ -100.0, 20.0, -100.0 }; - vertex_buffer[index_base + 1].position = .{ 100.0, 20.0, 100.0 }; - vertex_buffer[index_base + 2].position = .{ -100.0, 20.0, 100.0 }; - vertex_buffer[index_base + 3].position = .{ 100.0, 20.0, -100.0 }; - vertex_buffer[index_base + 0].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 1].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 2].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 3].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 0].uv = .{ 0.0, 0.0 }; - vertex_buffer[index_base + 1].uv = .{ 1.0, 1.0 }; - vertex_buffer[index_base + 2].uv = .{ 0.0, 1.0 }; - vertex_buffer[index_base + 3].uv = .{ 1.0, 0.0 }; - } - - { - const buffer_size = vertex_buffer.len * @sizeOf(Vertex); - app.vertex_buffer = core.device.createBuffer(&.{ - .usage = .{ .vertex = true }, - .size = roundToMultipleOf4(u64, buffer_size), - .mapped_at_creation = .true, - }); - var mapping = app.vertex_buffer.getMappedRange(Vertex, 0, vertex_buffer.len).?; - @memcpy(mapping[0..vertex_buffer.len], vertex_buffer); - app.vertex_buffer.unmap(); - } - { - const buffer_size = index_buffer.len * @sizeOf(u16); - app.index_buffer = core.device.createBuffer(&.{ - .usage = .{ .index = true }, - .size = roundToMultipleOf4(u64, buffer_size), - .mapped_at_creation = .true, - }); - var mapping = app.index_buffer.getMappedRange(u16, 0, index_buffer.len).?; - @memcpy(mapping[0..index_buffer.len], index_buffer); - app.index_buffer.unmap(); - } -} - -fn prepareGBufferTextureRenderTargets(app: *App) void { - var screen_extent = gpu.Extent3D{ - .width = app.screen_dimensions.width, - .height = app.screen_dimensions.height, - .depth_or_array_layers = 2, - }; - screen_extent.depth_or_array_layers = 1; - app.gbuffer.texture_2d_float16 = core.device.createTexture(&.{ - .size = screen_extent, - .format = .rgba16_float, - .mip_level_count = 1, - .sample_count = 1, - .usage = .{ - .texture_binding = true, - .render_attachment = true, - }, - }); - app.gbuffer.texture_albedo = core.device.createTexture(&.{ - .size = screen_extent, - .format = .bgra8_unorm, - .usage = .{ - .texture_binding = true, - .render_attachment = true, - }, - }); - app.gbuffer.texture_depth = core.device.createTexture(&.{ - .size = screen_extent, - .mip_level_count = 1, - .sample_count = 1, - .dimension = .dimension_2d, - .format = .depth24_plus, - .usage = .{ - .texture_binding = true, - .render_attachment = true, - }, - }); - - var texture_view_descriptor = gpu.TextureView.Descriptor{ - .format = .undefined, - .dimension = .dimension_2d, - .array_layer_count = 1, - .aspect = .all, - .base_array_layer = 0, - }; - - texture_view_descriptor.format = .rgba16_float; - app.gbuffer.texture_views[0] = app.gbuffer.texture_2d_float16.createView(&texture_view_descriptor); - - texture_view_descriptor.format = .bgra8_unorm; - app.gbuffer.texture_views[1] = app.gbuffer.texture_albedo.createView(&texture_view_descriptor); - - texture_view_descriptor.format = .depth24_plus; - app.gbuffer.texture_views[2] = app.gbuffer.texture_depth.createView(&texture_view_descriptor); -} - -fn prepareBindGroupLayouts(app: *App) void { - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.texture(0, .{ .fragment = true }, .unfilterable_float, .dimension_2d, false), - gpu.BindGroupLayout.Entry.texture(1, .{ .fragment = true }, .unfilterable_float, .dimension_2d, false), - gpu.BindGroupLayout.Entry.texture(2, .{ .fragment = true }, .depth, .dimension_2d, false), - }; - app.gbuffer_textures_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const min_binding_size = light_data_stride * max_num_lights * @sizeOf(f32); - const visibility = gpu.ShaderStageFlags{ .fragment = true, .compute = true }; - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer( - 0, - visibility, - .read_only_storage, - false, - min_binding_size, - ), - gpu.BindGroupLayout.Entry.buffer(1, visibility, .uniform, false, @sizeOf(u32)), - gpu.BindGroupLayout.Entry.buffer(2, .{ .fragment = true }, .uniform, false, @sizeOf(Mat4) * 2), - }; - app.lights.buffer_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer(0, .{ .fragment = true }, .uniform, false, @sizeOf(Vec2)), - }; - app.surface_size_uniform_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, false, @sizeOf(Mat4) * 2), - gpu.BindGroupLayout.Entry.buffer(1, .{ .vertex = true }, .uniform, false, @sizeOf(Mat4) * 2), - }; - app.scene_uniform_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer(0, .{ .compute = true }, .storage, false, @sizeOf(f32) * light_data_stride * max_num_lights), - gpu.BindGroupLayout.Entry.buffer(1, .{ .compute = true }, .uniform, false, @sizeOf(u32)), - gpu.BindGroupLayout.Entry.buffer(2, .{ .compute = true }, .uniform, false, camera_uniform_buffer_size), - }; - app.lights.buffer_compute_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } -} - -fn prepareRenderPipelineLayouts(app: *App) void { - { - // Write GBuffers pipeline layout - const bind_group_layouts = [_]*gpu.BindGroupLayout{app.scene_uniform_bind_group_layout}; - app.write_gbuffers_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - } - { - // GBuffers debug view pipeline layout - const bind_group_layouts = [_]*gpu.BindGroupLayout{ - app.gbuffer_textures_bind_group_layout, - app.surface_size_uniform_bind_group_layout, - }; - app.gbuffers_debug_view_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - } - { - // Deferred render pipeline layout - const bind_group_layouts = [_]*gpu.BindGroupLayout{ - app.gbuffer_textures_bind_group_layout, - app.lights.buffer_bind_group_layout, - app.surface_size_uniform_bind_group_layout, - }; - app.deferred_render_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - } -} - -fn prepareWriteGBuffersPipeline(app: *App) void { - const color_target_states = [_]gpu.ColorTargetState{ - .{ .format = .rgba16_float }, - .{ .format = .bgra8_unorm }, - }; - - const write_gbuffers_vertex_buffer_layout = gpu.VertexBufferLayout.init(.{ - .array_stride = @sizeOf(Vertex), - .step_mode = .vertex, - .attributes = &.{ - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "position"), .shader_location = 0 }, - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "normal"), .shader_location = 1 }, - .{ .format = .float32x2, .offset = @offsetOf(Vertex, "uv"), .shader_location = 2 }, - }, - }); - - const vertex_shader_module = core.device.createShaderModuleWGSL( - "vertexWriteGBuffers.wgsl", - @embedFile("vertexWriteGBuffers.wgsl"), - ); - const fragment_shader_module = core.device.createShaderModuleWGSL( - "fragmentWriteGBuffers.wgsl", - @embedFile("fragmentWriteGBuffers.wgsl"), - ); - - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .label = "gbuffers_pipeline", - .layout = app.write_gbuffers_pipeline_layout, - .primitive = .{ .cull_mode = .back }, - .depth_stencil = &.{ - .format = .depth24_plus, - .depth_write_enabled = .true, - .depth_compare = .less, - }, - .vertex = gpu.VertexState.init(.{ - .module = vertex_shader_module, - .entry_point = "main", - .buffers = &.{write_gbuffers_vertex_buffer_layout}, - }), - .fragment = &gpu.FragmentState.init(.{ - .module = fragment_shader_module, - .entry_point = "main", - .targets = &color_target_states, - }), - }; - app.write_gbuffers_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - - vertex_shader_module.release(); - fragment_shader_module.release(); -} - -fn prepareGBuffersDebugViewPipeline(app: *App) void { - const blend_component_descriptor = gpu.BlendComponent{ - .operation = .add, - .src_factor = .one, - .dst_factor = .zero, - }; - - const color_target_state = gpu.ColorTargetState{ - .format = core.descriptor.format, - .blend = &.{ - .color = blend_component_descriptor, - .alpha = blend_component_descriptor, - }, - }; - - const vertex_shader_module = core.device.createShaderModuleWGSL( - "vertexTextureQuad.wgsl", - @embedFile("vertexTextureQuad.wgsl"), - ); - const fragment_shader_module = core.device.createShaderModuleWGSL( - "fragmentGBuffersDebugView.wgsl", - @embedFile("fragmentGBuffersDebugView.wgsl"), - ); - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .layout = app.gbuffers_debug_view_pipeline_layout, - .primitive = .{ - .cull_mode = .back, - }, - .vertex = gpu.VertexState.init(.{ - .module = vertex_shader_module, - .entry_point = "main", - }), - .fragment = &gpu.FragmentState.init(.{ - .module = fragment_shader_module, - .entry_point = "main", - .targets = &.{color_target_state}, - }), - }; - app.gbuffers_debug_view_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - vertex_shader_module.release(); - fragment_shader_module.release(); -} - -fn prepareDeferredRenderPipeline(app: *App) void { - const blend_component_descriptor = gpu.BlendComponent{ - .operation = .add, - .src_factor = .one, - .dst_factor = .zero, - }; - - const color_target_state = gpu.ColorTargetState{ - .format = .bgra8_unorm, - .blend = &.{ - .color = blend_component_descriptor, - .alpha = blend_component_descriptor, - }, - }; - - const vertex_shader_module = core.device.createShaderModuleWGSL( - "vertexTextureQuad.wgsl", - @embedFile("vertexTextureQuad.wgsl"), - ); - const fragment_shader_module = core.device.createShaderModuleWGSL( - "fragmentDeferredRendering.wgsl", - @embedFile("fragmentDeferredRendering.wgsl"), - ); - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .layout = app.deferred_render_pipeline_layout, - .primitive = .{ - .cull_mode = .back, - }, - .vertex = gpu.VertexState.init(.{ - .module = vertex_shader_module, - .entry_point = "main", - }), - .fragment = &gpu.FragmentState.init(.{ - .module = fragment_shader_module, - .entry_point = "main", - .targets = &.{color_target_state}, - }), - }; - app.deferred_render_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - vertex_shader_module.release(); - fragment_shader_module.release(); -} - -fn setupRenderPasses(app: *App) void { - { - // Write GBuffer pass - app.write_gbuffer_pass.color_attachments = [_]gpu.RenderPassColorAttachment{ - .{ - .view = app.gbuffer.texture_views[0], - .load_op = .clear, - .store_op = .store, - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 1.0, - .a = 1.0, - }, - }, - .{ - .view = app.gbuffer.texture_views[1], - .load_op = .clear, - .store_op = .store, - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 0.0, - .a = 1.0, - }, - }, - }; - - app.write_gbuffer_pass.depth_stencil_attachment = gpu.RenderPassDepthStencilAttachment{ - .view = app.gbuffer.texture_views[2], - .depth_load_op = .clear, - .depth_store_op = .store, - .depth_clear_value = 1.0, - .stencil_clear_value = 1.0, - }; - - app.write_gbuffer_pass.descriptor = gpu.RenderPassDescriptor.init(.{ - .label = "write_gbuffer_pass", - .color_attachments = &app.write_gbuffer_pass.color_attachments, - .depth_stencil_attachment = &app.write_gbuffer_pass.depth_stencil_attachment, - }); - } - { - // Texture Quad Pass - app.texture_quad_pass.color_attachment = gpu.RenderPassColorAttachment{ - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 0.0, - .a = 1.0, - }, - .load_op = .clear, - .store_op = .store, - }; - - app.texture_quad_pass.descriptor = gpu.RenderPassDescriptor{ - .label = "texture_quad_pass(1)", - .color_attachment_count = 1, - .color_attachments = &[_]gpu.RenderPassColorAttachment{app.texture_quad_pass.color_attachment}, - }; - } -} - -fn prepareUniformBuffers(app: *App) void { - { - // Config uniform buffer - app.lights.config_uniform_buffer_size = @sizeOf(i32); - app.lights.config_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.lights.config_uniform_buffer_size, - .mapped_at_creation = .true, - }); - var config_data = app.lights.config_uniform_buffer.getMappedRange(i32, 0, 1).?; - config_data[0] = app.settings.lights_count; - app.lights.config_uniform_buffer.unmap(); - } - { - // Model uniform buffer - app.model_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = @sizeOf(Mat4) * 2, - }); - } - { - // Camera uniform buffer - app.camera_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = @sizeOf(Mat4) * 2, - }); - } - { - // Scene uniform bind group - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.model_uniform_buffer, - .size = @sizeOf(Mat4) * 2, - }, - .{ - .binding = 1, - .buffer = app.camera_uniform_buffer, - .size = camera_uniform_buffer_size, - }, - }; - const bind_group_layout = app.write_gbuffers_pipeline.getBindGroupLayout(0); - app.scene_uniform_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .label = "scene_uniform_bind_group", - .layout = bind_group_layout, - .entries = &bind_group_entries, - }), - ); - bind_group_layout.release(); - } - { - // Surface size uniform buffer - app.surface_size_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = @sizeOf(f32) * 4, - }); - } - { - // Surface size uniform bind group - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.surface_size_uniform_buffer, - .size = @sizeOf(f32) * 2, - }, - }; - app.surface_size_uniform_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.surface_size_uniform_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } - { - // GBuffer textures bind group - const bind_group_entries = [_]gpu.BindGroup.Entry{ - gpu.BindGroup.Entry.textureView(0, app.gbuffer.texture_views[0]), - gpu.BindGroup.Entry.textureView(1, app.gbuffer.texture_views[1]), - gpu.BindGroup.Entry.textureView(2, app.gbuffer.texture_views[2]), - }; - app.gbuffer_textures_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.gbuffer_textures_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } -} - -fn prepareComputePipelineLayout(app: *App) void { - const bind_group_layouts = [_]*gpu.BindGroupLayout{app.lights.buffer_compute_bind_group_layout}; - app.light_update_compute_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); -} - -fn prepareLightUpdateComputePipeline(app: *App) void { - const shader_module = core.device.createShaderModuleWGSL( - "lightUpdate.wgsl", - @embedFile("lightUpdate.wgsl"), - ); - app.light_update_compute_pipeline = core.device.createComputePipeline(&gpu.ComputePipeline.Descriptor{ - .compute = gpu.ProgrammableStageDescriptor{ - .module = shader_module, - .entry_point = "main", - }, - .layout = app.light_update_compute_pipeline_layout, - }); - shader_module.release(); -} - -fn prepareLights(app: *App) void { - // Lights data are uploaded in a storage buffer - // which could be updated/culled/etc. with a compute shader - const extent = comptime Vec3{ - light_extent_max[0] - light_extent_min[0], - light_extent_max[1] - light_extent_min[1], - light_extent_max[2] - light_extent_min[2], - }; - app.lights.buffer_size = @sizeOf(f32) * light_data_stride * max_num_lights; - app.lights.buffer = core.device.createBuffer(&.{ - .usage = .{ .storage = true }, - .size = app.lights.buffer_size, - .mapped_at_creation = .true, - }); - // We randomly populate lights randomly in a box range - // And simply move them along y-axis per frame to show they are dynamic lightings - var light_data = app.lights.buffer.getMappedRange(f32, 0, light_data_stride * max_num_lights).?; - - var xoroshiro = std.rand.Xoroshiro128.init(9273853284918); - const rng = std.rand.Random.init( - &xoroshiro, - std.rand.Xoroshiro128.fill, - ); - var i: usize = 0; - var offset: usize = 0; - while (i < max_num_lights) : (i += 1) { - offset = light_data_stride * i; - // Position - light_data[offset + 0] = rng.float(f32) * extent[0] + light_extent_min[0]; - light_data[offset + 1] = rng.float(f32) * extent[1] + light_extent_min[1]; - light_data[offset + 2] = rng.float(f32) * extent[2] + light_extent_min[2]; - light_data[offset + 3] = 1.0; - // Color - light_data[offset + 4] = rng.float(f32) * 2.0; - light_data[offset + 5] = rng.float(f32) * 2.0; - light_data[offset + 6] = rng.float(f32) * 2.0; - // Radius - light_data[offset + 7] = 20.0; - } - app.lights.buffer.unmap(); - // - // Lights extent buffer - // - app.lights.extent_buffer_size = @sizeOf(f32) * light_data_stride * max_num_lights; - app.lights.extent_buffer = core.device.createBuffer(&.{ - .usage = .{ .uniform = true, .copy_dst = true }, - .size = app.lights.extent_buffer_size, - }); - var light_extent_data = [1]f32{0.0} ** 8; - @memcpy(light_extent_data[0..3], &light_extent_min); - @memcpy(light_extent_data[4..7], &light_extent_max); - const queue = core.queue; - queue.writeBuffer( - app.lights.extent_buffer, - 0, - &light_extent_data, - ); - // - // Lights buffer bind group - // - { - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.lights.buffer, - .size = app.lights.buffer_size, - }, - .{ - .binding = 1, - .buffer = app.lights.config_uniform_buffer, - .size = app.lights.config_uniform_buffer_size, - }, - .{ - .binding = 2, - .buffer = app.camera_uniform_buffer, - .size = camera_uniform_buffer_size, - }, - }; - app.lights.buffer_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.lights.buffer_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } - // - // Lights buffer compute bind group - // - { - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.lights.buffer, - .size = app.lights.buffer_size, - }, - .{ - .binding = 1, - .buffer = app.lights.config_uniform_buffer, - .size = app.lights.config_uniform_buffer_size, - }, - .{ - .binding = 2, - .buffer = app.lights.extent_buffer, - .size = app.lights.extent_buffer_size, - }, - }; - const bind_group_layout = app.light_update_compute_pipeline.getBindGroupLayout(0); - app.lights.buffer_compute_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = bind_group_layout, - .entries = &bind_group_entries, - }), - ); - bind_group_layout.release(); - } -} - -fn prepareViewMatrices(app: *App) void { - const screen_dimensions = Dimensions2D(f32){ - .width = @as(f32, @floatFromInt(app.screen_dimensions.width)), - .height = @as(f32, @floatFromInt(app.screen_dimensions.height)), - }; - const aspect: f32 = screen_dimensions.width / screen_dimensions.height; - const fov: f32 = 2.0 * std.math.pi / 5.0; - const znear: f32 = 1.0; - const zfar: f32 = 2000.0; - app.view_matrices.projection_matrix = zm.perspectiveFovRhGl(fov, aspect, znear, zfar); - const eye_position = zm.Vec{ 0.0, 50.0, -100.0, 0.0 }; - app.view_matrices.up_vector = zm.Vec{ 0.0, 1.0, 0.0, 0.0 }; - app.view_matrices.origin = zm.Vec{ 0.0, 0.0, 0.0, 0.0 }; - const view_matrix = zm.lookAtRh( - eye_position, - app.view_matrices.origin, - app.view_matrices.up_vector, - ); - const view_proj_matrix: zm.Mat = zm.mul(view_matrix, app.view_matrices.projection_matrix); - // Move the model so it's centered. - const model_matrix = zm.translationV(zm.Vec{ 0.0, -45.0, 0.0, 0.0 }); - const queue = core.queue; - queue.writeBuffer( - app.camera_uniform_buffer, - 0, - &view_proj_matrix, - ); - queue.writeBuffer( - app.model_uniform_buffer, - 0, - &model_matrix, - ); - const invert_transpose_model_matrix = zm.transpose(zm.inverse(model_matrix)); - queue.writeBuffer( - app.model_uniform_buffer, - @sizeOf(Mat4), - &invert_transpose_model_matrix, - ); - // Pass the surface size to shader to help sample from gBuffer textures using coord - const surface_size = Vec2{ screen_dimensions.width, screen_dimensions.height }; - queue.writeBuffer( - app.surface_size_uniform_buffer, - 0, - &surface_size, - ); -} - -fn buildCommandBuffer(app: *App) !*gpu.CommandBuffer { - const back_buffer_view = core.swap_chain.getCurrentTextureView().?; - defer back_buffer_view.release(); - - const encoder = core.device.createCommandEncoder(null); - defer encoder.release(); - - std.debug.assert(app.screen_dimensions.width == core.descriptor.width); - std.debug.assert(app.screen_dimensions.height == core.descriptor.height); - - const dimensions = Dimensions2D(f32){ - .width = @as(f32, @floatFromInt(core.descriptor.width)), - .height = @as(f32, @floatFromInt(core.descriptor.height)), - }; - - { - // Write position, normal, albedo etc. data to gBuffers - const pass = encoder.beginRenderPass(&app.write_gbuffer_pass.descriptor); - pass.setViewport( - 0, - 0, - dimensions.width, - dimensions.height, - 0.0, - 1.0, - ); - pass.setScissorRect(0, 0, core.descriptor.width, core.descriptor.height); - pass.setPipeline(app.write_gbuffers_pipeline); - pass.setBindGroup(0, app.scene_uniform_bind_group, null); - pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * app.vertex_count); - pass.setIndexBuffer(app.index_buffer, .uint16, 0, @sizeOf(u16) * app.index_count); - pass.drawIndexed( - app.index_count, - 1, // instance_count - 0, // first_index - 0, // base_vertex - 0, // first_instance - ); - pass.end(); - pass.release(); - } - { - // Update lights position - const pass = encoder.beginComputePass(null); - pass.setPipeline(app.light_update_compute_pipeline); - pass.setBindGroup(0, app.lights.buffer_compute_bind_group, null); - pass.dispatchWorkgroups(@divExact(max_num_lights, 64), 1, 1); - pass.end(); - pass.release(); - } - app.texture_quad_pass.color_attachment.view = back_buffer_view; - app.texture_quad_pass.descriptor = gpu.RenderPassDescriptor{ - .label = "texture_quad_pass(0)", - .color_attachment_count = 1, - .color_attachments = &[_]gpu.RenderPassColorAttachment{app.texture_quad_pass.color_attachment}, - }; - - const pass = encoder.beginRenderPass(&app.texture_quad_pass.descriptor); - switch (app.settings.render_mode) { - .gbuffer_view => { - // GBuffers debug view - // Left: position - // Middle: normal - // Right: albedo (use uv to mimic a checkerboard texture) - pass.setPipeline(app.gbuffers_debug_view_pipeline); - pass.setBindGroup(0, app.gbuffer_textures_bind_group, null); - pass.setBindGroup(1, app.surface_size_uniform_bind_group, null); - pass.draw(6, 1, 0, 0); - }, - else => { - // Deferred rendering - pass.setPipeline(app.deferred_render_pipeline); - pass.setBindGroup(0, app.gbuffer_textures_bind_group, null); - pass.setBindGroup(1, app.lights.buffer_bind_group, null); - pass.setBindGroup(2, app.surface_size_uniform_bind_group, null); - pass.draw(6, 1, 0, 0); - }, - } - - pass.end(); - pass.release(); - - return encoder.finish(null); -} - -const modes = [_][:0]const u8{ "rendering", "gbuffers view" }; - -fn printControls(app: *App) void { - std.debug.print("[controls]\n", .{}); - std.debug.print("[p] paused: {}\n", .{app.is_paused}); - std.debug.print("[m] mode: {s}\n", .{modes[@intFromEnum(app.settings.render_mode)]}); - std.debug.print("[,] decrease lights: {}\n", .{app.settings.lights_count}); - std.debug.print("[.] increase lights: {}\n", .{app.settings.lights_count}); -} - -fn updateUI(app: *App, event: core.Event) void { - switch (event) { - .key_press => |ev| { - var update_lights = false; - switch (ev.key) { - .p => app.is_paused = !app.is_paused, - .m => { - const mode_index = @intFromEnum(app.settings.render_mode); - app.settings.render_mode = @enumFromInt((mode_index + 1) % modes.len); - }, - .comma => { - update_lights = true; - if (app.settings.lights_count >= 25) app.settings.lights_count -= 25; - }, - .period => { - update_lights = true; - app.settings.lights_count += 25; - }, - else => return, - } - - if (update_lights) core.queue.writeBuffer( - app.lights.config_uniform_buffer, - 0, - &[1]i32{app.settings.lights_count}, - ); - app.printControls(); - }, - else => {}, - } -} - -// TODO -// fn drawUI(app: *App) void { -// if (imgui.beginCombo("Mode", .{ .preview_value = modes[mode_index] })) { -// for (modes, 0..) |mode, mode_i| { -// const i = @as(u32, @intCast(mode_i)); -// if (imgui.selectable(mode, .{ .selected = mode_index == i })) { -// app.settings.render_mode = @as(RenderMode, @enumFromInt(mode_i)); -// } -// } -// } -// if (imgui.sliderInt("Light count", .{ .v = &app.settings.lights_count, .min = 1, .max = max_num_lights })) { -// queue.writeBuffer( -// app.lights.config_uniform_buffer, -// 0, -// &[1]i32{app.settings.lights_count}, -// ); -// } -// imgui.end(); -// } - -fn updateUniformBuffers(app: *App) void { - core.device.tick(); - app.camera_rotation += toRadians(360.0) * (app.delta_time / 5.0); // one rotation every 5s - const rotation = zm.rotationY(app.camera_rotation); - const eye_position = zm.mul(rotation, zm.Vec{ 0, 50, -100, 0 }); - const view_matrix = zm.lookAtRh(eye_position, app.view_matrices.origin, app.view_matrices.up_vector); - app.view_matrices.view_proj_matrix = zm.mul(view_matrix, app.view_matrices.projection_matrix); - const queue = core.queue; - queue.writeBuffer( - app.camera_uniform_buffer, - 0, - &app.view_matrices.view_proj_matrix, - ); - - const inv_view_proj_matrix = zm.inverse(app.view_matrices.view_proj_matrix); - queue.writeBuffer( - app.camera_uniform_buffer, - @sizeOf(Mat4), - &inv_view_proj_matrix, - ); -} - -inline fn roundToMultipleOf4(comptime T: type, value: T) T { - return (value + 3) & ~@as(T, 3); -} - -inline fn toRadians(degrees: f32) f32 { - return degrees * (std.math.pi / 180.0); -} diff --git a/src/core/examples/deferred-rendering/vertexTextureQuad.wgsl b/src/core/examples/deferred-rendering/vertexTextureQuad.wgsl deleted file mode 100644 index c2cfb18b..00000000 --- a/src/core/examples/deferred-rendering/vertexTextureQuad.wgsl +++ /dev/null @@ -1,11 +0,0 @@ -@vertex -fn main( - @builtin(vertex_index) VertexIndex : u32 -) -> @builtin(position) vec4 { - const pos = array( - vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), - vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0), - ); - - return vec4(pos[VertexIndex], 0.0, 1.0); -} diff --git a/src/core/examples/deferred-rendering/vertexWriteGBuffers.wgsl b/src/core/examples/deferred-rendering/vertexWriteGBuffers.wgsl deleted file mode 100644 index eb3b930e..00000000 --- a/src/core/examples/deferred-rendering/vertexWriteGBuffers.wgsl +++ /dev/null @@ -1,30 +0,0 @@ -struct Uniforms { - modelMatrix : mat4x4, - normalModelMatrix : mat4x4, -} -struct Camera { - viewProjectionMatrix : mat4x4, - invViewProjectionMatrix : mat4x4, -} -@group(0) @binding(0) var uniforms : Uniforms; -@group(0) @binding(1) var camera : Camera; - -struct VertexOutput { - @builtin(position) Position : vec4, - @location(0) fragNormal: vec3, // normal in world space - @location(1) fragUV: vec2, -} - -@vertex -fn main( - @location(0) position : vec3, - @location(1) normal : vec3, - @location(2) uv : vec2 -) -> VertexOutput { - var output : VertexOutput; - let worldPosition = (uniforms.modelMatrix * vec4(position, 1.0)).xyz; - output.Position = camera.viewProjectionMatrix * vec4(worldPosition, 1.0); - output.fragNormal = normalize((uniforms.normalModelMatrix * vec4(normal, 1.0)).xyz); - output.fragUV = uv; - return output; -} diff --git a/src/core/examples/deferred-rendering/vertex_writer.zig b/src/core/examples/deferred-rendering/vertex_writer.zig deleted file mode 100644 index 1610981e..00000000 --- a/src/core/examples/deferred-rendering/vertex_writer.zig +++ /dev/null @@ -1,188 +0,0 @@ -const std = @import("std"); - -/// Vertex writer manages the placement of vertices by tracking which are unique. If a duplicate vertex is added -/// with `put`, only it's index will be written to the index buffer. -/// `IndexType` should match the integer type used for the index buffer -pub fn VertexWriter(comptime VertexType: type, comptime IndexType: type) type { - return struct { - const MapEntry = struct { - packed_index: IndexType = null_index, - next_sparse: IndexType = null_index, - }; - - const null_index: IndexType = std.math.maxInt(IndexType); - - vertices: []VertexType, - indices: []IndexType, - sparse_to_packed_map: []MapEntry, - - /// Next index outside of the 1:1 mapping range for storing - /// position -> normal collisions - next_collision_index: IndexType, - - /// Next packed index - next_packed_index: IndexType, - written_indices_count: IndexType, - - /// Allocate storage and set default values - /// `sparse_vertices_count` is the number of vertices in the source before de-duplication / remapping - /// Put more succinctly, the largest index value in source index buffer - /// `max_vertex_count` is largest permutation of vertices assuming that {vertex, uv, normal} never map 1:1 and always - /// create a new mapping - pub fn init( - allocator: std.mem.Allocator, - indices_count: IndexType, - sparse_vertices_count: IndexType, - max_vertex_count: IndexType, - ) !@This() { - var result: @This() = undefined; - result.vertices = try allocator.alloc(VertexType, max_vertex_count); - result.indices = try allocator.alloc(IndexType, indices_count); - result.sparse_to_packed_map = try allocator.alloc(MapEntry, max_vertex_count); - result.next_collision_index = sparse_vertices_count; - result.next_packed_index = 0; - result.written_indices_count = 0; - @memset(result.sparse_to_packed_map, .{}); - return result; - } - - pub fn put(self: *@This(), vertex: VertexType, sparse_index: IndexType) void { - if (self.sparse_to_packed_map[sparse_index].packed_index == null_index) { - // New start of chain, reserve a new packed index and add entry to `index_map` - const packed_index = self.next_packed_index; - self.sparse_to_packed_map[sparse_index].packed_index = packed_index; - self.vertices[packed_index] = vertex; - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - self.next_packed_index += 1; - return; - } - var previous_sparse_index: IndexType = undefined; - var current_sparse_index = sparse_index; - while (current_sparse_index != null_index) { - const packed_index = self.sparse_to_packed_map[current_sparse_index].packed_index; - if (std.mem.eql(u8, &std.mem.toBytes(self.vertices[packed_index]), &std.mem.toBytes(vertex))) { - // We already have a record for this vertex in our chain - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - return; - } - previous_sparse_index = current_sparse_index; - current_sparse_index = self.sparse_to_packed_map[current_sparse_index].next_sparse; - } - // This is a new mapping for the given sparse index - const packed_index = self.next_packed_index; - const remapped_sparse_index = self.next_collision_index; - self.indices[self.written_indices_count] = packed_index; - self.vertices[packed_index] = vertex; - self.sparse_to_packed_map[previous_sparse_index].next_sparse = remapped_sparse_index; - self.sparse_to_packed_map[remapped_sparse_index].packed_index = packed_index; - self.next_packed_index += 1; - self.next_collision_index += 1; - self.written_indices_count += 1; - } - - pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { - allocator.free(self.vertices); - allocator.free(self.indices); - allocator.free(self.sparse_to_packed_map); - } - - pub fn indexBuffer(self: @This()) []IndexType { - return self.indices; - } - - pub fn vertexBuffer(self: @This()) []VertexType { - return self.vertices[0..self.next_packed_index]; - } - }; -} - -test "VertexWriter" { - const Vec3 = [3]f32; - const Vertex = extern struct { - position: Vec3, - normal: Vec3, - }; - - const expect = std.testing.expect; - const allocator = std.testing.allocator; - - const Face = struct { - position: [3]u16, - normal: [3]u16, - }; - - const vertices = [_]Vec3{ - Vec3{ 1.0, 0.0, 0.0 }, // 0: Position - Vec3{ 2.0, 0.0, 0.0 }, // 1: Position - Vec3{ 3.0, 0.0, 0.0 }, // 2: Position - Vec3{ 1.0, 0.0, 0.0 }, // 3: Normal - Vec3{ 4.0, 0.0, 0.0 }, // 4: Position - Vec3{ 0.0, 1.0, 0.0 }, // 5: Normal - Vec3{ 5.0, 0.0, 0.0 }, // 6: Position - Vec3{ 0.0, 0.0, 1.0 }, // 7: Normal - Vec3{ 1.0, 0.0, 1.0 }, // 8: Normal - Vec3{ 6.0, 0.0, 0.0 }, // 9: Position - }; - - const faces = [_]Face{ - .{ .position = .{ 0, 4, 2 }, .normal = .{ 7, 5, 3 } }, - .{ .position = .{ 2, 3, 9 }, .normal = .{ 3, 7, 8 } }, - .{ .position = .{ 9, 2, 4 }, .normal = .{ 8, 7, 5 } }, - .{ .position = .{ 2, 6, 1 }, .normal = .{ 3, 5, 7 } }, - .{ .position = .{ 9, 6, 0 }, .normal = .{ 5, 7, 8 } }, - }; - - var writer = try VertexWriter(Vertex, u32).init( - allocator, - faces.len * 3, // indices count - vertices.len, // original vertices count - faces.len * 3, // maximum vertices count - ); - defer writer.deinit(allocator); - - for (faces) |face| { - var x: usize = 0; - while (x < 3) : (x += 1) { - const position_index = face.position[x]; - const position = vertices[position_index]; - const normal = vertices[face.normal[x]]; - const vertex = Vertex{ - .position = position, - .normal = normal, - }; - writer.put(vertex, position_index); - } - } - - const indices = writer.indexBuffer(); - try expect(indices.len == faces.len * 3); - - // Face 0 - try expect(indices[0] == 0); // (0, 7) New - try expect(indices[1] == 1); // (4, 5) New - try expect(indices[2] == 2); // (2, 3) New - - // Face 1 - try expect(indices[3 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[3 + 1] == 3); // (3, 7) New - try expect(indices[3 + 2] == 4); // (9, 8) New - - // Face 2 - try expect(indices[6 + 0] == 4); // (9, 8) Duplicate - Reuse index - try expect(indices[6 + 1] == 5); // (2, 7) New normal mapping (Don't clobber) - try expect(indices[6 + 2] == 1); // (4, 5) Duplicate - Reuse Index - - // Face 3 - try expect(indices[9 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[9 + 1] == 6); // (6, 5) New - try expect(indices[9 + 2] == 7); // (1, 7) New - - // Face 4 - try expect(indices[12 + 0] == 8); // (9, 5) New normal mapping (Don't clobber) - try expect(indices[12 + 1] == 9); // (6, 7) New normal mapping (Don't clobber) - try expect(indices[12 + 2] == 10); // (0, 8) New normal mapping (Don't clobber) - - try expect(writer.vertexBuffer().len == 11); -} diff --git a/src/core/examples/pbr-basic/main.zig b/src/core/examples/pbr-basic/main.zig deleted file mode 100644 index a90bd715..00000000 --- a/src/core/examples/pbr-basic/main.zig +++ /dev/null @@ -1,923 +0,0 @@ -const std = @import("std"); - -const mach = @import("mach"); -const core = mach.core; -const gpu = mach.gpu; - -const m3d = @import("model3d"); -const zm = @import("zmath"); -const assets = @import("assets"); -const VertexWriter = @import("vertex_writer.zig").VertexWriter; - -pub const App = @This(); - -const Vec4 = [4]f32; -const Vec3 = [3]f32; -const Vec2 = [2]f32; -const Mat4 = [4]Vec4; - -fn Dimensions2D(comptime T: type) type { - return struct { - width: T, - height: T, - }; -} - -const Vertex = extern struct { - position: Vec3, - normal: Vec3, -}; - -const Model = struct { - vertex_count: u32, - index_count: u32, - vertex_buffer: *gpu.Buffer, - index_buffer: *gpu.Buffer, -}; - -const Material = struct { - const Params = extern struct { - roughness: f32, - metallic: f32, - color: Vec3, - }; - - name: []const u8, - params: Params, -}; - -const PressedKeys = packed struct(u16) { - right: bool = false, - left: bool = false, - up: bool = false, - down: bool = false, - padding: u12 = undefined, - - pub inline fn areKeysPressed(self: @This()) bool { - return (self.up or self.down or self.left or self.right); - } - - pub inline fn clear(self: *@This()) void { - self.right = false; - self.left = false; - self.up = false; - self.down = false; - } -}; - -const Camera = struct { - const Matrices = struct { - perspective: Mat4 = [1]Vec4{[1]f32{0.0} ** 4} ** 4, - view: Mat4 = [1]Vec4{[1]f32{0.0} ** 4} ** 4, - }; - - rotation: Vec3 = .{ 0.0, 0.0, 0.0 }, - position: Vec3 = .{ 0.0, 0.0, 0.0 }, - view_position: Vec4 = .{ 0.0, 0.0, 0.0, 0.0 }, - fov: f32 = 0.0, - znear: f32 = 0.0, - zfar: f32 = 0.0, - rotation_speed: f32 = 0.0, - movement_speed: f32 = 0.0, - updated: bool = false, - matrices: Matrices = .{}, - - pub fn calculateMovement(self: *@This(), pressed_keys: PressedKeys) void { - std.debug.assert(pressed_keys.areKeysPressed()); - const rotation_radians = Vec3{ - toRadians(self.rotation[0]), - toRadians(self.rotation[1]), - toRadians(self.rotation[2]), - }; - var camera_front = zm.Vec{ -zm.cos(rotation_radians[0]) * zm.sin(rotation_radians[1]), zm.sin(rotation_radians[0]), zm.cos(rotation_radians[0]) * zm.cos(rotation_radians[1]), 0 }; - camera_front = zm.normalize3(camera_front); - if (pressed_keys.up) { - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] + camera_front[0], - self.position[1] + camera_front[1], - self.position[2] + camera_front[2], - }; - } - if (pressed_keys.down) { - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] - camera_front[0], - self.position[1] - camera_front[1], - self.position[2] - camera_front[2], - }; - } - if (pressed_keys.right) { - camera_front = zm.cross3(.{ 0.0, 1.0, 0.0, 0.0 }, camera_front); - camera_front = zm.normalize3(camera_front); - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] - camera_front[0], - self.position[1] - camera_front[1], - self.position[2] - camera_front[2], - }; - } - if (pressed_keys.left) { - camera_front = zm.cross3(.{ 0.0, 1.0, 0.0, 0.0 }, camera_front); - camera_front = zm.normalize3(camera_front); - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] + camera_front[0], - self.position[1] + camera_front[1], - self.position[2] + camera_front[2], - }; - } - self.updateViewMatrix(); - } - - fn updateViewMatrix(self: *@This()) void { - const rotation_x = zm.rotationX(toRadians(self.rotation[2])); - const rotation_y = zm.rotationY(toRadians(self.rotation[1])); - const rotation_z = zm.rotationZ(toRadians(self.rotation[0])); - const rotation_matrix = zm.mul(rotation_z, zm.mul(rotation_x, rotation_y)); - - const translation_matrix: zm.Mat = zm.translationV(.{ - self.position[0], - self.position[1], - self.position[2], - 0, - }); - const view = zm.mul(translation_matrix, rotation_matrix); - self.matrices.view[0] = view[0]; - self.matrices.view[1] = view[1]; - self.matrices.view[2] = view[2]; - self.matrices.view[3] = view[3]; - self.view_position = .{ - -self.position[0], - self.position[1], - -self.position[2], - 0.0, - }; - self.updated = true; - } - - pub fn setMovementSpeed(self: *@This(), speed: f32) void { - self.movement_speed = speed; - } - - pub fn setPerspective(self: *@This(), fov: f32, aspect: f32, znear: f32, zfar: f32) void { - self.fov = fov; - self.znear = znear; - self.zfar = zfar; - const perspective = zm.perspectiveFovRhGl(toRadians(fov), aspect, znear, zfar); - self.matrices.perspective[0] = perspective[0]; - self.matrices.perspective[1] = perspective[1]; - self.matrices.perspective[2] = perspective[2]; - self.matrices.perspective[3] = perspective[3]; - } - - pub fn setRotationSpeed(self: *@This(), speed: f32) void { - self.rotation_speed = speed; - } - - pub fn setRotation(self: *@This(), rotation: Vec3) void { - self.rotation = rotation; - self.updateViewMatrix(); - } - - pub fn rotate(self: *@This(), delta: Vec2) void { - self.rotation[0] -= delta[1]; - self.rotation[1] -= delta[0]; - self.updateViewMatrix(); - } - - pub fn setPosition(self: *@This(), position: Vec3) void { - self.position = .{ - position[0], - -position[1], - position[2], - }; - self.updateViewMatrix(); - } -}; - -const UniformBuffers = struct { - const Params = struct { - buffer: *gpu.Buffer, - buffer_size: u64, - model_size: u64, - }; - const Buffer = struct { - buffer: *gpu.Buffer, - size: u32, - }; - ubo_matrices: Buffer, - ubo_params: Buffer, - material_params: Params, - object_params: Params, -}; - -const UboParams = struct { - lights: [4]Vec4, -}; - -const UboMatrices = extern struct { - projection: Mat4, - model: Mat4, - view: Mat4, - camera_position: Vec3, -}; - -const grid_element_count = grid_dimensions * grid_dimensions; - -const MaterialParamsDynamic = extern struct { - roughness: f32 = 0, - metallic: f32 = 0, - color: Vec3 = .{ 0, 0, 0 }, - padding: [236]u8 = [1]u8{0} ** 236, -}; -const MaterialParamsDynamicGrid = [grid_element_count]MaterialParamsDynamic; - -const ObjectParamsDynamic = extern struct { - position: Vec3 = .{ 0, 0, 0 }, - padding: [244]u8 = [1]u8{0} ** 244, -}; -const ObjectParamsDynamicGrid = [grid_element_count]ObjectParamsDynamic; - -// -// Globals -// - -const material_names = [11][:0]const u8{ - "Gold", "Copper", "Chromium", "Nickel", "Titanium", "Cobalt", "Platinum", - // Testing materials - "White", "Red", "Blue", "Black", -}; - -const object_names = [5][:0]const u8{ "Sphere", "Teapot", "Torusknot", "Venus", "Stanford Dragon" }; - -const materials = [_]Material{ - .{ .name = "Gold", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 0.765557, 0.336057 } } }, - .{ .name = "Copper", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.955008, 0.637427, 0.538163 } } }, - .{ .name = "Chromium", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.549585, 0.556114, 0.554256 } } }, - .{ .name = "Nickel", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 0.608679, 0.525649 } } }, - .{ .name = "Titanium", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.541931, 0.496791, 0.449419 } } }, - .{ .name = "Cobalt", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.662124, 0.654864, 0.633732 } } }, - .{ .name = "Platinum", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.672411, 0.637331, 0.585456 } } }, - // Testing colors - .{ .name = "White", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 1.0, 1.0 } } }, - .{ .name = "Red", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 0.0, 0.0 } } }, - .{ .name = "Blue", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.0, 0.0, 1.0 } } }, - .{ .name = "Black", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.0, 0.0, 0.0 } } }, -}; - -const grid_dimensions = 7; -const model_embeds = [_][:0]const u8{ - assets.sphere_m3d, - assets.teapot_m3d, - assets.torusknot_m3d, - assets.venus_m3d, - assets.stanford_dragon_m3d, -}; - -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - -// -// Member variables -// - -title_timer: core.Timer, -timer: core.Timer, -camera: Camera, -render_pipeline: *gpu.RenderPipeline, -render_pass_descriptor: gpu.RenderPassDescriptor, -bind_group: *gpu.BindGroup, -color_attachment: gpu.RenderPassColorAttachment, -depth_stencil_attachment_description: gpu.RenderPassDepthStencilAttachment, -depth_texture: *gpu.Texture, -depth_texture_view: *gpu.TextureView, -pressed_keys: PressedKeys, -models: [5]Model, -ubo_params: UboParams, -ubo_matrices: UboMatrices, -uniform_buffers: UniformBuffers, -material_params_dynamic: MaterialParamsDynamicGrid = [1]MaterialParamsDynamic{.{}} ** grid_element_count, -object_params_dynamic: ObjectParamsDynamicGrid = [1]ObjectParamsDynamic{.{}} ** grid_element_count, -uniform_buffers_dirty: bool, -buffers_bound: bool, -is_paused: bool, -current_material_index: usize, -current_object_index: usize, -mouse_position: core.Position, -is_rotating: bool, - -// -// Functions -// - -pub fn init(app: *App) !void { - try core.init(.{}); - app.timer = try core.Timer.start(); - app.title_timer = try core.Timer.start(); - - app.pressed_keys = .{}; - app.buffers_bound = false; - app.is_paused = false; - app.uniform_buffers_dirty = false; - app.current_material_index = 0; - app.current_object_index = 0; - app.mouse_position = .{ .x = 0, .y = 0 }; - app.is_rotating = false; - - setupCamera(app); - try loadModels(std.heap.c_allocator, app); - prepareUniformBuffers(app); - setupPipeline(app); - setupRenderPass(app); - app.printControls(); -} - -pub fn deinit(app: *App) void { - defer _ = gpa.deinit(); - defer core.deinit(); - - app.bind_group.release(); - app.render_pipeline.release(); - app.depth_texture_view.release(); - app.depth_texture.release(); - app.uniform_buffers.ubo_matrices.buffer.release(); - app.uniform_buffers.ubo_params.buffer.release(); - app.uniform_buffers.material_params.buffer.release(); - app.uniform_buffers.object_params.buffer.release(); -} - -pub fn update(app: *App) !bool { - var iter = core.pollEvents(); - while (iter.next()) |event| { - app.updateUI(event); - switch (event) { - .mouse_motion => |ev| { - if (app.is_rotating) { - const delta = Vec2{ - @as(f32, @floatCast((app.mouse_position.x - ev.pos.x) * app.camera.rotation_speed)), - @as(f32, @floatCast((app.mouse_position.y - ev.pos.y) * app.camera.rotation_speed)), - }; - app.mouse_position = ev.pos; - app.camera.rotate(delta); - app.uniform_buffers_dirty = true; - } - }, - .mouse_press => |ev| { - if (ev.button == .left) { - app.is_rotating = true; - app.mouse_position = ev.pos; - } - }, - .mouse_release => |ev| { - if (ev.button == .left) { - app.is_rotating = false; - } - }, - .key_press, .key_repeat => |ev| { - const key = ev.key; - if (key == .up or key == .w) app.pressed_keys.up = true; - if (key == .down or key == .s) app.pressed_keys.down = true; - if (key == .left or key == .a) app.pressed_keys.left = true; - if (key == .right or key == .d) app.pressed_keys.right = true; - }, - .framebuffer_resize => |ev| { - app.depth_texture_view.release(); - app.depth_texture.release(); - app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{ - .usage = .{ .render_attachment = true }, - .format = .depth24_plus_stencil8, - .sample_count = 1, - .size = .{ - .width = ev.width, - .height = ev.height, - .depth_or_array_layers = 1, - }, - }); - app.depth_texture_view = app.depth_texture.createView(&gpu.TextureView.Descriptor{ - .format = .depth24_plus_stencil8, - .dimension = .dimension_2d, - .array_layer_count = 1, - .aspect = .all, - }); - app.depth_stencil_attachment_description = gpu.RenderPassDepthStencilAttachment{ - .view = app.depth_texture_view, - .depth_load_op = .clear, - .depth_store_op = .store, - .depth_clear_value = 1.0, - .stencil_clear_value = 0, - .stencil_load_op = .clear, - .stencil_store_op = .store, - }; - - const aspect_ratio = @as(f32, @floatFromInt(ev.width)) / @as(f32, @floatFromInt(ev.height)); - app.camera.setPerspective(60.0, aspect_ratio, 0.1, 256.0); - app.uniform_buffers_dirty = true; - }, - .close => return true, - else => {}, - } - } - if (app.pressed_keys.areKeysPressed()) { - app.camera.calculateMovement(app.pressed_keys); - app.pressed_keys.clear(); - app.uniform_buffers_dirty = true; - } - - if (app.uniform_buffers_dirty) { - updateUniformBuffers(app); - app.uniform_buffers_dirty = false; - } - - const back_buffer_view = core.swap_chain.getCurrentTextureView().?; - app.color_attachment.view = back_buffer_view; - app.render_pass_descriptor = gpu.RenderPassDescriptor{ - .color_attachment_count = 1, - .color_attachments = &[_]gpu.RenderPassColorAttachment{app.color_attachment}, - .depth_stencil_attachment = &app.depth_stencil_attachment_description, - }; - const encoder = core.device.createCommandEncoder(null); - const current_model = app.models[app.current_object_index]; - - const pass = encoder.beginRenderPass(&app.render_pass_descriptor); - - const dimensions = Dimensions2D(f32){ - .width = @as(f32, @floatFromInt(core.descriptor.width)), - .height = @as(f32, @floatFromInt(core.descriptor.height)), - }; - pass.setViewport( - 0, - 0, - dimensions.width, - dimensions.height, - 0.0, - 1.0, - ); - pass.setScissorRect(0, 0, core.descriptor.width, core.descriptor.height); - pass.setPipeline(app.render_pipeline); - - if (!app.is_paused) { - app.updateLights(); - } - - var i: usize = 0; - while (i < (grid_dimensions * grid_dimensions)) : (i += 1) { - const alignment = 256; - const dynamic_offset: u32 = @as(u32, @intCast(i)) * alignment; - const dynamic_offsets = [2]u32{ dynamic_offset, dynamic_offset }; - pass.setBindGroup(0, app.bind_group, &dynamic_offsets); - if (!app.buffers_bound) { - pass.setVertexBuffer(0, current_model.vertex_buffer, 0, @sizeOf(Vertex) * current_model.vertex_count); - pass.setIndexBuffer(current_model.index_buffer, .uint32, 0, gpu.whole_size); - app.buffers_bound = true; - } - pass.drawIndexed( - current_model.index_count, // index_count - 1, // instance_count - 0, // first_index - 0, // base_vertex - 0, // first_instance - ); - } - - pass.end(); - pass.release(); - - var command = encoder.finish(null); - encoder.release(); - - const queue = core.queue; - queue.submit(&[_]*gpu.CommandBuffer{command}); - - command.release(); - core.swap_chain.present(); - back_buffer_view.release(); - app.buffers_bound = false; - - // update the window title every second - if (app.title_timer.read() >= 1.0) { - app.title_timer.reset(); - try core.printTitle("PBR Basic [ {d}fps ] [ Input {d}hz ]", .{ - core.frameRate(), - core.inputRate(), - }); - } - - return false; -} - -fn prepareUniformBuffers(app: *App) void { - comptime { - std.debug.assert(@sizeOf(ObjectParamsDynamic) == 256); - std.debug.assert(@sizeOf(MaterialParamsDynamic) == 256); - } - - app.uniform_buffers.ubo_matrices.size = roundToMultipleOf4(u32, @as(u32, @intCast(@sizeOf(UboMatrices)))) + 4; - app.uniform_buffers.ubo_matrices.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.ubo_matrices.size, - .mapped_at_creation = .false, - }); - - app.uniform_buffers.ubo_params.size = roundToMultipleOf4(u32, @as(u32, @intCast(@sizeOf(UboParams)))) + 4; - app.uniform_buffers.ubo_params.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.ubo_params.size, - .mapped_at_creation = .false, - }); - - // - // Material parameter uniform buffer - // - app.uniform_buffers.material_params.model_size = @sizeOf(Vec2) + @sizeOf(Vec3); - app.uniform_buffers.material_params.buffer_size = calculateConstantBufferByteSize(@sizeOf(MaterialParamsDynamicGrid)); - std.debug.assert(app.uniform_buffers.material_params.buffer_size >= app.uniform_buffers.material_params.model_size); - app.uniform_buffers.material_params.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.material_params.buffer_size, - .mapped_at_creation = .false, - }); - - // - // Object parameter uniform buffer - // - app.uniform_buffers.object_params.model_size = @sizeOf(Vec3) + 4; - app.uniform_buffers.object_params.buffer_size = calculateConstantBufferByteSize(@sizeOf(MaterialParamsDynamicGrid)) + 4; - std.debug.assert(app.uniform_buffers.object_params.buffer_size >= app.uniform_buffers.object_params.model_size); - app.uniform_buffers.object_params.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.object_params.buffer_size, - .mapped_at_creation = .false, - }); - - app.updateUniformBuffers(); - app.updateDynamicUniformBuffer(); - app.updateLights(); -} - -fn updateDynamicUniformBuffer(app: *App) void { - var index: u32 = 0; - var y: usize = 0; - while (y < grid_dimensions) : (y += 1) { - var x: usize = 0; - while (x < grid_dimensions) : (x += 1) { - const grid_dimensions_float = @as(f32, @floatFromInt(grid_dimensions)); - app.object_params_dynamic[index].position[0] = (@as(f32, @floatFromInt(x)) - (grid_dimensions_float / 2) * 2.5); - app.object_params_dynamic[index].position[1] = 0; - app.object_params_dynamic[index].position[2] = (@as(f32, @floatFromInt(y)) - (grid_dimensions_float / 2) * 2.5); - app.material_params_dynamic[index].metallic = zm.clamp(@as(f32, @floatFromInt(x)) / (grid_dimensions_float - 1), 0.1, 1.0); - app.material_params_dynamic[index].roughness = zm.clamp(@as(f32, @floatFromInt(y)) / (grid_dimensions_float - 1), 0.05, 1.0); - app.material_params_dynamic[index].color = materials[app.current_material_index].params.color; - index += 1; - } - } - const queue = core.queue; - queue.writeBuffer( - app.uniform_buffers.object_params.buffer, - 0, - &app.object_params_dynamic, - ); - queue.writeBuffer( - app.uniform_buffers.material_params.buffer, - 0, - &app.material_params_dynamic, - ); -} - -fn updateUniformBuffers(app: *App) void { - app.ubo_matrices.projection = app.camera.matrices.perspective; - app.ubo_matrices.view = app.camera.matrices.view; - const rotation_degrees = if (app.current_object_index == 1) @as(f32, -45.0) else @as(f32, -90.0); - const model = zm.rotationY(rotation_degrees); - app.ubo_matrices.model[0] = model[0]; - app.ubo_matrices.model[1] = model[1]; - app.ubo_matrices.model[2] = model[2]; - app.ubo_matrices.model[3] = model[3]; - app.ubo_matrices.camera_position = .{ - -app.camera.position[0], - -app.camera.position[1], - -app.camera.position[2], - }; - const queue = core.queue; - queue.writeBuffer(app.uniform_buffers.ubo_matrices.buffer, 0, &[_]UboMatrices{app.ubo_matrices}); -} - -fn updateLights(app: *App) void { - const p: f32 = 15.0; - app.ubo_params.lights[0] = Vec4{ -p, -p * 0.5, -p, 1.0 }; - app.ubo_params.lights[1] = Vec4{ -p, -p * 0.5, p, 1.0 }; - app.ubo_params.lights[2] = Vec4{ p, -p * 0.5, p, 1.0 }; - app.ubo_params.lights[3] = Vec4{ p, -p * 0.5, -p, 1.0 }; - const base_value = toRadians(@mod(app.timer.read() * 0.1, 1.0) * 360.0); - app.ubo_params.lights[0][0] = @sin(base_value) * 20.0; - app.ubo_params.lights[0][2] = @cos(base_value) * 20.0; - app.ubo_params.lights[1][0] = @cos(base_value) * 20.0; - app.ubo_params.lights[1][1] = @sin(base_value) * 20.0; - const queue = core.queue; - queue.writeBuffer( - app.uniform_buffers.ubo_params.buffer, - 0, - &[_]UboParams{app.ubo_params}, - ); -} - -fn setupPipeline(app: *App) void { - comptime { - std.debug.assert(@sizeOf(Vertex) == @sizeOf(f32) * 6); - } - - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - .{ - .binding = 0, - .visibility = .{ .vertex = true, .fragment = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .false, - .min_binding_size = app.uniform_buffers.ubo_matrices.size, - }, - }, - .{ - .binding = 1, - .visibility = .{ .fragment = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .false, - .min_binding_size = app.uniform_buffers.ubo_params.size, - }, - }, - .{ - .binding = 2, - .visibility = .{ .fragment = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .true, - .min_binding_size = app.uniform_buffers.material_params.model_size, - }, - }, - .{ - .binding = 3, - .visibility = .{ .vertex = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .true, - .min_binding_size = app.uniform_buffers.object_params.model_size, - }, - }, - }; - - const bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = bind_group_layout_entries[0..], - }), - ); - - const bind_group_layouts = [_]*gpu.BindGroupLayout{bind_group_layout}; - const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - - const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{ - .array_stride = @sizeOf(Vertex), - .step_mode = .vertex, - .attributes = &.{ - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "position"), .shader_location = 0 }, - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "normal"), .shader_location = 1 }, - }, - }); - - const blend_component_descriptor = gpu.BlendComponent{ - .operation = .add, - .src_factor = .one, - .dst_factor = .zero, - }; - - const color_target_state = gpu.ColorTargetState{ - .format = core.descriptor.format, - .blend = &.{ - .color = blend_component_descriptor, - .alpha = blend_component_descriptor, - }, - }; - - const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .layout = pipeline_layout, - .primitive = .{ - .cull_mode = .back, - }, - .depth_stencil = &.{ - .format = .depth24_plus_stencil8, - .depth_write_enabled = .true, - .depth_compare = .less, - }, - .fragment = &gpu.FragmentState.init(.{ - .module = shader_module, - .entry_point = "frag_main", - .targets = &.{color_target_state}, - }), - .vertex = gpu.VertexState.init(.{ - .module = shader_module, - .entry_point = "vertex_main", - .buffers = &.{vertex_buffer_layout}, - }), - }; - app.render_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - shader_module.release(); - - { - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.uniform_buffers.ubo_matrices.buffer, - .size = app.uniform_buffers.ubo_matrices.size, - }, - .{ - .binding = 1, - .buffer = app.uniform_buffers.ubo_params.buffer, - .size = app.uniform_buffers.ubo_params.size, - }, - .{ - .binding = 2, - .buffer = app.uniform_buffers.material_params.buffer, - .size = app.uniform_buffers.material_params.model_size, - }, - .{ - .binding = 3, - .buffer = app.uniform_buffers.object_params.buffer, - .size = app.uniform_buffers.object_params.model_size, - }, - }; - app.bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } -} - -fn setupRenderPass(app: *App) void { - app.color_attachment = gpu.RenderPassColorAttachment{ - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 0.0, - .a = 0.0, - }, - .load_op = .clear, - .store_op = .store, - }; - - app.depth_texture = core.device.createTexture(&.{ - .usage = .{ .render_attachment = true, .copy_src = true }, - .format = .depth24_plus_stencil8, - .sample_count = 1, - .size = .{ - .width = core.descriptor.width, - .height = core.descriptor.height, - .depth_or_array_layers = 1, - }, - }); - - app.depth_texture_view = app.depth_texture.createView(&.{ - .format = .depth24_plus_stencil8, - .dimension = .dimension_2d, - .array_layer_count = 1, - .aspect = .all, - }); - - app.depth_stencil_attachment_description = gpu.RenderPassDepthStencilAttachment{ - .view = app.depth_texture_view, - .depth_load_op = .clear, - .depth_store_op = .store, - .depth_clear_value = 1.0, - .stencil_clear_value = 0, - .stencil_load_op = .clear, - .stencil_store_op = .store, - }; -} - -fn loadModels(allocator: std.mem.Allocator, app: *App) !void { - for (model_embeds, 0..) |model_data, model_data_i| { - const m3d_model = m3d.load(model_data, null, null, null) orelse return error.LoadModelFailed; - - const vertex_count = m3d_model.handle.numvertex; - const face_count = m3d_model.handle.numface; - - var model: *Model = &app.models[model_data_i]; - - model.index_count = face_count * 3; - - var vertex_writer = try VertexWriter(Vertex, u32).init(allocator, face_count * 3, vertex_count, face_count * 3); - defer vertex_writer.deinit(allocator); - - const scale: f32 = 0.45; - const vertices = m3d_model.handle.vertex[0..vertex_count]; - var i: usize = 0; - while (i < face_count) : (i += 1) { - const face = m3d_model.handle.face[i]; - var x: usize = 0; - while (x < 3) : (x += 1) { - const vertex_index = face.vertex[x]; - const normal_index = face.normal[x]; - const vertex = Vertex{ - .position = .{ - vertices[vertex_index].x * scale, - vertices[vertex_index].y * scale, - vertices[vertex_index].z * scale, - }, - .normal = .{ - vertices[normal_index].x, - vertices[normal_index].y, - vertices[normal_index].z, - }, - }; - vertex_writer.put(vertex, vertex_index); - } - } - - const vertex_buffer = vertex_writer.vertexBuffer(); - const index_buffer = vertex_writer.indexBuffer(); - - model.vertex_count = @as(u32, @intCast(vertex_buffer.len)); - - model.vertex_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .vertex = true }, - .size = @sizeOf(Vertex) * model.vertex_count, - .mapped_at_creation = .false, - }); - const queue = core.queue; - queue.writeBuffer(model.vertex_buffer, 0, vertex_buffer); - - model.index_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .index = true }, - .size = @sizeOf(u32) * model.index_count, - .mapped_at_creation = .false, - }); - queue.writeBuffer(model.index_buffer, 0, index_buffer); - } -} - -fn printControls(app: *App) void { - std.debug.print("[controls]\n", .{}); - std.debug.print("[p] paused: {}\n", .{app.is_paused}); - std.debug.print("[m] material: {s}\n", .{material_names[app.current_material_index]}); - std.debug.print("[o] object: {s}\n", .{object_names[app.current_object_index]}); -} - -fn updateUI(app: *App, event: core.Event) void { - switch (event) { - .key_press => |ev| { - var update_uniform_buffers: bool = false; - switch (ev.key) { - .p => app.is_paused = !app.is_paused, - .m => { - app.current_material_index = (app.current_material_index + 1) % material_names.len; - update_uniform_buffers = true; - }, - .o => { - app.current_object_index = (app.current_object_index + 1) % object_names.len; - update_uniform_buffers = true; - }, - else => return, - } - app.printControls(); - if (update_uniform_buffers) { - updateDynamicUniformBuffer(app); - } - }, - else => {}, - } -} - -fn setupCamera(app: *App) void { - app.camera = Camera{ - .rotation_speed = 1.0, - .movement_speed = 1.0, - }; - const aspect_ratio: f32 = @as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)); - app.camera.setPosition(.{ 10.0, 6.0, 6.0 }); - app.camera.setRotation(.{ 62.5, 90.0, 0.0 }); - app.camera.setMovementSpeed(0.5); - app.camera.setPerspective(60.0, aspect_ratio, 0.1, 256.0); - app.camera.setRotationSpeed(0.25); -} - -inline fn roundToMultipleOf4(comptime T: type, value: T) T { - return (value + 3) & ~@as(T, 3); -} - -inline fn calculateConstantBufferByteSize(byte_size: usize) usize { - return (byte_size + 255) & ~@as(usize, 255); -} - -inline fn toRadians(degrees: f32) f32 { - return degrees * (std.math.pi / 180.0); -} diff --git a/src/core/examples/pbr-basic/shader.wgsl b/src/core/examples/pbr-basic/shader.wgsl deleted file mode 100644 index adf728cc..00000000 --- a/src/core/examples/pbr-basic/shader.wgsl +++ /dev/null @@ -1,118 +0,0 @@ -@group(0) @binding(0) var ubo : UBO; -@group(0) @binding(1) var uboParams : UBOShared; -@group(0) @binding(2) var material : MaterialParams; -@group(0) @binding(3) var object : ObjectParams; - -struct VertexOut { - @builtin(position) position_clip : vec4, - @location(0) fragPosition : vec3, - @location(1) fragNormal : vec3, -} - -struct MaterialParams { - roughness : f32, - metallic : f32, - r : f32, - g : f32, - b : f32 -} - -struct UBOShared { - lights : array, 4>, -} - -struct UBO { - projection : mat4x4, - model : mat4x4, - view : mat4x4, - camPos : vec3, -} - -struct ObjectParams { - position : vec3 -} - -@vertex fn vertex_main( - @location(0) position : vec3, - @location(1) normal : vec3 -) -> VertexOut { - var output : VertexOut; - var locPos = vec4(ubo.model * vec4(position, 1.0)); - output.fragPosition = locPos.xyz + object.position; - output.fragNormal = mat3x3(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz) * normal; - output.position_clip = ubo.projection * ubo.view * vec4(output.fragPosition, 1.0); - return output; -} - -@fragment fn frag_main( - @location(0) position : vec3, - @location(1) normal: vec3 -) -> @location(0) vec4 { - var N : vec3 = normalize(normal); - var V : vec3 = normalize(ubo.camPos - position); - var Lo = vec3(0.0); - // Specular contribution - for(var i: i32 = 0; i < 4; i++) { - var L : vec3 = normalize(uboParams.lights[i].xyz - position); - Lo += BRDF(L, V, N, material.metallic, material.roughness); - } - // Combine with ambient - var color : vec3 = material_color() * 0.02; - color += Lo; - // Gamma correct - color = pow(color, vec3(0.4545)); - return vec4(color, 1.0); -} - -const PI : f32 = 3.14159265359; - -fn material_color() -> vec3 { - return vec3(material.r, material.g, material.b); -} - -// Normal Distribution function -------------------------------------- -fn D_GGX(dotNH : f32, roughness : f32) -> f32 { - var alpha : f32 = roughness * roughness; - var alpha2 : f32 = alpha * alpha; - var denom : f32 = dotNH * dotNH * (alpha2 - 1.0) + 1.0; - return alpha2 / (PI * denom * denom); -} - -// Geometric Shadowing function -------------------------------------- -fn G_SchlicksmithGGX(dotNL : f32, dotNV : f32, roughness : f32) -> f32 { - var r : f32 = roughness + 1.0; - var k : f32 = (r * r) / 8.0; - var GL : f32 = dotNL / (dotNL * (1.0 - k) + k); - var GV : f32 = dotNV / (dotNV * (1.0 - k) + k); - return GL * GV; -} - -// Fresnel function ---------------------------------------------------- -fn F_Schlick(cosTheta : f32, metallic : f32) -> vec3 { - var F0 : vec3 = mix(vec3(0.04), material_color(), metallic); - var F : vec3 = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); - return F; -} - -// Specular BRDF composition -------------------------------------------- -fn BRDF(L : vec3, V : vec3, N : vec3, metallic : f32, roughness : f32) -> vec3 { - var H : vec3 = normalize(V + L); - var dotNV : f32 = clamp(dot(N, V), 0.0, 1.0); - var dotNL : f32 = clamp(dot(N, L), 0.0, 1.0); - var dotLH : f32 = clamp(dot(L, H), 0.0, 1.0); - var dotNH : f32 = clamp(dot(N, H), 0.0, 1.0); - var lightColor = vec3(1.0); - var color = vec3(0.0); - if(dotNL > 0.0) { - var rroughness : f32 = max(0.05, roughness); - // D = Normal distribution (Distribution of the microfacets) - var D : f32 = D_GGX(dotNH, roughness); - // G = Geometric shadowing term (Microfacets shadowing) - var G : f32 = G_SchlicksmithGGX(dotNL, dotNV, roughness); - // F = Fresnel factor (Reflectance depending on angle of incidence) - var F : vec3 = F_Schlick(dotNV, metallic); - var spec : vec3 = (D * F * G) / (4.0 * dotNL * dotNV); - color += spec * dotNL * lightColor; - } - return color; -} \ No newline at end of file diff --git a/src/core/examples/pbr-basic/vertex_writer.zig b/src/core/examples/pbr-basic/vertex_writer.zig deleted file mode 100644 index 1610981e..00000000 --- a/src/core/examples/pbr-basic/vertex_writer.zig +++ /dev/null @@ -1,188 +0,0 @@ -const std = @import("std"); - -/// Vertex writer manages the placement of vertices by tracking which are unique. If a duplicate vertex is added -/// with `put`, only it's index will be written to the index buffer. -/// `IndexType` should match the integer type used for the index buffer -pub fn VertexWriter(comptime VertexType: type, comptime IndexType: type) type { - return struct { - const MapEntry = struct { - packed_index: IndexType = null_index, - next_sparse: IndexType = null_index, - }; - - const null_index: IndexType = std.math.maxInt(IndexType); - - vertices: []VertexType, - indices: []IndexType, - sparse_to_packed_map: []MapEntry, - - /// Next index outside of the 1:1 mapping range for storing - /// position -> normal collisions - next_collision_index: IndexType, - - /// Next packed index - next_packed_index: IndexType, - written_indices_count: IndexType, - - /// Allocate storage and set default values - /// `sparse_vertices_count` is the number of vertices in the source before de-duplication / remapping - /// Put more succinctly, the largest index value in source index buffer - /// `max_vertex_count` is largest permutation of vertices assuming that {vertex, uv, normal} never map 1:1 and always - /// create a new mapping - pub fn init( - allocator: std.mem.Allocator, - indices_count: IndexType, - sparse_vertices_count: IndexType, - max_vertex_count: IndexType, - ) !@This() { - var result: @This() = undefined; - result.vertices = try allocator.alloc(VertexType, max_vertex_count); - result.indices = try allocator.alloc(IndexType, indices_count); - result.sparse_to_packed_map = try allocator.alloc(MapEntry, max_vertex_count); - result.next_collision_index = sparse_vertices_count; - result.next_packed_index = 0; - result.written_indices_count = 0; - @memset(result.sparse_to_packed_map, .{}); - return result; - } - - pub fn put(self: *@This(), vertex: VertexType, sparse_index: IndexType) void { - if (self.sparse_to_packed_map[sparse_index].packed_index == null_index) { - // New start of chain, reserve a new packed index and add entry to `index_map` - const packed_index = self.next_packed_index; - self.sparse_to_packed_map[sparse_index].packed_index = packed_index; - self.vertices[packed_index] = vertex; - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - self.next_packed_index += 1; - return; - } - var previous_sparse_index: IndexType = undefined; - var current_sparse_index = sparse_index; - while (current_sparse_index != null_index) { - const packed_index = self.sparse_to_packed_map[current_sparse_index].packed_index; - if (std.mem.eql(u8, &std.mem.toBytes(self.vertices[packed_index]), &std.mem.toBytes(vertex))) { - // We already have a record for this vertex in our chain - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - return; - } - previous_sparse_index = current_sparse_index; - current_sparse_index = self.sparse_to_packed_map[current_sparse_index].next_sparse; - } - // This is a new mapping for the given sparse index - const packed_index = self.next_packed_index; - const remapped_sparse_index = self.next_collision_index; - self.indices[self.written_indices_count] = packed_index; - self.vertices[packed_index] = vertex; - self.sparse_to_packed_map[previous_sparse_index].next_sparse = remapped_sparse_index; - self.sparse_to_packed_map[remapped_sparse_index].packed_index = packed_index; - self.next_packed_index += 1; - self.next_collision_index += 1; - self.written_indices_count += 1; - } - - pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { - allocator.free(self.vertices); - allocator.free(self.indices); - allocator.free(self.sparse_to_packed_map); - } - - pub fn indexBuffer(self: @This()) []IndexType { - return self.indices; - } - - pub fn vertexBuffer(self: @This()) []VertexType { - return self.vertices[0..self.next_packed_index]; - } - }; -} - -test "VertexWriter" { - const Vec3 = [3]f32; - const Vertex = extern struct { - position: Vec3, - normal: Vec3, - }; - - const expect = std.testing.expect; - const allocator = std.testing.allocator; - - const Face = struct { - position: [3]u16, - normal: [3]u16, - }; - - const vertices = [_]Vec3{ - Vec3{ 1.0, 0.0, 0.0 }, // 0: Position - Vec3{ 2.0, 0.0, 0.0 }, // 1: Position - Vec3{ 3.0, 0.0, 0.0 }, // 2: Position - Vec3{ 1.0, 0.0, 0.0 }, // 3: Normal - Vec3{ 4.0, 0.0, 0.0 }, // 4: Position - Vec3{ 0.0, 1.0, 0.0 }, // 5: Normal - Vec3{ 5.0, 0.0, 0.0 }, // 6: Position - Vec3{ 0.0, 0.0, 1.0 }, // 7: Normal - Vec3{ 1.0, 0.0, 1.0 }, // 8: Normal - Vec3{ 6.0, 0.0, 0.0 }, // 9: Position - }; - - const faces = [_]Face{ - .{ .position = .{ 0, 4, 2 }, .normal = .{ 7, 5, 3 } }, - .{ .position = .{ 2, 3, 9 }, .normal = .{ 3, 7, 8 } }, - .{ .position = .{ 9, 2, 4 }, .normal = .{ 8, 7, 5 } }, - .{ .position = .{ 2, 6, 1 }, .normal = .{ 3, 5, 7 } }, - .{ .position = .{ 9, 6, 0 }, .normal = .{ 5, 7, 8 } }, - }; - - var writer = try VertexWriter(Vertex, u32).init( - allocator, - faces.len * 3, // indices count - vertices.len, // original vertices count - faces.len * 3, // maximum vertices count - ); - defer writer.deinit(allocator); - - for (faces) |face| { - var x: usize = 0; - while (x < 3) : (x += 1) { - const position_index = face.position[x]; - const position = vertices[position_index]; - const normal = vertices[face.normal[x]]; - const vertex = Vertex{ - .position = position, - .normal = normal, - }; - writer.put(vertex, position_index); - } - } - - const indices = writer.indexBuffer(); - try expect(indices.len == faces.len * 3); - - // Face 0 - try expect(indices[0] == 0); // (0, 7) New - try expect(indices[1] == 1); // (4, 5) New - try expect(indices[2] == 2); // (2, 3) New - - // Face 1 - try expect(indices[3 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[3 + 1] == 3); // (3, 7) New - try expect(indices[3 + 2] == 4); // (9, 8) New - - // Face 2 - try expect(indices[6 + 0] == 4); // (9, 8) Duplicate - Reuse index - try expect(indices[6 + 1] == 5); // (2, 7) New normal mapping (Don't clobber) - try expect(indices[6 + 2] == 1); // (4, 5) Duplicate - Reuse Index - - // Face 3 - try expect(indices[9 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[9 + 1] == 6); // (6, 5) New - try expect(indices[9 + 2] == 7); // (1, 7) New - - // Face 4 - try expect(indices[12 + 0] == 8); // (9, 5) New normal mapping (Don't clobber) - try expect(indices[12 + 1] == 9); // (6, 7) New normal mapping (Don't clobber) - try expect(indices[12 + 2] == 10); // (0, 8) New normal mapping (Don't clobber) - - try expect(writer.vertexBuffer().len == 11); -} diff --git a/src/core/examples/sysgpu/deferred-rendering/fragmentDeferredRendering.wgsl b/src/core/examples/sysgpu/deferred-rendering/fragmentDeferredRendering.wgsl deleted file mode 100644 index 51d7a93c..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/fragmentDeferredRendering.wgsl +++ /dev/null @@ -1,89 +0,0 @@ - -@group(0) @binding(0) var gBufferNormal: texture_2d; -@group(0) @binding(1) var gBufferAlbedo: texture_2d; -@group(0) @binding(2) var gBufferDepth: texture_depth_2d; - -struct LightData { - position : vec4, - // TODO - vec3 alignment - color_x : f32, - color_y : f32, - color_z : f32, - radius : f32, -} -struct LightsBuffer { - lights: array, -} -@group(1) @binding(0) var lightsBuffer: LightsBuffer; - -struct Config { - numLights : u32, -} -struct Camera { - viewProjectionMatrix : mat4x4, - invViewProjectionMatrix : mat4x4, -} -@group(1) @binding(1) var config: Config; -@group(1) @binding(2) var camera: Camera; - -fn world_from_screen_coord(coord : vec2, depth_sample: f32) -> vec3 { - // reconstruct world-space position from the screen coordinate. - let posClip = vec4(coord.x * 2.0 - 1.0, (1.0 - coord.y) * 2.0 - 1.0, depth_sample, 1.0); - let posWorldW = camera.invViewProjectionMatrix * posClip; - let posWorld = posWorldW.xyz / posWorldW.www; - return posWorld; -} - -@fragment -fn main( - @builtin(position) coord : vec4 -) -> @location(0) vec4 { - // TODO - variable initialization - var result = vec3(0, 0, 0); - - let depth = textureLoad( - gBufferDepth, - vec2(floor(coord.xy)), - 0 - ); - - // Don't light the sky. - if (depth >= 1.0) { - discard; - } - - let bufferSize = textureDimensions(gBufferDepth); - let coordUV = coord.xy / vec2(bufferSize); - let position = world_from_screen_coord(coordUV, depth); - - let normal = textureLoad( - gBufferNormal, - vec2(floor(coord.xy)), - 0 - ).xyz; - - let albedo = textureLoad( - gBufferAlbedo, - vec2(floor(coord.xy)), - 0 - ).rgb; - - for (var i = 0u; i < config.numLights; i++) { - // TODO - vec3 alignment - let lightColor = vec3(lightsBuffer.lights[i].color_x, lightsBuffer.lights[i].color_y, lightsBuffer.lights[i].color_z); - let L = lightsBuffer.lights[i].position.xyz - position; - let distance = length(L); - if (distance > lightsBuffer.lights[i].radius) { - continue; - } - let lambert = max(dot(normal, normalize(L)), 0.0); - result += vec3( - lambert * pow(1.0 - distance / lightsBuffer.lights[i].radius, 2.0) * lightColor * albedo - ); - } - - // some manual ambient - result += vec3(0.2); - - return vec4(result, 1.0); -} diff --git a/src/core/examples/sysgpu/deferred-rendering/fragmentGBuffersDebugView.wgsl b/src/core/examples/sysgpu/deferred-rendering/fragmentGBuffersDebugView.wgsl deleted file mode 100644 index cedf7645..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/fragmentGBuffersDebugView.wgsl +++ /dev/null @@ -1,44 +0,0 @@ - -@group(0) @binding(0) var gBufferNormal: texture_2d; -@group(0) @binding(1) var gBufferAlbedo: texture_2d; -@group(0) @binding(2) var gBufferDepth: texture_depth_2d; - -@group(1) @binding(0) var canvas : CanvasConstants; - -struct CanvasConstants { - size: vec2, -} - -@fragment -fn main( - @builtin(position) coord : vec4 -) -> @location(0) vec4 { - var result : vec4; - let c = coord.xy / vec2(canvas.size.x, canvas.size.y); - if (c.x < 0.33333) { - let rawDepth = textureLoad( - gBufferDepth, - vec2(floor(coord.xy)), - 0 - ); - // remap depth into something a bit more visible - let depth = (1.0 - rawDepth) * 50.0; - result = vec4(depth); - } else if (c.x < 0.66667) { - result = textureLoad( - gBufferNormal, - vec2(floor(coord.xy)), - 0 - ); - result.x = (result.x + 1.0) * 0.5; - result.y = (result.y + 1.0) * 0.5; - result.z = (result.z + 1.0) * 0.5; - } else { - result = textureLoad( - gBufferAlbedo, - vec2(floor(coord.xy)), - 0 - ); - } - return result; -} diff --git a/src/core/examples/sysgpu/deferred-rendering/fragmentWriteGBuffers.wgsl b/src/core/examples/sysgpu/deferred-rendering/fragmentWriteGBuffers.wgsl deleted file mode 100644 index 3cd2f652..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/fragmentWriteGBuffers.wgsl +++ /dev/null @@ -1,22 +0,0 @@ -struct GBufferOutput { - @location(0) normal : vec4, - - // Textures: diffuse color, specular color, smoothness, emissive etc. could go here - @location(1) albedo : vec4, -} - -@fragment -fn main( - @location(0) fragNormal: vec3, - @location(1) fragUV : vec2 -) -> GBufferOutput { - // faking some kind of checkerboard texture - let uv = floor(30.0 * fragUV); - let c = 0.2 + 0.5 * ((uv.x + uv.y) - 2.0 * floor((uv.x + uv.y) / 2.0)); - - var output : GBufferOutput; - output.normal = vec4(fragNormal, 1.0); - output.albedo = vec4(c, c, c, 1.0); - - return output; -} diff --git a/src/core/examples/sysgpu/deferred-rendering/lightUpdate.wgsl b/src/core/examples/sysgpu/deferred-rendering/lightUpdate.wgsl deleted file mode 100644 index 548d8f8b..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/lightUpdate.wgsl +++ /dev/null @@ -1,37 +0,0 @@ -struct LightData { - position : vec4, - // TODO - vec3 alignment - color_x : f32, - color_y : f32, - color_z : f32, - radius : f32, -} -struct LightsBuffer { - lights: array, -} -@group(0) @binding(0) var lightsBuffer: LightsBuffer; - -struct Config { - numLights : u32, -} -@group(0) @binding(1) var config: Config; - -struct LightExtent { - min : vec4, - max : vec4, -} -@group(0) @binding(2) var lightExtent: LightExtent; - -@compute @workgroup_size(64, 1, 1) -fn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) { - var index = GlobalInvocationID.x; - if (index >= config.numLights) { - return; - } - - lightsBuffer.lights[index].position.y = lightsBuffer.lights[index].position.y - 0.5 - 0.003 * (f32(index) - 64.0 * floor(f32(index) / 64.0)); - - if (lightsBuffer.lights[index].position.y < lightExtent.min.y) { - lightsBuffer.lights[index].position.y = lightExtent.max.y; - } -} diff --git a/src/core/examples/sysgpu/deferred-rendering/main.zig b/src/core/examples/sysgpu/deferred-rendering/main.zig deleted file mode 100644 index cf542b7d..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/main.zig +++ /dev/null @@ -1,1225 +0,0 @@ -const std = @import("std"); - -const mach = @import("mach"); -const core = mach.core; -const gpu = mach.gpu; - -const m3d = @import("model3d"); -const zm = @import("zmath"); -const assets = @import("assets"); -const VertexWriter = @import("vertex_writer.zig").VertexWriter; - -pub const App = @This(); - -// Use experimental sysgpu graphics API -pub const use_sysgpu = true; - -const Vec2 = [2]f32; -const Vec3 = [3]f32; -const Vec4 = [4]f32; -const Mat4 = [4]Vec4; - -fn Dimensions2D(comptime T: type) type { - return struct { - width: T, - height: T, - }; -} - -const Vertex = extern struct { - position: Vec3, - normal: Vec3, - uv: Vec2, -}; - -const ViewMatrices = struct { - up_vector: zm.Vec, - origin: zm.Vec, - projection_matrix: zm.Mat, - view_proj_matrix: zm.Mat, -}; - -const TextureQuadPass = struct { - color_attachment: gpu.RenderPassColorAttachment, - descriptor: gpu.RenderPassDescriptor, -}; - -const WriteGBufferPass = struct { - color_attachments: [2]gpu.RenderPassColorAttachment, - depth_stencil_attachment: gpu.RenderPassDepthStencilAttachment, - descriptor: gpu.RenderPassDescriptor, -}; - -const RenderMode = enum(u32) { - rendering, - gbuffer_view, -}; - -const Settings = struct { - render_mode: RenderMode, - lights_count: i32, -}; - -// -// Constants -// - -const max_num_lights = 1024; -const light_data_stride = 8; -const light_extent_min = Vec3{ -50.0, -30.0, -50.0 }; -const light_extent_max = Vec3{ 50.0, 30.0, 50.0 }; -const camera_uniform_buffer_size = @sizeOf(Mat4) * 2; - -// -// Member variables -// - -const GBuffer = struct { - texture_2d_float16: *gpu.Texture, - texture_albedo: *gpu.Texture, - texture_depth: *gpu.Texture, - texture_views: [3]*gpu.TextureView, -}; - -const Lights = struct { - buffer: *gpu.Buffer, - buffer_size: u64, - extent_buffer: *gpu.Buffer, - extent_buffer_size: u64, - config_uniform_buffer: *gpu.Buffer, - config_uniform_buffer_size: u64, - buffer_bind_group: *gpu.BindGroup, - buffer_bind_group_layout: *gpu.BindGroupLayout, - buffer_compute_bind_group: *gpu.BindGroup, - buffer_compute_bind_group_layout: *gpu.BindGroupLayout, -}; - -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - -title_timer: core.Timer, -timer: core.Timer, -delta_time: f32, - -camera_rotation: f32, -vertex_buffer: *gpu.Buffer, -vertex_count: u32, -index_buffer: *gpu.Buffer, -index_count: u32, -gbuffer: GBuffer, -model_uniform_buffer: *gpu.Buffer, -camera_uniform_buffer: *gpu.Buffer, -surface_size_uniform_buffer: *gpu.Buffer, -lights: Lights, -view_matrices: ViewMatrices, - -// Bind groups -scene_uniform_bind_group: *gpu.BindGroup, -surface_size_uniform_bind_group: *gpu.BindGroup, -gbuffer_textures_bind_group: *gpu.BindGroup, - -// Bind group layouts -scene_uniform_bind_group_layout: *gpu.BindGroupLayout, -surface_size_uniform_bind_group_layout: *gpu.BindGroupLayout, -gbuffer_textures_bind_group_layout: *gpu.BindGroupLayout, - -// Pipelines -write_gbuffers_pipeline: *gpu.RenderPipeline, -gbuffers_debug_view_pipeline: *gpu.RenderPipeline, -deferred_render_pipeline: *gpu.RenderPipeline, -light_update_compute_pipeline: *gpu.ComputePipeline, - -// Pipeline layouts -write_gbuffers_pipeline_layout: *gpu.PipelineLayout, -gbuffers_debug_view_pipeline_layout: *gpu.PipelineLayout, -deferred_render_pipeline_layout: *gpu.PipelineLayout, -light_update_compute_pipeline_layout: *gpu.PipelineLayout, - -// Render pass descriptor -write_gbuffer_pass: WriteGBufferPass, -texture_quad_pass: TextureQuadPass, -settings: Settings, - -screen_dimensions: Dimensions2D(u32), -is_paused: bool, - -// -// Functions -// - -pub fn init(app: *App) !void { - try core.init(.{}); - - // This example has some frame-rate-dependent animation, so restrict frame rate to 60hz. - core.setFrameRateLimit(60); - - app.timer = try core.Timer.start(); - app.title_timer = try core.Timer.start(); - - app.camera_rotation = 0.0; - app.is_paused = false; - app.settings.render_mode = .rendering; - app.settings.lights_count = 128; - - app.screen_dimensions = Dimensions2D(u32){ - .width = core.descriptor.width, - .height = core.descriptor.height, - }; - - try app.loadMeshFromModel3d(std.heap.c_allocator, assets.stanford_dragon_m3d); - app.prepareGBufferTextureRenderTargets(); - app.prepareBindGroupLayouts(); - app.prepareRenderPipelineLayouts(); - app.prepareWriteGBuffersPipeline(); - app.prepareGBuffersDebugViewPipeline(); - app.prepareDeferredRenderPipeline(); - app.setupRenderPasses(); - app.prepareUniformBuffers(); - app.prepareComputePipelineLayout(); - app.prepareLightUpdateComputePipeline(); - app.prepareLights(); - app.prepareViewMatrices(); - app.printControls(); -} - -pub fn deinit(app: *App) void { - defer _ = gpa.deinit(); - defer core.deinit(); - - app.write_gbuffers_pipeline.release(); - app.gbuffers_debug_view_pipeline.release(); - app.deferred_render_pipeline.release(); - app.light_update_compute_pipeline.release(); - - app.write_gbuffers_pipeline_layout.release(); - app.gbuffers_debug_view_pipeline_layout.release(); - app.deferred_render_pipeline_layout.release(); - app.light_update_compute_pipeline_layout.release(); - - app.scene_uniform_bind_group.release(); - app.surface_size_uniform_bind_group.release(); - app.gbuffer_textures_bind_group.release(); - - app.lights.buffer.release(); - app.lights.extent_buffer.release(); - app.lights.config_uniform_buffer.release(); - app.lights.buffer_bind_group.release(); - app.lights.buffer_bind_group_layout.release(); - app.lights.buffer_compute_bind_group.release(); - app.lights.buffer_compute_bind_group_layout.release(); - - app.gbuffer.texture_views[0].release(); - app.gbuffer.texture_views[1].release(); - app.gbuffer.texture_views[2].release(); - - app.gbuffer.texture_2d_float16.release(); - app.gbuffer.texture_albedo.release(); - app.gbuffer.texture_depth.release(); - - app.scene_uniform_bind_group_layout.release(); - app.surface_size_uniform_bind_group_layout.release(); - app.gbuffer_textures_bind_group_layout.release(); - - app.surface_size_uniform_buffer.release(); - app.model_uniform_buffer.release(); - app.camera_uniform_buffer.release(); - app.vertex_buffer.release(); - app.index_buffer.release(); -} - -pub fn update(app: *App) !bool { - app.delta_time = app.timer.lap(); - - var iter = core.pollEvents(); - while (iter.next()) |event| { - app.updateUI(event); - switch (event) { - .framebuffer_resize => |ev| { - app.screen_dimensions.width = ev.width; - app.screen_dimensions.height = ev.height; - - // TODO: we use destroy() here instead of release() because our reference counting - // is wrong somewhere else. - app.gbuffer.texture_2d_float16.release(); - app.gbuffer.texture_albedo.release(); - app.gbuffer.texture_depth.release(); - app.gbuffer.texture_views[0].release(); - app.gbuffer.texture_views[1].release(); - app.gbuffer.texture_views[2].release(); - app.gbuffer_textures_bind_group.release(); - - app.prepareGBufferTextureRenderTargets(); - app.setupRenderPasses(); - - const bind_group_entries = [_]gpu.BindGroup.Entry{ - gpu.BindGroup.Entry.textureView(0, app.gbuffer.texture_views[0]), - gpu.BindGroup.Entry.textureView(1, app.gbuffer.texture_views[1]), - gpu.BindGroup.Entry.textureView(2, app.gbuffer.texture_views[2]), - }; - app.gbuffer_textures_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.gbuffer_textures_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - - app.prepareViewMatrices(); - }, - .close => return true, - else => {}, - } - } - - if (!app.is_paused) { - app.updateUniformBuffers(); - } - - const command = try app.buildCommandBuffer(); - const queue = core.queue; - queue.submit(&[_]*gpu.CommandBuffer{command}); - command.release(); - core.swap_chain.present(); - core.swap_chain.getCurrentTextureView().?.release(); - - // update the window title every second - if (app.title_timer.read() >= 1.0) { - app.title_timer.reset(); - try core.printTitle("Deferred Rendering [ {d}fps ] [ Input {d}hz ]", .{ - core.frameRate(), - core.inputRate(), - }); - } - - return false; -} - -fn loadMeshFromModel3d(app: *App, allocator: std.mem.Allocator, model_data: [:0]const u8) !void { - const m3d_model = m3d.load(model_data, null, null, null) orelse return error.LoadModelFailed; - - const vertex_count = m3d_model.handle.numvertex; - const vertices = m3d_model.handle.vertex[0..vertex_count]; - - const face_count = m3d_model.handle.numface; - app.index_count = (face_count * 3) + 6; - - var vertex_writer = try VertexWriter(Vertex, u16).init( - allocator, - @as(u16, @intCast(app.index_count)), - @as(u16, @intCast(vertex_count)), - @as(u16, @intCast(face_count * 3)), - ); - defer vertex_writer.deinit(allocator); - - const scale: f32 = 80.0; - const plane_xy = [2]usize{ 0, 1 }; - var extent_min = [2]f32{ std.math.floatMax(f32), std.math.floatMax(f32) }; - var extent_max = [2]f32{ std.math.floatMin(f32), std.math.floatMin(f32) }; - - var i: usize = 0; - while (i < face_count) : (i += 1) { - const face = m3d_model.handle.face[i]; - var x: usize = 0; - while (x < 3) : (x += 1) { - const vertex_index = face.vertex[x]; - const normal_index = face.normal[x]; - const position = Vec3{ - vertices[vertex_index].x * scale, - vertices[vertex_index].y * scale, - vertices[vertex_index].z * scale, - }; - extent_min[0] = @min(position[plane_xy[0]], extent_min[0]); - extent_min[1] = @min(position[plane_xy[1]], extent_min[1]); - extent_max[0] = @max(position[plane_xy[0]], extent_max[0]); - extent_max[1] = @max(position[plane_xy[1]], extent_max[1]); - const vertex = Vertex{ .position = position, .normal = .{ - vertices[normal_index].x, - vertices[normal_index].y, - vertices[normal_index].z, - }, .uv = .{ position[plane_xy[0]], position[plane_xy[1]] } }; - vertex_writer.put(vertex, @as(u16, @intCast(vertex_index))); - } - } - - const vertex_buffer = vertex_writer.vertices[0 .. vertex_writer.next_packed_index + 4]; - const index_buffer = vertex_writer.indices; - - app.vertex_count = @as(u32, @intCast(vertex_buffer.len)); - - // - // Compute UV values - // - for (vertex_buffer) |*vertex| { - vertex.uv = .{ - (vertex.uv[0] - extent_min[0]) / (extent_max[0] - extent_min[0]), - (vertex.uv[1] - extent_min[1]) / (extent_max[1] - extent_min[1]), - }; - } - - // - // Manually append ground plane to mesh - // - { - const last_vertex_index: u16 = @as(u16, @intCast(vertex_buffer.len - 4)); - const index_base = index_buffer.len - 6; - index_buffer[index_base + 0] = last_vertex_index; - index_buffer[index_base + 1] = last_vertex_index + 2; - index_buffer[index_base + 2] = last_vertex_index + 1; - index_buffer[index_base + 3] = last_vertex_index; - index_buffer[index_base + 4] = last_vertex_index + 1; - index_buffer[index_base + 5] = last_vertex_index + 3; - } - - { - const index_base = vertex_buffer.len - 4; - vertex_buffer[index_base + 0].position = .{ -100.0, 20.0, -100.0 }; - vertex_buffer[index_base + 1].position = .{ 100.0, 20.0, 100.0 }; - vertex_buffer[index_base + 2].position = .{ -100.0, 20.0, 100.0 }; - vertex_buffer[index_base + 3].position = .{ 100.0, 20.0, -100.0 }; - vertex_buffer[index_base + 0].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 1].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 2].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 3].normal = .{ 0.0, 1.0, 0.0 }; - vertex_buffer[index_base + 0].uv = .{ 0.0, 0.0 }; - vertex_buffer[index_base + 1].uv = .{ 1.0, 1.0 }; - vertex_buffer[index_base + 2].uv = .{ 0.0, 1.0 }; - vertex_buffer[index_base + 3].uv = .{ 1.0, 0.0 }; - } - - { - const buffer_size = vertex_buffer.len * @sizeOf(Vertex); - app.vertex_buffer = core.device.createBuffer(&.{ - .usage = .{ .vertex = true }, - .size = roundToMultipleOf4(u64, buffer_size), - .mapped_at_creation = .true, - }); - var mapping = app.vertex_buffer.getMappedRange(Vertex, 0, vertex_buffer.len).?; - @memcpy(mapping[0..vertex_buffer.len], vertex_buffer); - app.vertex_buffer.unmap(); - } - { - const buffer_size = index_buffer.len * @sizeOf(u16); - app.index_buffer = core.device.createBuffer(&.{ - .usage = .{ .index = true }, - .size = roundToMultipleOf4(u64, buffer_size), - .mapped_at_creation = .true, - }); - var mapping = app.index_buffer.getMappedRange(u16, 0, index_buffer.len).?; - @memcpy(mapping[0..index_buffer.len], index_buffer); - app.index_buffer.unmap(); - } -} - -fn prepareGBufferTextureRenderTargets(app: *App) void { - var screen_extent = gpu.Extent3D{ - .width = app.screen_dimensions.width, - .height = app.screen_dimensions.height, - .depth_or_array_layers = 2, - }; - screen_extent.depth_or_array_layers = 1; - app.gbuffer.texture_2d_float16 = core.device.createTexture(&.{ - .size = screen_extent, - .format = .rgba16_float, - .mip_level_count = 1, - .sample_count = 1, - .usage = .{ - .texture_binding = true, - .render_attachment = true, - }, - }); - app.gbuffer.texture_albedo = core.device.createTexture(&.{ - .size = screen_extent, - .format = .bgra8_unorm, - .usage = .{ - .texture_binding = true, - .render_attachment = true, - }, - }); - app.gbuffer.texture_depth = core.device.createTexture(&.{ - .size = screen_extent, - .mip_level_count = 1, - .sample_count = 1, - .dimension = .dimension_2d, - .format = .depth24_plus, - .usage = .{ - .texture_binding = true, - .render_attachment = true, - }, - }); - - var texture_view_descriptor = gpu.TextureView.Descriptor{ - .format = .undefined, - .dimension = .dimension_2d, - .array_layer_count = 1, - .aspect = .all, - .base_array_layer = 0, - }; - - texture_view_descriptor.format = .rgba16_float; - app.gbuffer.texture_views[0] = app.gbuffer.texture_2d_float16.createView(&texture_view_descriptor); - - texture_view_descriptor.format = .bgra8_unorm; - app.gbuffer.texture_views[1] = app.gbuffer.texture_albedo.createView(&texture_view_descriptor); - - texture_view_descriptor.format = .depth24_plus; - app.gbuffer.texture_views[2] = app.gbuffer.texture_depth.createView(&texture_view_descriptor); -} - -fn prepareBindGroupLayouts(app: *App) void { - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.texture(0, .{ .fragment = true }, .unfilterable_float, .dimension_2d, false), - gpu.BindGroupLayout.Entry.texture(1, .{ .fragment = true }, .unfilterable_float, .dimension_2d, false), - gpu.BindGroupLayout.Entry.texture(2, .{ .fragment = true }, .depth, .dimension_2d, false), - }; - app.gbuffer_textures_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const min_binding_size = light_data_stride * max_num_lights * @sizeOf(f32); - const visibility = gpu.ShaderStageFlags{ .fragment = true, .compute = true }; - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer( - 0, - visibility, - .read_only_storage, - false, - min_binding_size, - ), - gpu.BindGroupLayout.Entry.buffer(1, visibility, .uniform, false, @sizeOf(u32)), - gpu.BindGroupLayout.Entry.buffer(2, .{ .fragment = true }, .uniform, false, @sizeOf(Mat4) * 2), - }; - app.lights.buffer_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer(0, .{ .fragment = true }, .uniform, false, @sizeOf(Vec2)), - }; - app.surface_size_uniform_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer(0, .{ .vertex = true }, .uniform, false, @sizeOf(Mat4) * 2), - gpu.BindGroupLayout.Entry.buffer(1, .{ .vertex = true }, .uniform, false, @sizeOf(Mat4) * 2), - }; - app.scene_uniform_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } - { - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - gpu.BindGroupLayout.Entry.buffer(0, .{ .compute = true }, .storage, false, @sizeOf(f32) * light_data_stride * max_num_lights), - gpu.BindGroupLayout.Entry.buffer(1, .{ .compute = true }, .uniform, false, @sizeOf(u32)), - gpu.BindGroupLayout.Entry.buffer(2, .{ .compute = true }, .uniform, false, camera_uniform_buffer_size), - }; - app.lights.buffer_compute_bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = &bind_group_layout_entries, - }), - ); - } -} - -fn prepareRenderPipelineLayouts(app: *App) void { - { - // Write GBuffers pipeline layout - const bind_group_layouts = [_]*gpu.BindGroupLayout{app.scene_uniform_bind_group_layout}; - app.write_gbuffers_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - } - { - // GBuffers debug view pipeline layout - const bind_group_layouts = [_]*gpu.BindGroupLayout{ - app.gbuffer_textures_bind_group_layout, - app.surface_size_uniform_bind_group_layout, - }; - app.gbuffers_debug_view_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - } - { - // Deferred render pipeline layout - const bind_group_layouts = [_]*gpu.BindGroupLayout{ - app.gbuffer_textures_bind_group_layout, - app.lights.buffer_bind_group_layout, - app.surface_size_uniform_bind_group_layout, - }; - app.deferred_render_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - } -} - -fn prepareWriteGBuffersPipeline(app: *App) void { - const color_target_states = [_]gpu.ColorTargetState{ - .{ .format = .rgba16_float }, - .{ .format = .bgra8_unorm }, - }; - - const write_gbuffers_vertex_buffer_layout = gpu.VertexBufferLayout.init(.{ - .array_stride = @sizeOf(Vertex), - .step_mode = .vertex, - .attributes = &.{ - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "position"), .shader_location = 0 }, - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "normal"), .shader_location = 1 }, - .{ .format = .float32x2, .offset = @offsetOf(Vertex, "uv"), .shader_location = 2 }, - }, - }); - - const vertex_shader_module = core.device.createShaderModuleWGSL( - "vertexWriteGBuffers.wgsl", - @embedFile("vertexWriteGBuffers.wgsl"), - ); - const fragment_shader_module = core.device.createShaderModuleWGSL( - "fragmentWriteGBuffers.wgsl", - @embedFile("fragmentWriteGBuffers.wgsl"), - ); - - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .label = "gbuffers_pipeline", - .layout = app.write_gbuffers_pipeline_layout, - .primitive = .{ .cull_mode = .back }, - .depth_stencil = &.{ - .format = .depth24_plus, - .depth_write_enabled = .true, - .depth_compare = .less, - }, - .vertex = gpu.VertexState.init(.{ - .module = vertex_shader_module, - .entry_point = "main", - .buffers = &.{write_gbuffers_vertex_buffer_layout}, - }), - .fragment = &gpu.FragmentState.init(.{ - .module = fragment_shader_module, - .entry_point = "main", - .targets = &color_target_states, - }), - }; - app.write_gbuffers_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - - vertex_shader_module.release(); - fragment_shader_module.release(); -} - -fn prepareGBuffersDebugViewPipeline(app: *App) void { - const blend_component_descriptor = gpu.BlendComponent{ - .operation = .add, - .src_factor = .one, - .dst_factor = .zero, - }; - - const color_target_state = gpu.ColorTargetState{ - .format = core.descriptor.format, - .blend = &.{ - .color = blend_component_descriptor, - .alpha = blend_component_descriptor, - }, - }; - - const vertex_shader_module = core.device.createShaderModuleWGSL( - "vertexTextureQuad.wgsl", - @embedFile("vertexTextureQuad.wgsl"), - ); - const fragment_shader_module = core.device.createShaderModuleWGSL( - "fragmentGBuffersDebugView.wgsl", - @embedFile("fragmentGBuffersDebugView.wgsl"), - ); - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .layout = app.gbuffers_debug_view_pipeline_layout, - .primitive = .{ - .cull_mode = .back, - }, - .vertex = gpu.VertexState.init(.{ - .module = vertex_shader_module, - .entry_point = "main", - }), - .fragment = &gpu.FragmentState.init(.{ - .module = fragment_shader_module, - .entry_point = "main", - .targets = &.{color_target_state}, - }), - }; - app.gbuffers_debug_view_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - vertex_shader_module.release(); - fragment_shader_module.release(); -} - -fn prepareDeferredRenderPipeline(app: *App) void { - const blend_component_descriptor = gpu.BlendComponent{ - .operation = .add, - .src_factor = .one, - .dst_factor = .zero, - }; - - const color_target_state = gpu.ColorTargetState{ - .format = .bgra8_unorm, - .blend = &.{ - .color = blend_component_descriptor, - .alpha = blend_component_descriptor, - }, - }; - - const vertex_shader_module = core.device.createShaderModuleWGSL( - "vertexTextureQuad.wgsl", - @embedFile("vertexTextureQuad.wgsl"), - ); - const fragment_shader_module = core.device.createShaderModuleWGSL( - "fragmentDeferredRendering.wgsl", - @embedFile("fragmentDeferredRendering.wgsl"), - ); - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .layout = app.deferred_render_pipeline_layout, - .primitive = .{ - .cull_mode = .back, - }, - .vertex = gpu.VertexState.init(.{ - .module = vertex_shader_module, - .entry_point = "main", - }), - .fragment = &gpu.FragmentState.init(.{ - .module = fragment_shader_module, - .entry_point = "main", - .targets = &.{color_target_state}, - }), - }; - app.deferred_render_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - vertex_shader_module.release(); - fragment_shader_module.release(); -} - -fn setupRenderPasses(app: *App) void { - { - // Write GBuffer pass - app.write_gbuffer_pass.color_attachments = [_]gpu.RenderPassColorAttachment{ - .{ - .view = app.gbuffer.texture_views[0], - .load_op = .clear, - .store_op = .store, - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 1.0, - .a = 1.0, - }, - }, - .{ - .view = app.gbuffer.texture_views[1], - .load_op = .clear, - .store_op = .store, - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 0.0, - .a = 1.0, - }, - }, - }; - - app.write_gbuffer_pass.depth_stencil_attachment = gpu.RenderPassDepthStencilAttachment{ - .view = app.gbuffer.texture_views[2], - .depth_load_op = .clear, - .depth_store_op = .store, - .depth_clear_value = 1.0, - .stencil_clear_value = 1.0, - }; - - app.write_gbuffer_pass.descriptor = gpu.RenderPassDescriptor.init(.{ - .label = "write_gbuffer_pass", - .color_attachments = &app.write_gbuffer_pass.color_attachments, - .depth_stencil_attachment = &app.write_gbuffer_pass.depth_stencil_attachment, - }); - } - { - // Texture Quad Pass - app.texture_quad_pass.color_attachment = gpu.RenderPassColorAttachment{ - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 0.0, - .a = 1.0, - }, - .load_op = .clear, - .store_op = .store, - }; - - app.texture_quad_pass.descriptor = gpu.RenderPassDescriptor{ - .label = "texture_quad_pass(1)", - .color_attachment_count = 1, - .color_attachments = &[_]gpu.RenderPassColorAttachment{app.texture_quad_pass.color_attachment}, - }; - } -} - -fn prepareUniformBuffers(app: *App) void { - { - // Config uniform buffer - app.lights.config_uniform_buffer_size = @sizeOf(i32); - app.lights.config_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.lights.config_uniform_buffer_size, - .mapped_at_creation = .true, - }); - var config_data = app.lights.config_uniform_buffer.getMappedRange(i32, 0, 1).?; - config_data[0] = app.settings.lights_count; - app.lights.config_uniform_buffer.unmap(); - } - { - // Model uniform buffer - app.model_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = @sizeOf(Mat4) * 2, - }); - } - { - // Camera uniform buffer - app.camera_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = @sizeOf(Mat4) * 2, - }); - } - { - // Scene uniform bind group - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.model_uniform_buffer, - .size = @sizeOf(Mat4) * 2, - .elem_size = @sizeOf(Mat4) * 2, - }, - .{ - .binding = 1, - .buffer = app.camera_uniform_buffer, - .size = camera_uniform_buffer_size, - .elem_size = camera_uniform_buffer_size, - }, - }; - const bind_group_layout = app.write_gbuffers_pipeline.getBindGroupLayout(0); - app.scene_uniform_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .label = "scene_uniform_bind_group", - .layout = bind_group_layout, - .entries = &bind_group_entries, - }), - ); - bind_group_layout.release(); - } - { - // Surface size uniform buffer - app.surface_size_uniform_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = @sizeOf(f32) * 4, - }); - } - { - // Surface size uniform bind group - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.surface_size_uniform_buffer, - .size = @sizeOf(f32) * 2, - .elem_size = @sizeOf(f32) * 2, - }, - }; - app.surface_size_uniform_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.surface_size_uniform_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } - { - // GBuffer textures bind group - const bind_group_entries = [_]gpu.BindGroup.Entry{ - gpu.BindGroup.Entry.textureView(0, app.gbuffer.texture_views[0]), - gpu.BindGroup.Entry.textureView(1, app.gbuffer.texture_views[1]), - gpu.BindGroup.Entry.textureView(2, app.gbuffer.texture_views[2]), - }; - app.gbuffer_textures_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.gbuffer_textures_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } -} - -fn prepareComputePipelineLayout(app: *App) void { - const bind_group_layouts = [_]*gpu.BindGroupLayout{app.lights.buffer_compute_bind_group_layout}; - app.light_update_compute_pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); -} - -fn prepareLightUpdateComputePipeline(app: *App) void { - const shader_module = core.device.createShaderModuleWGSL( - "lightUpdate.wgsl", - @embedFile("lightUpdate.wgsl"), - ); - app.light_update_compute_pipeline = core.device.createComputePipeline(&gpu.ComputePipeline.Descriptor{ - .compute = gpu.ProgrammableStageDescriptor{ - .module = shader_module, - .entry_point = "main", - }, - .layout = app.light_update_compute_pipeline_layout, - }); - shader_module.release(); -} - -fn prepareLights(app: *App) void { - // Lights data are uploaded in a storage buffer - // which could be updated/culled/etc. with a compute shader - const extent = comptime Vec3{ - light_extent_max[0] - light_extent_min[0], - light_extent_max[1] - light_extent_min[1], - light_extent_max[2] - light_extent_min[2], - }; - app.lights.buffer_size = @sizeOf(f32) * light_data_stride * max_num_lights; - app.lights.buffer = core.device.createBuffer(&.{ - .usage = .{ .storage = true }, - .size = app.lights.buffer_size, - .mapped_at_creation = .true, - }); - // We randomly populate lights randomly in a box range - // And simply move them along y-axis per frame to show they are dynamic lightings - var light_data = app.lights.buffer.getMappedRange(f32, 0, light_data_stride * max_num_lights).?; - - var xoroshiro = std.rand.Xoroshiro128.init(9273853284918); - const rng = std.rand.Random.init( - &xoroshiro, - std.rand.Xoroshiro128.fill, - ); - var i: usize = 0; - var offset: usize = 0; - while (i < max_num_lights) : (i += 1) { - offset = light_data_stride * i; - // Position - light_data[offset + 0] = rng.float(f32) * extent[0] + light_extent_min[0]; - light_data[offset + 1] = rng.float(f32) * extent[1] + light_extent_min[1]; - light_data[offset + 2] = rng.float(f32) * extent[2] + light_extent_min[2]; - light_data[offset + 3] = 1.0; - // Color - light_data[offset + 4] = rng.float(f32) * 2.0; - light_data[offset + 5] = rng.float(f32) * 2.0; - light_data[offset + 6] = rng.float(f32) * 2.0; - // Radius - light_data[offset + 7] = 20.0; - } - app.lights.buffer.unmap(); - // - // Lights extent buffer - // - app.lights.extent_buffer_size = @sizeOf(f32) * light_data_stride * max_num_lights; - app.lights.extent_buffer = core.device.createBuffer(&.{ - .usage = .{ .uniform = true, .copy_dst = true }, - .size = app.lights.extent_buffer_size, - }); - var light_extent_data = [1]f32{0.0} ** 8; - @memcpy(light_extent_data[0..3], &light_extent_min); - @memcpy(light_extent_data[4..7], &light_extent_max); - const queue = core.queue; - queue.writeBuffer( - app.lights.extent_buffer, - 0, - &light_extent_data, - ); - // - // Lights buffer bind group - // - { - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.lights.buffer, - .size = app.lights.buffer_size, - .elem_size = @sizeOf(f32) * light_data_stride, - }, - .{ - .binding = 1, - .buffer = app.lights.config_uniform_buffer, - .size = app.lights.config_uniform_buffer_size, - .elem_size = @intCast(app.lights.config_uniform_buffer_size), - }, - .{ - .binding = 2, - .buffer = app.camera_uniform_buffer, - .size = camera_uniform_buffer_size, - .elem_size = camera_uniform_buffer_size, - }, - }; - app.lights.buffer_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = app.lights.buffer_bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } - // - // Lights buffer compute bind group - // - { - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.lights.buffer, - .size = app.lights.buffer_size, - .elem_size = @sizeOf(f32) * light_data_stride, - }, - .{ - .binding = 1, - .buffer = app.lights.config_uniform_buffer, - .size = app.lights.config_uniform_buffer_size, - .elem_size = @intCast(app.lights.config_uniform_buffer_size), - }, - .{ - .binding = 2, - .buffer = app.lights.extent_buffer, - .size = app.lights.extent_buffer_size, - .elem_size = @intCast(app.lights.extent_buffer_size), - }, - }; - const bind_group_layout = app.light_update_compute_pipeline.getBindGroupLayout(0); - app.lights.buffer_compute_bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = bind_group_layout, - .entries = &bind_group_entries, - }), - ); - bind_group_layout.release(); - } -} - -fn prepareViewMatrices(app: *App) void { - const screen_dimensions = Dimensions2D(f32){ - .width = @as(f32, @floatFromInt(app.screen_dimensions.width)), - .height = @as(f32, @floatFromInt(app.screen_dimensions.height)), - }; - const aspect: f32 = screen_dimensions.width / screen_dimensions.height; - const fov: f32 = 2.0 * std.math.pi / 5.0; - const znear: f32 = 1.0; - const zfar: f32 = 2000.0; - app.view_matrices.projection_matrix = zm.perspectiveFovRhGl(fov, aspect, znear, zfar); - const eye_position = zm.Vec{ 0.0, 50.0, -100.0, 0.0 }; - app.view_matrices.up_vector = zm.Vec{ 0.0, 1.0, 0.0, 0.0 }; - app.view_matrices.origin = zm.Vec{ 0.0, 0.0, 0.0, 0.0 }; - const view_matrix = zm.lookAtRh( - eye_position, - app.view_matrices.origin, - app.view_matrices.up_vector, - ); - const view_proj_matrix: zm.Mat = zm.mul(view_matrix, app.view_matrices.projection_matrix); - // Move the model so it's centered. - const model_matrix = zm.translationV(zm.Vec{ 0.0, -45.0, 0.0, 0.0 }); - const queue = core.queue; - queue.writeBuffer( - app.camera_uniform_buffer, - 0, - &view_proj_matrix, - ); - queue.writeBuffer( - app.model_uniform_buffer, - 0, - &model_matrix, - ); - const invert_transpose_model_matrix = zm.transpose(zm.inverse(model_matrix)); - queue.writeBuffer( - app.model_uniform_buffer, - @sizeOf(Mat4), - &invert_transpose_model_matrix, - ); - // Pass the surface size to shader to help sample from gBuffer textures using coord - const surface_size = Vec2{ screen_dimensions.width, screen_dimensions.height }; - queue.writeBuffer( - app.surface_size_uniform_buffer, - 0, - &surface_size, - ); -} - -fn buildCommandBuffer(app: *App) !*gpu.CommandBuffer { - const back_buffer_view = core.swap_chain.getCurrentTextureView().?; - defer back_buffer_view.release(); - - const encoder = core.device.createCommandEncoder(null); - defer encoder.release(); - - std.debug.assert(app.screen_dimensions.width == core.descriptor.width); - std.debug.assert(app.screen_dimensions.height == core.descriptor.height); - - const dimensions = Dimensions2D(f32){ - .width = @as(f32, @floatFromInt(core.descriptor.width)), - .height = @as(f32, @floatFromInt(core.descriptor.height)), - }; - - { - // Write position, normal, albedo etc. data to gBuffers - const pass = encoder.beginRenderPass(&app.write_gbuffer_pass.descriptor); - pass.setViewport( - 0, - 0, - dimensions.width, - dimensions.height, - 0.0, - 1.0, - ); - pass.setScissorRect(0, 0, core.descriptor.width, core.descriptor.height); - pass.setPipeline(app.write_gbuffers_pipeline); - pass.setBindGroup(0, app.scene_uniform_bind_group, null); - pass.setVertexBuffer(0, app.vertex_buffer, 0, @sizeOf(Vertex) * app.vertex_count); - pass.setIndexBuffer(app.index_buffer, .uint16, 0, @sizeOf(u16) * app.index_count); - pass.drawIndexed( - app.index_count, - 1, // instance_count - 0, // first_index - 0, // base_vertex - 0, // first_instance - ); - pass.end(); - pass.release(); - } - { - // Update lights position - const pass = encoder.beginComputePass(null); - pass.setPipeline(app.light_update_compute_pipeline); - pass.setBindGroup(0, app.lights.buffer_compute_bind_group, null); - pass.dispatchWorkgroups(@divExact(max_num_lights, 64), 1, 1); - pass.end(); - pass.release(); - } - app.texture_quad_pass.color_attachment.view = back_buffer_view; - app.texture_quad_pass.descriptor = gpu.RenderPassDescriptor{ - .label = "texture_quad_pass(0)", - .color_attachment_count = 1, - .color_attachments = &[_]gpu.RenderPassColorAttachment{app.texture_quad_pass.color_attachment}, - }; - - const pass = encoder.beginRenderPass(&app.texture_quad_pass.descriptor); - switch (app.settings.render_mode) { - .gbuffer_view => { - // GBuffers debug view - // Left: position - // Middle: normal - // Right: albedo (use uv to mimic a checkerboard texture) - pass.setPipeline(app.gbuffers_debug_view_pipeline); - pass.setBindGroup(0, app.gbuffer_textures_bind_group, null); - pass.setBindGroup(1, app.surface_size_uniform_bind_group, null); - pass.draw(6, 1, 0, 0); - }, - else => { - // Deferred rendering - pass.setPipeline(app.deferred_render_pipeline); - pass.setBindGroup(0, app.gbuffer_textures_bind_group, null); - pass.setBindGroup(1, app.lights.buffer_bind_group, null); - pass.setBindGroup(2, app.surface_size_uniform_bind_group, null); - pass.draw(6, 1, 0, 0); - }, - } - - pass.end(); - pass.release(); - - return encoder.finish(null); -} - -const modes = [_][:0]const u8{ "rendering", "gbuffers view" }; - -fn printControls(app: *App) void { - std.debug.print("[controls]\n", .{}); - std.debug.print("[p] paused: {}\n", .{app.is_paused}); - std.debug.print("[m] mode: {s}\n", .{modes[@intFromEnum(app.settings.render_mode)]}); - std.debug.print("[,] decrease lights: {}\n", .{app.settings.lights_count}); - std.debug.print("[.] increase lights: {}\n", .{app.settings.lights_count}); -} - -fn updateUI(app: *App, event: core.Event) void { - switch (event) { - .key_press => |ev| { - var update_lights = false; - switch (ev.key) { - .p => app.is_paused = !app.is_paused, - .m => { - const mode_index = @intFromEnum(app.settings.render_mode); - app.settings.render_mode = @enumFromInt((mode_index + 1) % modes.len); - }, - .comma => { - update_lights = true; - if (app.settings.lights_count >= 25) app.settings.lights_count -= 25; - }, - .period => { - update_lights = true; - app.settings.lights_count += 25; - }, - else => return, - } - - if (update_lights) core.queue.writeBuffer( - app.lights.config_uniform_buffer, - 0, - &[1]i32{app.settings.lights_count}, - ); - app.printControls(); - }, - else => {}, - } -} - -// TODO -// fn drawUI(app: *App) void { -// if (imgui.beginCombo("Mode", .{ .preview_value = modes[mode_index] })) { -// for (modes, 0..) |mode, mode_i| { -// const i = @as(u32, @intCast(mode_i)); -// if (imgui.selectable(mode, .{ .selected = mode_index == i })) { -// app.settings.render_mode = @as(RenderMode, @enumFromInt(mode_i)); -// } -// } -// } -// if (imgui.sliderInt("Light count", .{ .v = &app.settings.lights_count, .min = 1, .max = max_num_lights })) { -// queue.writeBuffer( -// app.lights.config_uniform_buffer, -// 0, -// &[1]i32{app.settings.lights_count}, -// ); -// } -// imgui.end(); -// } - -fn updateUniformBuffers(app: *App) void { - core.device.tick(); - app.camera_rotation += toRadians(360.0) * (app.delta_time / 5.0); // one rotation every 5s - const rotation = zm.rotationY(app.camera_rotation); - const eye_position = zm.mul(rotation, zm.Vec{ 0, 50, -100, 0 }); - const view_matrix = zm.lookAtRh(eye_position, app.view_matrices.origin, app.view_matrices.up_vector); - app.view_matrices.view_proj_matrix = zm.mul(view_matrix, app.view_matrices.projection_matrix); - const queue = core.queue; - queue.writeBuffer( - app.camera_uniform_buffer, - 0, - &app.view_matrices.view_proj_matrix, - ); - - const inv_view_proj_matrix = zm.inverse(app.view_matrices.view_proj_matrix); - queue.writeBuffer( - app.camera_uniform_buffer, - @sizeOf(Mat4), - &inv_view_proj_matrix, - ); -} - -inline fn roundToMultipleOf4(comptime T: type, value: T) T { - return (value + 3) & ~@as(T, 3); -} - -inline fn toRadians(degrees: f32) f32 { - return degrees * (std.math.pi / 180.0); -} diff --git a/src/core/examples/sysgpu/deferred-rendering/vertexTextureQuad.wgsl b/src/core/examples/sysgpu/deferred-rendering/vertexTextureQuad.wgsl deleted file mode 100644 index d906868b..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/vertexTextureQuad.wgsl +++ /dev/null @@ -1,12 +0,0 @@ -@vertex -fn main( - @builtin(vertex_index) VertexIndex : u32 -) -> @builtin(position) vec4 { - // TODO - array initialization - var pos = array, 6>( - vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), - vec2(-1.0, 1.0), vec2(1.0, -1.0), vec2(1.0, 1.0), - ); - - return vec4(pos[VertexIndex], 0.0, 1.0); -} diff --git a/src/core/examples/sysgpu/deferred-rendering/vertexWriteGBuffers.wgsl b/src/core/examples/sysgpu/deferred-rendering/vertexWriteGBuffers.wgsl deleted file mode 100644 index eb3b930e..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/vertexWriteGBuffers.wgsl +++ /dev/null @@ -1,30 +0,0 @@ -struct Uniforms { - modelMatrix : mat4x4, - normalModelMatrix : mat4x4, -} -struct Camera { - viewProjectionMatrix : mat4x4, - invViewProjectionMatrix : mat4x4, -} -@group(0) @binding(0) var uniforms : Uniforms; -@group(0) @binding(1) var camera : Camera; - -struct VertexOutput { - @builtin(position) Position : vec4, - @location(0) fragNormal: vec3, // normal in world space - @location(1) fragUV: vec2, -} - -@vertex -fn main( - @location(0) position : vec3, - @location(1) normal : vec3, - @location(2) uv : vec2 -) -> VertexOutput { - var output : VertexOutput; - let worldPosition = (uniforms.modelMatrix * vec4(position, 1.0)).xyz; - output.Position = camera.viewProjectionMatrix * vec4(worldPosition, 1.0); - output.fragNormal = normalize((uniforms.normalModelMatrix * vec4(normal, 1.0)).xyz); - output.fragUV = uv; - return output; -} diff --git a/src/core/examples/sysgpu/deferred-rendering/vertex_writer.zig b/src/core/examples/sysgpu/deferred-rendering/vertex_writer.zig deleted file mode 100644 index 1610981e..00000000 --- a/src/core/examples/sysgpu/deferred-rendering/vertex_writer.zig +++ /dev/null @@ -1,188 +0,0 @@ -const std = @import("std"); - -/// Vertex writer manages the placement of vertices by tracking which are unique. If a duplicate vertex is added -/// with `put`, only it's index will be written to the index buffer. -/// `IndexType` should match the integer type used for the index buffer -pub fn VertexWriter(comptime VertexType: type, comptime IndexType: type) type { - return struct { - const MapEntry = struct { - packed_index: IndexType = null_index, - next_sparse: IndexType = null_index, - }; - - const null_index: IndexType = std.math.maxInt(IndexType); - - vertices: []VertexType, - indices: []IndexType, - sparse_to_packed_map: []MapEntry, - - /// Next index outside of the 1:1 mapping range for storing - /// position -> normal collisions - next_collision_index: IndexType, - - /// Next packed index - next_packed_index: IndexType, - written_indices_count: IndexType, - - /// Allocate storage and set default values - /// `sparse_vertices_count` is the number of vertices in the source before de-duplication / remapping - /// Put more succinctly, the largest index value in source index buffer - /// `max_vertex_count` is largest permutation of vertices assuming that {vertex, uv, normal} never map 1:1 and always - /// create a new mapping - pub fn init( - allocator: std.mem.Allocator, - indices_count: IndexType, - sparse_vertices_count: IndexType, - max_vertex_count: IndexType, - ) !@This() { - var result: @This() = undefined; - result.vertices = try allocator.alloc(VertexType, max_vertex_count); - result.indices = try allocator.alloc(IndexType, indices_count); - result.sparse_to_packed_map = try allocator.alloc(MapEntry, max_vertex_count); - result.next_collision_index = sparse_vertices_count; - result.next_packed_index = 0; - result.written_indices_count = 0; - @memset(result.sparse_to_packed_map, .{}); - return result; - } - - pub fn put(self: *@This(), vertex: VertexType, sparse_index: IndexType) void { - if (self.sparse_to_packed_map[sparse_index].packed_index == null_index) { - // New start of chain, reserve a new packed index and add entry to `index_map` - const packed_index = self.next_packed_index; - self.sparse_to_packed_map[sparse_index].packed_index = packed_index; - self.vertices[packed_index] = vertex; - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - self.next_packed_index += 1; - return; - } - var previous_sparse_index: IndexType = undefined; - var current_sparse_index = sparse_index; - while (current_sparse_index != null_index) { - const packed_index = self.sparse_to_packed_map[current_sparse_index].packed_index; - if (std.mem.eql(u8, &std.mem.toBytes(self.vertices[packed_index]), &std.mem.toBytes(vertex))) { - // We already have a record for this vertex in our chain - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - return; - } - previous_sparse_index = current_sparse_index; - current_sparse_index = self.sparse_to_packed_map[current_sparse_index].next_sparse; - } - // This is a new mapping for the given sparse index - const packed_index = self.next_packed_index; - const remapped_sparse_index = self.next_collision_index; - self.indices[self.written_indices_count] = packed_index; - self.vertices[packed_index] = vertex; - self.sparse_to_packed_map[previous_sparse_index].next_sparse = remapped_sparse_index; - self.sparse_to_packed_map[remapped_sparse_index].packed_index = packed_index; - self.next_packed_index += 1; - self.next_collision_index += 1; - self.written_indices_count += 1; - } - - pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { - allocator.free(self.vertices); - allocator.free(self.indices); - allocator.free(self.sparse_to_packed_map); - } - - pub fn indexBuffer(self: @This()) []IndexType { - return self.indices; - } - - pub fn vertexBuffer(self: @This()) []VertexType { - return self.vertices[0..self.next_packed_index]; - } - }; -} - -test "VertexWriter" { - const Vec3 = [3]f32; - const Vertex = extern struct { - position: Vec3, - normal: Vec3, - }; - - const expect = std.testing.expect; - const allocator = std.testing.allocator; - - const Face = struct { - position: [3]u16, - normal: [3]u16, - }; - - const vertices = [_]Vec3{ - Vec3{ 1.0, 0.0, 0.0 }, // 0: Position - Vec3{ 2.0, 0.0, 0.0 }, // 1: Position - Vec3{ 3.0, 0.0, 0.0 }, // 2: Position - Vec3{ 1.0, 0.0, 0.0 }, // 3: Normal - Vec3{ 4.0, 0.0, 0.0 }, // 4: Position - Vec3{ 0.0, 1.0, 0.0 }, // 5: Normal - Vec3{ 5.0, 0.0, 0.0 }, // 6: Position - Vec3{ 0.0, 0.0, 1.0 }, // 7: Normal - Vec3{ 1.0, 0.0, 1.0 }, // 8: Normal - Vec3{ 6.0, 0.0, 0.0 }, // 9: Position - }; - - const faces = [_]Face{ - .{ .position = .{ 0, 4, 2 }, .normal = .{ 7, 5, 3 } }, - .{ .position = .{ 2, 3, 9 }, .normal = .{ 3, 7, 8 } }, - .{ .position = .{ 9, 2, 4 }, .normal = .{ 8, 7, 5 } }, - .{ .position = .{ 2, 6, 1 }, .normal = .{ 3, 5, 7 } }, - .{ .position = .{ 9, 6, 0 }, .normal = .{ 5, 7, 8 } }, - }; - - var writer = try VertexWriter(Vertex, u32).init( - allocator, - faces.len * 3, // indices count - vertices.len, // original vertices count - faces.len * 3, // maximum vertices count - ); - defer writer.deinit(allocator); - - for (faces) |face| { - var x: usize = 0; - while (x < 3) : (x += 1) { - const position_index = face.position[x]; - const position = vertices[position_index]; - const normal = vertices[face.normal[x]]; - const vertex = Vertex{ - .position = position, - .normal = normal, - }; - writer.put(vertex, position_index); - } - } - - const indices = writer.indexBuffer(); - try expect(indices.len == faces.len * 3); - - // Face 0 - try expect(indices[0] == 0); // (0, 7) New - try expect(indices[1] == 1); // (4, 5) New - try expect(indices[2] == 2); // (2, 3) New - - // Face 1 - try expect(indices[3 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[3 + 1] == 3); // (3, 7) New - try expect(indices[3 + 2] == 4); // (9, 8) New - - // Face 2 - try expect(indices[6 + 0] == 4); // (9, 8) Duplicate - Reuse index - try expect(indices[6 + 1] == 5); // (2, 7) New normal mapping (Don't clobber) - try expect(indices[6 + 2] == 1); // (4, 5) Duplicate - Reuse Index - - // Face 3 - try expect(indices[9 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[9 + 1] == 6); // (6, 5) New - try expect(indices[9 + 2] == 7); // (1, 7) New - - // Face 4 - try expect(indices[12 + 0] == 8); // (9, 5) New normal mapping (Don't clobber) - try expect(indices[12 + 1] == 9); // (6, 7) New normal mapping (Don't clobber) - try expect(indices[12 + 2] == 10); // (0, 8) New normal mapping (Don't clobber) - - try expect(writer.vertexBuffer().len == 11); -} diff --git a/src/core/examples/sysgpu/pbr-basic/main.zig b/src/core/examples/sysgpu/pbr-basic/main.zig deleted file mode 100644 index ac1cd266..00000000 --- a/src/core/examples/sysgpu/pbr-basic/main.zig +++ /dev/null @@ -1,932 +0,0 @@ -const std = @import("std"); - -const mach = @import("mach"); -const core = mach.core; -const gpu = mach.gpu; - -const m3d = @import("model3d"); -const zm = @import("zmath"); -const assets = @import("assets"); -const VertexWriter = @import("vertex_writer.zig").VertexWriter; - -pub const App = @This(); - -// Use experimental sysgpu graphics API -pub const use_sysgpu = true; - -const Vec4 = [4]f32; -const Vec3 = [3]f32; -const Vec2 = [2]f32; -const Mat4 = [4]Vec4; - -fn Dimensions2D(comptime T: type) type { - return struct { - width: T, - height: T, - }; -} - -const Vertex = extern struct { - position: Vec3, - normal: Vec3, -}; - -const Model = struct { - vertex_count: u32, - index_count: u32, - vertex_buffer: *gpu.Buffer, - index_buffer: *gpu.Buffer, -}; - -const Material = struct { - const Params = extern struct { - roughness: f32, - metallic: f32, - color: Vec3, - }; - - name: []const u8, - params: Params, -}; - -const PressedKeys = packed struct(u16) { - right: bool = false, - left: bool = false, - up: bool = false, - down: bool = false, - padding: u12 = undefined, - - pub inline fn areKeysPressed(self: @This()) bool { - return (self.up or self.down or self.left or self.right); - } - - pub inline fn clear(self: *@This()) void { - self.right = false; - self.left = false; - self.up = false; - self.down = false; - } -}; - -const Camera = struct { - const Matrices = struct { - perspective: Mat4 = [1]Vec4{[1]f32{0.0} ** 4} ** 4, - view: Mat4 = [1]Vec4{[1]f32{0.0} ** 4} ** 4, - }; - - rotation: Vec3 = .{ 0.0, 0.0, 0.0 }, - position: Vec3 = .{ 0.0, 0.0, 0.0 }, - view_position: Vec4 = .{ 0.0, 0.0, 0.0, 0.0 }, - fov: f32 = 0.0, - znear: f32 = 0.0, - zfar: f32 = 0.0, - rotation_speed: f32 = 0.0, - movement_speed: f32 = 0.0, - updated: bool = false, - matrices: Matrices = .{}, - - pub fn calculateMovement(self: *@This(), pressed_keys: PressedKeys) void { - std.debug.assert(pressed_keys.areKeysPressed()); - const rotation_radians = Vec3{ - toRadians(self.rotation[0]), - toRadians(self.rotation[1]), - toRadians(self.rotation[2]), - }; - var camera_front = zm.Vec{ -zm.cos(rotation_radians[0]) * zm.sin(rotation_radians[1]), zm.sin(rotation_radians[0]), zm.cos(rotation_radians[0]) * zm.cos(rotation_radians[1]), 0 }; - camera_front = zm.normalize3(camera_front); - if (pressed_keys.up) { - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] + camera_front[0], - self.position[1] + camera_front[1], - self.position[2] + camera_front[2], - }; - } - if (pressed_keys.down) { - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] - camera_front[0], - self.position[1] - camera_front[1], - self.position[2] - camera_front[2], - }; - } - if (pressed_keys.right) { - camera_front = zm.cross3(.{ 0.0, 1.0, 0.0, 0.0 }, camera_front); - camera_front = zm.normalize3(camera_front); - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] - camera_front[0], - self.position[1] - camera_front[1], - self.position[2] - camera_front[2], - }; - } - if (pressed_keys.left) { - camera_front = zm.cross3(.{ 0.0, 1.0, 0.0, 0.0 }, camera_front); - camera_front = zm.normalize3(camera_front); - camera_front[0] *= self.movement_speed; - camera_front[1] *= self.movement_speed; - camera_front[2] *= self.movement_speed; - self.position = Vec3{ - self.position[0] + camera_front[0], - self.position[1] + camera_front[1], - self.position[2] + camera_front[2], - }; - } - self.updateViewMatrix(); - } - - fn updateViewMatrix(self: *@This()) void { - const rotation_x = zm.rotationX(toRadians(self.rotation[2])); - const rotation_y = zm.rotationY(toRadians(self.rotation[1])); - const rotation_z = zm.rotationZ(toRadians(self.rotation[0])); - const rotation_matrix = zm.mul(rotation_z, zm.mul(rotation_x, rotation_y)); - - const translation_matrix: zm.Mat = zm.translationV(.{ - self.position[0], - self.position[1], - self.position[2], - 0, - }); - const view = zm.mul(translation_matrix, rotation_matrix); - self.matrices.view[0] = view[0]; - self.matrices.view[1] = view[1]; - self.matrices.view[2] = view[2]; - self.matrices.view[3] = view[3]; - self.view_position = .{ - -self.position[0], - self.position[1], - -self.position[2], - 0.0, - }; - self.updated = true; - } - - pub fn setMovementSpeed(self: *@This(), speed: f32) void { - self.movement_speed = speed; - } - - pub fn setPerspective(self: *@This(), fov: f32, aspect: f32, znear: f32, zfar: f32) void { - self.fov = fov; - self.znear = znear; - self.zfar = zfar; - const perspective = zm.perspectiveFovRhGl(toRadians(fov), aspect, znear, zfar); - self.matrices.perspective[0] = perspective[0]; - self.matrices.perspective[1] = perspective[1]; - self.matrices.perspective[2] = perspective[2]; - self.matrices.perspective[3] = perspective[3]; - } - - pub fn setRotationSpeed(self: *@This(), speed: f32) void { - self.rotation_speed = speed; - } - - pub fn setRotation(self: *@This(), rotation: Vec3) void { - self.rotation = rotation; - self.updateViewMatrix(); - } - - pub fn rotate(self: *@This(), delta: Vec2) void { - self.rotation[0] -= delta[1]; - self.rotation[1] -= delta[0]; - self.updateViewMatrix(); - } - - pub fn setPosition(self: *@This(), position: Vec3) void { - self.position = .{ - position[0], - -position[1], - position[2], - }; - self.updateViewMatrix(); - } -}; - -const UniformBuffers = struct { - const Params = struct { - buffer: *gpu.Buffer, - buffer_size: u64, - model_size: u64, - }; - const Buffer = struct { - buffer: *gpu.Buffer, - size: u32, - }; - ubo_matrices: Buffer, - ubo_params: Buffer, - material_params: Params, - object_params: Params, -}; - -const UboParams = struct { - lights: [4]Vec4, -}; - -const UboMatrices = extern struct { - projection: Mat4, - model: Mat4, - view: Mat4, - camera_position: Vec3, -}; - -const grid_element_count = grid_dimensions * grid_dimensions; - -const MaterialParamsDynamic = extern struct { - roughness: f32 = 0, - metallic: f32 = 0, - color: Vec3 = .{ 0, 0, 0 }, - padding: [236]u8 = [1]u8{0} ** 236, -}; -const MaterialParamsDynamicGrid = [grid_element_count]MaterialParamsDynamic; - -const ObjectParamsDynamic = extern struct { - position: Vec3 = .{ 0, 0, 0 }, - padding: [244]u8 = [1]u8{0} ** 244, -}; -const ObjectParamsDynamicGrid = [grid_element_count]ObjectParamsDynamic; - -// -// Globals -// - -const material_names = [11][:0]const u8{ - "Gold", "Copper", "Chromium", "Nickel", "Titanium", "Cobalt", "Platinum", - // Testing materials - "White", "Red", "Blue", "Black", -}; - -const object_names = [5][:0]const u8{ "Sphere", "Teapot", "Torusknot", "Venus", "Stanford Dragon" }; - -const materials = [_]Material{ - .{ .name = "Gold", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 0.765557, 0.336057 } } }, - .{ .name = "Copper", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.955008, 0.637427, 0.538163 } } }, - .{ .name = "Chromium", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.549585, 0.556114, 0.554256 } } }, - .{ .name = "Nickel", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 0.608679, 0.525649 } } }, - .{ .name = "Titanium", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.541931, 0.496791, 0.449419 } } }, - .{ .name = "Cobalt", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.662124, 0.654864, 0.633732 } } }, - .{ .name = "Platinum", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.672411, 0.637331, 0.585456 } } }, - // Testing colors - .{ .name = "White", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 1.0, 1.0 } } }, - .{ .name = "Red", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 1.0, 0.0, 0.0 } } }, - .{ .name = "Blue", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.0, 0.0, 1.0 } } }, - .{ .name = "Black", .params = .{ .roughness = 0.1, .metallic = 1.0, .color = .{ 0.0, 0.0, 0.0 } } }, -}; - -const grid_dimensions = 7; -const model_embeds = [_][:0]const u8{ - assets.sphere_m3d, - assets.teapot_m3d, - assets.torusknot_m3d, - assets.venus_m3d, - assets.stanford_dragon_m3d, -}; - -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - -// -// Member variables -// - -title_timer: core.Timer, -timer: core.Timer, -camera: Camera, -render_pipeline: *gpu.RenderPipeline, -render_pass_descriptor: gpu.RenderPassDescriptor, -bind_group: *gpu.BindGroup, -color_attachment: gpu.RenderPassColorAttachment, -depth_stencil_attachment_description: gpu.RenderPassDepthStencilAttachment, -depth_texture: *gpu.Texture, -depth_texture_view: *gpu.TextureView, -pressed_keys: PressedKeys, -models: [5]Model, -ubo_params: UboParams, -ubo_matrices: UboMatrices, -uniform_buffers: UniformBuffers, -material_params_dynamic: MaterialParamsDynamicGrid = [1]MaterialParamsDynamic{.{}} ** grid_element_count, -object_params_dynamic: ObjectParamsDynamicGrid = [1]ObjectParamsDynamic{.{}} ** grid_element_count, -uniform_buffers_dirty: bool, -buffers_bound: bool, -is_paused: bool, -current_material_index: usize, -current_object_index: usize, -mouse_position: core.Position, -is_rotating: bool, - -// -// Functions -// - -pub fn init(app: *App) !void { - try core.init(.{}); - app.timer = try core.Timer.start(); - app.title_timer = try core.Timer.start(); - - app.pressed_keys = .{}; - app.buffers_bound = false; - app.is_paused = false; - app.uniform_buffers_dirty = false; - app.current_material_index = 0; - app.current_object_index = 0; - app.mouse_position = .{ .x = 0, .y = 0 }; - app.is_rotating = false; - - setupCamera(app); - try loadModels(std.heap.c_allocator, app); - prepareUniformBuffers(app); - setupPipeline(app); - setupRenderPass(app); - app.printControls(); -} - -pub fn deinit(app: *App) void { - defer _ = gpa.deinit(); - defer core.deinit(); - - app.bind_group.release(); - app.render_pipeline.release(); - app.depth_texture_view.release(); - app.depth_texture.release(); - for (app.models) |model| { - model.index_buffer.release(); - model.vertex_buffer.release(); - } - app.uniform_buffers.ubo_matrices.buffer.release(); - app.uniform_buffers.ubo_params.buffer.release(); - app.uniform_buffers.material_params.buffer.release(); - app.uniform_buffers.object_params.buffer.release(); -} - -pub fn update(app: *App) !bool { - var iter = core.pollEvents(); - while (iter.next()) |event| { - app.updateUI(event); - switch (event) { - .mouse_motion => |ev| { - if (app.is_rotating) { - const delta = Vec2{ - @as(f32, @floatCast((app.mouse_position.x - ev.pos.x) * app.camera.rotation_speed)), - @as(f32, @floatCast((app.mouse_position.y - ev.pos.y) * app.camera.rotation_speed)), - }; - app.mouse_position = ev.pos; - app.camera.rotate(delta); - app.uniform_buffers_dirty = true; - } - }, - .mouse_press => |ev| { - if (ev.button == .left) { - app.is_rotating = true; - app.mouse_position = ev.pos; - } - }, - .mouse_release => |ev| { - if (ev.button == .left) { - app.is_rotating = false; - } - }, - .key_press, .key_repeat => |ev| { - const key = ev.key; - if (key == .up or key == .w) app.pressed_keys.up = true; - if (key == .down or key == .s) app.pressed_keys.down = true; - if (key == .left or key == .a) app.pressed_keys.left = true; - if (key == .right or key == .d) app.pressed_keys.right = true; - }, - .framebuffer_resize => |ev| { - app.depth_texture_view.release(); - app.depth_texture.release(); - app.depth_texture = core.device.createTexture(&gpu.Texture.Descriptor{ - .usage = .{ .render_attachment = true }, - .format = .depth24_plus_stencil8, - .sample_count = 1, - .size = .{ - .width = ev.width, - .height = ev.height, - .depth_or_array_layers = 1, - }, - }); - app.depth_texture_view = app.depth_texture.createView(&gpu.TextureView.Descriptor{ - .format = .depth24_plus_stencil8, - .dimension = .dimension_2d, - .array_layer_count = 1, - .aspect = .all, - }); - app.depth_stencil_attachment_description = gpu.RenderPassDepthStencilAttachment{ - .view = app.depth_texture_view, - .depth_load_op = .clear, - .depth_store_op = .store, - .depth_clear_value = 1.0, - .stencil_clear_value = 0, - .stencil_load_op = .clear, - .stencil_store_op = .store, - }; - - const aspect_ratio = @as(f32, @floatFromInt(ev.width)) / @as(f32, @floatFromInt(ev.height)); - app.camera.setPerspective(60.0, aspect_ratio, 0.1, 256.0); - app.uniform_buffers_dirty = true; - }, - .close => return true, - else => {}, - } - } - if (app.pressed_keys.areKeysPressed()) { - app.camera.calculateMovement(app.pressed_keys); - app.pressed_keys.clear(); - app.uniform_buffers_dirty = true; - } - - if (app.uniform_buffers_dirty) { - updateUniformBuffers(app); - app.uniform_buffers_dirty = false; - } - - const back_buffer_view = core.swap_chain.getCurrentTextureView().?; - app.color_attachment.view = back_buffer_view; - app.render_pass_descriptor = gpu.RenderPassDescriptor{ - .color_attachment_count = 1, - .color_attachments = &[_]gpu.RenderPassColorAttachment{app.color_attachment}, - .depth_stencil_attachment = &app.depth_stencil_attachment_description, - }; - const encoder = core.device.createCommandEncoder(null); - const current_model = app.models[app.current_object_index]; - - const pass = encoder.beginRenderPass(&app.render_pass_descriptor); - - const dimensions = Dimensions2D(f32){ - .width = @as(f32, @floatFromInt(core.descriptor.width)), - .height = @as(f32, @floatFromInt(core.descriptor.height)), - }; - pass.setViewport( - 0, - 0, - dimensions.width, - dimensions.height, - 0.0, - 1.0, - ); - pass.setScissorRect(0, 0, core.descriptor.width, core.descriptor.height); - pass.setPipeline(app.render_pipeline); - - if (!app.is_paused) { - app.updateLights(); - } - - var i: usize = 0; - while (i < (grid_dimensions * grid_dimensions)) : (i += 1) { - const alignment = 256; - const dynamic_offset: u32 = @as(u32, @intCast(i)) * alignment; - const dynamic_offsets = [2]u32{ dynamic_offset, dynamic_offset }; - pass.setBindGroup(0, app.bind_group, &dynamic_offsets); - if (!app.buffers_bound) { - pass.setVertexBuffer(0, current_model.vertex_buffer, 0, @sizeOf(Vertex) * current_model.vertex_count); - pass.setIndexBuffer(current_model.index_buffer, .uint32, 0, gpu.whole_size); - app.buffers_bound = true; - } - pass.drawIndexed( - current_model.index_count, // index_count - 1, // instance_count - 0, // first_index - 0, // base_vertex - 0, // first_instance - ); - } - - pass.end(); - pass.release(); - - var command = encoder.finish(null); - encoder.release(); - - const queue = core.queue; - queue.submit(&[_]*gpu.CommandBuffer{command}); - - command.release(); - core.swap_chain.present(); - back_buffer_view.release(); - app.buffers_bound = false; - - // update the window title every second - if (app.title_timer.read() >= 1.0) { - app.title_timer.reset(); - try core.printTitle("PBR Basic [ {d}fps ] [ Input {d}hz ]", .{ - core.frameRate(), - core.inputRate(), - }); - } - - return false; -} - -fn prepareUniformBuffers(app: *App) void { - comptime { - std.debug.assert(@sizeOf(ObjectParamsDynamic) == 256); - std.debug.assert(@sizeOf(MaterialParamsDynamic) == 256); - } - - app.uniform_buffers.ubo_matrices.size = roundToMultipleOf4(u32, @as(u32, @intCast(@sizeOf(UboMatrices)))) + 4; - app.uniform_buffers.ubo_matrices.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.ubo_matrices.size, - .mapped_at_creation = .false, - }); - - app.uniform_buffers.ubo_params.size = roundToMultipleOf4(u32, @as(u32, @intCast(@sizeOf(UboParams)))) + 4; - app.uniform_buffers.ubo_params.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.ubo_params.size, - .mapped_at_creation = .false, - }); - - // - // Material parameter uniform buffer - // - app.uniform_buffers.material_params.model_size = @sizeOf(Vec2) + @sizeOf(Vec3); - app.uniform_buffers.material_params.buffer_size = calculateConstantBufferByteSize(@sizeOf(MaterialParamsDynamicGrid)); - std.debug.assert(app.uniform_buffers.material_params.buffer_size >= app.uniform_buffers.material_params.model_size); - app.uniform_buffers.material_params.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.material_params.buffer_size, - .mapped_at_creation = .false, - }); - - // - // Object parameter uniform buffer - // - app.uniform_buffers.object_params.model_size = @sizeOf(Vec3) + 4; - app.uniform_buffers.object_params.buffer_size = calculateConstantBufferByteSize(@sizeOf(MaterialParamsDynamicGrid)) + 4; - std.debug.assert(app.uniform_buffers.object_params.buffer_size >= app.uniform_buffers.object_params.model_size); - app.uniform_buffers.object_params.buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .uniform = true }, - .size = app.uniform_buffers.object_params.buffer_size, - .mapped_at_creation = .false, - }); - - app.updateUniformBuffers(); - app.updateDynamicUniformBuffer(); - app.updateLights(); -} - -fn updateDynamicUniformBuffer(app: *App) void { - var index: u32 = 0; - var y: usize = 0; - while (y < grid_dimensions) : (y += 1) { - var x: usize = 0; - while (x < grid_dimensions) : (x += 1) { - const grid_dimensions_float = @as(f32, @floatFromInt(grid_dimensions)); - app.object_params_dynamic[index].position[0] = (@as(f32, @floatFromInt(x)) - (grid_dimensions_float / 2) * 2.5); - app.object_params_dynamic[index].position[1] = 0; - app.object_params_dynamic[index].position[2] = (@as(f32, @floatFromInt(y)) - (grid_dimensions_float / 2) * 2.5); - app.material_params_dynamic[index].metallic = zm.clamp(@as(f32, @floatFromInt(x)) / (grid_dimensions_float - 1), 0.1, 1.0); - app.material_params_dynamic[index].roughness = zm.clamp(@as(f32, @floatFromInt(y)) / (grid_dimensions_float - 1), 0.05, 1.0); - app.material_params_dynamic[index].color = materials[app.current_material_index].params.color; - index += 1; - } - } - const queue = core.queue; - queue.writeBuffer( - app.uniform_buffers.object_params.buffer, - 0, - &app.object_params_dynamic, - ); - queue.writeBuffer( - app.uniform_buffers.material_params.buffer, - 0, - &app.material_params_dynamic, - ); -} - -fn updateUniformBuffers(app: *App) void { - app.ubo_matrices.projection = app.camera.matrices.perspective; - app.ubo_matrices.view = app.camera.matrices.view; - const rotation_degrees = if (app.current_object_index == 1) @as(f32, -45.0) else @as(f32, -90.0); - const model = zm.rotationY(rotation_degrees); - app.ubo_matrices.model[0] = model[0]; - app.ubo_matrices.model[1] = model[1]; - app.ubo_matrices.model[2] = model[2]; - app.ubo_matrices.model[3] = model[3]; - app.ubo_matrices.camera_position = .{ - -app.camera.position[0], - -app.camera.position[1], - -app.camera.position[2], - }; - const queue = core.queue; - queue.writeBuffer(app.uniform_buffers.ubo_matrices.buffer, 0, &[_]UboMatrices{app.ubo_matrices}); -} - -fn updateLights(app: *App) void { - const p: f32 = 15.0; - app.ubo_params.lights[0] = Vec4{ -p, -p * 0.5, -p, 1.0 }; - app.ubo_params.lights[1] = Vec4{ -p, -p * 0.5, p, 1.0 }; - app.ubo_params.lights[2] = Vec4{ p, -p * 0.5, p, 1.0 }; - app.ubo_params.lights[3] = Vec4{ p, -p * 0.5, -p, 1.0 }; - const base_value = toRadians(@mod(app.timer.read() * 0.1, 1.0) * 360.0); - app.ubo_params.lights[0][0] = @sin(base_value) * 20.0; - app.ubo_params.lights[0][2] = @cos(base_value) * 20.0; - app.ubo_params.lights[1][0] = @cos(base_value) * 20.0; - app.ubo_params.lights[1][1] = @sin(base_value) * 20.0; - const queue = core.queue; - queue.writeBuffer( - app.uniform_buffers.ubo_params.buffer, - 0, - &[_]UboParams{app.ubo_params}, - ); -} - -fn setupPipeline(app: *App) void { - comptime { - std.debug.assert(@sizeOf(Vertex) == @sizeOf(f32) * 6); - } - - const bind_group_layout_entries = [_]gpu.BindGroupLayout.Entry{ - .{ - .binding = 0, - .visibility = .{ .vertex = true, .fragment = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .false, - .min_binding_size = app.uniform_buffers.ubo_matrices.size, - }, - }, - .{ - .binding = 1, - .visibility = .{ .fragment = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .false, - .min_binding_size = app.uniform_buffers.ubo_params.size, - }, - }, - .{ - .binding = 2, - .visibility = .{ .fragment = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .true, - .min_binding_size = app.uniform_buffers.material_params.model_size, - }, - }, - .{ - .binding = 3, - .visibility = .{ .vertex = true }, - .buffer = .{ - .type = .uniform, - .has_dynamic_offset = .true, - .min_binding_size = app.uniform_buffers.object_params.model_size, - }, - }, - }; - - const bind_group_layout = core.device.createBindGroupLayout( - &gpu.BindGroupLayout.Descriptor.init(.{ - .entries = bind_group_layout_entries[0..], - }), - ); - defer bind_group_layout.release(); - - const bind_group_layouts = [_]*gpu.BindGroupLayout{bind_group_layout}; - const pipeline_layout = core.device.createPipelineLayout(&gpu.PipelineLayout.Descriptor.init(.{ - .bind_group_layouts = &bind_group_layouts, - })); - defer pipeline_layout.release(); - - const vertex_buffer_layout = gpu.VertexBufferLayout.init(.{ - .array_stride = @sizeOf(Vertex), - .step_mode = .vertex, - .attributes = &.{ - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "position"), .shader_location = 0 }, - .{ .format = .float32x3, .offset = @offsetOf(Vertex, "normal"), .shader_location = 1 }, - }, - }); - - const blend_component_descriptor = gpu.BlendComponent{ - .operation = .add, - .src_factor = .one, - .dst_factor = .zero, - }; - - const color_target_state = gpu.ColorTargetState{ - .format = core.descriptor.format, - .blend = &.{ - .color = blend_component_descriptor, - .alpha = blend_component_descriptor, - }, - }; - - const shader_module = core.device.createShaderModuleWGSL("shader.wgsl", @embedFile("shader.wgsl")); - const pipeline_descriptor = gpu.RenderPipeline.Descriptor{ - .layout = pipeline_layout, - .primitive = .{ - .cull_mode = .back, - }, - .depth_stencil = &.{ - .format = .depth24_plus_stencil8, - .depth_write_enabled = .true, - .depth_compare = .less, - }, - .fragment = &gpu.FragmentState.init(.{ - .module = shader_module, - .entry_point = "frag_main", - .targets = &.{color_target_state}, - }), - .vertex = gpu.VertexState.init(.{ - .module = shader_module, - .entry_point = "vertex_main", - .buffers = &.{vertex_buffer_layout}, - }), - }; - app.render_pipeline = core.device.createRenderPipeline(&pipeline_descriptor); - shader_module.release(); - - { - const bind_group_entries = [_]gpu.BindGroup.Entry{ - .{ - .binding = 0, - .buffer = app.uniform_buffers.ubo_matrices.buffer, - .size = app.uniform_buffers.ubo_matrices.size, - }, - .{ - .binding = 1, - .buffer = app.uniform_buffers.ubo_params.buffer, - .size = app.uniform_buffers.ubo_params.size, - }, - .{ - .binding = 2, - .buffer = app.uniform_buffers.material_params.buffer, - .size = app.uniform_buffers.material_params.model_size, - }, - .{ - .binding = 3, - .buffer = app.uniform_buffers.object_params.buffer, - .size = app.uniform_buffers.object_params.model_size, - }, - }; - app.bind_group = core.device.createBindGroup( - &gpu.BindGroup.Descriptor.init(.{ - .layout = bind_group_layout, - .entries = &bind_group_entries, - }), - ); - } -} - -fn setupRenderPass(app: *App) void { - app.color_attachment = gpu.RenderPassColorAttachment{ - .clear_value = .{ - .r = 0.0, - .g = 0.0, - .b = 0.0, - .a = 0.0, - }, - .load_op = .clear, - .store_op = .store, - }; - - app.depth_texture = core.device.createTexture(&.{ - .usage = .{ .render_attachment = true, .copy_src = true }, - .format = .depth24_plus_stencil8, - .sample_count = 1, - .size = .{ - .width = core.descriptor.width, - .height = core.descriptor.height, - .depth_or_array_layers = 1, - }, - }); - - app.depth_texture_view = app.depth_texture.createView(&.{ - .format = .depth24_plus_stencil8, - .dimension = .dimension_2d, - .array_layer_count = 1, - .aspect = .all, - }); - - app.depth_stencil_attachment_description = gpu.RenderPassDepthStencilAttachment{ - .view = app.depth_texture_view, - .depth_load_op = .clear, - .depth_store_op = .store, - .depth_clear_value = 1.0, - .stencil_clear_value = 0, - .stencil_load_op = .clear, - .stencil_store_op = .store, - }; -} - -fn loadModels(allocator: std.mem.Allocator, app: *App) !void { - for (model_embeds, 0..) |model_data, model_data_i| { - const m3d_model = m3d.load(model_data, null, null, null) orelse return error.LoadModelFailed; - - const vertex_count = m3d_model.handle.numvertex; - const face_count = m3d_model.handle.numface; - - var model: *Model = &app.models[model_data_i]; - - model.index_count = face_count * 3; - - var vertex_writer = try VertexWriter(Vertex, u32).init(allocator, face_count * 3, vertex_count, face_count * 3); - defer vertex_writer.deinit(allocator); - - const scale: f32 = 0.45; - const vertices = m3d_model.handle.vertex[0..vertex_count]; - var i: usize = 0; - while (i < face_count) : (i += 1) { - const face = m3d_model.handle.face[i]; - var x: usize = 0; - while (x < 3) : (x += 1) { - const vertex_index = face.vertex[x]; - const normal_index = face.normal[x]; - const vertex = Vertex{ - .position = .{ - vertices[vertex_index].x * scale, - vertices[vertex_index].y * scale, - vertices[vertex_index].z * scale, - }, - .normal = .{ - vertices[normal_index].x, - vertices[normal_index].y, - vertices[normal_index].z, - }, - }; - vertex_writer.put(vertex, vertex_index); - } - } - - const vertex_buffer = vertex_writer.vertexBuffer(); - const index_buffer = vertex_writer.indexBuffer(); - - model.vertex_count = @as(u32, @intCast(vertex_buffer.len)); - - model.vertex_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .vertex = true }, - .size = @sizeOf(Vertex) * model.vertex_count, - .mapped_at_creation = .false, - }); - const queue = core.queue; - queue.writeBuffer(model.vertex_buffer, 0, vertex_buffer); - - model.index_buffer = core.device.createBuffer(&.{ - .usage = .{ .copy_dst = true, .index = true }, - .size = @sizeOf(u32) * model.index_count, - .mapped_at_creation = .false, - }); - queue.writeBuffer(model.index_buffer, 0, index_buffer); - } -} - -fn printControls(app: *App) void { - std.debug.print("[controls]\n", .{}); - std.debug.print("[p] paused: {}\n", .{app.is_paused}); - std.debug.print("[m] material: {s}\n", .{material_names[app.current_material_index]}); - std.debug.print("[o] object: {s}\n", .{object_names[app.current_object_index]}); -} - -fn updateUI(app: *App, event: core.Event) void { - switch (event) { - .key_press => |ev| { - var update_uniform_buffers: bool = false; - switch (ev.key) { - .p => app.is_paused = !app.is_paused, - .m => { - app.current_material_index = (app.current_material_index + 1) % material_names.len; - update_uniform_buffers = true; - }, - .o => { - app.current_object_index = (app.current_object_index + 1) % object_names.len; - update_uniform_buffers = true; - }, - else => return, - } - app.printControls(); - if (update_uniform_buffers) { - updateDynamicUniformBuffer(app); - } - }, - else => {}, - } -} - -fn setupCamera(app: *App) void { - app.camera = Camera{ - .rotation_speed = 1.0, - .movement_speed = 1.0, - }; - const aspect_ratio: f32 = @as(f32, @floatFromInt(core.descriptor.width)) / @as(f32, @floatFromInt(core.descriptor.height)); - app.camera.setPosition(.{ 10.0, 6.0, 6.0 }); - app.camera.setRotation(.{ 62.5, 90.0, 0.0 }); - app.camera.setMovementSpeed(0.5); - app.camera.setPerspective(60.0, aspect_ratio, 0.1, 256.0); - app.camera.setRotationSpeed(0.25); -} - -inline fn roundToMultipleOf4(comptime T: type, value: T) T { - return (value + 3) & ~@as(T, 3); -} - -inline fn calculateConstantBufferByteSize(byte_size: usize) usize { - return (byte_size + 255) & ~@as(usize, 255); -} - -inline fn toRadians(degrees: f32) f32 { - return degrees * (std.math.pi / 180.0); -} diff --git a/src/core/examples/sysgpu/pbr-basic/shader.wgsl b/src/core/examples/sysgpu/pbr-basic/shader.wgsl deleted file mode 100644 index 07cb4748..00000000 --- a/src/core/examples/sysgpu/pbr-basic/shader.wgsl +++ /dev/null @@ -1,119 +0,0 @@ -@group(0) @binding(0) var ubo : UBO; -@group(0) @binding(1) var uboParams : UBOShared; -@group(0) @binding(2) var material : MaterialParams; -@group(0) @binding(3) var object : ObjectParams; - -struct VertexOut { - @builtin(position) position_clip : vec4, - @location(0) fragPosition : vec3, - @location(1) fragNormal : vec3, -} - -struct MaterialParams { - roughness : f32, - metallic : f32, - r : f32, - g : f32, - b : f32 -} - -struct UBOShared { - lights : array, 4>, -} - -struct UBO { - projection : mat4x4, - model : mat4x4, - view : mat4x4, - camPos : vec3, -} - -struct ObjectParams { - position : vec3 -} - -@vertex fn vertex_main( - @location(0) position : vec3, - @location(1) normal : vec3 -) -> VertexOut { - var output : VertexOut; - var locPos = vec4(ubo.model * vec4(position, 1.0)); - output.fragPosition = locPos.xyz + object.position; - output.fragNormal = mat3x3(ubo.model[0].xyz, ubo.model[1].xyz, ubo.model[2].xyz) * normal; - output.position_clip = ubo.projection * ubo.view * vec4(output.fragPosition, 1.0); - return output; -} - -const PI : f32 = 3.14159265359; - -fn material_color() -> vec3 { - return vec3(material.r, material.g, material.b); -} - -// Normal Distribution function -------------------------------------- -fn D_GGX(dotNH : f32, roughness : f32) -> f32 { - var alpha : f32 = roughness * roughness; - var alpha2 : f32 = alpha * alpha; - var denom : f32 = dotNH * dotNH * (alpha2 - 1.0) + 1.0; - return alpha2 / (PI * denom * denom); -} - -// Geometric Shadowing function -------------------------------------- -fn G_SchlicksmithGGX(dotNL : f32, dotNV : f32, roughness : f32) -> f32 { - var r : f32 = roughness + 1.0; - var k : f32 = (r * r) / 8.0; - var GL : f32 = dotNL / (dotNL * (1.0 - k) + k); - var GV : f32 = dotNV / (dotNV * (1.0 - k) + k); - return GL * GV; -} - -// Fresnel function ---------------------------------------------------- -fn F_Schlick(cosTheta : f32, metallic : f32) -> vec3 { - var F0 : vec3 = mix(vec3(0.04), material_color(), metallic); - var F : vec3 = F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); - return F; -} - -// Specular BRDF composition -------------------------------------------- -fn BRDF(L : vec3, V : vec3, N : vec3, metallic : f32, roughness : f32) -> vec3 { - var H : vec3 = normalize(V + L); - var dotNV : f32 = clamp(dot(N, V), 0.0, 1.0); - var dotNL : f32 = clamp(dot(N, L), 0.0, 1.0); - var dotLH : f32 = clamp(dot(L, H), 0.0, 1.0); - var dotNH : f32 = clamp(dot(N, H), 0.0, 1.0); - var lightColor = vec3(1.0); - var color = vec3(0.0); - if(dotNL > 0.0) { - var rroughness : f32 = max(0.05, roughness); - // D = Normal distribution (Distribution of the microfacets) - var D : f32 = D_GGX(dotNH, roughness); - // G = Geometric shadowing term (Microfacets shadowing) - var G : f32 = G_SchlicksmithGGX(dotNL, dotNV, roughness); - // F = Fresnel factor (Reflectance depending on angle of incidence) - var F : vec3 = F_Schlick(dotNV, metallic); - var spec : vec3 = (D * F * G) / (4.0 * dotNL * dotNV); - color += spec * dotNL * lightColor; - } - return color; -} - -// TODO - global variable declaration order -@fragment fn frag_main( - @location(0) position : vec3, - @location(1) normal: vec3 -) -> @location(0) vec4 { - var N : vec3 = normalize(normal); - var V : vec3 = normalize(ubo.camPos - position); - var Lo = vec3(0.0); - // Specular contribution - for(var i: i32 = 0; i < 4; i++) { - var L : vec3 = normalize(uboParams.lights[i].xyz - position); - Lo += BRDF(L, V, N, material.metallic, material.roughness); - } - // Combine with ambient - var color : vec3 = material_color() * 0.02; - color += Lo; - // Gamma correct - color = pow(color, vec3(0.4545)); - return vec4(color, 1.0); -} diff --git a/src/core/examples/sysgpu/pbr-basic/vertex_writer.zig b/src/core/examples/sysgpu/pbr-basic/vertex_writer.zig deleted file mode 100644 index 1610981e..00000000 --- a/src/core/examples/sysgpu/pbr-basic/vertex_writer.zig +++ /dev/null @@ -1,188 +0,0 @@ -const std = @import("std"); - -/// Vertex writer manages the placement of vertices by tracking which are unique. If a duplicate vertex is added -/// with `put`, only it's index will be written to the index buffer. -/// `IndexType` should match the integer type used for the index buffer -pub fn VertexWriter(comptime VertexType: type, comptime IndexType: type) type { - return struct { - const MapEntry = struct { - packed_index: IndexType = null_index, - next_sparse: IndexType = null_index, - }; - - const null_index: IndexType = std.math.maxInt(IndexType); - - vertices: []VertexType, - indices: []IndexType, - sparse_to_packed_map: []MapEntry, - - /// Next index outside of the 1:1 mapping range for storing - /// position -> normal collisions - next_collision_index: IndexType, - - /// Next packed index - next_packed_index: IndexType, - written_indices_count: IndexType, - - /// Allocate storage and set default values - /// `sparse_vertices_count` is the number of vertices in the source before de-duplication / remapping - /// Put more succinctly, the largest index value in source index buffer - /// `max_vertex_count` is largest permutation of vertices assuming that {vertex, uv, normal} never map 1:1 and always - /// create a new mapping - pub fn init( - allocator: std.mem.Allocator, - indices_count: IndexType, - sparse_vertices_count: IndexType, - max_vertex_count: IndexType, - ) !@This() { - var result: @This() = undefined; - result.vertices = try allocator.alloc(VertexType, max_vertex_count); - result.indices = try allocator.alloc(IndexType, indices_count); - result.sparse_to_packed_map = try allocator.alloc(MapEntry, max_vertex_count); - result.next_collision_index = sparse_vertices_count; - result.next_packed_index = 0; - result.written_indices_count = 0; - @memset(result.sparse_to_packed_map, .{}); - return result; - } - - pub fn put(self: *@This(), vertex: VertexType, sparse_index: IndexType) void { - if (self.sparse_to_packed_map[sparse_index].packed_index == null_index) { - // New start of chain, reserve a new packed index and add entry to `index_map` - const packed_index = self.next_packed_index; - self.sparse_to_packed_map[sparse_index].packed_index = packed_index; - self.vertices[packed_index] = vertex; - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - self.next_packed_index += 1; - return; - } - var previous_sparse_index: IndexType = undefined; - var current_sparse_index = sparse_index; - while (current_sparse_index != null_index) { - const packed_index = self.sparse_to_packed_map[current_sparse_index].packed_index; - if (std.mem.eql(u8, &std.mem.toBytes(self.vertices[packed_index]), &std.mem.toBytes(vertex))) { - // We already have a record for this vertex in our chain - self.indices[self.written_indices_count] = packed_index; - self.written_indices_count += 1; - return; - } - previous_sparse_index = current_sparse_index; - current_sparse_index = self.sparse_to_packed_map[current_sparse_index].next_sparse; - } - // This is a new mapping for the given sparse index - const packed_index = self.next_packed_index; - const remapped_sparse_index = self.next_collision_index; - self.indices[self.written_indices_count] = packed_index; - self.vertices[packed_index] = vertex; - self.sparse_to_packed_map[previous_sparse_index].next_sparse = remapped_sparse_index; - self.sparse_to_packed_map[remapped_sparse_index].packed_index = packed_index; - self.next_packed_index += 1; - self.next_collision_index += 1; - self.written_indices_count += 1; - } - - pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void { - allocator.free(self.vertices); - allocator.free(self.indices); - allocator.free(self.sparse_to_packed_map); - } - - pub fn indexBuffer(self: @This()) []IndexType { - return self.indices; - } - - pub fn vertexBuffer(self: @This()) []VertexType { - return self.vertices[0..self.next_packed_index]; - } - }; -} - -test "VertexWriter" { - const Vec3 = [3]f32; - const Vertex = extern struct { - position: Vec3, - normal: Vec3, - }; - - const expect = std.testing.expect; - const allocator = std.testing.allocator; - - const Face = struct { - position: [3]u16, - normal: [3]u16, - }; - - const vertices = [_]Vec3{ - Vec3{ 1.0, 0.0, 0.0 }, // 0: Position - Vec3{ 2.0, 0.0, 0.0 }, // 1: Position - Vec3{ 3.0, 0.0, 0.0 }, // 2: Position - Vec3{ 1.0, 0.0, 0.0 }, // 3: Normal - Vec3{ 4.0, 0.0, 0.0 }, // 4: Position - Vec3{ 0.0, 1.0, 0.0 }, // 5: Normal - Vec3{ 5.0, 0.0, 0.0 }, // 6: Position - Vec3{ 0.0, 0.0, 1.0 }, // 7: Normal - Vec3{ 1.0, 0.0, 1.0 }, // 8: Normal - Vec3{ 6.0, 0.0, 0.0 }, // 9: Position - }; - - const faces = [_]Face{ - .{ .position = .{ 0, 4, 2 }, .normal = .{ 7, 5, 3 } }, - .{ .position = .{ 2, 3, 9 }, .normal = .{ 3, 7, 8 } }, - .{ .position = .{ 9, 2, 4 }, .normal = .{ 8, 7, 5 } }, - .{ .position = .{ 2, 6, 1 }, .normal = .{ 3, 5, 7 } }, - .{ .position = .{ 9, 6, 0 }, .normal = .{ 5, 7, 8 } }, - }; - - var writer = try VertexWriter(Vertex, u32).init( - allocator, - faces.len * 3, // indices count - vertices.len, // original vertices count - faces.len * 3, // maximum vertices count - ); - defer writer.deinit(allocator); - - for (faces) |face| { - var x: usize = 0; - while (x < 3) : (x += 1) { - const position_index = face.position[x]; - const position = vertices[position_index]; - const normal = vertices[face.normal[x]]; - const vertex = Vertex{ - .position = position, - .normal = normal, - }; - writer.put(vertex, position_index); - } - } - - const indices = writer.indexBuffer(); - try expect(indices.len == faces.len * 3); - - // Face 0 - try expect(indices[0] == 0); // (0, 7) New - try expect(indices[1] == 1); // (4, 5) New - try expect(indices[2] == 2); // (2, 3) New - - // Face 1 - try expect(indices[3 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[3 + 1] == 3); // (3, 7) New - try expect(indices[3 + 2] == 4); // (9, 8) New - - // Face 2 - try expect(indices[6 + 0] == 4); // (9, 8) Duplicate - Reuse index - try expect(indices[6 + 1] == 5); // (2, 7) New normal mapping (Don't clobber) - try expect(indices[6 + 2] == 1); // (4, 5) Duplicate - Reuse Index - - // Face 3 - try expect(indices[9 + 0] == 2); // (2, 3) Duplicate - Reuse index - try expect(indices[9 + 1] == 6); // (6, 5) New - try expect(indices[9 + 2] == 7); // (1, 7) New - - // Face 4 - try expect(indices[12 + 0] == 8); // (9, 5) New normal mapping (Don't clobber) - try expect(indices[12 + 1] == 9); // (6, 7) New normal mapping (Don't clobber) - try expect(indices[12 + 2] == 10); // (0, 8) New normal mapping (Don't clobber) - - try expect(writer.vertexBuffer().len == 11); -}