--- /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