--- /dev/null
+// === combine ===
+@vs vs_combine
+const vec2 positions[4] = { vec2(-1, 1), vec2(1, 1), vec2(1, -1), vec2(-1, -1) };
+const vec2 uvs[4] = { vec2(0, 1), vec2(1, 1), vec2(1, 0), vec2(0, 0) };
+const int indices[6] = { 0, 1, 2, 0, 2, 3 };
+
+layout(binding=0) uniform Game
+{
+ vec2 screen;
+ vec2 cam;
+};
+
+out vec2 uv;
+
+void main() {
+ int idx = indices[gl_VertexIndex];
+ vec2 pos = positions[idx];
+ uv = uvs[idx];
+
+ gl_Position = vec4(pos, 1, 1);
+}
+@end
+
+@fs fs_combine
+layout(binding=0) uniform texture2D tex_tex;
+layout(binding=1) uniform texture2D tex_shadow;
+layout(binding=2) uniform texture2D tex_light;
+layout(binding=0) uniform sampler smp;
+
+in vec2 uv;
+out vec4 frag_color;
+
+
+void main() {
+ vec4 tb = vec4(0.8,0.3,0.3,1);
+ if (mod(floor(uv.x*10), 2) != mod(floor(uv.y*10),2)) {
+ tb = vec4(0.3,0.3,0.8,1);
+ }
+ vec4 tt = texture(sampler2D(tex_tex, smp), uv);
+ vec4 ts = texture(sampler2D(tex_shadow, smp), uv);
+ vec4 tl = texture(sampler2D(tex_light, smp), uv);
+
+ vec3 l = tt.a * (tt.rgb * tl.rgb) + (1-tt.a) * (tl.rgb * tl.a);
+ l = ts.a * (l * ts.rgb) + (1-ts.a) * (l);
+ frag_color = vec4(l, 1);
+
+ vec3 a = (tb.rgb + tl.rgb) * tt.rgb * ts.rgb; // + tl.rgb * tl.a;
+ frag_color = vec4(a, 1);
+
+
+ vec3 bg = tb.rgb * tl.rgb;
+ vec3 bg_s = bg * (1-ts.a) + bg * ts.rgb * 0.5 * ts.a;
+ vec3 t = tt.rgb * tl.rgb;
+ vec3 t_s = t * (1-ts.a) + t * ts.rgb * 0.5 * ts.a;
+ vec3 bg_s_t_s = bg_s * (1-tt.a) + t_s * tt.a;
+ frag_color = vec4(bg_s_t_s, 1);
+
+ // frag_color = vec4(tl);
+}
+@end
+
+@program combine vs_combine fs_combine
+
+
+// === tex ===
+@vs vs_tex
+const vec2 positions[4] = { vec2(0, 1), vec2(1, 1), vec2(1, 0), vec2(0, 0) };
+const vec2 uvs[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
+const int indices[6] = { 0, 1, 2, 0, 2, 3 };
+
+layout(binding=0) uniform Game
+{
+ vec2 screen;
+ vec2 cam;
+};
+
+in vec2 inst_pos;
+in ivec2 inst_size;
+out vec2 uv;
+
+void main() {
+ int idx = indices[gl_VertexIndex];
+ vec2 pos = positions[idx];
+ uv = uvs[idx];
+
+ vec2 inst_pos_abs = inst_pos + inst_size * pos - cam - screen / 2;
+ gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
+}
+@end
+
+@fs fs_tex
+layout(binding=0) uniform texture2D tex;
+layout(binding=0) uniform sampler smp;
+
+in vec2 uv;
+out vec4 frag_color;
+
+void main() {
+ vec4 tex_color = texture(sampler2D(tex, smp), uv);
+ if (tex_color.a < 1)
+ discard;
+ frag_color = tex_color;
+}
+@end
+
+@program tex vs_tex fs_tex
+
+
+// === shadows ===
+@vs vs_shadow
+const vec2 positions[3] = { vec2(0, 1), vec2(1, 1), vec2(0.5, 0) };
+const vec2 uvs[3] = { vec2(0, 0), vec2(1, 0), vec2(0.5, 1) };
+
+layout(binding=0) uniform Game
+{
+ vec2 screen;
+ vec2 cam;
+};
+
+layout(binding=1) uniform Light
+{
+ vec3 color;
+ vec2 pos;
+ float reach;
+ float radius_glow;
+ float intensity;
+ float shadow_brightness;
+} light;
+
+in vec2 inst_pos;
+in ivec2 inst_size;
+out vec2 uv;
+out vec3 light_color;
+out float shadow_brightness;
+
+// https://gist.github.com/yiwenl/3f804e80d0930e34a0b33359259b556c
+vec2 rotate(vec2 v, float a) {
+ float s = sin(a);
+ float c = cos(a);
+ mat2 m = mat2(c, s, -s, c);
+ return m * v;
+}
+
+void main() {
+ int idx = gl_VertexIndex;
+ vec2 pos = positions[idx];
+ uv = uvs[idx];
+ light_color = light.color;
+ shadow_brightness = light.shadow_brightness;
+
+ if (idx == 2) { // keep pos for base
+ vec2 inst_pos_abs = inst_pos + inst_size * pos - cam - screen / 2;
+ gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
+ }
+ else {
+ vec2 anchor = vec2(0.5, 0);
+ vec2 anchor_abs = inst_pos + inst_size * anchor - cam - screen / 2;
+ vec2 light_pos_abs = light.pos - cam - screen / 2;
+ vec2 dir = anchor_abs - light_pos_abs;
+
+ vec2 left_right[] = {
+ rotate(dir, 1.5708),
+ rotate(dir, -1.5708),
+ };
+
+ float light_radius_factor = 1.5 / log(light.reach);
+
+ vec2 inst_pos_abs = anchor_abs + dir + left_right[idx] * light_radius_factor;
+ gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
+ }
+}
+@end
+
+@fs fs_shadow
+layout(binding=0) uniform texture2D tex;
+layout(binding=0) uniform sampler smp;
+
+in vec2 uv;
+in vec3 light_color;
+in float shadow_brightness;
+out vec4 frag_color;
+
+vec3 brighten(vec3 c, float f) {
+ vec3 d = vec3(1) - c;
+ return c + d * f;
+}
+
+vec4 blur(float radius, float resolution) {
+ float blur = radius/resolution;
+
+ float hstep = 1;
+ float vstep = 1;
+
+ vec4 sum = vec4(0.0);
+
+ sum += texture(sampler2D(tex, smp), vec2(uv.x - 4.0*blur*hstep, uv.y - 4.0*blur*vstep)) * 0.0162162162;
+ sum += texture(sampler2D(tex, smp), vec2(uv.x - 3.0*blur*hstep, uv.y - 3.0*blur*vstep)) * 0.0540540541;
+ sum += texture(sampler2D(tex, smp), vec2(uv.x - 2.0*blur*hstep, uv.y - 2.0*blur*vstep)) * 0.1216216216;
+ sum += texture(sampler2D(tex, smp), vec2(uv.x - 1.0*blur*hstep, uv.y - 1.0*blur*vstep)) * 0.1945945946;
+
+ sum += texture(sampler2D(tex, smp), vec2(uv.x, uv.y)) * 0.2270270270;
+
+ sum += texture(sampler2D(tex, smp), vec2(uv.x + 1.0*blur*hstep, uv.y + 1.0*blur*vstep)) * 0.1945945946;
+ sum += texture(sampler2D(tex, smp), vec2(uv.x + 2.0*blur*hstep, uv.y + 2.0*blur*vstep)) * 0.1216216216;
+ sum += texture(sampler2D(tex, smp), vec2(uv.x + 3.0*blur*hstep, uv.y + 3.0*blur*vstep)) * 0.0540540541;
+ sum += texture(sampler2D(tex, smp), vec2(uv.x + 4.0*blur*hstep, uv.y + 4.0*blur*vstep)) * 0.0162162162;
+
+ return sum;
+}
+
+void main() {
+ vec4 tex_color = blur(10, 1);
+ tex_color = texture(sampler2D(tex, smp), uv);
+
+ vec3 brighter = brighten(light_color, shadow_brightness);
+ frag_color = vec4(brighter * tex_color.a + vec3(1) * (1-tex_color.a), tex_color.a);
+}
+@end
+
+@program shadow vs_shadow fs_shadow
+
+
+// === lights ===
+@vs vs_light
+const vec2 positions[4] = { vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5) };
+const int indices[6] = { 0, 1, 2, 0, 2, 3 };
+
+layout(binding=0) uniform Game
+{
+ vec2 screen;
+ vec2 cam;
+};
+
+layout(binding=1) uniform Light
+{
+ vec3 color;
+ vec2 pos;
+ float reach;
+ float radius_glow;
+ float intensity;
+ float shadow_brightness;
+} light;
+
+out vec3 light_col;
+out vec2 light_pos;
+out float light_rea;
+out float light_glo;
+out float light_intensity;
+out vec2 fragpos;
+
+void main() {
+ int idx = indices[gl_VertexIndex];
+ vec2 pos = positions[idx];
+
+ vec2 inst_pos_ap = light.pos + light.reach * 2 * pos;
+ vec2 inst_pos_rp = inst_pos_ap - cam - screen / 2;
+ vec2 inst_pos_rw = inst_pos_rp / screen * 2;
+ gl_Position = vec4(inst_pos_rw, 1, 1);
+
+ light_col = light.color;
+ light_pos = light.pos;
+ light_rea = light.reach;
+ light_glo = light.radius_glow;
+ light_intensity = light.intensity;
+ fragpos = inst_pos_ap;
+}
+@end
+
+@fs fs_light
+layout(binding=0) uniform Game
+{
+ vec2 screen;
+ vec2 cam;
+};
+
+in vec3 light_col;
+in vec2 light_pos;
+in float light_rea;
+in float light_glo;
+in float light_intensity;
+in vec2 fragpos;
+out vec4 frag_color;
+
+float dist(vec2 a, vec2 b) {
+ float d1 = a.x - b.x;
+ float d2 = a.y - b.y;
+ return sqrt(d1*d1 + d2*d2);
+}
+
+void main() {
+ // vec2 frag_coord_abs = gl_FragCoord.xy * v_screen + v_cam + v_screen / 2;
+ // float factor = dist(light_pos, frag_coord_abs) / light_rad;
+ float factor_rea = 1 - (dist(light_pos, fragpos) / light_rea);
+ float factor_glo = 1 - (dist(light_pos, fragpos) / light_glo);
+ if (factor_rea > 1.0) discard;
+ frag_color = vec4(light_col.rgb * factor_rea, factor_glo * light_intensity);
+ // frag_color = vec4(vec3(mod(factor, 1.0)) * light_col, factor);
+}
+@end
+
+@program light vs_light fs_light
+++ /dev/null
-/* quad vertex shader */
-@vs vs
-const vec2 positions[4] = { vec2(0, 1), vec2(1, 1), vec2(1, 0), vec2(0, 0) };
-const vec2 uvs[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
-const int indices[6] = { 0, 1, 2, 0, 2, 3 };
-
-layout(binding=0) uniform Game
-{
- vec2 screen;
- vec2 cam;
-};
-
-in vec2 inst_pos;
-in ivec2 inst_size;
-out vec2 uv;
-
-void main() {
- int idx = indices[gl_VertexIndex];
- vec2 pos = positions[idx];
- uv = uvs[idx];
-
- vec2 inst_pos_abs = inst_pos + inst_size * pos - cam - screen / 2;
- gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
-}
-@end
-
-/* quad fragment shader */
-@fs fs
-layout(binding=0) uniform texture2D tex;
-layout(binding=0) uniform sampler smp;
-
-in vec2 uv;
-out vec4 frag_color;
-
-void main() {
- vec4 tex_color = texture(sampler2D(tex, smp), uv);
- if (tex_color.a < 1)
- discard;
- frag_color = tex_color;
-}
-@end
-
-/* quad shader program */
-@program quad vs fs
-
-
-
-
-
-@vs vs_shadow
-const vec2 positions[4] = { vec2(0, 1), vec2(1, 1), vec2(1, 0), vec2(0, 0) };
-const vec2 uvs[4] = { vec2(0, 0), vec2(1, 0), vec2(1, 1), vec2(0, 1) };
-const int indices[6] = { 0, 1, 2, 0, 2, 3 };
-
-layout(binding=0) uniform Game
-{
- vec2 screen;
- vec2 cam;
-};
-
-layout(binding=1) uniform Light
-{
- vec2 light_pos;
-};
-
-in vec2 inst_pos;
-in ivec2 inst_size;
-out vec2 uv;
-
-vec2 rotate(vec2 v, float a) {
- float s = sin(a);
- float c = cos(a);
- mat2 m = mat2(c, s, -s, c);
- return m * v;
-}
-
-void main() {
- int idx = indices[gl_VertexIndex];
- vec2 pos = positions[idx];
- uv = uvs[idx];
-
- if (idx == 2 || idx == 3) { // keep pos for lower vertices (base)
- vec2 inst_pos_abs = inst_pos + inst_size * pos - cam - screen / 2;
- gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
- }
- else {
- vec2 anchor = vec2(0.5, 0);
- vec2 anchor_abs = inst_pos + inst_size * anchor - cam - screen / 2;
- vec2 light_pos_abs = light_pos - cam - screen / 2;
- vec2 dir = anchor_abs - light_pos_abs;
-
- vec2 left_right[] = {
- rotate(dir, 1.5708),
- rotate(dir, -1.5708),
- };
-
- vec2 inst_pos_abs = anchor_abs + dir + left_right[idx] / 2;
- gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
- }
-}
-@end
-
-@fs fs_shadow
-layout(binding=0) uniform texture2D tex;
-layout(binding=0) uniform sampler smp;
-
-in vec2 uv;
-out vec4 frag_color;
-
-void main() {
- vec4 tex_color = texture(sampler2D(tex, smp), uv);
- // if (tex_color.a < 1)
- // discard;
- frag_color = vec4(0, 0, 0, 1);
-}
-@end
-
-@program shadow vs_shadow fs_shadow
-
-
-
-@vs vs_light
-const vec2 positions[4] = { vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5) };
-const int indices[6] = { 0, 1, 2, 0, 2, 3 };
-
-layout(binding=0) uniform Game
-{
- vec2 screen;
- vec2 cam;
-};
-
-in vec2 light_pos;
-
-void main() {
- int idx = indices[gl_VertexIndex];
- vec2 pos = positions[idx];
-
- vec2 light_size = vec2(10, 10);
-
- vec2 inst_pos_abs = light_pos + light_size * pos - cam - screen / 2;
- gl_Position = vec4(inst_pos_abs / screen * 2, 1, 1);
-}
-@end
-
-@fs fs_light
-out vec4 frag_color;
-
-void main() {
- frag_color = vec4(1, 0, 0, 1);
-}
-@end
-
-@program light vs_light fs_light
-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 },
});
.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{};
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;
},
});
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,
break :init i;
},
});
+ sg.initView(state.shaders.tex.bind.views[shd.VIEW_tex], .{
+ .texture = .{ .image = state.img },
+ });
}
}
}
});
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);
{
{
ig.igPushIDPtr(state.lights.items.ptr);
if (ig.igButton("+")) {
- _ = state.lights.appendAssumeCapacity(.{});
+ _ = state.lights.appendAssumeCapacity(DefaultLight);
}
var i: u16 = 0;
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);
}
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();
});
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 },
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(.{