refactoring code; pulling new code from other project
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 14 Feb 2022 03:14:06 +0000 (21:14 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 14 Feb 2022 03:14:06 +0000 (21:14 -0600)
16 files changed:
run_tree/assets/shaders/world_fragment.glsl [deleted file]
run_tree/assets/shaders/world_vertex.glsl [deleted file]
src/build.onyx
src/font.onyx [deleted file]
src/gfx/canvas.onyx [new file with mode: 0644]
src/gfx/font.onyx [new file with mode: 0644]
src/gfx/immediate.onyx [new file with mode: 0644]
src/gfx/mesh.onyx [new file with mode: 0644]
src/gfx/shader.onyx [new file with mode: 0644]
src/gfx/texture.onyx [new file with mode: 0644]
src/gfx/ui.onyx [new file with mode: 0644]
src/immediate.onyx [deleted file]
src/input.onyx
src/main.onyx
src/mesh.onyx [deleted file]
src/texture.onyx [deleted file]

diff --git a/run_tree/assets/shaders/world_fragment.glsl b/run_tree/assets/shaders/world_fragment.glsl
deleted file mode 100644 (file)
index dc1ff92..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-#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);
-}
diff --git a/run_tree/assets/shaders/world_vertex.glsl b/run_tree/assets/shaders/world_vertex.glsl
deleted file mode 100644 (file)
index ff7709c..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#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);
-}
index 858725b838c829341fc0693c0a67d7c56bce55a5..3dd0e443b9144ade2e27b7c80cf3fe323e8e7bde 100644 (file)
 #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"
diff --git a/src/font.onyx b/src/font.onyx
deleted file mode 100644 (file)
index 3e32076..0000000
+++ /dev/null
@@ -1,237 +0,0 @@
-
-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;
-}
-
diff --git a/src/gfx/canvas.onyx b/src/gfx/canvas.onyx
new file mode 100644 (file)
index 0000000..d2f08e4
--- /dev/null
@@ -0,0 +1,24 @@
+
+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);
+    }
+}
+
+
diff --git a/src/gfx/font.onyx b/src/gfx/font.onyx
new file mode 100644 (file)
index 0000000..094760e
--- /dev/null
@@ -0,0 +1,257 @@
+
+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;
+}
+
diff --git a/src/gfx/immediate.onyx b/src/gfx/immediate.onyx
new file mode 100644 (file)
index 0000000..23dd4eb
--- /dev/null
@@ -0,0 +1,180 @@
+
+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;
+        }
+    }
+}
diff --git a/src/gfx/mesh.onyx b/src/gfx/mesh.onyx
new file mode 100644 (file)
index 0000000..db769a9
--- /dev/null
@@ -0,0 +1,87 @@
+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);
+}
+
+
+
diff --git a/src/gfx/shader.onyx b/src/gfx/shader.onyx
new file mode 100644 (file)
index 0000000..246c3d1
--- /dev/null
@@ -0,0 +1,178 @@
+
+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;
+    }
+}
+
diff --git a/src/gfx/texture.onyx b/src/gfx/texture.onyx
new file mode 100644 (file)
index 0000000..5d51168
--- /dev/null
@@ -0,0 +1,38 @@
+
+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);
+}
+
diff --git a/src/gfx/ui.onyx b/src/gfx/ui.onyx
new file mode 100644 (file)
index 0000000..32264af
--- /dev/null
@@ -0,0 +1,471 @@
+//
+// 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,
+    };
+}
diff --git a/src/immediate.onyx b/src/immediate.onyx
deleted file mode 100644 (file)
index 65f6ce1..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-
-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;
-        }
-    }
-}
index a05c3f5a9324de085402837f41094ff2354c94c4..2a5f217cf407d12573050905c3a7b3bcfc4f81b3 100644 (file)
@@ -2,18 +2,27 @@ use package core
 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;
 
@@ -23,30 +32,14 @@ input_post_update :: () {
        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];
@@ -62,3 +55,47 @@ is_button_just_up   :: (button) => !buttons_this_frame[button] && buttons_last_f
 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);
+       }
+}
index 781da77c226a4ea0413e2d9c5d463fefae167be8..c530db197cf580671f6ed7daca4e36f59c168169 100644 (file)
@@ -45,19 +45,6 @@ create_window :: () => {
     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;
@@ -80,6 +67,8 @@ toggle_cursor_grabbed :: () {
 setup_opengl :: () {
     glInit(glfwGetLoadProcAddress());
 
+    input_bind_glfw_events(window);
+
     glEnable(GL_TEXTURE);
     glEnable(GL_DEPTH_TEST);
     glEnable(GL_CULL_FACE);
@@ -122,6 +111,14 @@ update :: (dt: f32) {
     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();
@@ -170,7 +167,6 @@ draw_scene :: () {
     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);
diff --git a/src/mesh.onyx b/src/mesh.onyx
deleted file mode 100644 (file)
index db769a9..0000000
+++ /dev/null
@@ -1,87 +0,0 @@
-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);
-}
-
-
-
diff --git a/src/texture.onyx b/src/texture.onyx
deleted file mode 100644 (file)
index 5d51168..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-
-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);
-}
-