1 const std = @import("std");
3 const ig = @import("cimgui_docking");
4 const sokol = @import("sokol");
5 const slog = sokol.log;
7 const sapp = sokol.app;
8 const sglue = sokol.glue;
9 const sfetch = sokol.fetch;
10 const simgui = sokol.imgui;
12 const stbi = @cImport({
13 @cInclude("stb_image.h");
16 const shd = @import("shd/quad.glsl.zig");
18 const state = struct {
19 const MaxSprites = 100;
20 const MaxLights = 100;
22 var pass_action: sg.PassAction = .{};
23 var bind: sg.Bindings = .{};
24 var pip: sg.Pipeline = .{};
26 var pip_shadow: sg.Pipeline = .{};
28 var bind_light: sg.Bindings = .{};
29 var pip_light: sg.Pipeline = .{};
31 var img_buf: [1024 * 1024]u8 = undefined;
33 var cam = [2]f32{ -300, -300 };
35 var sprites_buf: [MaxSprites]Sprite = undefined;
36 var sprites = std.ArrayListUnmanaged(Sprite).initBuffer(&state.sprites_buf);
37 var lights_buf: [MaxLights]Light = undefined;
38 var lights = std.ArrayListUnmanaged(Light).initBuffer(&state.lights_buf);
41 const Sprite = struct {
42 texid: usize = shd.IMG_tex,
43 pos: [2]f32 = [_]f32{ 0, 0 },
44 size: [2]i32 = [_]i32{ 300, 300 },
47 const Light = struct {
48 pos: [2]f32 = [_]f32{ 0, 0 },
51 export fn init() void {
52 // initialize sokol-gfx
54 .buffer_pool_size = 8,
56 .shader_pool_size = 8,
57 .pipeline_pool_size = 8,
58 .attachments_pool_size = 1,
59 .environment = sglue.environment(),
60 .logger = .{ .func = slog.func },
66 .logger = .{ .func = slog.func },
69 // initialize sokol-imgui
71 .logger = .{ .func = slog.func },
73 ig.igGetIO().*.ConfigFlags |= ig.ImGuiConfigFlags_DockingEnable;
75 // initial clear color
76 state.pass_action.colors[0] = .{
77 .load_action = .CLEAR,
78 .clear_value = .{ .r = 0.0, .g = 0.5, .b = 1.0, .a = 1.0 },
82 state.bind.images[shd.IMG_tex] = sg.allocImage();
84 state.bind.samplers[shd.SMP_smp] = sg.makeSampler(.{
85 .min_filter = sg.Filter.NEAREST,
86 .mag_filter = sg.Filter.NEAREST,
89 // dynamic vertex buffer for instancing data
90 state.bind.vertex_buffers[0] = sg.makeBuffer(.{
91 .usage = .{ .stream_update = true },
92 .size = state.MaxSprites * @sizeOf(Sprite),
95 // a shader and pipeline state object
96 state.pip = sg.makePipeline(.{
97 .shader = sg.makeShader(shd.quadShaderDesc(sg.queryBackend())),
99 var l = sg.VertexLayoutState{};
100 l.buffers[0].step_func = .PER_INSTANCE;
101 l.attrs[shd.ATTR_quad_inst_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Sprite, "pos") };
102 l.attrs[shd.ATTR_quad_inst_size] = .{ .format = .INT2, .offset = @offsetOf(Sprite, "size") };
103 l.buffers[0].stride = @sizeOf(Sprite);
109 state.pip_shadow = sg.makePipeline(.{
110 .shader = sg.makeShader(shd.shadowShaderDesc(sg.queryBackend())),
112 var l = sg.VertexLayoutState{};
113 l.buffers[0].step_func = .PER_INSTANCE;
114 l.attrs[shd.ATTR_shadow_inst_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Sprite, "pos") };
115 l.attrs[shd.ATTR_shadow_inst_size] = .{ .format = .INT2, .offset = @offsetOf(Sprite, "size") };
116 l.buffers[0].stride = @sizeOf(Sprite);
122 state.bind_light.vertex_buffers[0] = sg.makeBuffer(.{
123 .usage = .{ .stream_update = true },
124 .size = state.MaxLights * @sizeOf(Light),
127 state.pip_light = sg.makePipeline(.{
128 .shader = sg.makeShader(shd.lightShaderDesc(sg.queryBackend())),
130 var l = sg.VertexLayoutState{};
131 l.buffers[0].step_func = .PER_INSTANCE;
132 l.attrs[shd.ATTR_light_light_pos] = .{ .format = .FLOAT2, .offset = @offsetOf(Light, "pos") };
133 l.buffers[0].stride = @sizeOf(Light);
139 const FileCb = struct {
140 fn img(res: [*c]const sfetch.Response) callconv(.c) void {
141 if (res.*.finished) {
145 const pixels = stbi.stbi_load_from_memory(@ptrCast(res.*.data.ptr.?), @intCast(res.*.data.size), &w, &h, &n, 4);
146 if (pixels != null) {
147 sg.initImage(state.bind.images[shd.IMG_tex], .{
150 .pixel_format = sg.PixelFormat.RGBA8,
152 var i = sg.ImageData{};
153 i.subimage[0][0] = .{
155 .size = @intCast(w * h * 4),
165 .path = "img/test.png",
166 .callback = FileCb.img,
167 .buffer = .{ .ptr = &state.img_buf, .size = state.img_buf.len },
170 state.sprites.appendAssumeCapacity(.{});
171 state.lights.appendAssumeCapacity(.{ .pos = [_]f32{ 150, -300 } });
175 if (ig.igBegin("Hello Dear ImGui!", null, ig.ImGuiWindowFlags_None)) {
176 _ = ig.igColorEdit3("Background", &state.pass_action.colors[0].clear_value.r, ig.ImGuiColorEditFlags_None);
177 _ = ig.igDragFloat2("cam", &state.cam);
180 ig.igPushIDPtr(state.sprites.items.ptr);
181 if (ig.igButton("+")) {
182 _ = state.sprites.appendAssumeCapacity(.{});
186 while (i < state.sprites.items.len) {
187 var sprite = &state.sprites.items[i];
190 ig.igPushItemWidth(100);
191 _ = ig.igDragFloat2("pos", &sprite.pos);
193 _ = ig.igDragInt2("size", &sprite.size);
195 if (ig.igButton("-")) {
197 _ = state.sprites.swapRemove(i);
205 ig.igPushIDPtr(state.lights.items.ptr);
206 if (ig.igButton("+")) {
207 _ = state.lights.appendAssumeCapacity(.{});
211 while (i < state.lights.items.len) {
212 var light = &state.lights.items[i];
215 ig.igPushItemWidth(100);
216 _ = ig.igDragFloat2("pos", &light.pos);
218 if (ig.igButton("-")) {
220 _ = state.lights.swapRemove(i);
231 export fn frame() void {
234 const width = sapp.width();
235 const height = sapp.height();
236 const widthf = sapp.widthf();
237 const heightf = sapp.heightf();
239 // call simgui.newFrame() before any ImGui calls
243 .delta_time = sapp.frameDuration(),
244 .dpi_scale = sapp.dpiScale(),
248 // update instance vertex buffer
249 sg.updateBuffer(state.bind.vertex_buffers[0], sg.asRange(state.sprites.items));
250 sg.updateBuffer(state.bind_light.vertex_buffers[0], sg.asRange(state.lights.items));
252 sg.beginPass(.{ .action = state.pass_action, .swapchain = sglue.swapchain() });
255 sg.applyPipeline(state.pip_shadow);
256 sg.applyBindings(state.bind);
258 sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{
259 .screen = [_]f32{ widthf, heightf },
263 for (state.lights.items) |light| {
264 sg.applyUniforms(shd.UB_Light, sg.asRange(&shd.Light{
265 .light_pos = light.pos,
268 sg.draw(0, 6, @truncate(state.sprites.items.len));
272 sg.applyPipeline(state.pip);
273 sg.applyBindings(state.bind);
275 sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{
276 .screen = [_]f32{ widthf, heightf },
280 sg.draw(0, 6, @truncate(state.sprites.items.len));
283 sg.applyPipeline(state.pip_light);
284 sg.applyBindings(state.bind_light);
286 sg.applyUniforms(shd.UB_Game, sg.asRange(&shd.Game{
287 .screen = [_]f32{ widthf, heightf },
291 sg.draw(0, 6, @truncate(state.lights.items.len));
298 export fn cleanup() void {
304 export fn event(ev: [*c]const sapp.Event) void {
305 // forward input events to sokol-imgui
306 _ = simgui.handleEvent(ev.*);
313 .cleanup_cb = cleanup,
315 .window_title = "sokol-zig + Dear Imgui",
318 .icon = .{ .sokol_default = true },
319 .logger = .{ .func = slog.func },