+++ /dev/null
-#version 300 es
-precision mediump float;
-
-uniform sampler2D u_sampler;
-
-uniform vec3 u_fog_color;
-uniform float u_fog_start;
-uniform float u_fog_range;
-
-
-in vec4 v_color;
-in vec2 v_tex;
-in float v_tex_enabled;
-in float v_dist;
-
-out vec4 fragColor;
-void main() {
- vec4 tile_color = vec4((v_color * mix(vec4(1), texture(u_sampler, v_tex), v_tex_enabled)).xyz, 1);
-
- // Not going to rely on clamp() existing.
- float fog = min(1.0f, max(0.0f, (v_dist - u_fog_start) / u_fog_range));
-
- fragColor = mix(tile_color, vec4(u_fog_color, 1), fog);
-}
+++ /dev/null
-#version 300 es
-layout(location = 0) in vec3 a_pos;
-layout(location = 1) in vec2 a_tex;
-layout(location = 2) in uint a_data;
-
-layout(std140) uniform u_world_matrix_block {
- mat4 u_view;
- mat4 u_world;
- mat4 u_model;
-};
-
-out vec4 v_color;
-out vec2 v_tex;
-out float v_tex_enabled;
-out float v_dist;
-
-void main() {
- vec4 pos = u_world * u_model * vec4(a_pos, 1);
- v_dist = length(pos);
- gl_Position = u_view * pos;
-
- vec3 block_color = vec3(
- float((a_data & 0x0000FU) >> 0U) / 15.0,
- float((a_data & 0x000F0U) >> 4U) / 15.0,
- float((a_data & 0x00F00U) >> 8U) / 15.0
- ) * (float((a_data & 0x0F000U) >> 12U) / 15.0);
-
- v_color = vec4(block_color, 1);
- v_tex = a_tex;
- v_tex_enabled = float((a_data & 0x10000U) >> 16U);
-}
#load "camera"
#load "chunk"
#load "config"
-#load "font"
-#load "immediate"
#load "input"
#load "main"
-#load "mesh"
#load "physics"
#load "player"
#load "shader"
-#load "texture"
#load "utils"
#load "vecmath"
#load "world"
#load "worldgen"
+#load "gfx/canvas"
+#load "gfx/font"
+#load "gfx/immediate"
+#load "gfx/mesh"
+// #load "gfx/shader"
+#load "gfx/texture"
+#load "gfx/ui"
+
// Onyx library code
#load "stb_truetype"
#load "stb_image"
+++ /dev/null
-
-use package core
-use package core.intrinsics.onyx {__zero_value}
-use package stb_truetype
-use package opengles
-
-#local {
- font_registry: Map(FontDescriptor, Font);
- font_vbo: GLint;
- font_vao: GLint;
-
- font_shader: Shader;
- font_color: Color;
-}
-
-fonts_init :: () {
- map.init(^font_registry);
-
- font_shader = shader_make("./assets/shaders/font.glsl");
- shader_use(font_shader);
- shader_link_window_matrix_block(font_shader);
-
- glGenVertexArrays(1, ^font_vao);
- glBindVertexArray(font_vao);
-
- font_interp_buffer: GLint;
- glGenBuffers(1, ^font_interp_buffer);
- glBindBuffer(GL_ARRAY_BUFFER, font_interp_buffer);
- font_interp_data := f32.[
- 0.0, 0.0,
- 1.0, 0.0,
- 1.0, 1.0,
- 0.0, 1.0,
- ];
- glBufferData(GL_ARRAY_BUFFER, sizeof typeof font_interp_data, ~~^font_interp_data, GL_STATIC_DRAW);
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof f32, ~~0);
-
- font_index_buffer: GLint;
- glGenBuffers(1, ^font_index_buffer);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_index_buffer);
- font_index_data := u8.[ 0, 1, 2, 0, 2, 3 ];
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof typeof font_index_data, ~~^font_index_data, GL_STATIC_DRAW);
-
- glGenBuffers(1, ^font_vbo);
- glBindBuffer(GL_ARRAY_BUFFER, font_vbo);
- glBufferData(GL_ARRAY_BUFFER, 1024 * sizeof stbtt_aligned_quad, null, GL_STREAM_DRAW);
-
- for 1..5 {
- glEnableVertexAttribArray(it);
- glVertexAttribDivisor(it, 1);
- }
- glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~ 0);
- glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~16);
- glVertexAttribPointer(3, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~8);
- glVertexAttribPointer(4, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~24);
-
- glBindBuffer(GL_ARRAY_BUFFER, -1);
- glBindVertexArray(-1);
-
- font_set_color(.{0,0,0});
-}
-
-
-Font :: struct {
- texture: GLint;
- texture_width, texture_height: i32;
- chars: [] stbtt_packedchar;
- em: f32;
-}
-
-font_make :: (fd: FontDescriptor) -> Font {
- texture_size :: 256;
-
- char_data := memory.make_slice(stbtt_packedchar, 96);
-
- ttf_file := os.get_contents(fd.path);
- if ttf_file.count == 0 {
- println("Bad font");
- return __zero_value(Font);
- }
- defer cfree(ttf_file.data);
-
- pixels := calloc(texture_size * texture_size);
- defer cfree(pixels);
-
- ctx: stbtt_pack_context;
- stbtt_PackBegin(^ctx, pixels, texture_size, texture_size, 0, 1);
- stbtt_PackFontRange(^ctx, ttf_file.data, 0, ~~fd.size, #char " ", 96, char_data.data);
- stbtt_PackEnd(^ctx);
-
- texture: GLint;
- glGenTextures(1, ^texture);
- glBindTexture(GL_TEXTURE_2D, texture);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture_size, texture_size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glBindTexture(GL_TEXTURE_2D, 0);
-
- font := Font.{
- texture = texture,
- texture_width = texture_size,
- texture_height = texture_size,
- chars = char_data,
- em = ~~fd.size,
- };
-
- font_registry[fd] = font;
-
- return font;
-}
-
-font_set_color :: (color: Color) {
- font_color = color;
-}
-
-font_print :: (font: Font, x, y: f32, format: str, va: ..any) {
- buf: [1024] u8;
- msg := conv.format_va(buf, format, va);
- font_draw(font, x, y, msg);
-}
-
-font_draw :: (font: Font, x, y: f32, msg: str) {
- quads: ^stbtt_aligned_quad = alloc.from_stack(msg.count * sizeof stbtt_aligned_quad);
- quad_num := 0;
-
- x_, y_ := x, y;
-
- for msg {
- if it == #char "\n" {
- x_ = x;
- y_ += font.em + 2;
- }
-
- stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
- ~~(it - #char " "), ^x_, ^y_, ^quads[quad_num], false);
-
- quad_num += 1;
- }
-
- font_render(font, quads[0 .. quad_num]);
-}
-
-font_draw_centered :: (font: Font, x, y, max_width: f32, msg: str) {
- quads: ^stbtt_aligned_quad = alloc.from_stack(msg.count * sizeof stbtt_aligned_quad);
- quad_num := 0;
-
- width := font_get_width(font, msg);
- x_, y_ := x, y;
- x_ = (max_width - width) / 2 + x;
-
- for msg {
- if it == #char "\n" {
- x_ = (max_width - width) / 2 + x;
- y_ += font.em + 2;
- }
-
- stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
- ~~(it - #char " "), ^x_, ^y_, ^quads[quad_num], false);
-
- quad_num += 1;
- }
-
- font_render(font, quads[0 .. quad_num]);
-}
-
-#local font_render :: (font: Font, quads: [] stbtt_aligned_quad) {
- // If this is being used in conjunction with the immediate
- // rendering system, make sure the immediate objects are flushed
- // before trying to render over them.
- #if #defined(immediate_flush) {
- immediate_flush();
- }
-
- glBindBuffer(GL_ARRAY_BUFFER, font_vbo);
- glBufferSubData(GL_ARRAY_BUFFER, 0, quads.count * sizeof stbtt_aligned_quad, quads.data);
- glBindBuffer(GL_ARRAY_BUFFER, -1);
-
- glActiveTexture(GL_TEXTURE0);
- glBindTexture(GL_TEXTURE_2D, font.texture);
- shader_use(font_shader);
- shader_set_uniform(font_shader, #cstr "u_texture", 0);
- shader_set_uniform(font_shader, #cstr "u_color", font_color);
-
- glDisable(GL_DEPTH_TEST);
- glBindVertexArray(font_vao);
- glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, ~~0, quads.count);
- glBindTexture(GL_TEXTURE_2D, -1);
- glBindVertexArray(-1);
-}
-
-font_get_width :: (font: Font, msg: str) -> f32 {
- x_, y_ := 0.0f, 0.0f;
- width := 0.0f;
-
- quad: stbtt_aligned_quad;
- for msg {
- if it == #char "\n" {
- width = math.max(width, x_);
- x_ = 0;
- y_ += font.em + 2;
- continue;
- }
-
- stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
- ~~(it - #char " "), ^x_, ^y_, ^quad, false);
- }
-
- return math.max(x_, width);
-}
-
-
-FontDescriptor :: struct {
- path : str;
- size : u32;
-}
-
-#match hash.to_u32 (fd: FontDescriptor) => {
- name_hash := hash.to_u32(fd.path);
- size_hash := hash.to_u32(fd.size);
- return name_hash * 13 + size_hash * 17;
-}
-
-#operator == (f1, f2: FontDescriptor) => f1.path == f2.path && f1.size == f2.size;
-
-font_lookup :: (fd := FontDescriptor.{ "assets/calibri.ttf", 12 }) -> Font {
- if font_registry->has(fd) {
- return font_registry[fd];
- }
-
- font := font_make(fd);
- font_registry[fd] = font;
- return font;
-}
-
--- /dev/null
+
+use package core
+use package opengles
+
+
+Canvas :: struct {
+ framebuffer: GLint;
+ depth_stencil_buffer: GLint;
+ color_texture: GLint;
+}
+
+canvas_make :: () -> Canvas {
+ canvas: Canvas;
+}
+
+canvas_use :: (use canvas: ^Canvas) {
+ if canvas == null {
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ } else {
+ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ }
+}
+
+
--- /dev/null
+
+use package core
+use package core.intrinsics.onyx {__zero_value}
+use package stb_truetype
+use package opengles
+
+#local {
+ font_registry: Map(FontDescriptor, Font);
+ font_vbo: GLint;
+ font_vao: GLint;
+
+ font_shader: Shader;
+ font_color: Color;
+}
+
+fonts_init :: () {
+ map.init(^font_registry);
+
+ font_shader = shader_make("./assets/shaders/font.glsl");
+ shader_use(font_shader);
+ shader_link_window_matrix_block(font_shader);
+
+ glGenVertexArrays(1, ^font_vao);
+ glBindVertexArray(font_vao);
+
+ font_interp_buffer: GLint;
+ glGenBuffers(1, ^font_interp_buffer);
+ glBindBuffer(GL_ARRAY_BUFFER, font_interp_buffer);
+ font_interp_data := f32.[
+ 0.0, 0.0,
+ 1.0, 0.0,
+ 1.0, 1.0,
+ 0.0, 1.0,
+ ];
+ glBufferData(GL_ARRAY_BUFFER, sizeof typeof font_interp_data, ~~^font_interp_data, GL_STATIC_DRAW);
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, false, 2 * sizeof f32, ~~0);
+
+ font_index_buffer: GLint;
+ glGenBuffers(1, ^font_index_buffer);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, font_index_buffer);
+ font_index_data := u8.[ 0, 1, 2, 0, 2, 3 ];
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof typeof font_index_data, ~~^font_index_data, GL_STATIC_DRAW);
+
+ glGenBuffers(1, ^font_vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, font_vbo);
+ glBufferData(GL_ARRAY_BUFFER, 1024 * sizeof stbtt_aligned_quad, null, GL_STREAM_DRAW);
+
+ for 1..5 {
+ glEnableVertexAttribArray(it);
+ glVertexAttribDivisor(it, 1);
+ }
+ glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~ 0);
+ glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~16);
+ glVertexAttribPointer(3, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~8);
+ glVertexAttribPointer(4, 2, GL_FLOAT, false, sizeof stbtt_aligned_quad, ~~24);
+
+ glBindBuffer(GL_ARRAY_BUFFER, -1);
+ glBindVertexArray(-1);
+
+ font_set_color(.{0,0,0});
+}
+
+
+Font :: struct {
+ texture: GLint;
+ texture_width, texture_height: i32;
+ chars: [] stbtt_packedchar;
+ em: f32;
+}
+
+font_make :: (fd: FontDescriptor) -> Font {
+ texture_size :: 256;
+
+ char_data := memory.make_slice(stbtt_packedchar, 96);
+
+ ttf_file := os.get_contents(fd.path);
+ if ttf_file.count == 0 {
+ println("Bad font");
+ return __zero_value(Font);
+ }
+ defer cfree(ttf_file.data);
+
+ pixels := calloc(texture_size * texture_size);
+ defer cfree(pixels);
+
+ ctx: stbtt_pack_context;
+ stbtt_PackBegin(^ctx, pixels, texture_size, texture_size, 0, 1);
+ stbtt_PackFontRange(^ctx, ttf_file.data, 0, ~~fd.size, #char " ", 96, char_data.data);
+ stbtt_PackEnd(^ctx);
+
+ texture: GLint;
+ glGenTextures(1, ^texture);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, texture_size, texture_size, 0, GL_ALPHA, GL_UNSIGNED_BYTE, pixels);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ font := Font.{
+ texture = texture,
+ texture_width = texture_size,
+ texture_height = texture_size,
+ chars = char_data,
+ em = ~~fd.size,
+ };
+
+ font_registry[fd] = font;
+
+ return font;
+}
+
+font_set_color :: (color: Color) {
+ font_color = color;
+}
+
+font_print :: (font: Font, x, y: f32, format: str, va: ..any) {
+ buf: [1024] u8;
+ msg := conv.format_va(buf, format, va);
+ font_draw(font, x, y, msg);
+}
+
+font_draw :: (font: Font, x, y: f32, msg: str) {
+ quads: ^stbtt_aligned_quad = alloc.from_stack(msg.count * sizeof stbtt_aligned_quad);
+ quad_num := 0;
+
+ x_, y_ := x, y;
+
+ for msg {
+ if it == #char "\n" {
+ x_ = x;
+ y_ += font.em + 2;
+ }
+
+ stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
+ ~~(it - #char " "), ^x_, ^y_, ^quads[quad_num], false);
+
+ quad_num += 1;
+ }
+
+ font_render(font, quads[0 .. quad_num]);
+}
+
+font_draw_centered :: (font: Font, x, y, max_width: f32, msg: str) {
+ quads: ^stbtt_aligned_quad = alloc.from_stack(msg.count * sizeof stbtt_aligned_quad);
+ quad_num := 0;
+
+ width := font_get_width(font, msg);
+ x_, y_ := x, y;
+ x_ = (max_width - width) / 2 + x;
+
+ for msg {
+ if it == #char "\n" {
+ x_ = (max_width - width) / 2 + x;
+ y_ += font.em + 2;
+ }
+
+ stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
+ ~~(it - #char " "), ^x_, ^y_, ^quads[quad_num], false);
+
+ quad_num += 1;
+ }
+
+ font_render(font, quads[0 .. quad_num]);
+}
+
+#local font_render :: (font: Font, quads: [] stbtt_aligned_quad) {
+ // If this is being used in conjunction with the immediate
+ // rendering system, make sure the immediate objects are flushed
+ // before trying to render over them.
+ #if #defined(immediate_flush) {
+ immediate_flush();
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, font_vbo);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, quads.count * sizeof stbtt_aligned_quad, quads.data);
+ glBindBuffer(GL_ARRAY_BUFFER, -1);
+
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, font.texture);
+ shader_use(font_shader);
+ shader_set_uniform(font_shader, #cstr "u_texture", 0);
+ shader_set_uniform(font_shader, #cstr "u_color", font_color);
+
+ glDisable(GL_DEPTH_TEST);
+ glBindVertexArray(font_vao);
+ glDrawElementsInstanced(GL_TRIANGLES, 6, GL_UNSIGNED_BYTE, ~~0, quads.count);
+ glBindTexture(GL_TEXTURE_2D, -1);
+ glBindVertexArray(-1);
+}
+
+font_get_width :: (font: Font, msg: str) -> f32 {
+ x_, y_ := 0.0f, 0.0f;
+ width := 0.0f;
+
+ quad: stbtt_aligned_quad;
+ for msg {
+ if it == #char "\n" {
+ width = math.max(width, x_);
+ x_ = 0;
+ y_ += font.em + 2;
+ continue;
+ }
+
+ stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
+ ~~(it - #char " "), ^x_, ^y_, ^quad, false);
+ }
+
+ return math.max(x_, width);
+}
+
+font_get_height :: (font: Font, msg: str) -> f32 {
+ x_, y_ := 0.0f, 0.0f;
+ width := 0.0f;
+
+ quad: stbtt_aligned_quad;
+ for msg {
+ if it == #char "\n" {
+ width = math.max(width, x_);
+ x_ = 0;
+ y_ += font.em + 2;
+ continue;
+ }
+
+ stbtt_GetPackedQuad(font.chars.data, font.texture_width, font.texture_height,
+ ~~(it - #char " "), ^x_, ^y_, ^quad, false);
+ }
+
+ return y_ + font.em + 2;
+}
+
+
+FontDescriptor :: struct {
+ path : str;
+ size : u32;
+}
+
+#match hash.to_u32 (fd: FontDescriptor) => {
+ name_hash := hash.to_u32(fd.path);
+ size_hash := hash.to_u32(fd.size);
+ return name_hash * 13 + size_hash * 17;
+}
+
+#operator == (f1, f2: FontDescriptor) => f1.path == f2.path && f1.size == f2.size;
+
+font_lookup :: (fd := FontDescriptor.{ "assets/calibri.ttf", 12 }) -> Font {
+ if font_registry->has(fd) {
+ return font_registry[fd];
+ }
+
+ font := font_make(fd);
+ font_registry[fd] = font;
+ return font;
+}
+
--- /dev/null
+
+use package core
+use package opengles
+use package glfw3
+
+#local window_width :: camera.window_width
+#local window_height :: camera.window_height
+
+immediate_init :: () {
+ memory.alloc_slice(^vertex_data, Maximum_Vertex_Count);
+ vertex_count = 0;
+
+ imgui_shader = shader_make(Shader_Path);
+ shader_use(imgui_shader);
+ shader_link_window_matrix_block(imgui_shader);
+ shader_set_uniform(imgui_shader, #cstr "u_texture_enabled", 0.0f);
+ shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
+
+ immediate_mesh = mesh_make(vertex_data, .[], GL_DYNAMIC_DRAW);
+ immediate_color = .{0,0,0};
+}
+
+immediate_flush :: () {
+ if vertex_count == 0 do return;
+
+ shader_use(imgui_shader);
+ shader_set_uniform(imgui_shader, #cstr "u_texture_enabled", 1.0f if rendering_type == .Image else 0.0f);
+
+ immediate_mesh.vertex_count = vertex_count;
+ mesh_update_verticies(immediate_mesh, vertex_data);
+
+ mesh_draw(immediate_mesh);
+
+ vertex_count = 0;
+ rendering_type = .Plain;
+}
+
+immediate_clear :: (color: Color) {
+ glClearColor(color.r, color.g, color.b, color.a);
+ glClear(GL_COLOR_BUFFER_BIT);
+}
+
+immediate_set_color :: (color: Color) {
+ immediate_color = color;
+}
+
+immediate_vertex :: (x, y: f32, t_x := 0.0f, t_y := 0.0f) {
+ if vertex_count >= Maximum_Vertex_Count do immediate_flush();
+ set_rendering_type(.Plain);
+
+ vertex_data[vertex_count] = .{ .{x, y}, .{t_x, t_y}, immediate_color };
+}
+
+immediate_triangle :: (x1, x2, x3: Vector2) {
+ if vertex_count + 3 > Maximum_Vertex_Count do immediate_flush();
+ set_rendering_type(.Plain);
+
+ vertex_data[vertex_count + 0] = .{ x1, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 1] = .{ x2, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 2] = .{ x3, .{0,0}, immediate_color };
+ vertex_count += 3;
+}
+
+immediate_rectangle :: (x, y, w, h: f32) {
+ if vertex_count + 6 > Maximum_Vertex_Count do immediate_flush();
+ set_rendering_type(.Plain);
+
+ vertex_data[vertex_count + 0] = .{ .{x, y}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 1] = .{ .{x+w, y}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 3] = .{ .{x, y}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 5] = .{ .{x, y+h}, .{0,0}, immediate_color };
+ vertex_count += 6;
+}
+
+immediate_image :: (image: ^Texture, x, y, w, h: f32) {
+ if vertex_count > 0 do immediate_flush();
+
+ set_rendering_type(.Image);
+ texture_use(image);
+ shader_use(imgui_shader);
+ shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
+
+ vertex_data[vertex_count + 0] = .{ .{x, y}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 1] = .{ .{x+w, y}, .{1,0}, immediate_color };
+ vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{1,1}, immediate_color };
+ vertex_data[vertex_count + 3] = .{ .{x, y}, .{0,0}, immediate_color };
+ vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{1,1}, immediate_color };
+ vertex_data[vertex_count + 5] = .{ .{x, y+h}, .{0,1}, immediate_color };
+ vertex_count += 6;
+}
+
+immediate_subimage :: (image: ^Texture, x, y, w, h: f32, tx, ty, tw, th: f32) {
+ if vertex_count > 0 do immediate_flush();
+
+ set_rendering_type(.Image);
+ texture_use(image);
+ shader_use(imgui_shader);
+ shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
+
+ sx := tx / ~~ image.width;
+ sy := ty / ~~ image.height;
+ sw := tw / ~~ image.width;
+ sh := th / ~~ image.height;
+
+ vertex_data[vertex_count + 0] = .{ .{x, y}, .{sx, sy}, immediate_color };
+ vertex_data[vertex_count + 1] = .{ .{x+w, y}, .{sx+sw, sy}, immediate_color };
+ vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{sx+sw,sy+sh}, immediate_color };
+ vertex_data[vertex_count + 3] = .{ .{x, y}, .{sx, sy}, immediate_color };
+ vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{sx+sw,sy+sh}, immediate_color };
+ vertex_data[vertex_count + 5] = .{ .{x, y+h}, .{sx, sy+sh}, immediate_color };
+ vertex_count += 6;
+}
+
+immediate_ellipse :: () {}
+
+immediate_push_scissor :: (x, y, w, h: f32) {
+ // Assuming that x, y, w, and h are in screen (window) coordinates.
+
+ scissors << .{x, y, w, h};
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(~~x, ~~(window_height - (y + h)), ~~w, ~~h);
+}
+
+immediate_pop_scissor :: () {
+ if scissors.count > 0 {
+ array.pop(^scissors);
+ }
+
+ if scissors.count > 0 {
+ glEnable(GL_SCISSOR_TEST);
+ s := scissors[scissors.count - 1];
+ glScissor(~~s.x, ~~(window_height - (s.y + s.h)), ~~s.w, ~~s.h);
+ } else {
+ glDisable(GL_SCISSOR_TEST);
+ }
+}
+
+Color :: struct {
+ r, g, b : f32;
+ a := 1.0f;
+}
+
+Immediate_Vertex :: struct {
+ pos: Vector2;
+ tex: Vector2;
+ color: Color;
+}
+
+#local {
+ Shader_Path :: "./assets/shaders/imgui.glsl"
+ imgui_shader: Shader;
+
+ Maximum_Vertex_Count :: 1023;
+ vertex_count: i32;
+ vertex_data: [] Immediate_Vertex;
+
+ immediate_color: Color;
+
+ immediate_mesh: ^Mesh(Immediate_Vertex);
+
+ Rendering_Type :: enum {
+ Plain;
+ Image;
+ }
+ rendering_type := Rendering_Type.Plain;
+
+ Scissor :: struct {
+ x, y, w, h: f32;
+ }
+ scissors: [..] Scissor;
+
+ set_rendering_type :: (new_type: typeof rendering_type) {
+ if rendering_type != new_type {
+ immediate_flush();
+ rendering_type = new_type;
+ }
+ }
+}
--- /dev/null
+use package core
+use package opengles
+
+Mesh :: struct (Vertex_Type: type_expr) {
+ handle: GLint;
+ vertex_handle: GLint;
+ index_handle: GLint;
+
+ vertex_count: u32;
+ index_count: u32;
+ primitive: GLuint = GL_TRIANGLES;
+}
+
+mesh_make :: (verticies: [] $T, indicies: [] u32, gl_hint := GL_STATIC_DRAW) -> ^Mesh(T) {
+ mesh := new(Mesh(T));
+ mesh.vertex_count = verticies.count;
+ mesh.index_count = indicies.count;
+
+ glGenVertexArrays(1, ^mesh.handle);
+ glBindVertexArray(mesh.handle);
+
+ glGenBuffers(1, ^mesh.vertex_handle);
+ glBindBuffer(GL_ARRAY_BUFFER, mesh.vertex_handle);
+ glBufferData(GL_ARRAY_BUFFER, sizeof T * verticies.count, verticies.data, gl_hint);
+
+ vertex_info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(T);
+ vertex_attr := 0;
+ for attr: vertex_info.members {
+ defer vertex_attr += 1;
+ glEnableVertexAttribArray(vertex_attr);
+
+ switch attr.type {
+ case Vector2 do glVertexAttribPointer(vertex_attr, 2, GL_FLOAT, false, sizeof T, ~~attr.offset);
+ case Vector3 do glVertexAttribPointer(vertex_attr, 3, GL_FLOAT, false, sizeof T, ~~attr.offset);
+ case u32 do glVertexAttribIPointer(vertex_attr, 1, GL_UNSIGNED_INT, sizeof T, ~~attr.offset);
+ case i32 do glVertexAttribIPointer(vertex_attr, 1, GL_INT, sizeof T, ~~attr.offset);
+
+ // It would be nice to not have to have all the cases here.
+ // Instead allow for an extensible way of defining them at compile time.
+ case Color do glVertexAttribPointer(vertex_attr, 4, GL_FLOAT, false, sizeof T, ~~attr.offset);
+
+ case #default {
+ buf: [256] u8;
+ msg := conv.str_format(buf, "Unknown type for GL vertex attribute, {}.", attr.type);
+ assert(false, msg);
+ }
+ }
+ }
+
+ if indicies.count > 0 {
+ glGenBuffers(1, ^mesh.index_handle);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.index_handle);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof i32 * indicies.count, indicies.data, gl_hint);
+ }
+
+ glBindVertexArray(-1);
+
+ return mesh;
+}
+
+mesh_update_verticies :: (use mesh: ^Mesh, verticies: [] mesh.Vertex_Type) {
+ @TODO // Add bounds checking to arrays here.
+
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_handle);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, verticies.count * sizeof mesh.Vertex_Type, verticies.data);
+ glBindBuffer(GL_ARRAY_BUFFER, -1);
+}
+
+mesh_draw :: (use mesh: ^Mesh) {
+ glBindVertexArray(handle);
+ if index_count > 0 {
+ glDrawElements(primitive, index_count, GL_UNSIGNED_INT, ~~0);
+ } else {
+ glDrawArrays(primitive, 0, vertex_count);
+ }
+ glBindVertexArray(-1);
+}
+
+mesh_free :: (mesh: ^Mesh) {
+ glDeleteBuffers(1, ^mesh.vertex_handle);
+ glDeleteBuffers(1, ^mesh.index_handle);
+ glDeleteVertexArrays(1, ^mesh.handle);
+ cfree(mesh);
+}
+
+
+
--- /dev/null
+
+use package core
+use package opengles
+
+Shader :: struct {
+ vs, fs: GLuint;
+ prog: GLuint;
+}
+
+window_matrix_block_buffer: GLuint;
+world_matrix_block_buffer: GLuint;
+
+shaders_init :: () {
+ glGenBuffers(1, ^window_matrix_block_buffer);
+ glGenBuffers(1, ^world_matrix_block_buffer);
+
+ glBindBuffer(GL_UNIFORM_BUFFER, window_matrix_block_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof f32 * 16, null, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_UNIFORM_BUFFER, world_matrix_block_buffer);
+ glBufferData(GL_UNIFORM_BUFFER, sizeof f32 * (16 + 16), null, GL_DYNAMIC_DRAW);
+
+ glBindBuffer(GL_UNIFORM_BUFFER, -1);
+
+ glBindBufferBase(GL_UNIFORM_BUFFER, WINDOW_MATRIX_BLOCK, window_matrix_block_buffer);
+ glBindBufferBase(GL_UNIFORM_BUFFER, WORLD_MATRIX_BLOCK, world_matrix_block_buffer);
+}
+
+shader_make :: (shader_path: str) -> Shader {
+ shader_source := os.get_contents(shader_path);
+ vs := compile_shader(shader_source, GL_VERTEX_SHADER);
+ fs := compile_shader(shader_source, GL_FRAGMENT_SHADER);
+
+ prog := link_program(vs, fs);
+
+ return Shader.{vs, fs, prog};
+}
+
+shader_use :: (shader: Shader) {
+ glUseProgram(shader.prog);
+}
+
+#local {
+ WINDOW_MATRIX_BLOCK :: 0;
+ WORLD_MATRIX_BLOCK :: 1;
+}
+
+shader_link_window_matrix_block :: (use shader: Shader) {
+ matrix_block_index := glGetUniformBlockIndex(prog, #cstr "u_window_matrix_block");
+ glUniformBlockBinding(prog, matrix_block_index, WINDOW_MATRIX_BLOCK);
+}
+
+shader_link_world_matrix_block :: (use shader: Shader) {
+ matrix_block_index := glGetUniformBlockIndex(prog, #cstr "u_world_matrix_block");
+ glUniformBlockBinding(prog, matrix_block_index, WORLD_MATRIX_BLOCK);
+}
+
+shader_set_uniform :: (shader: Shader, uniform: cstr, value: $T) {
+ glUseProgram(shader.prog);
+ location := glGetUniformLocation(shader.prog, uniform);
+
+ set_uniform_internal(location, value);
+
+ set_uniform_internal :: #match {
+ macro (location: GLint, value: u32) do glUniform1i(location, value); ,
+ macro (location: GLint, value: f32) do glUniform1f(location, value); ,
+ macro (location: GLint, value: Vector3) do glUniform3f(location, value.x, value.y, value.z); ,
+ macro (location: GLint, value: Color) do glUniform4f(location, value.r, value.g, value.b, value.a); ,
+
+ macro (location: GLint, value: $T) {
+ buffer: [1024] u8;
+ assert(false, conv.format(buffer, "Bad shader_set_uniform case: {}", T));
+ }
+ }
+}
+
+update_view_matrix :: () {
+ matrix : [16] f32;
+ top := 0.0f;
+ left := 0.0f;
+ right := cast(f32) window_width;
+ bottom := cast(f32) window_height;
+ far := 10.0f;
+ near := 0f;
+
+ matrix[0] = 2 / (right - left);
+ matrix[5] = 2 / (top - bottom);
+ matrix[10] = -2 / (far - near);
+ matrix[12] = -(right + left) / (right - left);
+ matrix[13] = -(top + bottom) / (top - bottom);
+ matrix[14] = -(far + near) / (far - near);
+ matrix[15] = 1;
+
+ glBindBuffer(GL_UNIFORM_BUFFER, window_matrix_block_buffer);
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof typeof matrix, ^matrix);
+ glBindBuffer(GL_UNIFORM_BUFFER, -1);
+}
+
+update_world_matrix :: () {
+ world_mat: [16] f32;
+ world_mat[0] = 1;
+ world_mat[5] = 1;
+ world_mat[10] = 1;
+ world_mat[15] = 1;
+
+ glBindBuffer(GL_UNIFORM_BUFFER, world_matrix_block_buffer);
+ glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof typeof world_mat, ^world_mat);
+ glBindBuffer(GL_UNIFORM_BUFFER, -1);
+}
+
+update_model_matrix :: (v: Vector2) {
+ model_mat: [16] f32;
+ model_mat[0] = 1;
+ model_mat[5] = 1;
+ model_mat[10] = 1;
+ model_mat[12] = v.x;
+ model_mat[13] = v.y;
+ model_mat[14] = 0;
+ model_mat[15] = 1;
+
+ glBindBuffer(GL_UNIFORM_BUFFER, world_matrix_block_buffer);
+ glBufferSubData(GL_UNIFORM_BUFFER, 16 * sizeof f32, sizeof typeof model_mat, ^model_mat);
+ glBindBuffer(GL_UNIFORM_BUFFER, -1);
+}
+
+
+#local {
+ compile_shader :: (source: str, type: GLenum) -> GLint {
+ shader := glCreateShader(type);
+
+ #persist VERTEX_HEADER := """
+#version 300 es
+#define VERTEX_SHADER 1
+#define COMM out
+ """;
+
+ #persist FRAGMENT_HEADER := """
+#version 300 es
+#define FRAGMENT_SHADER 1
+#define COMM in
+ """;
+
+ header := VERTEX_HEADER if type == GL_VERTEX_SHADER else FRAGMENT_HEADER;
+ sources : [] ^u8 = .[ header.data, source.data ];
+ source_lens : [] i32 = .[ header.count, source.count ];
+
+ glShaderSource(shader, 2, sources.data, source_lens.data);
+ glCompileShader(shader);
+
+ success: GLint;
+ if glGetShaderiv(shader, GL_COMPILE_STATUS, ^success); success == GL_FALSE {
+ buf_data: [2048] u8;
+ buf := str.{ ~~buf_data, 0 };
+ glGetShaderInfoLog(shader, 2048, ^buf.count, buf.data);
+ println(buf);
+ }
+
+ return shader;
+ }
+
+ link_program :: (vertex_shader, fragment_shader: GLint) -> GLuint {
+ prog := glCreateProgram();
+ glAttachShader(prog, vertex_shader);
+ glAttachShader(prog, fragment_shader);
+ glLinkProgram(prog);
+
+ success: GLint;
+ if glGetProgramiv(prog, GL_LINK_STATUS, ^success); success == GL_FALSE {
+ buf_data: [1024] u8;
+ buf := str.{ ~~buf_data, 0 };
+ glGetProgramInfoLog(prog, 1024, ^buf.count, buf.data);
+ println(buf);
+ }
+
+ return prog;
+ }
+}
+
--- /dev/null
+
+use package core
+use package opengles
+use package stb_image
+
+Texture :: struct {
+ texture: GLint;
+ width, height, channels: i32;
+ filename: str;
+}
+
+texture_make :: (path: cstr) -> Texture {
+ tex: Texture;
+ tex.filename = path |> string.from_cstr();
+ pixels := stbi_load(path, ^tex.width, ^tex.height, ^tex.channels, 4);
+ assert(pixels != null, "Failed to load texture.");
+ defer stbi_image_free(pixels);
+
+ glGenTextures(1, ^tex.texture);
+ glBindTexture(GL_TEXTURE_2D, tex.texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
+
+ // Are these sensible defaults?
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ return tex;
+}
+
+texture_use :: (use tex: ^Texture, texture_binding := 0) {
+ glActiveTexture(GL_TEXTURE0 + texture_binding);
+ glBindTexture(GL_TEXTURE_2D, texture);
+}
+
--- /dev/null
+//
+// Very simple immediate mode UI
+//
+
+use package core
+use package opengles
+use package glfw3
+
+UI_Id :: u32
+
+ui_end_frame :: () {
+ hot_item_depth_needed = hot_item_depth;
+ if !hot_item_was_set do set_hot_item(0);
+ hot_item_depth = 0;
+ hot_item_was_set = false;
+
+ for^ animation_states.entries {
+ if !it.value.accessed_this_frame || (it.value.click_time == 0 && it.value.hover_time == 0) {
+ map.delete(^animation_states, it.key);
+ }
+ }
+
+ for^ animation_states.entries {
+ it.value.accessed_this_frame = false;
+ }
+}
+
+//
+// Buttons
+//
+Button_Theme :: struct {
+ use text_theme := Text_Theme.{};
+ use animation_theme := Animation_Theme.{};
+
+ background_color := Color.{ 0.1, 0.1, 0.1 };
+ hover_color := Color.{ 0.3, 0.3, 0.3 };
+ click_color := Color.{ 0.5, 0.5, 0.7 };
+
+ border_color := Color.{0.2, 0.2, 0.2};
+ border_width := 2.0f;
+
+ active := false;
+}
+
+#local default_button_theme := Button_Theme.{};
+
+draw_button :: (use r: Rect, text: str, theme := ^default_button_theme, site := #callsite, increment := 0) -> bool {
+ result := false;
+
+ hash := get_site_hash(site, increment);
+ animation_state := get_animation(hash);
+ mx, my := mouse_get_position();
+
+ contains := Rect.contains(r, .{~~mx, ~~my});
+
+ if is_active_item(hash) {
+ if is_button_just_up(GLFW_MOUSE_BUTTON_LEFT) {
+ if is_hot_item(hash) && contains {
+ result = true;
+ animation_state.click_time = 1.0f;
+ }
+
+ set_active_item(0);
+ }
+ } elseif is_hot_item(hash) {
+ if is_button_down(GLFW_MOUSE_BUTTON_LEFT) {
+ set_active_item(hash);
+ }
+ }
+
+ if contains {
+ set_hot_item(hash);
+ }
+
+ if is_hot_item(hash) || theme.active {
+ move_towards(^animation_state.hover_time, 1.0f, theme.hover_speed);
+ } else {
+ move_towards(^animation_state.hover_time, 0.0f, theme.hover_speed);
+ }
+
+ border_width := theme.border_width;
+
+ immediate_set_color(theme.border_color);
+ immediate_rectangle(x, y, w, h);
+
+ surface_color := color_lerp(animation_state.hover_time, theme.background_color, theme.hover_color);
+ surface_color = color_lerp(animation_state.click_time, surface_color, theme.click_color);
+ immediate_set_color(surface_color);
+ immediate_rectangle(x + border_width, y + border_width, w - border_width * 2, h - border_width * 2);
+
+ font := font_lookup(.{theme.font_name, theme.font_size});
+ font_height := font_get_height(font, text);
+ font_set_color(theme.text_color);
+ font_draw_centered(font, x, y + (h - font_height) / 2 + font.em - 2, w, text);
+
+ move_towards(^animation_state.click_time, 0.0f, theme.click_decay_speed);
+
+ return result;
+}
+
+
+//
+// Textbox
+//
+Textbox_Theme :: struct {
+ use text_theme := Text_Theme.{
+ text_color = .{ 0, 0, 0 }
+ };
+
+ use animation_theme := Animation_Theme.{};
+
+ background_color := Color.{ 0.8, 0.8, 0.8 };
+ hover_color := Color.{ 1.0, 1.0, 1.0 };
+ click_color := Color.{ 0.5, 0.5, 0.7 };
+
+ border_color := Color.{ 0.2, 0.2, 0.2 };
+ border_width := 6.0f; @InPixels
+
+ cursor_color := Color.{ 0.5, 0.5, 0.5 };
+ cursor_width := 4.0f; @InPixels
+ cursor_blink_speed := 0.04f; // Bigger is faster
+
+ placeholder_text_color := Color.{ 0.5, 0.5, 0.5 };
+}
+
+#local {
+ default_textbox_theme := Textbox_Theme.{};
+
+ Textbox_Editing_State :: struct {
+ hash: UI_Id = 0;
+
+ cursor_position: i32 = 0;
+ cursor_animation := 0.0f;
+ cursor_animation_speed := 0.02f;
+ }
+
+ textbox_editing_state := Textbox_Editing_State.{};
+}
+
+draw_textbox :: (use r: Rect, text_buffer: ^[..] u8, placeholder := null_str, theme := ^default_textbox_theme, site := #callsite, increment := 0) -> bool {
+ result := false;
+
+ hash := get_site_hash(site, increment);
+ animation_state := get_animation(hash);
+ mx, my := mouse_get_position();
+
+ border_width := theme.border_width;
+ text_color := theme.text_color;
+ text := str.{text_buffer.data, text_buffer.count};
+ if text.count == 0 && placeholder.count > 0 {
+ text = placeholder;
+ text_color = theme.placeholder_text_color;
+ }
+
+ font := font_lookup(.{theme.font_name, theme.font_size});
+ text_width := font_get_width(font, text);
+ text_height := font_get_height(font, text);
+
+ text_x := x + border_width;
+ text_y := y + font.em + (h - text_height) / 2;
+
+ contains := Rect.contains(r, .{~~mx, ~~my});
+
+ if is_hot_item(hash) && !is_active_item(hash) {
+ if is_button_down(GLFW_MOUSE_BUTTON_LEFT) && contains {
+ set_active_item(hash);
+ textbox_editing_state.hash = hash;
+ textbox_editing_state.cursor_animation_speed = theme.cursor_blink_speed;
+ }
+ }
+
+ if is_active_item(hash) {
+ if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) && !contains {
+ set_active_item(0);
+ textbox_editing_state.hash = 0;
+ textbox_editing_state.cursor_position = 0;
+ }
+ }
+
+ if contains {
+ set_hot_item(hash);
+ }
+
+ if textbox_editing_state.hash == hash {
+ move_towards(^textbox_editing_state.cursor_animation, 0.0f, textbox_editing_state.cursor_animation_speed);
+ if textbox_editing_state.cursor_animation <= 0.0f do textbox_editing_state.cursor_animation = 1.0f;
+
+ if is_button_down(GLFW_MOUSE_BUTTON_LEFT) && contains {
+ textbox_editing_state.cursor_animation = 1.0f;
+ // textbox_editing_state.cursor_position = get_cursor_position(text_buffer, text_x, text_y, theme.font_size, ~~mx, ~~my);
+ }
+
+ keys := input_get_keys_this_frame();
+ if keys.count > 0 {
+ for key: keys {
+ switch key {
+ case GLFW_KEY_LEFT do textbox_editing_state.cursor_position -= 1;
+ case GLFW_KEY_RIGHT do textbox_editing_state.cursor_position += 1;
+ case GLFW_KEY_END do textbox_editing_state.cursor_position = text_buffer.count;
+ case GLFW_KEY_HOME do textbox_editing_state.cursor_position = 0;
+
+ case GLFW_KEY_BACKSPACE {
+ array.pop(text_buffer);
+ textbox_editing_state.cursor_position = math.max(~~0, textbox_editing_state.cursor_position - 1);
+ }
+
+ case GLFW_KEY_DELETE {
+ array.delete(text_buffer, textbox_editing_state.cursor_position);
+ }
+
+ case #default {
+ if key >= #char " " && key <= 128 {
+ array.push(text_buffer, ~~key);
+ textbox_editing_state.cursor_position += 1;
+ }
+ }
+ }
+ }
+
+ textbox_editing_state.cursor_position = math.clamp(textbox_editing_state.cursor_position, 0, text_buffer.count);
+ textbox_editing_state.cursor_animation = 1.0f;
+
+ text = str.{text_buffer.data, text_buffer.count};
+ }
+ }
+
+ if is_hot_item(hash) {
+ move_towards(^animation_state.hover_time, 1.0f, theme.hover_speed);
+ } else {
+ move_towards(^animation_state.hover_time, 0.0f, theme.hover_speed);
+ }
+
+ immediate_push_scissor(x, y, w, h);
+ immediate_set_color(theme.border_color);
+ immediate_rectangle(x, y, w, h);
+
+ surface_color := color_lerp(animation_state.hover_time, theme.background_color, theme.hover_color);
+ surface_color = color_lerp(animation_state.click_time, surface_color, theme.click_color);
+ immediate_set_color(surface_color);
+ immediate_rectangle(x + border_width, y + border_width, w - border_width * 2, h - border_width * 2);
+
+ // Draw the cursor on textboxes
+
+ font_set_color(theme.text_color);
+ font_draw(font, text_x, text_y, text); // This is technically a frame late for updating the text?
+
+ move_towards(^animation_state.click_time, 0.0f, theme.click_decay_speed);
+
+ immediate_pop_scissor();
+ return result;
+}
+
+
+
+
+//
+// Checkboxes
+//
+Checkbox_Theme :: struct {
+ use text_theme := Text_Theme.{};
+ use animation_theme := Animation_Theme.{};
+
+ box_color := Color.{ 0.2, 0.2, 0.2 };
+ box_border_width := 4.0f; @InPixels
+ box_size := 20.0f; @InPixels
+
+ checked_color := Color.{ 1, 0, 0 };
+ checked_hover_color := Color.{ 1, 0.6, 0.6 };
+
+ background_color := Color.{ 0.05, 0.05, 0.05 }; // Background of the checkbox button.
+ hover_color := Color.{ 0.3, 0.3, 0.3 };
+ click_color := Color.{ 0.5, 0.5, 0.7 };
+}
+
+#local default_checkbox_theme := Checkbox_Theme.{};
+
+draw_checkbox :: (use r: Rect, value: ^bool, text: str, theme := ^default_checkbox_theme, site := #callsite, increment := 0) -> bool {
+ result := false;
+
+ hash := get_site_hash(site, increment);
+ animation_state := get_animation(hash);
+ mx, my := mouse_get_position();
+
+ contains := Rect.contains(r, .{~~mx, ~~my});
+
+ if is_active_item(hash) {
+ if is_button_just_up(GLFW_MOUSE_BUTTON_LEFT) {
+ if is_hot_item(hash) && contains {
+ result = true;
+ *value = !*value;
+ animation_state.click_time = 1.0f;
+ }
+
+ set_active_item(0);
+ }
+ } elseif is_hot_item(hash) {
+ if is_button_down(GLFW_MOUSE_BUTTON_LEFT) {
+ set_active_item(hash);
+ }
+ }
+
+ if contains {
+ set_hot_item(hash);
+ }
+
+ if is_hot_item(hash) {
+ move_towards(^animation_state.hover_time, 1.0f, theme.hover_speed);
+ } else {
+ move_towards(^animation_state.hover_time, 0.0f, theme.hover_speed);
+ }
+
+ box_border_width := theme.box_border_width;
+ box_size := theme.box_size;
+
+ immediate_set_color(theme.box_color);
+ immediate_rectangle(x + 4, y + (h - box_size) / 2, box_size, box_size);
+
+ surface_color : Color;
+ if *value {
+ surface_color = theme.checked_color;
+ surface_color = color_lerp(animation_state.hover_time, surface_color, theme.checked_hover_color);
+
+ } else {
+ surface_color = theme.background_color;
+ surface_color = color_lerp(animation_state.hover_time, surface_color, theme.hover_color);
+ }
+
+ surface_color = color_lerp(animation_state.click_time, surface_color, theme.click_color);
+
+ immediate_set_color(surface_color);
+ immediate_rectangle(x + 4 + box_border_width, y + (h - box_size) / 2 + box_border_width, box_size - box_border_width * 2, box_size - box_border_width * 2);
+
+ font := font_lookup(.{theme.font_name, theme.font_size});
+ text_width := font_get_width(font, text);
+ text_height := font_get_height(font, text);
+
+ font_set_color(theme.text_color);
+ font_draw(font, x + box_size + 4 * 2, y + font.em + (h - text_height) / 2, text);
+
+ move_towards(^animation_state.click_time, 0.0f, theme.click_decay_speed);
+
+ return result;
+}
+
+#if !#defined(Rect) {
+ Rect :: struct {
+ x, y, w, h: f32;
+
+ intersects :: (r1, r2: Rect) -> bool {
+ return r1.x <= r2.x + r2.w
+ && r1.x + r1.w >= r2.x
+ && r1.y <= r2.y + r2.h
+ && r1.y + r1.h >= r2.y;
+ }
+
+ contains :: (r: Rect, p: Vector2) -> bool {
+ return r.x <= p.x && r.x + r.w >= p.x
+ && r.y <= p.y && r.y + r.h >= p.y;
+ }
+ }
+}
+
+#if !#defined(move_towards) {
+ move_towards :: (v: ^$T, target: T, diff: T) {
+ if math.abs(target - *v) <= diff {
+ *v = target;
+ return;
+ }
+
+ if *v < target do *v += diff;
+ if *v > target do *v -= diff;
+ }
+}
+
+
+#local {
+ hot_item : UI_Id = 0
+ active_item : UI_Id = 0
+ hot_item_was_set := false
+
+ hot_item_depth := 0;
+ hot_item_depth_needed := 0;
+
+ set_active_item :: (id: UI_Id) -> bool {
+ active_item = id;
+ return true;
+ }
+
+ set_hot_item :: (id: UI_Id, force := false) -> bool {
+ if active_item != 0 do return false;
+
+ if force {
+ hot_item_was_set = true;
+ hot_item = id;
+ return true;
+ }
+
+ hot_item_depth += 1;
+ if hot_item_depth >= hot_item_depth_needed {
+ hot_item_was_set = true;
+ hot_item = id;
+ return true;
+ }
+
+ return false;
+ }
+
+ is_active_item :: (id: UI_Id) -> bool {
+ return active_item == id;
+ }
+
+ is_hot_item :: (id: UI_Id) -> bool {
+ return hot_item == id;
+ }
+
+ Text_Theme :: struct {
+ text_color := Color.{1, 1, 1};
+ font_name := "./assets/fonts/calibri.ttf";
+ font_size := 18;
+ }
+
+ Animation_Theme :: struct {
+ hover_speed := 0.1f;
+ click_decay_speed := 0.08f;
+ }
+
+ Animation_State :: struct {
+ hover_time := 0.0f;
+ click_time := 0.0f;
+
+ accessed_this_frame := false;
+ }
+
+ animation_states : Map(UI_Id, Animation_State);
+
+ get_animation :: (id: UI_Id) -> ^Animation_State {
+ retval := map.get_ptr(^animation_states, id);
+ if retval == null {
+ animation_states[id] = .{};
+ retval = ^animation_states[id];
+ }
+
+ retval.accessed_this_frame = true;
+ return retval;
+ }
+
+ has_active_animation :: () -> bool {
+ for^ animation_states.entries {
+ if it.value.hover_time != 0.0f || it.value.hover_time != 0.0f do return true;
+ if it.value.click_time != 0.0f || it.value.click_time != 0.0f do return true;
+ }
+
+ return false;
+ }
+
+ get_site_hash :: macro (site: CallSite, increment := 0) -> UI_Id {
+ hash :: package core.hash
+ file_hash := hash.to_u32(site.file);
+ line_hash := hash.to_u32(site.line);
+ column_hash := hash.to_u32(site.column);
+
+ return ~~ (file_hash * 0x472839 + line_hash * 0x6849210 + column_hash * 0x1248382 + increment);
+ }
+
+ color_lerp :: macro (t: f32, c1, c2: Color) => Color.{
+ r = c1.r * (1 - t) + c2.r * t,
+ g = c1.g * (1 - t) + c2.g * t,
+ b = c1.b * (1 - t) + c2.b * t,
+ a = c1.a * (1 - t) + c2.a * t,
+ };
+}
+++ /dev/null
-
-use package core
-use package opengles
-use package glfw3
-
-immediate_init :: () {
- memory.alloc_slice(^vertex_data, Maximum_Vertex_Count);
- vertex_count = 0;
-
- imgui_shader = shader_make(Shader_Path);
- shader_use(imgui_shader);
- shader_link_window_matrix_block(imgui_shader);
- shader_set_uniform(imgui_shader, #cstr "u_texture_enabled", 0.0f);
- shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
-
- immediate_mesh = mesh_make(vertex_data, .[], GL_DYNAMIC_DRAW);
- immediate_color = .{0,0,0};
-}
-
-immediate_flush :: () {
- if vertex_count == 0 do return;
-
- shader_use(imgui_shader);
- shader_set_uniform(imgui_shader, #cstr "u_texture_enabled", 1.0f if rendering_type == .Image else 0.0f);
-
- immediate_mesh.vertex_count = vertex_count;
- mesh_update_verticies(immediate_mesh, vertex_data);
-
- mesh_draw(immediate_mesh);
-
- vertex_count = 0;
- rendering_type = .Plain;
-}
-
-immediate_clear :: (color: Color) {
- glClearColor(color.r, color.g, color.b, color.a);
- glClear(GL_COLOR_BUFFER_BIT);
-}
-
-immediate_set_color :: (color: Color) {
- immediate_color = color;
-}
-
-immediate_vertex :: (x, y: f32, t_x := 0.0f, t_y := 0.0f) {
- if vertex_count >= Maximum_Vertex_Count do immediate_flush();
- set_rendering_type(.Plain);
-
- vertex_data[vertex_count] = .{ .{x, y}, .{t_x, t_y}, immediate_color };
-}
-
-immediate_triangle :: (x1, x2, x3: Vector2) {
- if vertex_count + 3 > Maximum_Vertex_Count do immediate_flush();
- set_rendering_type(.Plain);
-
- vertex_data[vertex_count + 0] = .{ x1, .{0,0}, immediate_color };
- vertex_data[vertex_count + 1] = .{ x2, .{0,0}, immediate_color };
- vertex_data[vertex_count + 2] = .{ x3, .{0,0}, immediate_color };
- vertex_count += 3;
-}
-
-immediate_rectangle :: (x, y, w, h: f32) {
- if vertex_count + 6 > Maximum_Vertex_Count do immediate_flush();
- set_rendering_type(.Plain);
-
- vertex_data[vertex_count + 0] = .{ .{x, y}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 1] = .{ .{x+w, y}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 3] = .{ .{x, y}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 5] = .{ .{x, y+h}, .{0,0}, immediate_color };
- vertex_count += 6;
-}
-
-immediate_image :: (image: ^Texture, x, y, w, h: f32) {
- if vertex_count > 0 do immediate_flush();
-
- set_rendering_type(.Image);
- texture_use(image);
- shader_use(imgui_shader);
- shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
-
- vertex_data[vertex_count + 0] = .{ .{x, y}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 1] = .{ .{x+w, y}, .{1,0}, immediate_color };
- vertex_data[vertex_count + 2] = .{ .{x+w, y+h}, .{1,1}, immediate_color };
- vertex_data[vertex_count + 3] = .{ .{x, y}, .{0,0}, immediate_color };
- vertex_data[vertex_count + 4] = .{ .{x+w, y+h}, .{1,1}, immediate_color };
- vertex_data[vertex_count + 5] = .{ .{x, y+h}, .{0,1}, immediate_color };
- vertex_count += 6;
-}
-
-immediate_ellipse :: () {}
-
-Color :: struct {
- r, g, b : f32;
- a := 1.0f;
-}
-
-Immediate_Vertex :: struct {
- pos: Vector2;
- tex: Vector2;
- color: Color;
-}
-
-#local {
- Shader_Path :: "./assets/shaders/imgui.glsl"
- imgui_shader: Shader;
-
- Maximum_Vertex_Count :: 1023;
- vertex_count: i32;
- vertex_data: [] Immediate_Vertex;
-
- immediate_color: Color;
-
- immediate_mesh: ^Mesh(Immediate_Vertex);
-
- Rendering_Type :: enum {
- Plain;
- Image;
- }
- rendering_type := Rendering_Type.Plain;
-
- set_rendering_type :: (new_type: typeof rendering_type) {
- if rendering_type != new_type {
- immediate_flush();
- rendering_type = new_type;
- }
- }
-}
use package glfw3
#local {
- keys_this_frame: [..] u32
- keys_last_frame: [..] u32
+ keys_this_frame : [..] u32 // Keys currently being pressed this frame
+ keys_pulse_frame : [..] u32 // Keys pressed during this frame, only set once per keypress
+ keys_last_frame : [..] u32 // Keys being pressed in the last frame
- buttons_this_frame: [8] bool
- buttons_last_frame: [8] bool
+ buttons_this_frame: [8] bool // Mouse buttons being pressed this frame
+ buttons_last_frame: [8] bool // Mouse buttons being pressed last frame
}
input_update :: () {
glfwGetCursorPos(window, ^mouse_x, ^mouse_y);
+
+ array.clear(^keys_pulse_frame);
+ for keys_this_frame {
+ if !array.contains(keys_last_frame, it) {
+ keys_pulse_frame << it;
+ }
+ }
}
input_post_update :: () {
+ array.clear(^keys_pulse_frame);
array.clear(^keys_last_frame);
for keys_this_frame do keys_last_frame << it;
last_mouse_y = mouse_y;
}
-handle_key_event :: (key, scancode, action, mod: u32) {
- if action == GLFW_PRESS {
- keys_this_frame << key;
- }
-
- if action == GLFW_RELEASE {
- array.remove(^keys_this_frame, key);
- }
+input_get_keys_this_frame :: () -> [] u32 {
+ return keys_pulse_frame;
}
is_key_down :: (key) => array.contains(keys_this_frame, key);
is_key_just_down :: (key) => array.contains(keys_this_frame, key) && !array.contains(keys_last_frame, key);
is_key_just_up :: (key) => !array.contains(keys_this_frame, key) && array.contains(keys_last_frame, key);
-handle_button_event :: (button, action, mod: u32) {
- if action == GLFW_PRESS {
- buttons_this_frame[button] = true;
- }
-
- if action == GLFW_RELEASE {
- buttons_this_frame[button] = false;
- }
-}
-
is_button_down :: (button) => buttons_this_frame[button];
is_button_just_down :: (button) => buttons_this_frame[button] && !buttons_last_frame[button];
is_button_just_up :: (button) => !buttons_this_frame[button] && buttons_last_frame[button];
mouse_get_delta :: () -> (f64, f64) {
return mouse_x - last_mouse_x, mouse_y - last_mouse_y;
}
+
+mouse_get_delta_vector :: () -> Vector2 {
+ dmx, dmy := mouse_get_delta();
+ return .{ ~~dmx, ~~dmy };
+}
+
+mouse_get_position :: () -> (f64, f64) {
+ return mouse_x, mouse_y;
+}
+
+mouse_get_position_vector :: () -> Vector2 {
+ return .{ ~~mouse_x, ~~mouse_y };
+}
+
+
+input_bind_glfw_events :: (window: GLFWwindow_p) {
+ glfwSetKeyCallback(window, INPUT_KEY_EVENT);
+ glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT);
+}
+
+#local {
+ INPUT_BUTTON_EVENT :: "__input_button_event"
+ INPUT_KEY_EVENT :: "__input_key_event"
+}
+
+#export INPUT_BUTTON_EVENT (window: GLFWwindow_p, button, action, mod: u32) {
+ if action == GLFW_PRESS {
+ buttons_this_frame[button] = true;
+ }
+
+ if action == GLFW_RELEASE {
+ buttons_this_frame[button] = false;
+ }
+}
+
+#export INPUT_KEY_EVENT (window: GLFWwindow_p, key, scancode, action, mod: u32) {
+ if action == GLFW_PRESS {
+ keys_this_frame << key;
+ }
+
+ if action == GLFW_RELEASE {
+ array.remove(^keys_this_frame, key);
+ }
+}
update_window_matrix();
}
-#export "on_key" (window: GLFWwindow_p, key, scancode, action, mod: u32) {
- if key == GLFW_KEY_ESCAPE && action == GLFW_PRESS {
- if cursor_grabbed do toggle_cursor_grabbed();
- else do glfwSetWindowShouldClose(window, true);
- }
-
- handle_key_event(key, scancode, action, mod);
-}
-
-#export "on_mouse_button" (window: GLFWwindow_p, button, action, mod: u32) {
- handle_button_event(button, action, mod);
-}
-
cursor_grabbed := false;
toggle_cursor_grabbed :: () {
cursor_grabbed = !cursor_grabbed;
setup_opengl :: () {
glInit(glfwGetLoadProcAddress());
+ input_bind_glfw_events(window);
+
glEnable(GL_TEXTURE);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
input_update();
defer input_post_update();
+ if is_key_just_down(GLFW_KEY_ESCAPE) {
+ if cursor_grabbed {
+ toggle_cursor_grabbed();
+ } else {
+ glfwSetWindowShouldClose(window, true);
+ }
+ }
+
if !cursor_grabbed {
if is_button_just_down(GLFW_MOUSE_BUTTON_LEFT) {
toggle_cursor_grabbed();
if debug_screen {
immediate_set_color(.{1, 0, 1, 0.5});
immediate_rectangle(0, 0, 528, 220);
- immediate_flush();
player_chunk := world_position_to_chunk(world, player.body.pos);
font_print(font, 0, 32, "FPS: {}", game_fps);
+++ /dev/null
-use package core
-use package opengles
-
-Mesh :: struct (Vertex_Type: type_expr) {
- handle: GLint;
- vertex_handle: GLint;
- index_handle: GLint;
-
- vertex_count: u32;
- index_count: u32;
- primitive: GLuint = GL_TRIANGLES;
-}
-
-mesh_make :: (verticies: [] $T, indicies: [] u32, gl_hint := GL_STATIC_DRAW) -> ^Mesh(T) {
- mesh := new(Mesh(T));
- mesh.vertex_count = verticies.count;
- mesh.index_count = indicies.count;
-
- glGenVertexArrays(1, ^mesh.handle);
- glBindVertexArray(mesh.handle);
-
- glGenBuffers(1, ^mesh.vertex_handle);
- glBindBuffer(GL_ARRAY_BUFFER, mesh.vertex_handle);
- glBufferData(GL_ARRAY_BUFFER, sizeof T * verticies.count, verticies.data, gl_hint);
-
- vertex_info := cast(^type_info.Type_Info_Struct) type_info.get_type_info(T);
- vertex_attr := 0;
- for attr: vertex_info.members {
- defer vertex_attr += 1;
- glEnableVertexAttribArray(vertex_attr);
-
- switch attr.type {
- case Vector2 do glVertexAttribPointer(vertex_attr, 2, GL_FLOAT, false, sizeof T, ~~attr.offset);
- case Vector3 do glVertexAttribPointer(vertex_attr, 3, GL_FLOAT, false, sizeof T, ~~attr.offset);
- case u32 do glVertexAttribIPointer(vertex_attr, 1, GL_UNSIGNED_INT, sizeof T, ~~attr.offset);
- case i32 do glVertexAttribIPointer(vertex_attr, 1, GL_INT, sizeof T, ~~attr.offset);
-
- // It would be nice to not have to have all the cases here.
- // Instead allow for an extensible way of defining them at compile time.
- case Color do glVertexAttribPointer(vertex_attr, 4, GL_FLOAT, false, sizeof T, ~~attr.offset);
-
- case #default {
- buf: [256] u8;
- msg := conv.str_format(buf, "Unknown type for GL vertex attribute, {}.", attr.type);
- assert(false, msg);
- }
- }
- }
-
- if indicies.count > 0 {
- glGenBuffers(1, ^mesh.index_handle);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.index_handle);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof i32 * indicies.count, indicies.data, gl_hint);
- }
-
- glBindVertexArray(-1);
-
- return mesh;
-}
-
-mesh_update_verticies :: (use mesh: ^Mesh, verticies: [] mesh.Vertex_Type) {
- @TODO // Add bounds checking to arrays here.
-
- glBindBuffer(GL_ARRAY_BUFFER, vertex_handle);
- glBufferSubData(GL_ARRAY_BUFFER, 0, verticies.count * sizeof mesh.Vertex_Type, verticies.data);
- glBindBuffer(GL_ARRAY_BUFFER, -1);
-}
-
-mesh_draw :: (use mesh: ^Mesh) {
- glBindVertexArray(handle);
- if index_count > 0 {
- glDrawElements(primitive, index_count, GL_UNSIGNED_INT, ~~0);
- } else {
- glDrawArrays(primitive, 0, vertex_count);
- }
- glBindVertexArray(-1);
-}
-
-mesh_free :: (mesh: ^Mesh) {
- glDeleteBuffers(1, ^mesh.vertex_handle);
- glDeleteBuffers(1, ^mesh.index_handle);
- glDeleteVertexArrays(1, ^mesh.handle);
- cfree(mesh);
-}
-
-
-
+++ /dev/null
-
-use package core
-use package opengles
-use package stb_image
-
-Texture :: struct {
- texture: GLint;
- width, height, channels: i32;
- filename: str;
-}
-
-texture_make :: (path: cstr) -> Texture {
- tex: Texture;
- tex.filename = path |> string.from_cstr();
- pixels := stbi_load(path, ^tex.width, ^tex.height, ^tex.channels, 4);
- assert(pixels != null, "Failed to load texture.");
- defer stbi_image_free(pixels);
-
- glGenTextures(1, ^tex.texture);
- glBindTexture(GL_TEXTURE_2D, tex.texture);
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
-
- // Are these sensible defaults?
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-
- glBindTexture(GL_TEXTURE_2D, 0);
-
- return tex;
-}
-
-texture_use :: (use tex: ^Texture, texture_binding := 0) {
- glActiveTexture(GL_TEXTURE0 + texture_binding);
- glBindTexture(GL_TEXTURE_2D, texture);
-}
-