summaryrefslogtreecommitdiff
path: root/src/render
diff options
context:
space:
mode:
authorkasull <qsullian@gmail.com>2026-03-01 20:18:08 -0500
committerkasull <qsullian@gmail.com>2026-03-01 20:18:08 -0500
commit413ef78504278d37080b9b59a652e4bbd5e2a0fc (patch)
tree67be817cf765725dd2d08c14b2d0ce8c12b20997 /src/render
parent71dd7fcccb45a54d85ae23a95a8a8905ed21fe15 (diff)
editor ui rework
add responsive editor relayout for window resize rework header with back/save, view mode buttons, and 2d view type selector add clipped, scrollable assets and contextual tool panels simplify tool button wiring and repeated layout logic simplify 2d top-down editor internals and shared grid update hooks
Diffstat (limited to 'src/render')
-rw-r--r--src/render/gl.cpp237
-rw-r--r--src/render/gl.h9
-rw-r--r--src/render/gl_3d.cpp13
3 files changed, 206 insertions, 53 deletions
diff --git a/src/render/gl.cpp b/src/render/gl.cpp
index e44a165..d18cd53 100644
--- a/src/render/gl.cpp
+++ b/src/render/gl.cpp
@@ -8,6 +8,10 @@
#include "../util.h"
I32 SAMPLER_INDICES[255];
+const I32 GL_WINDOW_MIN_W = 800;
+const I32 GL_WINDOW_MIN_H = 600;
+const I32 GL_WINDOW_ASPECT_W = 4;
+const I32 GL_WINDOW_ASPECT_H = 3;
GL_DATA* gl_inst;
@@ -15,6 +19,128 @@ GL_DATA* gl_instance() {
return gl_inst;
}
+static void gl_apply_canvas_size( GL_DATA* gl, I32 width, I32 height );
+static void gl_handle_window_size_change( GL_DATA* gl, I32 width, I32 height );
+static void gl_update_screen_ratio_uniforms( GL_DATA* gl, I32 width, I32 height );
+
+static void gl_query_canvas_size( GL_DATA* gl, I32* width, I32* height ) {
+ I32 w = 0;
+ I32 h = 0;
+ if( gl && gl->window ) {
+ SDL_GL_GetDrawableSize( gl->window, &w, &h );
+ if( w <= 0 || h <= 0 )
+ SDL_GetWindowSize( gl->window, &w, &h );
+ }
+
+ if( width ) *width = max( 1, w );
+ if( height ) *height = max( 1, h );
+}
+
+static void gl_constrain_window_size( I32* width, I32* height ) {
+ if( !width || !height )
+ return;
+
+ I32 in_w = max( GL_WINDOW_MIN_W, *width );
+ I32 in_h = max( GL_WINDOW_MIN_H, *height );
+
+ I32 keep_w_h = (I32)( ( (I64)in_w * GL_WINDOW_ASPECT_H + GL_WINDOW_ASPECT_W / 2 ) / GL_WINDOW_ASPECT_W );
+ keep_w_h = max( GL_WINDOW_MIN_H, keep_w_h );
+
+ I32 keep_h_w = (I32)( ( (I64)in_h * GL_WINDOW_ASPECT_W + GL_WINDOW_ASPECT_H / 2 ) / GL_WINDOW_ASPECT_H );
+ keep_h_w = max( GL_WINDOW_MIN_W, keep_h_w );
+
+ I32 d_keep_w = abs( keep_w_h - in_h );
+ I32 d_keep_h = abs( keep_h_w - in_w );
+
+ if( d_keep_w <= d_keep_h ) {
+ *width = in_w;
+ *height = keep_w_h;
+ } else {
+ *width = keep_h_w;
+ *height = in_h;
+ }
+}
+
+static void gl_handle_window_size_change( GL_DATA* gl, I32 width, I32 height ) {
+ if( !gl || !gl->window )
+ return;
+
+ I32 target_w = width;
+ I32 target_h = height;
+ if( target_w <= 0 || target_h <= 0 )
+ SDL_GetWindowSize( gl->window, &target_w, &target_h );
+
+ gl_constrain_window_size( &target_w, &target_h );
+
+ I32 wnd_w, wnd_h;
+ SDL_GetWindowSize( gl->window, &wnd_w, &wnd_h );
+ if( ( target_w != wnd_w || target_h != wnd_h ) && !gl->window_constraint_active ) {
+ gl->window_constraint_active = 1;
+ SDL_SetWindowSize( gl->window, target_w, target_h );
+ gl->window_constraint_active = 0;
+ }
+
+ I32 draw_w, draw_h;
+ gl_query_canvas_size( gl, &draw_w, &draw_h );
+ gl_apply_canvas_size( gl, draw_w, draw_h );
+}
+
+static I32 SDLCALL gl_event_filter( void* userdata, SDL_Event* e ) {
+ GL_DATA* gl = (GL_DATA*)userdata;
+ if( !gl || !e )
+ return 1;
+
+ if( e->type == SDL_WINDOWEVENT ) {
+ if( e->window.event == SDL_WINDOWEVENT_SIZE_CHANGED
+ || e->window.event == SDL_WINDOWEVENT_RESIZED ) {
+ gl_handle_window_size_change( gl, e->window.data1, e->window.data2 );
+
+ if( gl->resize_repaint_cb && !gl->resize_repaint_active ) {
+ gl->resize_repaint_active = 1;
+ gl->resize_repaint_cb( gl->resize_repaint_userdata );
+ gl->resize_repaint_active = 0;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static void gl_apply_canvas_size( GL_DATA* gl, I32 width, I32 height ) {
+ width = max( 1, width );
+ height = max( 1, height );
+
+ gl->canvas_size[0] = width;
+ gl->canvas_size[1] = height;
+ if( canvas ) {
+ canvas[0] = width;
+ canvas[1] = height;
+ }
+
+ gl->clip_start = { 0.f, 0.f };
+ gl->clip_dim = { (F32)width, (F32)height };
+
+ gl_update_screen_ratio_uniforms( gl, width, height );
+
+ glViewport( 0, 0, width, height );
+ glUseProgram( 0 );
+}
+
+static void gl_update_screen_ratio_uniforms( GL_DATA* gl, I32 width, I32 height ) {
+ F32 screen_ratio[] = {
+ 2.f / (F32)max( 1, width ),
+ 2.f / (F32)max( 1, height ),
+ 1.f,
+ 1.f
+ };
+
+ gl->programs.each( fn( GL_SHADER_PROGRAM** it ) {
+ glUseProgram( (*it)->id );
+ I32 ratio_location = glGetUniformLocation( (*it)->id, "g_screenratio" );
+ glUniform4fv( ratio_location, 1, &screen_ratio[0] );
+ } );
+}
+
GL_DATA* gl_create( I32* _canvas ) {
if( gl_inst ) {
dlog( "gl_create() : fatal: gl instance already exists\n" );
@@ -24,6 +150,10 @@ GL_DATA* gl_create( I32* _canvas ) {
if( !font_mutex.align )
thread_mutex_init( &font_mutex );
+ SDL_SetHint( SDL_HINT_WINDOWS_ENABLE_MESSAGELOOP, "1" );
+ SDL_SetHint( SDL_HINT_WINDOWS_DPI_SCALING, "0" );
+ SDL_SetHint( SDL_HINT_VIDEO_HIGHDPI_DISABLED, "1" );
+
if( !!SDL_Init( SDL_INIT_VIDEO | SDL_VIDEO_OPENGL ) ) {
dlog( "gl_create() could not init SDL: %s\n", SDL_GetError() );
return 0;
@@ -40,14 +170,18 @@ GL_DATA* gl_create( I32* _canvas ) {
SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
+ I32 start_w = _canvas[0];
+ I32 start_h = _canvas[1];
+ gl_constrain_window_size( &start_w, &start_h );
+
GL_DATA* gl = new GL_DATA;
gl->window = SDL_CreateWindow(
"game",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
- _canvas[0],
- _canvas[1],
- SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN
+ start_w,
+ start_h,
+ SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE
);
if( !gl->window ) {
@@ -56,6 +190,8 @@ GL_DATA* gl_create( I32* _canvas ) {
return 0;
}
+ SDL_SetWindowMinimumSize( gl->window, GL_WINDOW_MIN_W, GL_WINDOW_MIN_H );
+
gl->ctx = SDL_GL_CreateContext( gl->window );
if( !gl->ctx ) {
dlog( "gl_init() could not create context: %s\n", SDL_GetError() );
@@ -85,10 +221,8 @@ GL_DATA* gl_create( I32* _canvas ) {
SAMPLER_INDICES[i] = i;
gl->programs.clear();
- memcpy( gl->canvas_size, _canvas, sizeof(I32) * 2 );
- gl->clip_start = { 0, 0 };
- gl->clip_dim = { (F32)gl->canvas_size[0], (F32)gl->canvas_size[1] };
- glViewport( 0, 0, gl->canvas_size[0], gl->canvas_size[1] );
+ gl_handle_window_size_change( gl, start_w, start_h );
+ SDL_SetEventFilter( gl_event_filter, gl );
gl_inst = gl;
return gl;
@@ -105,6 +239,8 @@ void gl_gen_buffers( GL_DATA* gl ) {
}
void gl_destroy( GL_DATA *gl ) {
+ SDL_SetEventFilter( 0, 0 );
+
gl->programs.each( fn( GL_SHADER_PROGRAM** it ) { gl_program_destroy( gl, *it ); } );
gl->fonts.each( fn( GL_FONT** it ) { gl_font_destroy( gl, *it ); } );
gl->textures.each( fn( GL_TEX2D** it ) { gl_texture_destroy( gl, *it ); } );
@@ -120,27 +256,31 @@ void gl_destroy( GL_DATA *gl ) {
void gl_update_window( GL_DATA* gl, I32* size ) {
if( !gl->window )
return;
- SDL_SetWindowSize( gl->window, size[0], size[1] );
+
+ I32 width = size ? size[0] : 0;
+ I32 height = size ? size[1] : 0;
+ gl_constrain_window_size( &width, &height );
+ SDL_SetWindowSize( gl->window, width, height );
SDL_SetWindowPosition( gl->window, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED );
+ gl_handle_window_size_change( gl, width, height );
+}
- gl->canvas_size[0] = size[0];
- gl->canvas_size[1] = size[1];
+void gl_sync_window_size( GL_DATA* gl ) {
+ if( !gl || !gl->window )
+ return;
- gl->programs.each( fn( GL_SHADER_PROGRAM** it ) {
- F32 screen_ratio[] = {
- 2.f / (F32)gl->canvas_size[0],
- 2.f / (F32)gl->canvas_size[1],
- 1.f,
- 1.f
- };
+ SDL_PumpEvents();
+ I32 w, h;
+ SDL_GetWindowSize( gl->window, &w, &h );
+ gl_handle_window_size_change( gl, w, h );
+}
- glUseProgram( (*it)->id );
- I32 ratio_location = glGetUniformLocation( (*it)->id, "g_screenratio" );
- glUniform4fv( ratio_location, 1, &screen_ratio[0] );
- } );
+void gl_set_resize_repaint_callback( GL_DATA* gl, GL_RESIZE_REPAINT_CB cb, void* userdata ) {
+ if( !gl )
+ return;
- glViewport( 0, 0, gl->canvas_size[0], gl->canvas_size[1] );
- glUseProgram( 0 );
+ gl->resize_repaint_cb = cb;
+ gl->resize_repaint_userdata = userdata;
}
STAT gl_shader_compile( GL_DATA* gl, GL_SHADER_DEF* shader ) {
@@ -318,6 +458,29 @@ STAT gl_beginframe( GL_DATA* gl ) {
return STAT_OK;
}
+STAT gl_poll_events( GL_DATA* gl ) {
+ SDL_Event e;
+ while( SDL_PollEvent( &e ) ) {
+ input_on_event( &e );
+
+ switch( e.type ) {
+ case SDL_QUIT:
+ return STAT_BREAK;
+ case SDL_WINDOWEVENT: {
+ if( e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED
+ || e.window.event == SDL_WINDOWEVENT_RESIZED ) {
+ gl_handle_window_size_change( gl, e.window.data1, e.window.data2 );
+ }
+ } break;
+ case SDL_KEYDOWN:
+ return e.key.keysym.sym == SDLK_ESCAPE ? STAT_BREAK : STAT_OK;
+ default: break;
+ }
+ }
+
+ return STAT_OK;
+}
+
STAT gl_endframe( GL_DATA* gl ) {
SDL_GL_SwapWindow( gl->window );
@@ -333,20 +496,6 @@ STAT gl_endframe( GL_DATA* gl ) {
gl->fps = 1.0f / gl->frametime;
input_frame_end();
-
- SDL_Event e;
- while( SDL_PollEvent( &e ) ) {
- input_on_event( &e );
-
- switch( e.type ) {
- case SDL_QUIT:
- return STAT_BREAK;
- case SDL_KEYDOWN:
- return e.key.keysym.sym == SDLK_ESCAPE ? STAT_BREAK : STAT_OK;
- default: break;
- }
- }
-
return STAT_OK;
}
@@ -372,19 +521,7 @@ void gl_reset_clip( GL_DATA* gl ) {
void gl_set_viewport( GL_DATA* gl, VEC2 start, VEC2 dim ) {
I32 vpy = (I32)gl->canvas_size[1] - start.y - dim.y;
glViewport( (I32)start.x, vpy, (I32)dim.x, (I32)dim.y );
-
- gl->programs.each( fn( GL_SHADER_PROGRAM** it ) {
- F32 screen_ratio[] = {
- 2.f / (F32)dim.x,
- 2.f / (F32)dim.y,
- 1.f,
- 1.f
- };
-
- glUseProgram( (*it)->id );
- I32 ratio_location = glGetUniformLocation( (*it)->id, "g_screenratio" );
- glUniform4fv( ratio_location, 1, &screen_ratio[0] );
- } );
+ gl_update_screen_ratio_uniforms( gl, (I32)dim.x, (I32)dim.y );
gl->viewport_start = start;
gl->viewport_dim = dim;
diff --git a/src/render/gl.h b/src/render/gl.h
index e4e2d5a..dfb76b3 100644
--- a/src/render/gl.h
+++ b/src/render/gl.h
@@ -9,6 +9,7 @@
typedef struct GL_FONT *PGL_FONT;
typedef struct GL_DATA *PGL_DATA;
+typedef void(*GL_RESIZE_REPAINT_CB)( void* userdata );
struct VERTEX {
VEC2 pos;
@@ -71,6 +72,11 @@ typedef struct GL_DATA {
MAT4* proj_matrix;
GLuint vao;
+
+ GL_RESIZE_REPAINT_CB resize_repaint_cb{};
+ void* resize_repaint_userdata{};
+ U8 resize_repaint_active{};
+ U8 window_constraint_active{};
} *PGL_DATA;
GL_DATA* gl_instance();
@@ -79,6 +85,7 @@ void gl_destroy( GL_DATA* gl );
void gl_gen_buffers( GL_DATA* gl );
STAT gl_beginframe( GL_DATA* gl );
STAT gl_endframe( GL_DATA* gl );
+STAT gl_poll_events( GL_DATA* gl );
STAT gl_shader_compile( GL_DATA* gl, GL_SHADER_DEF* shader );
void gl_shader_destroy( GL_DATA* gl, GL_SHADER_DEF* shader );
GL_SHADER_PROGRAM* gl_program_create( GL_DATA* gl, const char* name, U32 size = sizeof(GL_SHADER_PROGRAM) );
@@ -89,6 +96,8 @@ GL_TEX2D* gl_texture_from_file( GL_DATA* gl, const char* name );
GL_TEX2D* gl_texture_from_bitmap( GL_DATA* gl, const char* name, U8* bitmap, U32 width, U32 height );
void gl_texture_destroy( GL_DATA* gl, GL_TEX2D* tex );
void gl_update_window( GL_DATA* gl, I32* size );
+void gl_sync_window_size( GL_DATA* gl );
+void gl_set_resize_repaint_callback( GL_DATA* gl, GL_RESIZE_REPAINT_CB cb, void* userdata );
void gl_set_clip( GL_DATA* gl, VEC2 start, VEC2 dim );
void gl_get_clip( GL_DATA* gl, VEC2* start, VEC2* dim );
void gl_set_viewport( GL_DATA* gl, VEC2 start, VEC2 dim );
diff --git a/src/render/gl_3d.cpp b/src/render/gl_3d.cpp
index 40162e8..8449500 100644
--- a/src/render/gl_3d.cpp
+++ b/src/render/gl_3d.cpp
@@ -21,13 +21,20 @@ void gl_3d_projection_setup( GL_SHADER_PROGRAM *_gl3d, VEC3 pos, F32 fov_deg, F3
GL3D* gl3d = (GL3D*)_gl3d;
MAT4 proj, yaw_rot, pitch_rot, view, transl, tmp;
- F32 fov_rad = m_deg2rad( fov_deg );
+ F32 fov_y_rad = m_deg2rad( fov_deg );
F32 yaw_rad = m_deg2rad( remainderf( yaw + 90.f, 360.f ) );
F32 pitch_rad = m_deg2rad( remainderf( pitch , 360.f ) );
- gl3d->aspect = winsize.x / winsize.y;
+ gl3d->aspect = winsize.y > 0.f ? winsize.x / winsize.y : 1.f;
+ if( gl3d->aspect <= 0.000001f )
+ gl3d->aspect = 1.f;
+
+ const F32 min_fovy = m_deg2rad( 1.f );
+ const F32 max_fovy = m_deg2rad( 179.f );
+ if( fov_y_rad < min_fovy ) fov_y_rad = min_fovy;
+ if( fov_y_rad > max_fovy ) fov_y_rad = max_fovy;
gl3d->winsize = winsize;
- mat4_perspective( &proj, fov_rad, gl3d->aspect, near, far );
+ mat4_perspective( &proj, fov_y_rad, gl3d->aspect, near, far );
mat4_rotation_y( &yaw_rot, -yaw_rad );
mat4_rotation_x( &pitch_rot, -pitch_rad );
mat4_translation( &transl, -pos.x, -pos.z, -pos.y );