X-Git-Url: https://gitweb.ps.run/sporegirl/blobdiff_plain/56e50a2a7c2694ef7ac9d82f2bb2ff1aedabc43a..refs/heads/main:/src/main.zig diff --git a/src/main.zig b/src/main.zig index 4813e69..bf77621 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,61 +1,11 @@ -const std = @import("std"); - -const ig = @import("cimgui_docking"); -const sokol = @import("sokol"); -const slog = sokol.log; -const sg = sokol.gfx; -const sapp = sokol.app; -const sglue = sokol.glue; -const sfetch = sokol.fetch; -const simgui = sokol.imgui; - -const stbi = @cImport({ - @cInclude("stb_image.h"); -}); - -const shd = @import("shd/quad.glsl.zig"); - -const state = struct { - const MaxSprites = 100; - const MaxLights = 100; - - var pass_action: sg.PassAction = .{}; - var bind: sg.Bindings = .{}; - var pip: sg.Pipeline = .{}; - - var pip_shadow: sg.Pipeline = .{}; - - var bind_light: sg.Bindings = .{}; - var pip_light: sg.Pipeline = .{}; - - var img_buf: [1024 * 1024]u8 = undefined; - - var cam = [2]f32{ -300, -300 }; - - var sprites_buf: [MaxSprites]Sprite = undefined; - var sprites = std.ArrayListUnmanaged(Sprite).initBuffer(&state.sprites_buf); - var lights_buf: [MaxLights]Light = undefined; - var lights = std.ArrayListUnmanaged(Light).initBuffer(&state.lights_buf); -}; - -const Sprite = struct { - texid: usize = shd.IMG_tex, - pos: [2]f32 = [_]f32{ 0, 0 }, - size: [2]i32 = [_]i32{ 300, 300 }, -}; - -const Light = struct { - pos: [2]f32 = [_]f32{ 0, 0 }, -}; - export fn init() void { // initialize sokol-gfx sg.setup(.{ .buffer_pool_size = 8, - .image_pool_size = 4, + .image_pool_size = 16, .shader_pool_size = 8, .pipeline_pool_size = 8, - .attachments_pool_size = 1, + .view_pool_size = 16, .environment = sglue.environment(), .logger = .{ .func = slog.func }, }); @@ -65,48 +15,142 @@ export fn init() void { .num_lanes = 1, .logger = .{ .func = slog.func }, }); - - // initialize sokol-imgui + stime.setup(); simgui.setup(.{ .logger = .{ .func = slog.func }, }); ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable; // initial clear color - state.pass_action.colors[0] = .{ + state.shaders.tex.pass.action.colors[0] = sg.ColorAttachmentAction{ + .load_action = .CLEAR, + .store_action = .STORE, + .clear_value = .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 }, + }; + state.shaders.shadow.pass.action.colors[0] = sg.ColorAttachmentAction{ .load_action = .CLEAR, - .clear_value = .{ .r = 0.0, .g = 0.5, .b = 1.0, .a = 1.0 }, + .store_action = .STORE, + .clear_value = .{ .r = 1.0, .g = 1.0, .b = 1.0, .a = 0.0 }, }; + state.shaders.light.pass.action.colors[0] = sg.ColorAttachmentAction{ + .load_action = .CLEAR, + .store_action = .STORE, + .clear_value = .{ .r = 0.0, .g = 0.0, .b = 0.0, .a = 0.0 }, + }; + + // combining pass + state.shaders.tex.img = sg.makeImage(.{ + .usage = .{ .color_attachment = true }, + .width = state.OffscreenWidth, + .height = state.OffscreenHeight, + .sample_count = state.OffscreenSampleCount, + .pixel_format = .RGBA8, + }); + state.shaders.tex.depth = sg.makeImage(.{ + .usage = .{ .depth_stencil_attachment = true }, + .width = state.OffscreenWidth, + .height = state.OffscreenHeight, + .sample_count = state.OffscreenSampleCount, + .pixel_format = .DEPTH, + }); + state.shaders.shadow.img = sg.makeImage(.{ + .usage = .{ .color_attachment = true }, + .width = state.OffscreenWidth, + .height = state.OffscreenHeight, + .sample_count = state.OffscreenSampleCount, + .pixel_format = .RGBA8, + }); + state.shaders.shadow.depth = sg.makeImage(.{ + .usage = .{ .depth_stencil_attachment = true }, + .width = state.OffscreenWidth, + .height = state.OffscreenHeight, + .sample_count = state.OffscreenSampleCount, + .pixel_format = .DEPTH, + }); + state.shaders.light.img = sg.makeImage(.{ + .usage = .{ .color_attachment = true }, + .width = state.OffscreenWidth, + .height = state.OffscreenHeight, + .sample_count = state.OffscreenSampleCount, + .pixel_format = .RGBA8, + }); + state.shaders.light.depth = sg.makeImage(.{ + .usage = .{ .depth_stencil_attachment = true }, + .width = state.OffscreenWidth, + .height = state.OffscreenHeight, + .sample_count = state.OffscreenSampleCount, + .pixel_format = .DEPTH, + }); + + state.shaders.combine.bind.views[shd.VIEW_tex_tex] = sg.makeView(.{ + .texture = .{ .image = state.shaders.tex.img }, + }); + state.shaders.combine.bind.views[shd.VIEW_tex_shadow] = sg.makeView(.{ + .texture = .{ .image = state.shaders.shadow.img }, + }); + state.shaders.combine.bind.views[shd.VIEW_tex_light] = sg.makeView(.{ + .texture = .{ .image = state.shaders.light.img }, + }); + state.shaders.combine.bind.samplers[shd.SMP_smp] = sg.makeSampler(.{ + .min_filter = sg.Filter.LINEAR, + .mag_filter = sg.Filter.LINEAR, + }); - // default pass - state.bind.images[shd.IMG_tex] = sg.allocImage(); + state.shaders.combine.pip = sg.makePipeline(.{ + .shader = sg.makeShader(shd.combineShaderDesc(sg.queryBackend())), + .depth = .{ + .compare = .LESS_EQUAL, + .write_enabled = true, + }, + }); - state.bind.samplers[shd.SMP_smp] = sg.makeSampler(.{ + // texture pass + state.img = sg.allocImage(); + state.shaders.tex.pass.attachments.colors[0] = sg.makeView(.{ + .color_attachment = .{ .image = state.shaders.tex.img }, + }); + state.shaders.tex.pass.attachments.depth_stencil = sg.makeView(.{ + .depth_stencil_attachment = .{ .image = state.shaders.tex.depth }, + }); + state.shaders.tex.bind.views[shd.VIEW_tex] = sg.allocView(); + state.shaders.tex.bind.samplers[shd.SMP_smp] = sg.makeSampler(.{ .min_filter = sg.Filter.NEAREST, .mag_filter = sg.Filter.NEAREST, + .wrap_u = sg.Wrap.CLAMP_TO_EDGE, + .wrap_v = sg.Wrap.CLAMP_TO_EDGE, }); // dynamic vertex buffer for instancing data - state.bind.vertex_buffers[0] = sg.makeBuffer(.{ + state.shaders.tex.bind.vertex_buffers[0] = sg.makeBuffer(.{ .usage = .{ .stream_update = true }, .size = state.MaxSprites * @sizeOf(Sprite), }); // a shader and pipeline state object - state.pip = sg.makePipeline(.{ - .shader = sg.makeShader(shd.quadShaderDesc(sg.queryBackend())), + state.shaders.tex.pip = sg.makePipeline(.{ + .shader = sg.makeShader(shd.texShaderDesc(sg.queryBackend())), .layout = init: { var l = sg.VertexLayoutState{}; l.buffers[0].step_func = .PER_INSTANCE; - l.attrs[shd.ATTR_quad_inst_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Sprite, "pos") }; - l.attrs[shd.ATTR_quad_inst_size] = .{ .format = .INT2, .offset = @offsetOf(Sprite, "size") }; + l.attrs[shd.ATTR_tex_inst_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Sprite, "pos") }; + l.attrs[shd.ATTR_tex_inst_size] = .{ .format = .INT2, .offset = @offsetOf(Sprite, "size") }; l.buffers[0].stride = @sizeOf(Sprite); break :init l; }, + .depth = .{ + .compare = .LESS_EQUAL, + .write_enabled = true, + }, }); // shadow pass - state.pip_shadow = sg.makePipeline(.{ + state.shaders.shadow.pass.attachments.colors[0] = sg.makeView(.{ + .color_attachment = .{ .image = state.shaders.shadow.img }, + }); + state.shaders.shadow.pass.attachments.depth_stencil = sg.makeView(.{ + .depth_stencil_attachment = .{ .image = state.shaders.shadow.depth }, + }); + state.shaders.shadow.pip = sg.makePipeline(.{ .shader = sg.makeShader(shd.shadowShaderDesc(sg.queryBackend())), .layout = init: { var l = sg.VertexLayoutState{}; @@ -116,22 +160,58 @@ export fn init() void { l.buffers[0].stride = @sizeOf(Sprite); break :init l; }, + .depth = .{ + .compare = .LESS_EQUAL, + .write_enabled = true, + }, + .colors = init: { + var c: [4]sg.ColorTargetState = @splat(.{}); + c[0] = .{ + .blend = .{ + .enabled = true, + .src_factor_rgb = .DST_COLOR, + .dst_factor_rgb = .ZERO, + .op_rgb = .ADD, + .src_factor_alpha = .ONE, + .dst_factor_alpha = .ONE, + .op_alpha = .ADD, + }, + }; + break :init c; + }, }); // light pass - state.bind_light.vertex_buffers[0] = sg.makeBuffer(.{ + state.shaders.light.pass.attachments.colors[0] = sg.makeView(.{ + .color_attachment = .{ .image = state.shaders.light.img }, + }); + state.shaders.light.pass.attachments.depth_stencil = sg.makeView(.{ + .depth_stencil_attachment = .{ .image = state.shaders.light.depth }, + }); + state.shaders.light.bind.vertex_buffers[0] = sg.makeBuffer(.{ .usage = .{ .stream_update = true }, - .size = state.MaxLights * @sizeOf(Light), + .size = state.MaxLights * @sizeOf(shd.Light), }); - state.pip_light = sg.makePipeline(.{ + state.shaders.light.pip = sg.makePipeline(.{ .shader = sg.makeShader(shd.lightShaderDesc(sg.queryBackend())), - .layout = init: { - var l = sg.VertexLayoutState{}; - l.buffers[0].step_func = .PER_INSTANCE; - l.attrs[shd.ATTR_light_light_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Light, "pos") }; - l.buffers[0].stride = @sizeOf(Light); - break :init l; + .depth = .{ + .compare = .LESS_EQUAL, + .write_enabled = true, + }, + .blend_color = sg.Color{ .r=0.5,.g=0.5,.b=0.5,.a = 0.5 }, + .colors = init: { + var c: [4]sg.ColorTargetState = @splat(.{}); + c[0] = .{ + .blend = .{ + .enabled = true, + .src_factor_rgb = .SRC_COLOR, + .dst_factor_rgb = .ONE_MINUS_SRC_COLOR, + .src_factor_alpha = .ONE, + .dst_factor_alpha = .ONE, + }, + }; + break :init c; }, }); @@ -142,9 +222,10 @@ export fn init() void { var w: c_int = 0; var h: c_int = 0; var n: c_int = 0; + // TODO: free memory? const pixels = stbi.stbi_load_from_memory(@ptrCast(res.*.data.ptr.?), @intCast(res.*.data.size), &w, &h, &n, 4); if (pixels != null) { - sg.initImage(state.bind.images[shd.IMG_tex], .{ + sg.initImage(state.img, .{ .width = w, .height = h, .pixel_format = sg.PixelFormat.RGBA8, @@ -157,6 +238,9 @@ export fn init() void { break :init i; }, }); + sg.initView(state.shaders.tex.bind.views[shd.VIEW_tex], .{ + .texture = .{ .image = state.img }, + }); } } } @@ -168,12 +252,33 @@ export fn init() void { }); state.sprites.appendAssumeCapacity(.{}); - state.lights.appendAssumeCapacity(.{ .pos = [_]f32{ 150, -300 } }); + state.lights.appendAssumeCapacity(DefaultLight); + state.lights.appendAssumeCapacity(DefaultLight); + state.lights.items[0].color = [_]f32{ 0.8, 1, 0.8 }; +} + +export fn cleanup() void { + simgui.shutdown(); + sfetch.shutdown(); + sg.shutdown(); +} + +export fn event(ev: [*c]const sapp.Event) void { + // forward input events to sokol-imgui + _ = simgui.handleEvent(ev.*); + + switch (ev.*.type) { + .MOUSE_MOVE => { + state.lights.items[0].pos[0] = ev.*.mouse_x + state.cam[0]; + state.lights.items[0].pos[1] = (sapp.heightf() - ev.*.mouse_y) + state.cam[1]; + }, + else => {}, + } } fn ui() void { if (ig.igBegin("Hello Dear ImGui!", null, ig.ImGuiWindowFlags_None)) { - _ = ig.igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None); + ig.igLabelText("fps", "%d", state.fps); _ = ig.igDragFloat2("cam", &state.cam); { @@ -204,7 +309,7 @@ fn ui() void { { ig.igPushIDPtr(state.lights.items.ptr); if (ig.igButton("+")) { - _ = state.lights.appendAssumeCapacity(.{}); + _ = state.lights.appendAssumeCapacity(DefaultLight); } var i: u16 = 0; @@ -213,8 +318,18 @@ fn ui() void { i += 1; ig.igPushIDInt(i); ig.igPushItemWidth(100); + _ = ig.igColorEdit3("color", &light.color, ig.ImGuiColorEditFlags_None); + ig.igSameLine(); _ = ig.igDragFloat2("pos", &light.pos); ig.igSameLine(); + _ = ig.igDragFloat("reach", &light.reach); + ig.igSameLine(); + _ = ig.igDragFloat("radius_glow", &light.radius_glow); + ig.igSameLine(); + _ = ig.igDragFloatEx("intensity", &light.intensity, 0.01, 0, 1, "%.3f", ig.ImGuiSliderFlags_None); + ig.igSameLine(); + _ = ig.igDragFloatEx("shadow_brightness", &light.shadow_brightness, 0.01, 0, 1, "%.3f", ig.ImGuiSliderFlags_None); + ig.igSameLine(); if (ig.igButton("-")) { i -= 1; _ = state.lights.swapRemove(i); @@ -229,8 +344,18 @@ fn ui() void { } export fn frame() void { + // sfetch sfetch.dowork(); + // fps + const now = stime.now(); + if (stime.ms(stime.diff(now, state.time)) >= 1000) { + state.fps = state.frames; + state.frames = 0; + state.time = now; + } + state.frames += 1; + const width = sapp.width(); const height = sapp.height(); const widthf = sapp.widthf(); @@ -245,32 +370,32 @@ export fn frame() void { }); ui(); - // update instance vertex buffer - sg.updateBuffer(state.bind.vertex_buffers[0], sg.asRange(state.sprites.items)); - sg.updateBuffer(state.bind_light.vertex_buffers[0], sg.asRange(state.lights.items)); - - sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() }); - // shadow pass - sg.applyPipeline(state.pip_shadow); - sg.applyBindings(state.bind); + sg.beginPass(state.shaders.shadow.pass); + + sg.applyPipeline(state.shaders.shadow.pip); + sg.applyBindings(state.shaders.tex.bind); sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{ .screen = [_]f32{ widthf, heightf }, .cam = state.cam, })); - for (state.lights.items) |light| { - sg.applyUniforms(shd.UB_Light, sg.asRange(&shd.Light{ - .light_pos = light.pos, - })); + for (state.lights.items) |*light| { + sg.applyUniforms(shd.UB_Light, sg.asRange(light)); - sg.draw(0, 6, @truncate(state.sprites.items.len)); + sg.draw(0, 3, @truncate(state.sprites.items.len)); } - // default pass - sg.applyPipeline(state.pip); - sg.applyBindings(state.bind); + sg.endPass(); + + // texture pass + sg.beginPass(state.shaders.tex.pass); + + sg.updateBuffer(state.shaders.tex.bind.vertex_buffers[0], sg.asRange(state.sprites.items)); + + sg.applyPipeline(state.shaders.tex.pip); + sg.applyBindings(state.shaders.tex.bind); sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{ .screen = [_]f32{ widthf, heightf }, @@ -279,32 +404,136 @@ export fn frame() void { sg.draw(0, 6, @truncate(state.sprites.items.len)); + sg.endPass(); + // light pass - sg.applyPipeline(state.pip_light); - sg.applyBindings(state.bind_light); + sg.beginPass(state.shaders.light.pass); + + sg.updateBuffer(state.shaders.light.bind.vertex_buffers[0], sg.asRange(state.lights.items)); + + sg.applyPipeline(state.shaders.light.pip); + sg.applyBindings(state.shaders.light.bind); + + sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{ + .screen = [_]f32{ widthf, heightf }, + .cam = state.cam, + })); + + for (state.lights.items) |*light| { + sg.applyUniforms(shd.UB_Light, sg.asRange(light)); + + sg.draw(0, 6, 1); + } + + sg.endPass(); + + // combine pass + sg.beginPass(.{ .action = state.shaders.combine.pass_action, .swapchain = sglue.swapchain() }); + + sg.applyPipeline(state.shaders.combine.pip); + sg.applyBindings(state.shaders.combine.bind); sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{ .screen = [_]f32{ widthf, heightf }, .cam = state.cam, })); - sg.draw(0, 6, @truncate(state.lights.items.len)); + sg.draw(0, 6, 1); simgui.render(); sg.endPass(); sg.commit(); } -export fn cleanup() void { - simgui.shutdown(); - sfetch.shutdown(); - sg.shutdown(); -} +const state = struct { + var fps: u64 = 0; + var frames: u64 = 0; + var time: u64 = 0; -export fn event(ev: [*c]const sapp.Event) void { - // forward input events to sokol-imgui - _ = simgui.handleEvent(ev.*); -} + const OffscreenWidth = 1920; + const OffscreenHeight = 1080; + const OffscreenSampleCount = 1; + const MaxSprites = 100; + const MaxLights = 100; + + const shaders = struct { + const combine = struct { + var pass_action: sg.PassAction = .{}; + var bind: sg.Bindings = .{}; + var pip: sg.Pipeline = .{}; + }; + const tex = struct { + var pass: sg.Pass = .{}; + var bind: sg.Bindings = .{}; + var pip: sg.Pipeline = .{}; + var img: sg.Image = .{}; + var depth: sg.Image = .{}; + }; + const shadow = struct { + var pass: sg.Pass = .{}; + // var bind: sg.Bindings = .{}; + var pip: sg.Pipeline = .{}; + var img: sg.Image = .{}; + var depth: sg.Image = .{}; + }; + const light = struct { + var pass: sg.Pass = .{}; + var bind: sg.Bindings = .{}; + var pip: sg.Pipeline = .{}; + var img: sg.Image = .{}; + var depth: sg.Image = .{}; + }; + }; + + var img_buf: [1024 * 1024]u8 = undefined; + var img: sg.Image = undefined; + + var cam = [2]f32{ -300, -300 }; + + var sprites_buf: [MaxSprites]Sprite = undefined; + var sprites = std.ArrayListUnmanaged(Sprite).initBuffer(&state.sprites_buf); + var lights_buf: [MaxLights]shd.Light = undefined; + var lights = std.ArrayListUnmanaged(shd.Light).initBuffer(&state.lights_buf); +}; + +const DefaultLight = shd.Light{ + .pos = [_]f32{ 150, -300 }, + .color = [_]f32{ 1, 0.8, 0 }, + .reach = 500, + .radius_glow = 200, + .intensity = 1.0, + .shadow_brightness = 0.5, +}; + +const Sprite = struct { + texid: usize = shd.VIEW_tex, + pos: [2]f32 = [_]f32{ 0, 0 }, + size: [2]i32 = [_]i32{ 300, 300 }, +}; + +// const Light = struct { +// color: [3]f32 = [_]f32{ 1, 0, 0 }, +// pos: [2]f32 = [_]f32{ 0, 0 }, +// radius: f32 = 100, +// }; + +const std = @import("std"); + +const ig = @import("cimgui_docking"); +const sokol = @import("sokol"); +const slog = sokol.log; +const sg = sokol.gfx; +const sapp = sokol.app; +const stime = sokol.time; +const sglue = sokol.glue; +const sfetch = sokol.fetch; +const simgui = sokol.imgui; + +const stbi = @cImport({ + @cInclude("stb_image.h"); +}); + +const shd = @import("shd/main.glsl.zig"); pub fn main() void { sapp.run(.{