Gut raylib usage from the codebase.

Going to either fully commit to sokol or if it fails, rolling the platform layer myself.
This commit is contained in:
Edward R. Gonzalez 2024-05-25 11:52:23 -04:00
parent 13c3032dba
commit 797ab227e9
25 changed files with 571 additions and 1368 deletions

View File

@ -3,11 +3,10 @@ package sectr
// Scratch space
import sokol_gfx "thirdparty:sokol/gfx"
import rl "vendor:raylib"
DebugData :: struct {
square_size : i32,
square_pos : rl.Vector2,
square_pos : Vec2,
debug_text_vis : b32,
draw_debug_text_y : f32,
@ -35,10 +34,6 @@ DebugData :: struct {
lorem_content : []byte,
lorem_parse : PWS_ParseResult,
// Test 3d Viewport
cam_vp : rl.Camera3D,
viewport_rt : rl.RenderTexture,
gfx_clear_demo_pass_action : sokol_gfx.Pass_Action,
gfx_tri_demo_state : struct {
pipeline : sokol_gfx.Pipeline,

View File

@ -6,8 +6,6 @@ import "core:mem"
import "core:mem/virtual"
import "core:os"
import rl "vendor:raylib"
Str_App_State := "App State"
#region("Memory")
@ -167,6 +165,7 @@ AppWindow :: struct {
extent : Extents2, // Window half-size
dpi_scale : f32, // Dots per inch scale (provided by raylib via glfw)
ppcm : f32, // Dots per centimetre
resized : b32, // Extent changed this frame
}
FontData :: struct {
@ -241,9 +240,14 @@ State :: struct {
font_rec_mono_semicasual_reg : FontID,
default_font : FontID,
// Context tracking
// These are used as implicit contextual states when doing immediate mode interfaces
// or for event callbacks that need their context assigned
// There are two potential UI contextes for this prototype so far,
// the screen-space UI and the current workspace UI.
// This is used so that the ui api doesn't need to have the user pass the context every single time.
// This is used so that the ui api doesn't need to have the user pass the context through every proc.
ui_context : ^UI_State,
ui_floating_context : ^UI_FloatingManager,

View File

@ -116,12 +116,12 @@ theme_table_row :: proc( is_even : bool ) -> UI_Theme
if ! loaded
{
app_color := app_color_theme()
table_bg : Color
table_bg : RGBA8
if is_even {
table_bg = app_color.table_even_bg_color
table_bg = app_color.table_even_bg
}
else {
table_bg = app_color.table_odd_bg_color
table_bg = app_color.table_odd_bg
}
layout := UI_Layout {
flags = {},

View File

@ -1,32 +1,29 @@
package sectr
import rl "vendor:raylib"
RGBA8 :: struct { r, g, b, a : u8 }
Color_Blue :: RGBA8 { 90, 90, 230, 255 }
Color_Red :: RGBA8 { 230, 90, 90, 255 }
Color_White :: RGBA8 { 255, 255, 255, 255 }
Color :: rl.Color
Color_Blue :: rl.BLUE
// Color_Green :: rl.GREEN
Color_Red :: rl.RED
Color_White :: rl.WHITE
Color_Transparent :: RGBA8 { 0, 0, 0, 0 }
Color_BG :: RGBA8 { 55, 55, 60, 255 }
Color_BG_TextBox :: RGBA8 { 32, 32, 32, 180 }
Color_BG_Panel :: RGBA8 { 32, 32, 32, 255 }
Color_BG_Panel_Translucent :: RGBA8 { 32, 32, 32, 220 }
Color_BG_TextBox_Green :: RGBA8 { 102, 102, 110, 255 }
Color_Frame_Disabled :: RGBA8 { 22, 22, 22, 120 }
Color_Frame_Hover :: RGBA8 { 122, 122, 125, 200 }
Color_Frame_Select :: RGBA8 { 188, 188, 188, 220 }
Color_GreyRed :: RGBA8 { 220, 100, 100, 50 }
Color_White_A125 :: RGBA8 { 255, 255, 255, 165 }
Color_Black :: RGBA8 { 0, 0, 0, 255 }
Color_Green :: RGBA8 { 0, 180, 0, 255 }
Color_ResizeHandle :: RGBA8 { 80, 80, 90, 180 }
Color_Transparent :: Color { 0, 0, 0, 0 }
Color_BG :: Color { 55, 55, 60, 255 }
Color_BG_TextBox :: Color { 32, 32, 32, 180 }
Color_BG_Panel :: Color { 32, 32, 32, 255 }
Color_BG_Panel_Translucent :: Color { 32, 32, 32, 220 }
Color_BG_TextBox_Green :: Color { 102, 102, 110, 255 }
Color_Frame_Disabled :: Color { 22, 22, 22, 120 }
Color_Frame_Hover :: Color { 122, 122, 125, 200 }
Color_Frame_Select :: Color { 188, 188, 188, 220 }
Color_GreyRed :: Color { 220, 100, 100, 50 }
Color_White_A125 :: Color { 255, 255, 255, 165 }
Color_Black :: Color { 0, 0, 0, 255 }
Color_Green :: Color { 0, 180, 0, 255 }
Color_ResizeHandle :: Color { 80, 80, 90, 180 }
RGBA8_3D_BG :: RGBA8 { 188, 182 , 170, 255 }
Color_3D_BG :: Color { 188, 182 , 170, 255 }
Color_Debug_UI_Padding_Bounds :: Color { 40, 195, 170, 160 }
Color_Debug_UI_Content_Bounds :: Color { 170, 120, 240, 160 }
RGBA8_Debug_UI_Padding_Bounds :: RGBA8 { 40, 195, 170, 160 }
RGBA8_Debug_UI_Content_Bounds :: RGBA8 { 170, 120, 240, 160 }
// TODO(Ed): The entire rendering pass should be post-processed by a tone curve configurable for the user
// This is how you properly support any tonality of light or dark themes and not have it be base don the monitors raw output.
@ -51,8 +48,8 @@ AppColorTheme :: struct {
resize_hndl_hot,
resize_hndl_active,
table_even_bg_color,
table_odd_bg_color,
table_even_bg,
table_odd_bg,
text_default,
text_hot,
@ -66,116 +63,116 @@ AppColorTheme :: struct {
window_panel_bg,
window_panel_border \
: Color
: RGBA8
}
App_Thm_Dark :: AppColorTheme {
light_limit = Color {185, 185, 185, 255},
dark_limit = Color { 6, 6, 6, 255},
light_limit = RGBA8 {185, 185, 185, 255},
dark_limit = RGBA8 { 6, 6, 6, 255},
bg = Color {16, 16, 16, 255},
bg = RGBA8 {16, 16, 16, 255},
border_default = Color { 54, 54, 54, 255},
border_default = RGBA8 { 54, 54, 54, 255},
btn_bg_default = Color { 32, 32, 32, 255},
btn_bg_hot = Color { 80, 80, 100, 255},
btn_bg_active = Color { 100, 130, 180, 255},
btn_bg_default = RGBA8 { 32, 32, 32, 255},
btn_bg_hot = RGBA8 { 80, 80, 100, 255},
btn_bg_active = RGBA8 { 100, 130, 180, 255},
input_box_bg = Color { 20, 20, 20, 255},
input_box_bg_hot = Color { 25, 25, 25, 255},
input_box_bg_active = Color { 15, 15, 15, 255},
input_box_bg = RGBA8 { 20, 20, 20, 255},
input_box_bg_hot = RGBA8 { 25, 25, 25, 255},
input_box_bg_active = RGBA8 { 15, 15, 15, 255},
resize_hndl_default = Color_Transparent,
resize_hndl_hot = Color { 72, 72, 72, 90},
resize_hndl_active = Color { 88, 88, 88, 90},
resize_hndl_hot = RGBA8 { 72, 72, 72, 90},
resize_hndl_active = RGBA8 { 88, 88, 88, 90},
table_even_bg_color = Color { 35, 35, 35, 255},
table_odd_bg_color = Color { 30, 30, 30, 255},
table_even_bg = RGBA8 { 35, 35, 35, 255},
table_odd_bg = RGBA8 { 30, 30, 30, 255},
text_default = Color {140, 137, 135, 255},
text_hot = Color {210, 210, 210, 255},
text_active = Color {255, 255, 255, 255},
text_default = RGBA8 {140, 137, 135, 255},
text_hot = RGBA8 {210, 210, 210, 255},
text_active = RGBA8 {255, 255, 255, 255},
translucent_panel = Color { 30, 30, 30, 50},
translucent_panel = RGBA8 { 30, 30, 30, 50},
window_bar_border = Color{74, 74, 74, 255},
window_bar_bg = Color{32, 32, 32, 255},
window_btn_close_bg_hot = Color{65, 45, 45, 255},
window_bar_border = RGBA8{74, 74, 74, 255},
window_bar_bg = RGBA8{32, 32, 32, 255},
window_btn_close_bg_hot = RGBA8{65, 45, 45, 255},
window_panel_bg = Color{ 20, 20, 20, 50},
window_panel_border = Color{ 84, 84, 84, 255},
window_panel_bg = RGBA8{ 20, 20, 20, 50},
window_panel_border = RGBA8{ 84, 84, 84, 255},
}
App_Thm_Dusk :: AppColorTheme {
light_limit = Color {125, 125, 125, 255},
dark_limit = Color { 10, 10, 10, 255},
light_limit = RGBA8 {125, 125, 125, 255},
dark_limit = RGBA8 { 10, 10, 10, 255},
bg = Color {33, 33, 33, 255},
bg = RGBA8 {33, 33, 33, 255},
border_default = Color { 64, 64, 64, 255},
border_default = RGBA8 { 64, 64, 64, 255},
btn_bg_default = Color { 40, 40, 40, 255},
btn_bg_hot = Color { 60, 60, 70, 255},
btn_bg_active = Color { 90, 100, 130, 255},
btn_bg_default = RGBA8 { 40, 40, 40, 255},
btn_bg_hot = RGBA8 { 60, 60, 70, 255},
btn_bg_active = RGBA8 { 90, 100, 130, 255},
input_box_bg = Color { 20, 20, 20, 255},
input_box_bg_hot = Color { 25, 25, 25, 255},
input_box_bg_active = Color { 15, 15, 15, 255},
input_box_bg = RGBA8 { 20, 20, 20, 255},
input_box_bg_hot = RGBA8 { 25, 25, 25, 255},
input_box_bg_active = RGBA8 { 15, 15, 15, 255},
resize_hndl_default = Color_Transparent,
resize_hndl_hot = Color { 72, 72, 72, 90},
resize_hndl_active = Color { 88, 88, 88, 90},
resize_hndl_hot = RGBA8 { 72, 72, 72, 90},
resize_hndl_active = RGBA8 { 88, 88, 88, 90},
table_even_bg_color = Color { 35, 35, 35, 255},
table_odd_bg_color = Color { 30, 30, 30, 255},
table_even_bg = RGBA8 { 35, 35, 35, 255},
table_odd_bg = RGBA8 { 30, 30, 30, 255},
text_default = Color {120, 117, 115, 255},
text_hot = Color {180, 180, 180, 255},
text_active = Color {240, 240, 240, 255},
text_default = RGBA8 {120, 117, 115, 255},
text_hot = RGBA8 {180, 180, 180, 255},
text_active = RGBA8 {240, 240, 240, 255},
translucent_panel = Color { 10, 10, 10, 50},
translucent_panel = RGBA8 { 10, 10, 10, 50},
window_bar_border = Color { 64, 64, 64, 255}, // border_default
window_bar_bg = Color{35, 35, 35, 255},
window_btn_close_bg_hot = Color{45, 35, 35, 255},
window_bar_border = RGBA8 { 64, 64, 64, 255}, // border_default
window_bar_bg = RGBA8{35, 35, 35, 255},
window_btn_close_bg_hot = RGBA8{45, 35, 35, 255},
window_panel_bg = Color { 10, 10, 10, 50}, // translucent_panel
window_panel_border = Color{24, 24, 24, 255},
window_panel_bg = RGBA8 { 10, 10, 10, 50}, // translucent_panel
window_panel_border = RGBA8{24, 24, 24, 255},
}
App_Thm_Light :: AppColorTheme {
light_limit = Color {195, 195, 195, 255},
dark_limit = Color { 60, 60, 60, 255},
light_limit = RGBA8 {195, 195, 195, 255},
dark_limit = RGBA8 { 60, 60, 60, 255},
bg = Color {135, 135, 135, 255},
bg = RGBA8 {135, 135, 135, 255},
border_default = Color { 174, 174, 174, 255},
border_default = RGBA8 { 174, 174, 174, 255},
btn_bg_default = Color { 160, 160, 160, 255},
btn_bg_hot = Color { 145, 145, 155, 255},
btn_bg_active = Color { 124, 124, 136, 255},
btn_bg_default = RGBA8 { 160, 160, 160, 255},
btn_bg_hot = RGBA8 { 145, 145, 155, 255},
btn_bg_active = RGBA8 { 124, 124, 136, 255},
input_box_bg = Color {115, 115, 115, 255},
input_box_bg_hot = Color {125, 125, 125, 255},
input_box_bg_active = Color {105, 105, 105, 255},
input_box_bg = RGBA8 {115, 115, 115, 255},
input_box_bg_hot = RGBA8 {125, 125, 125, 255},
input_box_bg_active = RGBA8 {105, 105, 105, 255},
resize_hndl_default = Color_Transparent,
resize_hndl_hot = Color { 95, 95, 95, 90},
resize_hndl_active = Color { 80, 80, 80, 90},
resize_hndl_hot = RGBA8 { 95, 95, 95, 90},
resize_hndl_active = RGBA8 { 80, 80, 80, 90},
table_even_bg_color = Color {150, 150, 150, 255},
table_odd_bg_color = Color {160, 160, 160, 255},
table_even_bg = RGBA8 {150, 150, 150, 255},
table_odd_bg = RGBA8 {160, 160, 160, 255},
text_default = Color { 55, 55, 55, 255},
text_hot = Color { 85, 85, 85, 255},
text_active = Color { 45, 45, 49, 255},
text_default = RGBA8 { 55, 55, 55, 255},
text_hot = RGBA8 { 85, 85, 85, 255},
text_active = RGBA8 { 45, 45, 49, 255},
translucent_panel = Color { 110, 110, 110, 50},
translucent_panel = RGBA8 { 110, 110, 110, 50},
window_bar_border = Color{ 174, 174, 174, 255}, // border_default
window_bar_bg = Color{ 155, 155, 155, 255},
window_btn_close_bg_hot = Color{ 145, 135, 135, 255},
window_bar_border = RGBA8{ 174, 174, 174, 255}, // border_default
window_bar_bg = RGBA8{ 155, 155, 155, 255},
window_btn_close_bg_hot = RGBA8{ 145, 135, 135, 255},
window_panel_bg = Color {135, 135, 135, 50}, // translucent_panel
window_panel_border = Color{184, 184, 184, 255},
window_panel_bg = RGBA8 {135, 135, 135, 50}, // translucent_panel
window_panel_border = RGBA8{184, 184, 184, 255},
}

View File

@ -15,8 +15,6 @@ import sokol_app "thirdparty:sokol/app"
import sokol_gfx "thirdparty:sokol/gfx"
import sokol_app_gfx_glue "thirdparty:sokol/glue"
import rl "vendor:raylib"
Path_Assets :: "../assets/"
Path_Shaders :: "../shaders/"
Path_Input_Replay :: "scratch.sectr_replay"
@ -104,15 +102,17 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
}
// Setup input frame poll references
input = & input_data[1]
input_prev = & input_data[0]
for & input in input_data {
using input
error : AllocatorError
keyboard_events.keys_pressed, error = array_init_reserve(KeyCode, persistent_slab_allocator(), Kilo)
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.keys_pressed array")
keyboard_events.chars_pressed, error = array_init_reserve(rune, persistent_slab_allocator(), Kilo)
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.chars_pressed array")
{
input = & input_data[1]
input_prev = & input_data[0]
for & input in input_data {
using input
error : AllocatorError
keyboard_events.keys_pressed, error = array_init_reserve(KeyCode, persistent_slab_allocator(), Kilo)
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.keys_pressed array")
keyboard_events.chars_pressed, error = array_init_reserve(rune, persistent_slab_allocator(), Kilo)
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.chars_pressed array")
}
}
// Configuration Load
@ -305,9 +305,9 @@ startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem
{
using project.workspace
cam = {
target = { 0, 0 },
offset = transmute(Vec2) app_window.extent,
rotation = 0,
position = { 0, 0 },
view = transmute(Vec2) app_window.extent,
// rotation = 0,
zoom = 1.0,
}
// cam = {

View File

@ -1,424 +0,0 @@
package sectr
import "base:runtime"
import c "core:c/libc"
import "core:dynlib"
import "core:mem"
import "core:mem/virtual"
import "core:os"
import "core:slice"
import "core:strings"
import "core:time"
import "core:prof/spall"
import rl "vendor:raylib"
when false {
Path_Assets :: "../assets/"
Path_Shaders :: "../shaders/"
Path_Input_Replay :: "scratch.sectr_replay"
Persistent_Slab_DBG_Name := "Peristent Slab"
Frame_Slab_DBG_Name := "Frame Slab"
Transient_Slab_DBG_Name := "Transient Slab"
ModuleAPI :: struct {
lib : dynlib.Library,
write_time : FileTime,
lib_version : i32,
startup : type_of( startup ),
shutdown : type_of( sectr_shutdown ),
reload : type_of( reload ),
tick : type_of( tick ),
clean_frame : type_of( clean_frame ),
}
@export
startup :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^Logger )
{
spall.SCOPED_EVENT( & prof.ctx, & prof.buffer, #procedure )
Memory_App.profiler = prof
startup_tick := time.tick_now()
logger_init( & Memory_App.logger, "Sectr", host_logger.file_path, host_logger.file )
context.logger = to_odin_logger( & Memory_App.logger )
// Setup memory for the first time
{
using Memory_App;
persistent = persistent_mem
frame = frame_mem
transient = transient_mem
files_buffer = files_buffer_mem
context.allocator = transient_allocator()
context.temp_allocator = transient_allocator()
// TODO(Ed) : Put on the transient allocator a slab allocator (transient slab)
}
state := new( State, persistent_allocator() )
Memory_App.state = state
using state
// Setup Persistent Slabs & String Cache
{
alignment := uint(mem.DEFAULT_ALIGNMENT)
policy_ptr := & default_slab_policy
push( policy_ptr, SlabSizeClass { 128 * Kilobyte, 1 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 256 * Kilobyte, 2 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 512 * Kilobyte, 4 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 1 * Megabyte, 16 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 1 * Megabyte, 32 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 1 * Megabyte, 64 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 2 * Megabyte, 128 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 2 * Megabyte, 256 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 2 * Megabyte, 512 * Kilobyte, alignment })
push( policy_ptr, SlabSizeClass { 2 * Megabyte, 1 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 2 * Megabyte, 2 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 4 * Megabyte, 4 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 8 * Megabyte, 8 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 16 * Megabyte, 16 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 32 * Megabyte, 32 * Megabyte, alignment })
push( policy_ptr, SlabSizeClass { 64 * Megabyte, 64 * Megabyte, alignment })
// push( policy_ptr, SlabSizeClass { 128 * Megabyte, 128 * Megabyte, alignment })
// push( policy_ptr, SlabSizeClass { 256 * Megabyte, 256 * Megabyte, alignment })
// push( policy_ptr, SlabSizeClass { 512 * Megabyte, 512 * Megabyte, alignment })
alloc_error : AllocatorError
persistent_slab, alloc_error = slab_init( policy_ptr, allocator = persistent_allocator(), dbg_name = Persistent_Slab_DBG_Name )
verify( alloc_error == .None, "Failed to allocate the persistent slab" )
transient_slab, alloc_error = slab_init( & default_slab_policy, allocator = transient_allocator(), dbg_name = Transient_Slab_DBG_Name )
verify( alloc_error == .None, "Failed to allocate transient slab" )
transient_clear_time = 120 // Seconds, 2 Minutes
string_cache = str_cache_init()
}
// Setup input frame poll references
input = & input_data[1]
input_prev = & input_data[0]
for & input in input_data {
using input
error : AllocatorError
keyboard_events.keys_pressed, error = array_init_reserve(KeyCode, persistent_slab_allocator(), Kilo)
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.keys_pressed array")
keyboard_events.chars_pressed, error = array_init_reserve(rune, persistent_slab_allocator(), Kilo)
ensure(error == AllocatorError.None, "Failed to allocate input.keyboard_events.chars_pressed array")
}
// Configuration Load
// TODO(Ed): Make this actually load from an ini
{
using config
resolution_width = 1000
resolution_height = 600
refresh_rate = 0
cam_min_zoom = 0.10
cam_max_zoom = 30.0
cam_zoom_mode = .Smooth
cam_zoom_smooth_snappiness = 4.0
cam_zoom_sensitivity_digital = 0.2
cam_zoom_sensitivity_smooth = 4.0
engine_refresh_hz = 0
timing_fps_moving_avg_alpha = 0.9
ui_resize_border_width = 5
color_theme = App_Thm_Dusk
}
Desired_OS_Scheduler_MS :: 1
sleep_is_granular = set__scheduler_granularity( Desired_OS_Scheduler_MS )
// Rough setup of window with rl stuff
{
// rl.Odin_SetMalloc( RL_MALLOC )
rl.SetConfigFlags( {
rl.ConfigFlag.WINDOW_RESIZABLE,
rl.ConfigFlag.WINDOW_TOPMOST,
})
window_width : i32 = cast(i32) config.resolution_width
window_height : i32 = cast(i32) config.resolution_height
win_title : cstring = "Sectr Prototype"
rl.InitWindow( window_width, window_height, win_title )
log( "Raylib initialized and window opened" )
window := & state.app_window
window.extent.x = f32(window_width) * 0.5
window.extent.y = f32(window_height) * 0.5
// We do not support non-uniform DPI.
window.dpi_scale = rl.GetWindowScaleDPI().x
window.ppcm = os_default_ppcm * window.dpi_scale
// Determining current monitor and setting the target frametime based on it..
monitor_id = rl.GetCurrentMonitor()
monitor_refresh_hz = rl.GetMonitorRefreshRate( monitor_id )
if config.engine_refresh_hz == 0 {
config.engine_refresh_hz = uint(monitor_refresh_hz)
}
}
// Basic Font Setup
{
font_provider_startup()
// path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" })
// font_rec_mono_semicasual_reg = font_load( path_rec_mono_semicasual_reg, 24.0, "RecMonoSemiCasual_Regular" )
// path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } )
// font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" )
path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" }, transient_allocator() )
font_firacode = font_load( path_firacode, 24.0, "FiraCode" )
default_font = font_firacode
log( "Default font loaded" )
}
// Setup the screen ui state
{
ui_startup( & screen_ui.base, cache_allocator = persistent_slab_allocator() )
ui_floating_startup( & screen_ui.floating, persistent_slab_allocator(), 1 * Kilobyte, 1 * Kilobyte, "screen ui floating manager" )
using screen_ui
menu_bar.pos = { -60, 0 }
// menu_bar.pos = Vec2(app_window.extent) * { -1, 1 }
menu_bar.size = {140, 40}
settings_menu.min_size = {250, 200}
}
// Demo project setup
// TODO(Ed): This will eventually have to occur when the user either creates or loads a workspace. I don't know
{
using project
path = str_intern("./")
name = str_intern( "First Project" )
workspace.name = str_intern( "First Workspace" )
{
using project.workspace
cam = {
target = { 0, 0 },
offset = transmute(Vec2) app_window.extent,
rotation = 0,
zoom = 1.0,
}
// cam = {
// position = { 0, 0, -100 },
// target = { 0, 0, 0 },
// up = { 0, 1, 0 },
// fovy = 90,
// projection = rl.CameraProjection.ORTHOGRAPHIC,
// }
// Setup workspace UI state
ui_startup( & workspace.ui, cache_allocator = persistent_slab_allocator() )
}
debug.path_lorem = str_fmt("C:/projects/SectrPrototype/examples/Lorem Ipsum.txt", allocator = persistent_slab_allocator())
alloc_error : AllocatorError; success : bool
debug.lorem_content, success = os.read_entire_file( debug.path_lorem, persistent_slab_allocator() )
debug.lorem_parse, alloc_error = pws_parser_parse( transmute(string) debug.lorem_content, persistent_slab_allocator() )
verify( alloc_error == .None, "Faield to parse due to allocation failure" )
// Render texture test
// debug.viewport_rt = rl.LoadRenderTexture( 1280, 720 )
// debug.proto_text_shader = rl.LoadShader( "C:/projects/SectrPrototype/code/shaders/text_shader.vs", "C:/projects/SectrPrototype/code/shaders/text_shader.fs" )
}
startup_ms := duration_ms( time.tick_lap_time( & startup_tick))
log( str_fmt_tmp("Startup time: %v ms", startup_ms) )
// Make sure to cleanup transient before continuing...
// From here on, tarnsinet usage has to be done with care.
// For most cases, the frame allocator should be more than enough.
}
// For some reason odin's symbols conflict with native foreign symbols...
@export
sectr_shutdown :: proc()
{
context.logger = to_odin_logger( & Memory_App.logger )
if Memory_App.persistent == nil {
return
}
state := get_state()
// Replay
{
file_close( Memory_App.replay.active_file )
}
font_provider_shutdown()
log("Module shutdown complete")
}
@export
reload :: proc( prof : ^SpallProfiler, persistent_mem, frame_mem, transient_mem, files_buffer_mem : ^VArena, host_logger : ^ Logger )
{
spall.SCOPED_EVENT( & prof.ctx, & prof.buffer, #procedure )
Memory_App.profiler = prof
context.logger = to_odin_logger( & Memory_App.logger )
using Memory_App;
persistent = persistent_mem
frame = frame_mem
transient = transient_mem
files_buffer = files_buffer_mem
context.allocator = persistent_allocator()
context.temp_allocator = transient_allocator()
Memory_App.state = get_state()
using state
// Procedure Addresses are not preserved on hot-reload. They must be restored for persistent data.
// The only way to alleviate this is to either do custom handles to allocators
// Or as done below, correct containers using allocators on reload.
// Thankfully persistent dynamic allocations are rare, and thus we know exactly which ones they are.
slab_reload( persistent_slab, persistent_allocator() )
hmap_chained_reload( font_provider_data.font_cache, persistent_allocator())
slab_reload( string_cache.slab, persistent_allocator() )
zpl_hmap_reload( & string_cache.table, persistent_slab_allocator())
slab_reload( frame_slab, frame_allocator())
slab_reload( transient_slab, transient_allocator())
ui_reload( & get_state().project.workspace.ui, cache_allocator = persistent_slab_allocator() )
log("Module reloaded")
}
@export
tick :: proc( host_delta_time : f64, host_delta_ns : Duration ) -> b32
{
profile( "Client Tick" )
context.logger = to_odin_logger( & Memory_App.logger )
state := get_state(); using state
should_close : b32
client_tick := time.tick_now()
{
profile("Work frame")
// Setup Frame Slab
{
alloc_error : AllocatorError
frame_slab, alloc_error = slab_init( & default_slab_policy, bucket_reserve_num = 0,
allocator = frame_allocator(),
dbg_name = Frame_Slab_DBG_Name,
should_zero_buckets = true )
verify( alloc_error == .None, "Failed to allocate frame slab" )
}
context.allocator = frame_slab_allocator()
context.temp_allocator = transient_allocator()
rl.PollInputEvents()
debug.draw_ui_box_bounds_points = false
debug.draw_UI_padding_bounds = false
debug.draw_ui_content_bounds = false
// config.color_theme = App_Thm_Light
// config.color_theme = App_Thm_Dusk
config.color_theme = App_Thm_Dark
should_close = update( host_delta_time )
render()
rl.SwapScreenBuffer()
}
// Timing
{
// profile("Client tick timing processing")
// config.engine_refresh_hz = uint(monitor_refresh_hz)
// config.engine_refresh_hz = 6
frametime_target_ms = 1.0 / f64(config.engine_refresh_hz) * S_To_MS
sub_ms_granularity_required := frametime_target_ms <= Frametime_High_Perf_Threshold_MS
frametime_delta_ns = time.tick_lap_time( & client_tick )
frametime_delta_ms = duration_ms( frametime_delta_ns )
frametime_delta_seconds = duration_seconds( frametime_delta_ns )
frametime_elapsed_ms = frametime_delta_ms + host_delta_time
if frametime_elapsed_ms < frametime_target_ms
{
sleep_ms := frametime_target_ms - frametime_elapsed_ms
pre_sleep_tick := time.tick_now()
if sleep_ms > 0 {
thread_sleep( cast(Duration) sleep_ms * MS_To_NS )
// thread__highres_wait( sleep_ms )
}
sleep_delta_ns := time.tick_lap_time( & pre_sleep_tick)
sleep_delta_ms := duration_ms( sleep_delta_ns )
if sleep_delta_ms < sleep_ms {
// log( str_fmt_tmp("frametime sleep was off by: %v ms", sleep_delta_ms - sleep_ms ))
}
frametime_elapsed_ms += sleep_delta_ms
for ; frametime_elapsed_ms < frametime_target_ms; {
sleep_delta_ns = time.tick_lap_time( & pre_sleep_tick)
sleep_delta_ms = duration_ms( sleep_delta_ns )
frametime_elapsed_ms += sleep_delta_ms
}
}
config.timing_fps_moving_avg_alpha = 0.99
frametime_avg_ms = mov_avg_exp( f64(config.timing_fps_moving_avg_alpha), frametime_elapsed_ms, frametime_avg_ms )
fps_avg = 1 / (frametime_avg_ms * MS_To_S)
if frametime_elapsed_ms > 60.0 {
context.allocator = transient_allocator()
log( str_fmt("Big tick! %v ms", frametime_elapsed_ms), LogLevel.Warning )
}
}
return should_close
}
@export
clean_frame :: proc()
{
// profile( #procedure)
state := get_state(); using state
context.logger = to_odin_logger( & Memory_App.logger )
free_all( frame_allocator() )
transient_clear_elapsed += frametime_delta32()
if transient_clear_elapsed >= transient_clear_time && ! transinet_clear_lock
{
transient_clear_elapsed = 0
free_all( transient_allocator() )
alloc_error : AllocatorError
transient_slab, alloc_error = slab_init( & default_slab_policy, allocator = transient_allocator(), dbg_name = Transient_Slab_DBG_Name )
verify( alloc_error == .None, "Failed to allocate transient slab" )
}
}
} // when false

View File

@ -26,8 +26,9 @@ sokol_app_frame_callback :: proc "c" () {
window := & state.app_window
if int(window.extent.x) != int(sokol_width) || int(window.extent.y) != int(sokol_height) {
window.extent.x = sokol_width
window.extent.y = sokol_height
window.resized = true
window.extent.x = sokol_width * 0.5
window.extent.y = sokol_height * 0.5
log("sokol_app: Event-based frame callback triggered (detected a resize")
}
@ -38,6 +39,8 @@ sokol_app_frame_callback :: proc "c" () {
client_tick := time.tick_now()
should_close |= tick_work_frame( sokol_delta_ms )
tick_frametime( & client_tick, sokol_delta_ms, sokol_delta_ns )
window.resized = false
}
sokol_app_cleanup_callback :: proc "c" () {

View File

@ -1,403 +0,0 @@
package sectr
import "core:fmt"
import rl "vendor:raylib"
when false {
range2_to_rl_rect :: #force_inline proc "contextless"( range : Range2 ) -> rl.Rectangle
{
rect := rl.Rectangle {
range.min.x,
range.max.y,
abs(range.max.x - range.min.x),
abs(range.max.y - range.min.y),
}
return rect
}
draw_rectangle :: #force_inline proc "contextless" ( rect : rl.Rectangle, box : ^UI_RenderBoxInfo ) {
using box
if style.corner_radii[0] > 0 {
rl.DrawRectangleRounded( rect, style.corner_radii[0], 9, style.bg_color )
}
else {
rl.DrawRectangleRec( rect, style.bg_color )
}
}
draw_rectangle_lines :: #force_inline proc "contextless" ( rect : rl.Rectangle, box : ^UI_RenderBoxInfo, color : Color, thickness : f32 ) {
using box
if style.corner_radii[0] > 0 {
rl.DrawRectangleRoundedLines( rect, style.corner_radii[0], 9, thickness, color )
}
else {
rl.DrawRectangleLinesEx( rect, thickness, color )
}
}
render :: proc()
{
profile(#procedure)
state := get_state(); using state
render_mode_3d()
rl.BeginDrawing()
rl.ClearBackground( app_color_theme().bg )
render_mode_2d_workspace()
render_mode_screenspace()
rl.EndDrawing()
}
// Experimental 3d viewport, not really the focus of this prototype
// Until we can have a native or interpreted program render to it its not very useful.
// Note(Ed): Other usecase could be 3d vis notes & math/graphical debug
render_mode_3d :: proc()
{
profile(#procedure)
state := get_state(); using state
rl.BeginDrawing()
rl.BeginTextureMode( debug.viewport_rt )
rl.BeginMode3D( debug.cam_vp )
rl.ClearBackground( Color_3D_BG )
rl.EndMode3D()
rl.EndTextureMode()
rl.EndDrawing()
}
// TODO(Ed): Eventually this needs to become a 'viewport within a UI'
// This would allow the user to have more than one workspace open at the same time
render_mode_2d_workspace :: proc()
{
profile(#procedure)
state := get_state(); using state
cam := & project.workspace.cam
win_extent := state.app_window.extent
rl.BeginMode2D( project.workspace.cam )
// Draw 3D Viewport
when false
{
viewport_size := Vec2 { 1280.0, 720.0 }
vp_half_size := viewport_size * 0.5
viewport_box := range2( -vp_half_size, vp_half_size )
viewport_render := range2(
ws_view_to_render_pos( viewport_box.min),
ws_view_to_render_pos( viewport_box.max),
)
viewport_rect := range2_to_rl_rect( viewport_render )
rl.DrawTextureRec( debug.viewport_rt.texture, viewport_rect, -vp_half_size, Color_White )
}
// draw_text( "This is text in world space", { 0, 200 }, 16.0 )
cam_zoom_ratio := 1.0 / cam.zoom
view_bounds := view_get_bounds()
when false
{
render_view := Range2 { pts = {
ws_view_to_render_pos( view_bounds.min),
ws_view_to_render_pos( view_bounds.max),
}}
view_rect := rl.Rectangle {
render_view.min.x,
render_view.max.y,
abs(render_view.max.x - render_view.min.x),
abs(render_view.max.y - render_view.min.y),
}
rl.DrawRectangleRounded( view_rect, 0.3, 9, { 255, 0, 0, 20 } )
}
ImguiRender:
{
profile("Imgui Render")
ui := & state.project.workspace.ui
root := ui.root
if root == nil || root.num_children == 0 {
break ImguiRender
}
state.ui_context = ui
current := root.first
for & current in array_to_slice(ui.render_queue)
{
profile("Box")
style := current.style
computed := current.computed
if ! intersects_range2( view_bounds, computed.bounds ) {
continue
}
profile_begin("render space calc")
render_bounds := range2(
ws_view_to_render_pos(computed.bounds.min),
ws_view_to_render_pos(computed.bounds.max),
)
render_padding := range2(
ws_view_to_render_pos(computed.padding.min),
ws_view_to_render_pos(computed.padding.max),
)
render_content := range2(
ws_view_to_render_pos(computed.content.min),
ws_view_to_render_pos(computed.content.max),
)
rect_bounds := range2_to_rl_rect( render_bounds )
rect_padding := range2_to_rl_rect( render_padding )
rect_content := range2_to_rl_rect( render_content )
profile_end()
profile_begin("raylib drawing")
if style.bg_color.a != 0
{
draw_rectangle( rect_bounds, & current )
}
if current.border_width > 0 {
draw_rectangle_lines( rect_bounds, & current, style.border_color, current.border_width )
}
line_thickness := 1 * cam_zoom_ratio
if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) {
draw_rectangle_lines( rect_padding, & current, Color_Debug_UI_Padding_Bounds, line_thickness )
}
else if debug.draw_ui_content_bounds {
draw_rectangle_lines( rect_content, & current, Color_Debug_UI_Content_Bounds, line_thickness )
}
point_radius := 3 * cam_zoom_ratio
// profile_begin("circles")
if debug.draw_ui_box_bounds_points
{
computed_size := computed.bounds.p1 - computed.bounds.p0
// center := Vec2 {
// render_bounds.p0.x + computed_size.x * 0.5,
// render_bounds.p0.y - computed_size.y * 0.5,
// }
// rl.DrawCircleV( center, point_radius, Color_White )
rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
}
// profile_end()
profile_end()
if len(current.text.str) > 0 {
ws_view_draw_text( current.text, ws_view_to_render_pos(computed.text_pos * {1, -1}), current.font_size, style.text_color )
}
}
}
//endregion Imgui Render
if debug.mouse_vis {
cursor_world_pos := screen_to_ws_view_pos(input.mouse.pos)
rl.DrawCircleV( ws_view_to_render_pos(cursor_world_pos), 5, Color_GreyRed )
}
rl.DrawCircleV( { 0, 0 }, 1 * cam_zoom_ratio, Color_White )
rl.EndMode2D()
}
render_mode_screenspace :: proc ()
{
profile("Render Screenspace")
state := get_state(); using state
replay := & Memory_App.replay
cam := & project.workspace.cam
win_extent := state.app_window.extent
render_screen_ui()
debug_text :: proc( format : string, args : ..any )
{
@static draw_text_scratch : [Kilobyte * 64]u8
state := get_state(); using state
if debug.draw_debug_text_y > 800 {
debug.draw_debug_text_y = 0
}
cam := & project.workspace.cam
screen_corners := screen_get_corners()
position := screen_corners.top_right
position.x -= app_window.extent.x
position.y -= debug.draw_debug_text_y
content := str_fmt_buffer( draw_text_scratch[:], format, ..args )
debug_draw_text( content, position, 12.0 )
debug.draw_debug_text_y += 14
}
if debug.debug_text_vis
{
fps_msg := str_fmt( "FPS: %f", fps_avg)
fps_msg_width := measure_text_size( fps_msg, default_font, 12.0, 0.0 ).x
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } - { 5, 5 }
debug_draw_text( fps_msg, fps_msg_pos, 12.0, color = rl.GREEN )
// debug_text( "Screen Width : %v", rl.GetScreenWidth () )
// debug_text( "Screen Height: %v", rl.GetScreenHeight() )
// debug_text( "frametime_target_ms : %f ms", frametime_target_ms )
debug_text( "frametime : %f ms", frametime_delta_ms )
// debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms )
if replay.mode == ReplayMode.Record {
debug_text( "Recording Input")
}
if replay.mode == ReplayMode.Playback {
debug_text( "Replaying Input")
}
// debug_text("Zoom Target: %v", project.workspace.zoom_target)
if debug.mouse_vis {
debug_text("Mouse Vertical Wheel: %v", input.mouse.vertical_wheel )
debug_text("Mouse Delta : %v", input.mouse.delta )
debug_text("Mouse Position (Render) : %v", input.mouse.raw_pos )
debug_text("Mouse Position (Screen) : %v", input.mouse.pos )
debug_text("Mouse Position (Workspace View): %v", screen_to_ws_view_pos(input.mouse.pos) )
rl.DrawCircleV( input.mouse.raw_pos, 10, Color_White_A125 )
rl.DrawCircleV( screen_to_render_pos(input.mouse.pos), 2, Color_BG )
}
ui := & project.workspace.ui
if true
{
debug_text("Box Count (Workspace): %v", ui.built_box_count )
hot_box := ui_box_from_key( ui.curr_cache, ui.hot )
active_box := ui_box_from_key( ui.curr_cache, ui.active )
if hot_box != nil {
debug_text("Worksapce Hot Box : %v", hot_box.label.str )
debug_text("Workspace Hot Range2: %v", hot_box.computed.bounds.pts)
}
if active_box != nil{
debug_text("Workspace Active Box: %v", active_box.label.str )
}
}
ui = & screen_ui
if true
{
debug_text("Box Count: %v", ui.built_box_count )
hot_box := ui_box_from_key( ui.curr_cache, ui.hot )
active_box := ui_box_from_key( ui.curr_cache, ui.active )
if hot_box != nil {
debug_text("Hot Box : %v", hot_box.label.str )
debug_text("Hot Range2: %v", hot_box.computed.bounds.pts)
}
if active_box != nil{
debug_text("Active Box: %v", active_box.label.str )
}
}
debug.draw_debug_text_y = 14
}
}
// A non-zoomable static-view for ui
// Only a scalar factor may be applied to the size of widgets & fonts
// 'Window tiled' panels reside here
render_screen_ui :: proc()
{
profile(#procedure)
using state := get_state()
//region App UI
Render_App_UI:
{
profile("App UI")
ui := & state.screen_ui
state.ui_context = ui
root := ui.root
if root.num_children == 0 {
break Render_App_UI
}
for & current in array_to_slice(ui.render_queue)
{
profile("Box")
style := current.style
computed := & current.computed
profile_begin("Coordinate space conversion")
render_bounds := range2(
screen_to_render_pos(computed.bounds.min),
screen_to_render_pos(computed.bounds.max),
)
render_padding := range2(
screen_to_render_pos(computed.padding.min),
screen_to_render_pos(computed.padding.max),
)
render_content := range2(
screen_to_render_pos(computed.content.min),
screen_to_render_pos(computed.content.max),
)
rect_bounds := range2_to_rl_rect( render_bounds )
rect_padding := range2_to_rl_rect( render_padding )
rect_content := range2_to_rl_rect( render_content )
profile_end()
profile_begin("raylib drawing")
if style.bg_color.a != 0
{
draw_rectangle( rect_bounds, & current )
}
if current.border_width > 0 {
draw_rectangle_lines( rect_bounds, & current, style.border_color, current.border_width )
}
line_thickness : f32 = 1
if debug.draw_UI_padding_bounds && equal_range2(computed.content, computed.padding) {
draw_rectangle_lines( rect_padding, & current, Color_Debug_UI_Padding_Bounds, line_thickness )
}
else if debug.draw_ui_content_bounds {
draw_rectangle_lines( rect_content, & current, Color_Debug_UI_Content_Bounds, line_thickness )
}
point_radius : f32 = 3
if debug.draw_ui_box_bounds_points
{
computed_size := computed.bounds.p1 - computed.bounds.p0
if false
{
center := Vec2 {
render_bounds.p0.x + computed_size.x * 0.5,
render_bounds.p0.y - computed_size.y * 0.5,
}
rl.DrawCircleV( center, point_radius, Color_White )
}
rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
}
if len(current.text.str) > 0 && style.font.key != 0 {
draw_text_screenspace( current.text, screen_to_render_pos(computed.text_pos), current.font_size, style.text_color )
}
profile_end()
}
}
//endregion App UI
}
} // when false

View File

@ -22,7 +22,7 @@ render :: proc()
}
// Triangle Demo
if true
if false
{
using debug.gfx_tri_demo_state
sokol_gfx.begin_pass(sokol_gfx.Pass { action = pass_action, swapchain = sokol_glue.swapchain() })
@ -34,4 +34,14 @@ render :: proc()
sokol_gfx.end_pass()
sokol_gfx.commit()
}
// Batching Enqueue Boxes
// Mixed with the batching enqueue for text
//Begin
// Flush boxs
// flush text
// End
}

View File

@ -1,226 +0,0 @@
package sectr
import "core:math"
import "core:strings"
import "core:unicode/utf8"
import rl "vendor:raylib"
when false {
debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
{
// profile(#procedure)
state := get_state(); using state
if len( content ) == 0 {
return
}
runes, alloc_error := to_runes( content, frame_allocator() )
// runes, alloc_error := to_runes( content, context.temp_allocator )
// verify( alloc_error == AllocatorError.None, "Failed to temp allocate runes" )
font := font
if font.key == Font_Default.key {
// if ( len(font) == 0 ) {
font = default_font
}
pos := screen_to_render_pos(pos)
px_size := size
rl_font := to_rl_Font(font, px_size )
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
rl.DrawTextCodepoints( rl_font,
raw_data(runes), cast(i32) len(runes),
position = transmute(rl.Vector2) pos,
fontSize = px_size,
spacing = 0.0,
tint = color );
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
}
draw_text_screenspace :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
{
// profile(#procedure)
state := get_state(); using state
if len( content.str ) == 0 {
return
}
font := font
if font.key == Font_Default.key {
font = default_font
}
pos := pos
rl_font := to_rl_Font(font, size )
runes := content.runes
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
rl.DrawTextCodepoints( rl_font,
raw_data(runes), cast(i32) len(runes),
position = transmute(rl.Vector2) pos,
fontSize = size,
spacing = 0.0,
tint = color );
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
}
ws_view_draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
{
// profile(#procedure)
state := get_state(); using state
if len( content ) == 0 {
return
}
runes, alloc_error := to_runes( content, frame_allocator() )
verify( alloc_error == AllocatorError.None, "Failed to temp allocate runes" )
font := font
if font.key == Font_Default.key {
// if len(font) == 0 {
font = default_font
}
pos := ws_view_to_render_pos(pos)
px_size := size
zoom_adjust := px_size * project.workspace.cam.zoom
rl_font := to_rl_Font(font, zoom_adjust )
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
rl.DrawTextCodepoints( rl_font,
raw_data(runes), cast(i32) len(runes),
position = transmute(rl.Vector2) pos,
fontSize = px_size,
spacing = 0.0,
tint = color );
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
}
when true
{
ws_view_draw_text_StrRunesPair :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
{
profile(#procedure)
state := get_state(); using state
if len( content.str ) == 0 {
return
}
font := font
if font.key == Font_Default.key {
font = default_font
}
pos := ws_view_to_render_pos(pos)
px_size := size
zoom_adjust := px_size * project.workspace.cam.zoom
rl_font := to_rl_Font(font, zoom_adjust )
runes := content.runes
profile_begin("raylib draw codepoints related")
// rl.DrawTextCodepoints( rl_font,
// raw_data(runes), cast(i32) len(runes),
// position = transmute(rl.Vector2) pos,
// fontSize = px_size,
// spacing = 0.0,
// tint = color );
rl.DrawTextEx(rl_font,
strings.clone_to_cstring(content.str),
position = transmute(rl.Vector2) pos,
fontSize = px_size,
spacing = 0.0,
tint = color
)
profile_end()
}
}
else
{
ws_view_draw_text_StrRunesPair :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
{
profile(#procedure)
state := get_state(); using state
// We need an alternative way to draw text to the screen (the above is way to expensive)
// Possibly need to watch handmade hero...
}
}
// Raylib's equivalent doesn't take a length for the string (making it a pain in the ass)
// So this is a 1:1 copy except it takes Odin strings
measure_text_size_raylib :: proc( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2
{
// profile(#procedure)
px_size := math.round( points_to_pixels( font_size ) )
rl_font := to_rl_Font( font, font_size )
// This is a static var within raylib. We don't have getter access to it.
// Note(Ed) : raylib font size is in pixels so this is also.
@static text_line_spacing : f32 = 15
text_size : Vec2
if rl_font.texture.id == 0 || len(text) == 0 {
return text_size
}
temp_byte_counter : i32 = 0 // Used to count longer text line num chars
byte_counter : i32 = 0
text_width : f32 = 0.0
temp_text_width : f32 = 0.0 // Used to counter longer text line width
text_height := cast(f32) rl_font.baseSize
scale_factor := px_size / text_height
letter : rune
index : i32 = 0
for id : i32 = 0; id < i32(len(text));
{
byte_counter += 1
next : i32 = 0
ctext := cast(cstring) ( & raw_data( text )[id] )
letter = rl.GetCodepointNext( ctext, & next )
index = rl.GetGlyphIndex( rl_font, letter )
id += 1
if letter != rune('\n')
{
if rl_font.glyphs[index].advanceX != 0 {
text_width += f32(rl_font.glyphs[index].advanceX)
}
else {
text_width += rl_font.recs[index].width + f32(rl_font.glyphs[index].offsetX)
}
}
else
{
if temp_text_width < text_width {
temp_text_width = text_width
}
byte_counter = 0
text_width = 0
text_height += text_line_spacing
if temp_byte_counter < byte_counter {
temp_byte_counter = byte_counter
}
}
}
if temp_text_width < text_width {
temp_text_width = text_width
}
text_size.x = temp_text_width * scale_factor + f32(temp_byte_counter - 1) * spacing
text_size.y = text_height * scale_factor
return text_size
}
} // when false

View File

@ -6,8 +6,6 @@ import "core:math/linalg"
import "core:os"
import str "core:strings"
import rl "vendor:raylib"
DebugActions :: struct {
load_project : b32,
save_project : b32,
@ -75,12 +73,9 @@ update :: proc( delta_time : f64 ) -> b32
workspace := & project.workspace
cam := & workspace.cam
if rl.IsWindowResized() {
window := & state.app_window
window.extent.x = f32(rl.GetScreenWidth()) * 0.5
window.extent.y = f32(rl.GetScreenHeight()) * 0.5
project.workspace.cam.offset = transmute(Vec2) window.extent
window := & state.app_window
if window.resized {
project.workspace.cam.view = transmute(Vec2) window.extent
}
state.input, state.input_prev = swap( state.input, state.input_prev )
@ -189,13 +184,13 @@ update :: proc( delta_time : f64 ) -> b32
- cast(f32) i32(debug_actions.cam_move_up) + cast(f32) i32(debug_actions.cam_move_down),
}
move_velocity *= digital_move_speed * f32(delta_time)
cam.target += move_velocity
cam.position += move_velocity
if debug_actions.cam_mouse_pan
{
if is_within_screenspace(input.mouse.pos) {
pan_velocity := input.mouse.delta * vec2(1, -1) * ( 1 / cam.zoom )
cam.target -= pan_velocity
cam.position -= pan_velocity
}
}
}
@ -252,6 +247,7 @@ update :: proc( delta_time : f64 ) -> b32
debug.last_mouse_pos = input.mouse.pos
should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose()
// should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose()
should_shutdown : b32 = false
return should_shutdown
}

View File

@ -1,2 +1,2 @@
package sectr
package sectr

View File

@ -0,0 +1,353 @@
/*
Yet another port of fontstash.
I decided t use this instead of the odin port as it already deviated from the original impl.
So The code was small enough that I mine as well learn it by porting for my use case.
Original copyright for fonstash.h:
------------------------------------------------------------------------------
Copyright (c) 2009-2013 Mikko Mononen memon@inside.org
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
------------------------------------------------------------------------------
*/
package sectr
import stbtt "vendor:stb/truetype"
Range2_i16 :: struct #raw_union {
using pts : Vec2_i16,
using xy : struct {
x0, y0, x1, y1 : i16,
}
}
Vec2_i16 :: [2]i16
FSTASH_Invalid :: -1
FSTASH_Hash_Lut_Size :: 256
FSTASH_Max_Fallbacks :: 20
FSTASH_Max_States :: 20
FSTASH_Vertex_Count :: 1024
FSTASH_Init_Atlas_Nodes :: 256
FSTASH_FontLuts :: [FSTASH_Hash_Lut_Size]i32
FSTASH_FontFallbacks :: [FSTASH_Max_Fallbacks]i32
FSTASH_HandleErrorProc :: #type proc( uptr : rawptr, error, val : i32 )
FSTASH_RenderCreateProc :: #type proc( uptr : rawptr, width, height : i32 )
FSTASH_RenderResizeProc :: #type proc( uptr : rawptr, width, height : i32 )
FSTASH_RenderUpdateProc :: #type proc( uptr : rawptr, rect : ^i32, data : ^u8 )
FSTASH_RenderDrawProc :: #type proc( uptr : rawptr, verts : ^f32, tcoords : ^f32, colors : ^i32, num_verts : i32 )
FSTASH_RenderDelete :: #type proc( uptr : rawptr )
FSTASH_AlignFlag :: enum u32 {
Left,
Center,
Right,
Top,
Middle,
Bottom,
Baseline,
}
FSTASH_AlignFlags :: bit_set[ FSTASH_AlignFlag; u32 ]
// FONSflags
FSTASH_QuadLocation :: enum u32 {
Top_Left = 1,
Bottom_Left = 2,
}
FSTASH_Atlas :: struct {
dud : i32,
}
FSTASH_AtlasNode :: struct {
x, y, width : i16,
}
FSTASH_ErrorCode :: enum u32 {
Atlas_Full,
Scratch_Full,
States_Overflow,
States_Underflow,
}
FSTASH_Quad :: struct {
x0, y0, s0, t0 : f32,
x1, y1, s1, t1 : f32,
}
FSTASH_Font :: struct {
info : stbtt.fontinfo,
name : string,
data : []byte,
free_data : bool,
ascender : f32,
descender : f32,
line_height : f32,
glyphs : Array(FSTASH_Glyph),
lut : FSTASH_FontLuts,
fallbacks : FSTASH_FontFallbacks,
num_fallbacks : i32,
}
FSTASH_Glyph :: struct {
codepoint : rune,
index, next : i32,
size, blur : i16,
x_advance : i16,
box : Range2_i16,
offset : Vec2_i16,
}
FSTASH_Params :: struct {
width, height : i32,
quad_location : FSTASH_QuadLocation, // (flags)
render_create : FSTASH_RenderCreateProc,
render_resize : FSTASH_RenderResizeProc,
render_update : FSTASH_RenderUpdateProc,
render_draw : FSTASH_RenderDrawProc,
render_delete : FSTASH_RenderDelete,
}
FSTASH_State :: struct {
font : i32,
alignment : i32,
size : f32,
color : [4]u8,
blur : f32,
spacing : f32,
}
FSTASH_TextIter :: struct {
x, y : f32,
next_x, next_y : f32,
scale, spacing : f32,
isize, iblur : i16,
font : ^FSTASH_Font,
prev_glyph_id : i32,
codepoint : rune,
utf8_state : rune,
str : string,
next : string,
end : string,
}
FSTASH_Context :: struct {
params : FSTASH_Params,
// Atlas
atlas : Array(FSTASH_AtlasNode),
texture_data : []byte,
width, height : i32,
// ----
normalized_size : Vec2,
verts : [FSTASH_Vertex_Count * 2]f32,
tcoords : [FSTASH_Vertex_Count * 2]f32,
colors : [FSTASH_Vertex_Count ]f32,
states : [FSTASH_Max_States]FSTASH_State,
num_states : i32,
handle_error : FSTASH_HandleErrorProc,
error_uptr : rawptr,
}
fstash_decode_utf8 :: proc( state : ^rune, codepoint : ^rune, to_decode : byte ) -> bool
{
UTF8_Accept :: 0
UTF8_Reject :: 1
@static UTF8_Decode_Table := [?]u8 {
// The first part of the table maps bytes to character classes that
// to reduce the size of the transition table and create bitmasks.
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5F
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7F
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9F
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // A0..BF
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // C0..DF
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8, // E0..FF
// The second part is a transition table that maps a combination
// of a state of the automaton and a character class to a state.
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
12,36,12,12,12,12,12,12,12,12,12,12,
}
to_decode_rune := rune(to_decode)
type := UTF8_Decode_Table[to_decode_rune]
// Update codepoint otherwise initialize it.
(codepoint^) = ((state^) != UTF8_Accept) ? \
((to_decode_rune & 0x3F) | ((codepoint^) << 6)) \
: ((0xFF >> type) & (to_decode_rune))
(state^) = cast(rune)(UTF8_Decode_Table[256 + (state^) * 16 + rune(type)])
return (state^) == UTF8_Accept
}
fstash_atlas_delete :: proc ( ctx : ^FSTASH_Context ) {
using ctx
array_free( ctx.atlas )
}
fstash_atlas_expand :: proc( ctx : ^FSTASH_Context, width, height : i32 )
{
if width > ctx.width {
fstash_atlas_insert( ctx, ctx.atlas.num, ctx.width, 0, width - ctx.width )
}
ctx.width = width
ctx.height = height
}
fstash_atlas_init :: proc( ctx : ^FSTASH_Context, width, height : i32, num_nodes : u32 = FSTASH_Init_Atlas_Nodes )
{
error : AllocatorError
ctx.atlas, error = array_init_reserve( FSTASH_AtlasNode, context.allocator, u64(num_nodes), dbg_name = "font atlas" )
ensure(error != AllocatorError.None, "Failed to allocate font atlas")
ctx.width = width
ctx.height = height
array_append( & ctx.atlas, FSTASH_AtlasNode{ width = i16(width)} )
}
fstash_atlas_insert :: proc( ctx : ^FSTASH_Context, id : u64, x, y, width : i32 ) -> (error : AllocatorError)
{
error = array_append_at( & ctx.atlas, FSTASH_AtlasNode{ i16(x), i16(y), i16(width) }, id )
return
}
fstash_atlas_remove :: proc( ctx : ^FSTASH_Context, id : u64 )
{
array_remove_at( ctx.atlas, id )
}
fstash_atlas_reset :: proc( ctx : ^FSTASH_Context, width, height : i32 )
{
ctx.width = width
ctx.height = height
array_clear( ctx.atlas )
array_append( & ctx.atlas, FSTASH_AtlasNode{ width = i16(width)} )
}
fstash_atlas_add_skyline_level :: proc (ctx : ^FSTASH_Context, id : u64, x, y, width, height : i32 ) -> (error : AllocatorError)
{
insert :: fstash_atlas_insert
remove :: fstash_atlas_remove
error = insert( ctx, id, x, y + height, width)
if error != AllocatorError.None {
ensure( false, "Failed to insert into atlas")
return
}
// Delete skyline segments that fall under the shadow of the new segment.
for sky_id := id; sky_id < ctx.atlas.num; sky_id += 1
{
curr := & ctx.atlas.data[sky_id ]
next := & ctx.atlas.data[sky_id + 1]
if curr.x >= next.x + next.width do break
shrink := i16(next.x + next.width - curr.x)
curr.x += shrink
curr.width -= shrink
if curr.width > 0 do break
remove(ctx, sky_id)
sky_id -= 1
}
// Merge same height skyline segments that are next to each other.
for sky_id := id; sky_id < ctx.atlas.num - 1;
{
curr := & ctx.atlas.data[sky_id ]
next := & ctx.atlas.data[sky_id + 1]
if curr.y == next.y {
curr.width += next.width
remove(ctx, sky_id + 1)
}
else {
sky_id += 1
}
}
return
}
fstash_atlas_rect_fits :: proc( ctx : ^FSTASH_Context, location, width, height : i32 ) -> (max_height : i32)
{
// Checks if there is enough space at the location of skyline span 'i',
// and return the max height of all skyline spans under that at that location,
// (think tetris block being dropped at that position). Or -1 if no space found.
atlas := array_to_slice(ctx.atlas)
node := atlas[location]
space_left : i32
if i32(node.x) + width > ctx.width {
max_height = -1
return
}
space_left = width;
y := i32(node.y)
location := location
for ; space_left > 0;
{
if u64(location) == ctx.atlas.num {
max_height = -1
return
}
node := atlas[location]
y := max(y, i32(node.y))
if y + height > ctx.height {
max_height = -1
return
}
space_left -= i32(node.width)
location += 1
}
max_height = y
return
}
fstash_atlas_add_rect :: proc( ctx : ^FSTASH_Context, )
{
}

View File

@ -1,13 +1,5 @@
package sectr
import "core:fmt"
import "core:math"
import "core:mem"
import "core:path/filepath"
import "core:os"
import rl "vendor:raylib"
Font_Largest_Px_Size :: 32
Font_Size_Interval :: 2
@ -35,158 +27,30 @@ FontTag :: struct {
point_size : f32
}
FontGlyphsRender :: struct {
size : i32,
count : i32,
padding : i32,
texture : rl.Texture2D,
recs : [^]rl.Rectangle, // Characters rectangles in texture
glyphs : [^]rl.GlyphInfo, // Characters info data
}
FontDef :: struct {
path_file : string,
// TODO(Ed) : you may have to store font data in the future if we render on demand
// data : []u8,
default_size : i32,
size_table : [Font_Largest_Px_Size / Font_Size_Interval] FontGlyphsRender,
placeholder : int,
}
FontProviderData :: struct {
// font_cache : HMapZPL(FontDef),
font_cache : HMapChainedPtr(FontDef),
}
font_provider_startup :: proc()
{
profile(#procedure)
state := get_state()
font_provider_data := & get_state().font_provider_data; using font_provider_data
font_cache_alloc_error : AllocatorError
font_cache, font_cache_alloc_error = hmap_chained_init(FontDef, hmap_closest_prime(1 * Kilo), persistent_allocator(), dbg_name = "font_cache" )
verify( font_cache_alloc_error == AllocatorError.None, "Failed to allocate font_cache" )
log("font_cache created")
log("font_provider initialized")
}
font_provider_shutdown :: proc()
{
font_provider_data := & get_state().font_provider_data; using font_provider_data
for & entry in font_cache.lookup
{
if entry == nil do continue
def := entry.value
for & px_render in def.size_table {
using px_render
rl.UnloadFontData( glyphs, count )
rl.UnloadTexture ( texture )
rl.MemFree( recs )
}
}
}
font_load :: proc( path_file : string,
font_load :: proc(ath_file : string,
default_size : f32 = Font_Load_Use_Default_Size,
desired_id : string = Font_Load_Gen_ID
) -> FontID
{
profile(#procedure)
log( str_fmt("Loading font: %v", path_file))
font_provider_data := & get_state().font_provider_data; using font_provider_data
font_data, read_succeded : = os.read_entire_file( path_file )
verify( b32(read_succeded), str_fmt("Failed to read font file for: %v", path_file) )
font_data_size := cast(i32) len(font_data)
desired_id := desired_id
// Use file name as key
if len(desired_id) == 0 {
// NOTE(Ed): This should never be used except for laziness so I'll be throwing a warning everytime.
log("desired_key not provided, using file name. Give it a proper name!", LogLevel.Warning)
// desired_id = cast(FontID) file_name_from_path(path_file)
desired_id = file_name_from_path(path_file)
}
default_size := default_size
if default_size == Font_Load_Use_Default_Size {
default_size = Font_Default_Point_Size
}
key := cast(u64) crc32( transmute([]byte) desired_id )
def, set_error := hmap_chained_set(font_cache, key, FontDef{})
verify( set_error == AllocatorError.None, "Failed to add new font entry to cache" )
def.path_file = path_file
def.default_size = i32(points_to_pixels(default_size))
// TODO(Ed): this is slow & eats quite a bit of memory early on. Setup a more on demand load for a specific size.
// Also, we need to eventually switch to a SDF shader for rendering
// Render all sizes at once
// Note(Ed) : We only generate textures for even multiples of the font.
for font_size : i32 = Font_Size_Interval; font_size <= Font_Largest_Px_Size; font_size += Font_Size_Interval
{
profile("font size render")
id := (font_size / Font_Size_Interval) + (font_size % Font_Size_Interval)
px_render := & def.size_table[id - 1]
using px_render
size = font_size
count = 95 // This is the default codepoint count from raylib when loading a font.
padding = Font_TTF_Default_Chars_Padding
glyphs = rl.LoadFontData( raw_data(font_data), font_data_size,
fontSize = size,
codepoints = nil,
codepointCount = count,
type = rl.FontType.DEFAULT )
verify( glyphs != nil, str_fmt("Failed to load glyphs for font: %v at desired size: %v", desired_id, size ) )
atlas := rl.GenImageFontAtlas( glyphs, & recs, count, size, padding, i32(Font_Atlas_Packing_Method.Raylib_Basic) )
texture = rl.LoadTextureFromImage( atlas )
// glyphs_slice := slice_ptr( glyphs, count )
// for glyph in glyphs_slice {
// TODO(Ed) : See if above can properly reference
// NOTE(raylib): Update glyphs[i].image to use alpha, required to be used on image_draw_text()
for glyph_id : i32 = 0; glyph_id < count; glyph_id += 1 {
glyph := & glyphs[glyph_id]
rl.UnloadImage( glyph.image )
glyph.image = rl.ImageFromImage( atlas, recs[glyph_id] )
}
rl.UnloadImage( atlas )
}
return { key, desired_id }
}
Font_Use_Default_Size :: f32(0.0)
to_rl_Font :: proc( id : FontID, size := Font_Use_Default_Size ) -> rl.Font
{
font_provider_data := & get_state().font_provider_data; using font_provider_data
even_size := math.round(size * (1.0/f32(Font_Size_Interval))) * f32(Font_Size_Interval)
size := clamp( i32( even_size), 4, Font_Largest_Px_Size )
def := hmap_chained_get( font_cache, id.key )
size = size if size != i32(Font_Use_Default_Size) else def.default_size
id := (size / Font_Size_Interval) + (size % Font_Size_Interval)
px_render := & def.size_table[ id - 1 ]
rl_font : rl.Font
rl_font.baseSize = px_render.size
rl_font.glyphCount = px_render.count
rl_font.glyphPadding = px_render.padding
rl_font.glyphs = px_render.glyphs
rl_font.recs = px_render.recs
rl_font.texture = px_render.texture
return rl_font
return {}
}

View File

@ -1,6 +1,27 @@
package sectr
context_ext :: proc( $ Type : typeid ) -> (^Type) {
context_usr :: #force_inline proc( $ Type : typeid ) -> (^Type) {
return cast(^Type) context.user_ptr
}
ContextExt :: struct {
stack : StackFixed(rawptr, 1024),
}
// Assign return value to context.user_ptr
// context_ext_init :: proc() -> rawptr
// {
// }
context_ext :: #force_inline proc() -> ^ContextExt {
return cast(^ContextExt) context.user_ptr
}
context_push :: proc( value : ^($Type) ) {
push( & context_ext().stack, value )
}
context_pop :: proc( value : ^($Type) ) {
pop( & context_ext().stack )
}

View File

@ -19,14 +19,14 @@ MemoryTracker :: struct {
Track_Memory :: false
tracker_msg_buffer : [Kilobyte * 16]u8
// tracker_msg_buffer : [Kilobyte * 16]u8
memtracker_clear :: proc ( tracker : MemoryTracker ) {
when ! Track_Memory {
return
}
temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
context.temp_allocator = arena_allocator(& temp_arena)
// temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
// context.temp_allocator = arena_allocator(& temp_arena)
logf("Clearing tracker: %v", tracker.name)
memtracker_dump_entries(tracker);
@ -38,8 +38,8 @@ memtracker_init :: proc ( tracker : ^MemoryTracker, allocator : Allocator, num_e
when ! Track_Memory {
return
}
temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
context.temp_allocator = arena_allocator(& temp_arena)
// temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
// context.temp_allocator = arena_allocator(& temp_arena)
tracker.name = name
@ -56,8 +56,8 @@ memtracker_register :: proc( tracker : ^MemoryTracker, new_entry : MemoryTracker
return
}
profile(#procedure)
temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
context.temp_allocator = arena_allocator(& temp_arena)
// temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
// context.temp_allocator = arena_allocator(& temp_arena)
if tracker.entries.num == tracker.entries.capacity {
ensure(false, "Memory tracker entries array full, can no longer register any more allocations")
@ -110,8 +110,8 @@ memtracker_unregister :: proc( tracker : MemoryTracker, to_remove : MemoryTracke
return
}
profile(#procedure)
temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
context.temp_allocator = arena_allocator(& temp_arena)
// temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
// context.temp_allocator = arena_allocator(& temp_arena)
entries := array_to_slice(tracker.entries)
for idx in 0..< tracker.entries.num
@ -139,8 +139,8 @@ memtracker_check_for_collisions :: proc ( tracker : MemoryTracker )
return
}
profile(#procedure)
temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
context.temp_allocator = arena_allocator(& temp_arena)
// temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
// context.temp_allocator = arena_allocator(& temp_arena)
entries := array_to_slice(tracker.entries)
for idx in 1 ..< tracker.entries.num {
@ -161,8 +161,8 @@ memtracker_dump_entries :: proc( tracker : MemoryTracker )
when ! Track_Memory {
return
}
temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
context.temp_allocator = arena_allocator(& temp_arena)
// temp_arena : Arena; arena_init(& temp_arena, tracker_msg_buffer[:])
// context.temp_allocator = arena_allocator(& temp_arena)
log( "Dumping Memory Tracker:")
for idx in 0 ..< tracker.entries.num {

View File

@ -0,0 +1 @@
package sectr

View File

@ -118,15 +118,22 @@ logger_interface :: proc(
str_to_file_ln( logger.file, to_string(builder) )
}
// TODO(Ed): Use a fixed size block allocation for message formatting used by core_log
// This will prevent stack overflows with the virtual arena debug logs at worst case and not need to do
// some inline arena allocation on-site such as with the memory tracker
// This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators.
Logger_Allocator_Buffer : [32 * Kilobyte]u8
log :: proc( msg : string, level := LogLevel.Info, loc := #caller_location ) {
temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
context.allocator = arena_allocator(& temp_arena)
context.temp_allocator = arena_allocator(& temp_arena)
core_log.log( level, msg, location = loc )
}
logf :: proc( fmt : string, args : ..any, level := LogLevel.Info, loc := #caller_location ) {
// context.allocator = transient_allocator()
temp_arena : Arena; arena_init(& temp_arena, Logger_Allocator_Buffer[:])
context.allocator = arena_allocator(& temp_arena)
context.temp_allocator = arena_allocator(& temp_arena)
core_log.logf( level, fmt, ..args, location = loc )
}

View File

@ -134,7 +134,7 @@ pws_parser_lex :: proc ( text : string, allocator : Allocator ) -> ( PWS_LexResu
rune_type :: proc( codepoint : rune ) -> PWS_TokenType
{
using self := context_ext( PWS_LexerData)
using self := context_usr( PWS_LexerData)
switch codepoint
{
@ -177,7 +177,7 @@ pws_parser_lex :: proc ( text : string, allocator : Allocator ) -> ( PWS_LexResu
make_token :: proc ( byte_offset : int ) -> AllocatorError
{
self := context_ext( PWS_LexerData); using self
self := context_usr( PWS_LexerData); using self
if previous_rune == Rune_Carriage_Return && current_rune != Rune_Line_Feed {
ensure(false, "Rouge Carriage Return")
@ -268,7 +268,7 @@ pws_parser_parse :: proc( text : string, allocator : Allocator ) -> ( PWS_ParseR
//region Helper procs
eat_line :: #force_inline proc()
{
self := context_ext( PWS_ParseData); using self
self := context_usr( PWS_ParseData); using self
tok := cast( ^PWS_Token) head
line.type = .Line

View File

@ -13,7 +13,7 @@ vec2_json_unmarshal :: proc( value : ^ json.Value ) -> Vec2 {
}
}
color_json_unmarshal :: proc( value : ^ json.Value ) -> Color {
color_json_unmarshal :: proc( value : ^ json.Value ) -> RGBA8 {
json_color := value.(json.Array)
r := u8(json_color[0].(json.Float))
g := u8(json_color[1].(json.Float))

View File

@ -6,8 +6,6 @@ Ultimately the user's window ppcm (pixels-per-centimeter) determins how all virt
*/
package sectr
import rl "vendor:raylib"
// The points to pixels and pixels to points are our only reference to accurately converting
// an object from world space to screen-space.
// This prototype engine will have all its spacial unit base for distances in virtual pixels.
@ -97,7 +95,13 @@ range2_pixels_to_cm :: #force_inline proc "contextless"( range : Range2 ) -> Ran
//endregion
Camera :: rl.Camera2D
Camera :: struct {
view : Extents2,
position : Vec2,
zoom : f32,
}
Camera_Default := Camera { zoom = 1 }
CameraZoomMode :: enum u32 {
Digital,
@ -117,8 +121,8 @@ BoundsCorners2 :: struct {
top_left, top_right, bottom_left, bottom_right: Vec2,
}
Extents2 :: distinct Vec2
Extents2i :: distinct Vec2i
Extents2 :: Vec2
Extents2i :: Vec2i
WS_Pos :: struct {
tile_id : Vec2i,
@ -162,8 +166,8 @@ view_get_bounds :: #force_inline proc "contextless"() -> Range2 {
cam := & project.workspace.cam
screen_extent := state.app_window.extent
cam_zoom_ratio := 1.0 / cam.zoom
bottom_left := Vec2 { cam.target.x, -cam.target.y } + Vec2 { -screen_extent.x, -screen_extent.y} * cam_zoom_ratio
top_right := Vec2 { cam.target.x, -cam.target.y } + Vec2 { screen_extent.x, screen_extent.y} * cam_zoom_ratio
bottom_left := Vec2 { cam.position.x, -cam.position.y } + Vec2 { -screen_extent.x, -screen_extent.y} * cam_zoom_ratio
top_right := Vec2 { cam.position.x, -cam.position.y } + Vec2 { screen_extent.x, screen_extent.y} * cam_zoom_ratio
return range2( bottom_left, top_right )
}
@ -173,10 +177,10 @@ view_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
cam := & project.workspace.cam
cam_zoom_ratio := 1.0 / cam.zoom
screen_extent := state.app_window.extent * cam_zoom_ratio
top_left := cam.target + Vec2 { -screen_extent.x, screen_extent.y }
top_right := cam.target + Vec2 { screen_extent.x, screen_extent.y }
bottom_left := cam.target + Vec2 { -screen_extent.x, -screen_extent.y }
bottom_right := cam.target + Vec2 { screen_extent.x, -screen_extent.y }
top_left := cam.position + Vec2 { -screen_extent.x, screen_extent.y }
top_right := cam.position + Vec2 { screen_extent.x, screen_extent.y }
bottom_left := cam.position + Vec2 { -screen_extent.x, -screen_extent.y }
bottom_right := cam.position + Vec2 { screen_extent.x, -screen_extent.y }
return { top_left, top_right, bottom_left, bottom_right }
}
@ -196,7 +200,7 @@ render_to_ws_view_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
screen_to_ws_view_pos :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
state := get_state(); using state
cam := & project.workspace.cam
result := Vec2 { cam.target.x, -cam.target.y} + Vec2 { pos.x, pos.y } * (1 / cam.zoom)
result := Vec2 { cam.position.x, -cam.position.y} + Vec2 { pos.x, pos.y } * (1 / cam.zoom)
return result
}

View File

@ -62,9 +62,9 @@ UI_LayoutSide :: struct {
UI_LayoutFlag :: enum u32 {
// Will perform scissor pass on children to their parent's bounds
// Will NOT perform scissor pass on children to their parent's bounds
// (Specified in the parent)
Clip_Children_To_Bounds,
Dont_Clip_Children_To_bounds,
// Enforces the box will always remain in a specific position relative to the parent.
// Overriding the anchors and margins.

View File

@ -86,6 +86,7 @@ UI_Parent_Stack_Size :: 512
// UI_Built_Boxes_Array_Size :: 8
UI_Built_Boxes_Array_Size :: 128 * Kilobyte
// TODO(Ed): Rename to UI_Context
UI_State :: struct {
// TODO(Ed) : Use these
// build_arenas : [2]Arena,

View File

@ -17,8 +17,8 @@ UI_StylePreset :: enum u32 {
}
UI_Style :: struct {
bg_color : Color,
border_color : Color,
bg_color : RGBA8,
border_color : RGBA8,
// TODO(Ed): We cannot support individual corners unless we add it to raylib (or finally change the rendering backend)
corner_radii : [Corner.Count]f32,
@ -33,7 +33,7 @@ UI_Style :: struct {
// shader : UI_Shader,
font : FontID,
text_color : Color,
text_color : RGBA8,
// TODO(Ed) : Support setting the cursor state
cursor : UI_Cursor,