Canvas :: struct {
+ width, height: i32;
+
framebuffer: GLint;
depth_stencil_buffer: GLint;
color_texture: GLint;
}
-canvas_make :: () -> Canvas {
+canvas_make :: (width, height: i32) -> Canvas {
canvas: Canvas;
+ canvas.width = width;
+ canvas.height = height;
+
+ glGenFramebuffers(1, ^canvas.framebuffer);
+ glBindFramebuffer(GL_FRAMEBUFFER, canvas.framebuffer);
+
+ glGenTextures(1, ^canvas.color_texture);
+ glBindTexture(GL_TEXTURE_2D, canvas.color_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, null);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, canvas.color_texture, 0);
+
+ glGenRenderbuffers(1, ^canvas.depth_stencil_buffer);
+ glBindRenderbuffer(GL_RENDERBUFFER, canvas.depth_stencil_buffer);
+ glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
+ glBindRenderbuffer(GL_RENDERBUFFER, 0);
+ glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, canvas.depth_stencil_buffer);
+
+ if glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE {
+ debug_log(.Error, "Framebuffer is not complete!");
+ }
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ return canvas;
+}
+
+canvas_free :: (use canvas: ^Canvas) {
+ glDeleteFramebuffers(1, ^canvas.framebuffer);
+ glDeleteTextures(1, ^canvas.color_texture);
+ glDeleteRenderbuffers(1, ^canvas.depth_stencil_buffer);
}
canvas_use :: (use canvas: ^Canvas) {
if canvas == null {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
+ update_view_matrix();
} else {
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
+ // update_view_matrix(width, height);
}
}
+canvas_to_texture :: (canvas: ^Canvas) => Texture.{ canvas.color_texture, canvas.width, canvas.height, 3, "<canvas>" };
font_shader = shader_make("./assets/shaders/font.glsl");
shader_use(font_shader);
shader_link_window_matrix_block(font_shader);
+ shader_link_world_matrix_block(font_shader);
glGenVertexArrays(1, ^font_vao);
glBindVertexArray(font_vao);
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_link_world_matrix_block(imgui_shader);
shader_set_uniform(imgui_shader, #cstr "u_texture_enabled", 0.0f);
shader_set_uniform(imgui_shader, #cstr "u_texture", 0);
scissors << .{x, y, w, h};
glEnable(GL_SCISSOR_TEST);
- glScissor(~~x, ~~(window_height - ~~(y + h)), ~~w, ~~h);
+ glScissor(~~x, cast(i32) camera.window_height - ~~(y + h), ~~w, ~~h);
}
immediate_pop_scissor :: () {
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);
+ glScissor(~~s.x, cast(i32) camera.window_height - ~~(s.y + s.h), ~~s.w, ~~s.h);
} else {
glDisable(GL_SCISSOR_TEST);
}
}
+immediate_set_scroll :: (scroll_x, scroll_y: f32) {
+ offset = .{ scroll_x, scroll_y };
+ update_model_matrix(.{ offset.x, offset.y, 0 });
+}
+
+immediate_get_scroll :: () => offset;
+
Color :: struct {
r, g, b : f32;
a := 1.0f;
}
scissors: [..] Scissor;
+ offset: Vector2;
+
set_rendering_type :: (new_type: typeof rendering_type) {
if rendering_type != new_type {
immediate_flush();
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);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0 + texture_binding);
glBindTexture(GL_TEXTURE_2D, texture);
}
+
+
+Texture_Wrap :: enum {
+ Clamp :: GL_CLAMP_TO_EDGE | 0;
+ Repeat :: GL_REPEAT | 0;
+ Mirrored_Repeat :: GL_MIRRORED_REPEAT | 0;
+}
+
+texture_wrap :: (use tex: ^Texture, wrap: Texture_Wrap) {
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, ~~ wrap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, ~~ wrap);
+}
\ No newline at end of file
return result;
}
+
+
+scrolling_region_start :: (r: Rect, max_y_scroll := 10000.0f, site := #callsite, increment := 0) {
+ hash := get_site_hash(site, increment);
+ mx, my := mouse_get_position();
+ state := map.get_ptr(^scroll_states, hash);
+ if state == null {
+ animation_states[hash] = .{};
+ state = ^scroll_states[hash];
+ }
+
+ contains := Rect.contains(r, .{~~mx, ~~my});
+ if contains {
+ scroll_delta := mouse_get_scroll_vector();
+ state.xscroll -= scroll_delta.x * 20;
+ state.yscroll -= scroll_delta.y * 20;
+
+ state.yscroll = math.clamp(state.yscroll, 0, max_y_scroll);
+ }
+
+ immediate_flush();
+ immediate_push_scissor(r.x, r.y, r.w, r.h);
+ immediate_set_scroll(-state.xscroll, -state.yscroll);
+}
+
+scrolling_region_stop :: () {
+ immediate_flush();
+ immediate_pop_scissor();
+ immediate_set_scroll(0, 0);
+}
+
#if !#defined(Rect) {
Rect :: struct {
x, y, w, h: f32;
*v = target;
return;
}
-
- if *v < target do *v += diff;
- if *v > target do *v -= diff;
}
}
return false;
}
+
+ Scroll_State :: struct {
+ xscroll: f32;
+ yscroll: f32;
+ }
+
+ scroll_states : Map(UI_Id, Scroll_State);
+
get_site_hash :: macro (site: CallSite, increment := 0) -> UI_Id {
hash :: package core.hash
file_hash := hash.to_u32(site.file);
use package core
use package glfw3
+// If you are offseting the mouse coordinate for world space
+// or UI scrolling etc., set this function to be the function
+// to ask for the current offset. Currently scaling the mouse
+// coordinate is not supported, only linear translations.
+Mouse_Offset_Function :: immediate_get_scroll
+
+
#local {
- 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
+ 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 // Mouse buttons being pressed this frame
- buttons_last_frame: [8] bool // Mouse buttons being pressed last frame
+ buttons_this_frame: [8] bool // Mouse buttons being pressed this frame
+ buttons_last_frame: [8] bool // Mouse buttons being pressed last frame
+
+ scroll_x: f64
+ scroll_y: f64
}
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;
- }
- }
+ 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;
+ array.clear(^keys_pulse_frame);
+ array.clear(^keys_last_frame);
+ for keys_this_frame do keys_last_frame << it;
- for 8 do buttons_last_frame[it] = buttons_this_frame[it];
+ for 8 do buttons_last_frame[it] = buttons_this_frame[it];
- last_mouse_x = mouse_x;
- last_mouse_y = mouse_y;
+ last_mouse_x = mouse_x;
+ last_mouse_y = mouse_y;
+ scroll_x = 0;
+ scroll_y = 0;
}
input_get_keys_this_frame :: () -> [] u32 {
- return keys_pulse_frame;
+ return keys_pulse_frame;
}
is_key_down :: (key) => array.contains(keys_this_frame, key);
is_button_just_up :: (button) => !buttons_this_frame[button] && buttons_last_frame[button];
#local {
- last_mouse_x: f64;
- last_mouse_y: f64;
+ last_mouse_x: f64;
+ last_mouse_y: f64;
- mouse_x: f64;
- mouse_y: f64;
+ mouse_x: f64;
+ mouse_y: f64;
}
mouse_get_delta :: () -> (f64, f64) {
- return mouse_x - last_mouse_x, mouse_y - last_mouse_y;
+ return mouse_x - last_mouse_x, mouse_y - last_mouse_y;
}
mouse_get_delta_vector :: () -> Vector2 {
- dmx, dmy := mouse_get_delta();
- return .{ ~~dmx, ~~dmy };
+ dmx, dmy := mouse_get_delta();
+ return .{ ~~dmx, ~~dmy };
}
mouse_get_position :: () -> (f64, f64) {
- return mouse_x, mouse_y;
+ mx, my := mouse_x, mouse_y;
+ #if #defined(Mouse_Offset_Function) {
+ scroll := Mouse_Offset_Function();
+ mx -= ~~ scroll.x;
+ my -= ~~ scroll.y;
+ }
+ return mx, my;
}
mouse_get_position_vector :: () -> Vector2 {
- return .{ ~~mouse_x, ~~mouse_y };
+ mx, my := mouse_x, mouse_y;
+ #if #defined(Mouse_Offset_Function) {
+ scroll := Mouse_Offset_Function();
+ mx -= ~~ scroll.x;
+ my -= ~~ scroll.y;
+ }
+ return .{ ~~mx, ~~my };
+}
+
+mouse_get_scroll :: () -> (f64, f64) {
+ return scroll_x, scroll_y;
}
+mouse_get_scroll_vector :: () -> Vector2 {
+ return .{ ~~scroll_x, ~~scroll_y };
+}
input_bind_glfw_events :: (window: GLFWwindow_p) {
- glfwSetKeyCallback(window, INPUT_KEY_EVENT);
- glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT);
+ glfwSetKeyCallback(window, INPUT_KEY_EVENT);
+ glfwSetMouseButtonCallback(window, INPUT_BUTTON_EVENT);
+ glfwSetScrollCallback(window, INPUT_SCROLL_EVENT);
}
#local {
- INPUT_BUTTON_EVENT :: "__input_button_event"
- INPUT_KEY_EVENT :: "__input_key_event"
+ INPUT_BUTTON_EVENT :: "__input_button_event"
+ INPUT_KEY_EVENT :: "__input_key_event"
+ INPUT_SCROLL_EVENT :: "__input_scroll_event"
}
#export INPUT_BUTTON_EVENT (window: GLFWwindow_p, button, action, mod: u32) {
- if action == GLFW_PRESS {
- buttons_this_frame[button] = true;
- }
+ if action == GLFW_PRESS {
+ buttons_this_frame[button] = true;
+ }
- if action == GLFW_RELEASE {
- buttons_this_frame[button] = false;
- }
+ 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_PRESS {
+ keys_this_frame << key;
+ }
+
+ if action == GLFW_RELEASE {
+ array.remove(^keys_this_frame, key);
+ }
+}
- if action == GLFW_RELEASE {
- array.remove(^keys_this_frame, key);
- }
+#export INPUT_SCROLL_EVENT (window: GLFWwindow_p, xoff, yoff: f64) {
+ scroll_x = xoff;
+ scroll_y = yoff;
}