]> gitweb.ps.run Git - sporegirl/blobdiff - src/main.zig
update main.zig
[sporegirl] / src / main.zig
index 92e1da531ff832ec4541134d446a04a153e1c1d5..4813e6909aee5ec0e9a913d2d3a6b794ce8b7f24 100644 (file)
@@ -1,52 +1,95 @@
-const use_docking = @import("build_options").docking;
-const ig = if (use_docking) @import("cimgui_docking") else @import("cimgui");
+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 shd = @import("shader/quad.glsl.zig");
+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 pass_action: sg.PassAction = .{};
-    var show_first_window: bool = true;
-    var show_second_window: bool = true;
+
+    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,
+        .shader_pool_size = 8,
+        .pipeline_pool_size = 8,
+        .attachments_pool_size = 1,
         .environment = sglue.environment(),
         .logger = .{ .func = slog.func },
     });
+    sfetch.setup(.{
+        .max_requests = 2,
+        .num_channels = 2,
+        .num_lanes = 1,
+        .logger = .{ .func = slog.func },
+    });
 
     // initialize sokol-imgui
     simgui.setup(.{
         .logger = .{ .func = slog.func },
     });
-    if (use_docking) {
-        ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable;
-    }
+    ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable;
 
-    // a vertex buffer
-    state.bind.vertex_buffers[0] = sg.makeBuffer(.{
-        .data = sg.asRange(&[_]f32{
-            // positions      colors
-            -0.5, 0.5,  0.5, 1.0, 0.0, 0.0, 1.0,
-            0.5,  0.5,  0.5, 0.0, 1.0, 0.0, 1.0,
-            0.5,  -0.5, 0.5, 0.0, 0.0, 1.0, 1.0,
-            -0.5, -0.5, 0.5, 1.0, 1.0, 0.0, 1.0,
-        }),
+    // initial clear color
+    state.pass_action.colors[0] = .{
+        .load_action = .CLEAR,
+        .clear_value = .{ .r = 0.0, .g = 0.5, .b = 1.0, .a = 1.0 },
+    };
+
+    // default pass
+    state.bind.images[shd.IMG_tex] = sg.allocImage();
+
+    state.bind.samplers[shd.SMP_smp] = sg.makeSampler(.{
+        .min_filter = sg.Filter.NEAREST,
+        .mag_filter = sg.Filter.NEAREST,
     });
 
-    // an index buffer
-    state.bind.index_buffer = sg.makeBuffer(.{
-        .usage = .{ .index_buffer = true },
-        .data = sg.asRange(&[_]u16{ 0, 1, 2, 0, 2, 3 }),
+    // dynamic vertex buffer for instancing data
+    state.bind.vertex_buffers[0] = sg.makeBuffer(.{
+        .usage = .{ .stream_update = true },
+        .size = state.MaxSprites * @sizeOf(Sprite),
     });
 
     // a shader and pipeline state object
@@ -54,41 +97,199 @@ export fn init() void {
         .shader = sg.makeShader(shd.quadShaderDesc(sg.queryBackend())),
         .layout = init: {
             var l = sg.VertexLayoutState{};
-            l.attrs[shd.ATTR_quad_position].format = .FLOAT3;
-            l.attrs[shd.ATTR_quad_color0].format = .FLOAT4;
+            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.buffers[0].stride = @sizeOf(Sprite);
             break :init l;
         },
-        .index_type = .UINT16,
     });
 
-    // initial clear color
-    state.pass_action.colors[0] = .{
-        .load_action = .CLEAR,
-        .clear_value = .{ .r = 0.0, .g = 0.5, .b = 1.0, .a = 1.0 },
+    // shadow pass
+    state.pip_shadow = sg.makePipeline(.{
+        .shader = sg.makeShader(shd.shadowShaderDesc(sg.queryBackend())),
+        .layout = init: {
+            var l = sg.VertexLayoutState{};
+            l.buffers[0].step_func = .PER_INSTANCE;
+            l.attrs[shd.ATTR_shadow_inst_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Sprite, "pos") };
+            l.attrs[shd.ATTR_shadow_inst_size] = .{ .format = .INT2, .offset = @offsetOf(Sprite, "size") };
+            l.buffers[0].stride = @sizeOf(Sprite);
+            break :init l;
+        },
+    });
+
+    // light pass
+    state.bind_light.vertex_buffers[0] = sg.makeBuffer(.{
+        .usage = .{ .stream_update = true },
+        .size = state.MaxLights * @sizeOf(Light),
+    });
+
+    state.pip_light = 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;
+        },
+    });
+
+    // fetch resources
+    const FileCb = struct {
+        fn img(res: [*c]const sfetch.Response) callconv(.c) void {
+            if (res.*.finished) {
+                var w: c_int = 0;
+                var h: c_int = 0;
+                var n: c_int = 0;
+                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], .{
+                        .width = w,
+                        .height = h,
+                        .pixel_format = sg.PixelFormat.RGBA8,
+                        .data = init: {
+                            var i = sg.ImageData{};
+                            i.subimage[0][0] = .{
+                                .ptr = pixels,
+                                .size = @intCast(w * h * 4),
+                            };
+                            break :init i;
+                        },
+                    });
+                }
+            }
+        }
     };
+    _ = sfetch.send(.{
+        .path = "img/test.png",
+        .callback = FileCb.img,
+        .buffer = .{ .ptr = &state.img_buf, .size = state.img_buf.len },
+    });
+
+    state.sprites.appendAssumeCapacity(.{});
+    state.lights.appendAssumeCapacity(.{ .pos = [_]f32{ 150, -300 } });
+}
+
+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.igDragFloat2("cam", &state.cam);
+
+        {
+            ig.igPushIDPtr(state.sprites.items.ptr);
+            if (ig.igButton("+")) {
+                _ = state.sprites.appendAssumeCapacity(.{});
+            }
+
+            var i: u16 = 0;
+            while (i < state.sprites.items.len) {
+                var sprite = &state.sprites.items[i];
+                i += 1;
+                ig.igPushIDInt(i);
+                ig.igPushItemWidth(100);
+                _ = ig.igDragFloat2("pos", &sprite.pos);
+                ig.igSameLine();
+                _ = ig.igDragInt2("size", &sprite.size);
+                ig.igSameLine();
+                if (ig.igButton("-")) {
+                    i -= 1;
+                    _ = state.sprites.swapRemove(i);
+                }
+                ig.igPopID();
+            }
+            ig.igPopID();
+        }
+
+        {
+            ig.igPushIDPtr(state.lights.items.ptr);
+            if (ig.igButton("+")) {
+                _ = state.lights.appendAssumeCapacity(.{});
+            }
+
+            var i: u16 = 0;
+            while (i < state.lights.items.len) {
+                var light = &state.lights.items[i];
+                i += 1;
+                ig.igPushIDInt(i);
+                ig.igPushItemWidth(100);
+                _ = ig.igDragFloat2("pos", &light.pos);
+                ig.igSameLine();
+                if (ig.igButton("-")) {
+                    i -= 1;
+                    _ = state.lights.swapRemove(i);
+                }
+                ig.igPopID();
+            }
+            ig.igPopID();
+        }
+
+        ig.igEnd();
+    }
 }
 
 export fn frame() void {
+    sfetch.dowork();
+
+    const width = sapp.width();
+    const height = sapp.height();
+    const widthf = sapp.widthf();
+    const heightf = sapp.heightf();
+
     // call simgui.newFrame() before any ImGui calls
     simgui.newFrame(.{
-        .width = sapp.width(),
-        .height = sapp.height(),
+        .width = width,
+        .height = height,
         .delta_time = sapp.frameDuration(),
         .dpi_scale = sapp.dpiScale(),
     });
+    ui();
 
-    if (ig.igBegin("Hello Dear ImGui!", &state.show_first_window, ig.ImGuiWindowFlags_None)) {
-        _ = ig.igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None);
-        _ = ig.igText("Dear ImGui Version: %s", ig.IMGUI_VERSION);
-        ig.igEnd();
-    }
-
+    // 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));
 
-    // call simgui.render() inside a sokol-gfx pass
     sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() });
+
+    // shadow pass
+    sg.applyPipeline(state.pip_shadow);
+    sg.applyBindings(state.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,
+        }));
+
+        sg.draw(0, 6, @truncate(state.sprites.items.len));
+    }
+
+    // default pass
     sg.applyPipeline(state.pip);
     sg.applyBindings(state.bind);
-    sg.draw(0, 6, 1);
+
+    sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{
+        .screen = [_]f32{ widthf, heightf },
+        .cam = state.cam,
+    }));
+
+    sg.draw(0, 6, @truncate(state.sprites.items.len));
+
+    // light pass
+    sg.applyPipeline(state.pip_light);
+    sg.applyBindings(state.bind_light);
+
+    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));
+
     simgui.render();
     sg.endPass();
     sg.commit();
@@ -96,6 +297,7 @@ export fn frame() void {
 
 export fn cleanup() void {
     simgui.shutdown();
+    sfetch.shutdown();
     sg.shutdown();
 }