diff --git a/assets/FiraCode-Regular.ttf b/assets/FiraCode-Regular.ttf new file mode 100644 index 0000000..bd73685 Binary files /dev/null and b/assets/FiraCode-Regular.ttf differ diff --git a/assets/Squidgy Slimes.ttf b/assets/Squidgy Slimes.ttf new file mode 100644 index 0000000..c97ed39 Binary files /dev/null and b/assets/Squidgy Slimes.ttf differ diff --git a/code/api.odin b/code/api.odin index af2ced9..037e3db 100644 --- a/code/api.odin +++ b/code/api.odin @@ -46,17 +46,24 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ transient_slice := slice_ptr( memory_after( persistent_slice), memory_trans_temp_size ) temp_slice := slice_ptr( memory_after( transient_slice), memory_trans_temp_size ) - // We assign the beginning of the block to be the host's persistent memory's arena. - // Then we offset past the arena and determine its slice to be the amount left after for the size of host's persistent. - persistent = tracked_allocator_init_vmem( persistent_slice, internals_size ) - transient = tracked_allocator_init_vmem( transient_slice, internals_size ) - temp = tracked_allocator_init_vmem( temp_slice , internals_size ) + when Use_TrackingAllocator { + // We assign the beginning of the block to be the host's persistent memory's arena. + // Then we offset past the arena and determine its slice to be the amount left after for the size of host's persistent. + persistent = tracked_allocator_init_vmem( persistent_slice, internals_size ) + transient = tracked_allocator_init_vmem( transient_slice, internals_size ) + temp = tracked_allocator_init_vmem( temp_slice , internals_size ) + } + else { + persistent = arena_allocator_init_vmem( persistent_slice ) + transient = arena_allocator_init_vmem( transient_slice ) + temp = arena_allocator_init_vmem( temp_slice ) + } - context.allocator = tracked_allocator( transient ) - context.temp_allocator = tracked_allocator( temp ) + context.allocator = transient_allocator() + context.temp_allocator = temp_allocator() } - state := new( State, tracked_allocator( memory.persistent ) ) + state := new( State, persistent_allocator() ) using state context.user_ptr = state @@ -64,7 +71,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ input = & input_data[1] input_prev = & input_data[0] - rl.SetConfigFlags( { rl.ConfigFlag.WINDOW_RESIZABLE, rl.ConfigFlag.WINDOW_TOPMOST } ) + rl.SetConfigFlags( { rl.ConfigFlag.WINDOW_RESIZABLE /*, rl.ConfigFlag.WINDOW_TOPMOST*/ } ) // Rough setup of window with rl stuff window_width : i32 = 1000 @@ -79,7 +86,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ // We do not support non-uniform DPI. window.dpi_scale = rl.GetWindowScaleDPI().x - window.dpc = os_default_dpc * window.dpi_scale + window.ppcm = os_default_ppcm * window.dpi_scale // Determining current monitor and setting the target frametime based on it.. monitor_id = rl.GetCurrentMonitor() @@ -89,17 +96,25 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ // Basic Font Setup { + font_provider_startup() path_rec_mono_semicasual_reg := strings.concatenate( { Path_Assets, "RecMonoSemicasual-Regular-1.084.ttf" }) - cstr := strings.clone_to_cstring( path_rec_mono_semicasual_reg ) + font_rec_mono_semicasual_reg = font_load( path_rec_mono_semicasual_reg, 24.0, "RecMonoSemiCasual_Regular" ) - font_data, read_succeded : = os.read_entire_file( path_rec_mono_semicasual_reg ) - verify( ! read_succeded, fmt.tprintf("Failed to read font file for: %v", path_rec_mono_semicasual_reg) ) + path_squidgy_slimes := strings.concatenate( { Path_Assets, "Squidgy Slimes.ttf" } ) + font_squidgy_slimes = font_load( path_squidgy_slimes, 24.0, "Squidgy_Slime" ) - font_rec_mono_semicasual_reg = rl.LoadFontEx( cstr, cast(i32) points_to_pixels(24.0), nil, 0 ) - delete( cstr) + path_firacode := strings.concatenate( { Path_Assets, "FiraCode-Regular.ttf" } ) + font_firacode = font_load( path_firacode, 24.0, "FiraCode" ) - rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything? - default_font = font_rec_mono_semicasual_reg + // font_data, read_succeded : = os.read_entire_file( path_rec_mono_semicasual_reg ) + // verify( ! read_succeded, fmt.tprintf("Failed to read font file for: %v", path_rec_mono_semicasual_reg) ) + + // cstr := strings.clone_to_cstring( path_rec_mono_semicasual_reg ) + // font_rec_mono_semicasual_reg = rl.LoadFontEx( cstr, cast(i32) points_to_pixels(24.0), nil, 0 ) + // delete( cstr) + + // rl.GuiSetFont( font_rec_mono_semicasual_reg ) // TODO(Ed) : Does this do anything? + default_font = font_firacode log( "Default font loaded" ) } @@ -112,10 +127,10 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ { using project.workspace cam = { - target = { 0, 0 }, - offset = transmute(Vec2) window.extent, + target = { 0, 0 }, + offset = transmute(Vec2) window.extent, rotation = 0, - zoom = 1.0, + zoom = 1.0, } // cam = { // position = { 0, 0, -100 }, @@ -150,11 +165,8 @@ sectr_shutdown :: proc() os.close( memory.replay.active_file ) } - // Raylib - { - rl.UnloadFont ( state.font_rec_mono_semicasual_reg ) - rl.CloseWindow() - } + font_provider_shutdown() + log("Module shutdown complete") } @@ -171,10 +183,19 @@ reload :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ L transient_slice := slice_ptr( memory_after( persistent_slice), memory_trans_temp_size ) temp_slice := slice_ptr( memory_after( transient_slice), memory_trans_temp_size ) - persistent = cast( ^TrackedAllocator ) & persistent_slice[0] - transient = cast( ^TrackedAllocator ) & transient_slice[0] - temp = cast( ^TrackedAllocator ) & temp_slice[0] + when Use_TrackingAllocator { + persistent = cast( ^ TrackedAllocator ) & persistent_slice[0] + transient = cast( ^ TrackedAllocator ) & transient_slice[0] + temp = cast( ^ TrackedAllocator ) & temp_slice[0] + } + else { + persistent = cast( ^ Arena ) & persistent_slice[0] + transient = cast( ^ Arena ) & transient_slice[0] + temp = cast( ^ Arena ) & temp_slice[0] + } + context.allocator = transient_allocator() + context.temp_allocator = temp_allocator() log("Module reloaded") } @@ -192,7 +213,11 @@ tick :: proc ( delta_time : f64 ) -> b32 } @export -clean_temp :: proc() -{ - mem.tracking_allocator_clear( & memory.temp.tracker ) +clean_temp :: proc() { + when Use_TrackingAllocator { + mem.tracking_allocator_clear( & memory.temp.tracker ) + } + else { + free_all( temp_allocator() ) + } } diff --git a/code/env.odin b/code/env.odin index 8ece4ca..ae2a013 100644 --- a/code/env.odin +++ b/code/env.odin @@ -11,18 +11,66 @@ import rl "vendor:raylib" memory : Memory memory_chunk_size :: 2 * Gigabyte -memory_persistent_size :: 128 * Megabyte +memory_persistent_size :: 256 * Megabyte memory_trans_temp_size :: (memory_chunk_size - memory_persistent_size ) / 2 -Memory :: struct { - live : virtual.Arena, - snapshot : []u8, - persistent : ^ TrackedAllocator, - transient : ^ TrackedAllocator, - temp : ^ TrackedAllocator, +// TODO(Ed): There is an issue with mutex locks on the tracking allocator.. +Use_TrackingAllocator :: false - replay : ReplayState, - logger : Logger, +when Use_TrackingAllocator +{ + Memory :: struct { + live : virtual.Arena, + snapshot : []u8, + + persistent : ^ TrackedAllocator, + transient : ^ TrackedAllocator, + temp : ^ TrackedAllocator, + + replay : ReplayState, + logger : Logger, + } +} +else +{ + Memory :: struct { + live : virtual.Arena, + snapshot : []u8, + + persistent : ^ Arena, + transient : ^ Arena, + temp : ^ Arena, + + replay : ReplayState, + logger : Logger, + } +} + +persistent_allocator :: proc () -> Allocator { + when Use_TrackingAllocator { + return tracked_allocator( memory.persistent ) + } + else { + return arena_allocator( memory.persistent ) + } +} + +transient_allocator :: proc () -> Allocator { + when Use_TrackingAllocator { + return tracked_allocator( memory.transient ) + } + else { + return arena_allocator( memory.transient ) + } +} + +temp_allocator :: proc () -> Allocator { + when Use_TrackingAllocator { + return tracked_allocator( memory.temp ) + } + else { + return arena_allocator( memory.temp ) + } } save_snapshot :: proc( snapshot : [^]u8 ) { @@ -38,10 +86,14 @@ load_snapshot :: proc( snapshot : [^]u8 ) { AppConfig :: struct { resolution_width : uint, resolution_height : uint, - refresh_rate : uint + refresh_rate : uint, + min_zoom : uint, + max_zoom : uint, } State :: struct { + font_provider_data : FontProviderData, + input_data : [2] InputState, input_prev : ^ InputState, input : ^ InputState, @@ -59,18 +111,25 @@ State :: struct { engine_refresh_hz : i32, engine_refresh_target : i32, - font_rec_mono_semicasual_reg : Font, - default_font : Font, + font_firacode : FontID, + font_squidgy_slimes : FontID, + font_rec_mono_semicasual_reg : FontID, + default_font : FontID, } get_state :: proc "contextless" () -> ^ State { - return cast( ^ State ) raw_data( memory.persistent.backing.data ) + when Use_TrackingAllocator { + return cast( ^ State ) raw_data( memory.persistent.backing.data ) + } + else { + return cast( ^ State ) raw_data( memory.persistent. data ) + } } AppWindow :: struct { extent : Extents2, // Window half-size dpi_scale : f32, // Dots per inch scale (provided by raylib via glfw) - dpc : f32, // Dots per centimetre + ppcm : f32, // Dots per centimetre } Project :: struct { diff --git a/code/font_provider.odin b/code/font_provider.odin new file mode 100644 index 0000000..1537a04 --- /dev/null +++ b/code/font_provider.odin @@ -0,0 +1,174 @@ +package sectr + +import "core:fmt" +import "core:math" +import "core:mem" +import "core:path/filepath" +import "core:os" + +import rl "vendor:raylib" + +Font_Arena_Size :: 32 * Megabyte +Font_Largest_Px_Size :: 96 + +Font_Default :: "" +Font_Default_Point_Size :: 18.0 + +Font_TTF_Default_Chars_Padding :: 4 + +Font_Load_Use_Default_Size :: -1 +Font_Load_Gen_ID :: "" + +Font_Atlas_Packing_Method :: enum u32 { + Raylib_Basic = 0, // Basic packing algo + Skyeline_Rect = 1, // stb_pack_rect +} + +// TODO(Ed) : This isn't good enough for what we need font wise.. +Font :: rl.Font + +// TODO(Ed) : Use this instead of the raylib font directly +FontID :: distinct string +FontTag :: struct { + key : FontID, + 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, + data : [] u8, + default_size : i32, + size_table : [Font_Largest_Px_Size] FontGlyphsRender, +} + +FontProviderData :: struct { + font_arena : Arena, + font_cache : ^ map [FontID] FontDef, +} + +font_provider_startup :: proc() +{ + font_provider_data := & get_state().font_provider_data; using font_provider_data + + data, alloc_result := alloc_bytes( Font_Arena_Size, allocator = persistent_allocator() ) + verify( alloc_result != AllocatorError.None, "Failed to allocate memory for font_arena from persistent" ) + log("font_arena allocated from persistent memory") + + arena_init( & font_arena, data ) + + font_cache = new( type_of(font_cache ^), arena_allocator( & font_arena) ) + 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 key, & value in font_cache { + for & px_render in value.size_table { + using px_render + rl.UnloadFontData( glyphs, count ) + rl.UnloadTexture ( texture ) + rl.MemFree( recs ) + } + } +} + +font_load :: proc ( path_file : string, + default_size : f32 = Font_Load_Use_Default_Size, + desired_id : FontID = Font_Load_Gen_ID +) -> FontID +{ + font_provider_data := & get_state().font_provider_data; using font_provider_data + + font_data, read_succeded : = os.read_entire_file( path_file ) + verify( ! read_succeded, fmt.tprintf("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!") + desired_id = cast(FontID) file_name_from_path(path_file) + } + + default_size := default_size + if default_size == Font_Load_Use_Default_Size { + default_size = Font_Default_Point_Size + } + + font_cache[desired_id] = {} + def := & font_cache[desired_id]; + def.path_file = path_file + def.data = font_data + def.default_size = i32(points_to_pixels(default_size)) + + // TODO(Ed): this is extremely slow + // Render all sizes at once + for id : i32 = 0; id < Font_Largest_Px_Size; id += 1 + { + px_render := & def.size_table[id] + using px_render + size = id + 1 + 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, fmt.tprintf("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 ) + rl.SetTextureFilter( texture, rl.TextureFilter.POINT ) + + // 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 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 + + size := clamp( i32(math.round(size)), 12, Font_Largest_Px_Size ) + + verify( size > Font_Largest_Px_Size, "attempt to get a font larger than the largest loaded pixel size" ) + def := & font_cache[id] + size = size if size != i32(Font_Use_Default_Size) else def.default_size + px_render := & def.size_table[ size - 1 ] + + rl_font : rl.Font + rl_font.baseSize = px_render.size + rl_font.charsCount = px_render.count + rl_font.charsPadding = px_render.padding + rl_font.glyphs = px_render.glyphs + rl_font.recs = px_render.recs + rl_font.texture = px_render.texture + return rl_font +} diff --git a/code/grime.odin b/code/grime.odin index 07370bc..b255835 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -4,6 +4,7 @@ package sectr import "core:mem" import "core:mem/virtual" +import "core:path/filepath" Byte :: 1 Kilobyte :: 1024 * Byte @@ -27,7 +28,9 @@ terabyte :: proc ( tb : $ integer_type ) -> integer_type { } Allocator :: mem.Allocator +AllocatorError :: mem.Allocator_Error alloc :: mem.alloc +alloc_bytes :: mem.alloc_bytes Arena :: mem.Arena arena_allocator :: mem.arena_allocator arena_init :: mem.arena_init @@ -36,12 +39,9 @@ slice_ptr :: mem.slice_ptr Tracking_Allocator :: mem.Tracking_Allocator tracking_allocator :: mem.tracking_allocator tracking_allocator_init :: mem.tracking_allocator_init +file_name_from_path :: filepath.short_stem OS_Type :: type_of(ODIN_OS) -import rl "vendor:raylib" - -Font :: rl.Font - get_bounds :: proc { box_get_bounds, view_get_bounds, diff --git a/code/grime_memory.odin b/code/grime_memory.odin index a5a0f31..c7b77b6 100644 --- a/code/grime_memory.odin +++ b/code/grime_memory.odin @@ -86,3 +86,16 @@ tracked_allocator_init_vmem :: proc( vmem : [] byte, internals_size : int ) -> ^ tracking_allocator_init( & result.tracker, arena_allocator( backing ), arena_allocator( internals ) ) return result } + +arena_allocator_init_vmem :: proc ( vmem : [] byte ) -> ^ Arena +{ + arena_size :: size_of( Arena) + backing_size := len(vmem) + + result := cast( ^ Arena) & vmem[0] + result_slice := slice_ptr( & vmem[0], arena_size ) + + backing_slice := slice_ptr( memory_after( result_slice), backing_size ) + arena_init( result, backing_slice ) + return result +} diff --git a/code/space.odin b/code/space.odin index f6314ee..e1d27e9 100644 --- a/code/space.odin +++ b/code/space.odin @@ -6,13 +6,16 @@ import rl "vendor:raylib" // an object from world space to screen-space. // This prototype engine will have all its spacial unit base for distances in centimetres. -Inches_To_CM :: cast(f32)2.54 -Points_Per_CM :: cast(f32)28.3465 -CM_Per_Point :: cast(f32)1.0 / 28.3465 // Precalculated reciprocal for multiplication -DPT_DPC :: cast(f32)72.0 * Inches_To_CM +Inches_To_CM :: cast(f32) 2.54 +Points_Per_CM :: cast(f32) 28.3465 +CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM +CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM +DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm +DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm when ODIN_OS == OS_Type.Windows { - os_default_dpc :: 96 * Inches_To_CM + op_default_dpcm :: 72.0 * Inches_To_CM + os_default_ppcm :: 96.0 * Inches_To_CM // 1 inch = 2.54 cm, 96 inch * 2.54 = 243.84 DPC } @@ -31,7 +34,6 @@ points_to_pixels :: proc { vec2_points_to_pixels, } - //region Unit Conversion Impl // cm_to_points :: proc ( cm : f32 ) -> f32 { @@ -46,43 +48,43 @@ points_to_pixels :: proc { // } f32_cm_to_pixels :: proc(cm: f32) -> f32 { - screen_dpc := get_state().app_window.dpc - return cm * screen_dpc + screen_ppcm := get_state().app_window.ppcm + return cm * screen_ppcm } f32_pixels_to_cm :: proc(pixels: f32) -> f32 { - screen_dpc := get_state().app_window.dpc - cm_per_pixel := 1.0 / screen_dpc + screen_ppcm := get_state().app_window.ppcm + cm_per_pixel := 1.0 / screen_ppcm return pixels * cm_per_pixel } f32_points_to_pixels :: proc(points: f32) -> f32 { - screen_dpc := get_state().app_window.dpc - cm_per_pixel := 1.0 / screen_dpc - return points * DPT_DPC * cm_per_pixel + screen_ppcm := get_state().app_window.ppcm + cm_per_pixel := 1.0 / screen_ppcm + return points * DPT_PPCM * cm_per_pixel } f32_pixels_to_points :: proc(pixels: f32) -> f32 { - screen_dpc := get_state().app_window.dpc - cm_per_pixel := 1.0 / screen_dpc + screen_ppcm := get_state().app_window.ppcm + cm_per_pixel := 1.0 / screen_ppcm return pixels * cm_per_pixel * Points_Per_CM } vec2_cm_to_pixels :: proc(v: Vec2) -> Vec2 { - screen_dpc := get_state().app_window.dpc - return v * screen_dpc + screen_ppcm := get_state().app_window.ppcm + return v * screen_ppcm } vec2_pixels_to_cm :: proc(v: Vec2) -> Vec2 { - screen_dpc := get_state().app_window.dpc - cm_per_pixel := 1.0 / screen_dpc + screen_ppcm := get_state().app_window.ppcm + cm_per_pixel := 1.0 / screen_ppcm return v * cm_per_pixel } vec2_points_to_pixels :: proc(vpoints: Vec2) -> Vec2 { - screen_dpc := get_state().app_window.dpc - cm_per_pixel := 1.0 / screen_dpc - return vpoints * DPT_DPC * cm_per_pixel + screen_ppcm := get_state().app_window.ppcm + cm_per_pixel := 1.0 / screen_ppcm + return vpoints * DPT_PPCM * cm_per_pixel } @@ -107,7 +109,7 @@ BoundsCorners2 :: struct { top_left, top_right, bottom_left, bottom_right: Vec2, } -Extents2 :: distinct Vec2 +Extents2 :: distinct Vec2 Extents2i :: distinct Vec2i WS_Pos :: struct { @@ -116,72 +118,72 @@ WS_Pos :: struct { } bounds2_radius :: proc(bounds: Bounds2) -> f32 { - return max(bounds.bottom_right.x, bounds.top_left.y) + return max( bounds.bottom_right.x, bounds.top_left.y ) } extent_from_size :: proc(size: AreaSize) -> Extents2 { - return transmute(Extents2)size * 2.0 + return transmute(Extents2) size * 2.0 } screen_size :: proc "contextless" () -> AreaSize { extent := get_state().app_window.extent - return transmute(AreaSize)(extent * 2.0) + return transmute(AreaSize) ( extent * 2.0 ) } screen_get_corners :: proc() -> BoundsCorners2 { - state := get_state();using state + state := get_state();using state screen_extent := state.app_window.extent - top_left := Vec2{-screen_extent.x, screen_extent.y} - top_right := Vec2{screen_extent.x, screen_extent.y} - bottom_left := Vec2{-screen_extent.x, -screen_extent.y} - bottom_right := Vec2{screen_extent.x, -screen_extent.y} - return {top_left, top_right, bottom_left, bottom_right} + top_left := Vec2 { -screen_extent.x, screen_extent.y } + top_right := Vec2 { screen_extent.x, screen_extent.y } + bottom_left := Vec2 { -screen_extent.x, -screen_extent.y } + bottom_right := Vec2 { screen_extent.x, -screen_extent.y } + return { top_left, top_right, bottom_left, bottom_right } } view_get_bounds :: proc() -> Bounds2 { - state := get_state();using state - cam := &project.workspace.cam + state := get_state(); using state + cam := & project.workspace.cam screen_extent := state.app_window.extent - top_left := cam.target + Vec2{-screen_extent.x, screen_extent.y} - bottom_right := cam.target + Vec2{screen_extent.x, -screen_extent.y} - return {top_left, bottom_right} + top_left := cam.target + Vec2 { -screen_extent.x, screen_extent.y} + bottom_right := cam.target + Vec2 { screen_extent.x, -screen_extent.y} + return { top_left, bottom_right } } view_get_corners :: proc() -> BoundsCorners2 { - state := get_state();using state - cam := &project.workspace.cam + state := get_state(); using state + 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} - return {top_left, top_right, bottom_left, bottom_right} + 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 } + return { top_left, top_right, bottom_left, bottom_right } } screen_to_world :: proc(pos: Vec2) -> Vec2 { - state := get_state();using state - cam := &project.workspace.cam - return vec2_pixels_to_cm(cam.target + pos * (1 / cam.zoom)) + state := get_state(); using state + cam := & project.workspace.cam + return vec2_pixels_to_cm( cam.target + pos * (1 / cam.zoom) ) } screen_to_render :: proc(pos: Vec2) -> Vec2 { - screen_extent := transmute(Vec2)get_state().project.workspace.cam.offset - return pos + {screen_extent.x, -screen_extent.y} + screen_extent := transmute(Vec2) get_state().project.workspace.cam.offset + return pos + { screen_extent.x, -screen_extent.y } } world_screen_extent :: proc() -> Extents2 { - state := get_state();using state + state := get_state(); using state cam_zoom_ratio := 1.0 / project.workspace.cam.zoom return app_window.extent * cam_zoom_ratio } world_to_screen_pos :: proc(position: Vec2) -> Vec2 { - return {position.x, position.y * -1} + return { position.x, position.y * -1 } } world_to_screen_no_zoom :: proc(position: Vec2) -> Vec2 { - state := get_state();using state + state := get_state(); using state cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom - return {position.x, position.y * -1} * cam_zoom_ratio + return { position.x, position.y * -1 } * cam_zoom_ratio } diff --git a/code/text.odin b/code/text.odin index 88c509d..16a3cc2 100644 --- a/code/text.odin +++ b/code/text.odin @@ -1,9 +1,10 @@ package sectr +import "core:math" import "core:unicode/utf8" import rl "vendor:raylib" -debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : rl.Font = {} ) +debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default ) { state := get_state(); using state @@ -13,30 +14,63 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co runes := utf8.string_to_runes( content, context.temp_allocator ) font := font - if ( font.glyphs == nil ) { + if ( len(font) == 0 ) { font = default_font } - pos := screen_to_render(pos) - rl.DrawTextCodepoints( font, + px_size := size + + rl_font := to_rl_Font(font, px_size ) + rl.DrawTextCodepoints( rl_font, raw_data(runes), cast(i32) len(runes), position = transmute(rl.Vector2) pos, - fontSize = size, + fontSize = px_size, + spacing = 0.0, + tint = color ); +} + +debug_draw_text_world :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default ) +{ + state := get_state(); using state + + if len( content ) == 0 { + return + } + runes := utf8.string_to_runes( content, context.temp_allocator ) + + font := font + if ( len(font) == 0 ) { + font = default_font + } + pos := world_to_screen_pos(pos) + + px_size := size + zoom_adjust := px_size * project.workspace.cam.zoom + + rl_font := to_rl_Font(font, zoom_adjust ) + rl.DrawTextCodepoints( rl_font, + raw_data(runes), cast(i32) len(runes), + position = transmute(rl.Vector2) pos, + fontSize = px_size, spacing = 0.0, tint = color ); } // 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 :: proc ( text : string, font : Font, font_size, spacing : f32 ) -> AreaSize +measure_text_size :: proc ( text : string, font : FontID, font_size := Font_Use_Default_Size, spacing : f32 ) -> AreaSize { + 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 : AreaSize - if font.texture.id == 0 || len(text) == 0 { + if rl_font.texture.id == 0 || len(text) == 0 { return text_size } @@ -46,30 +80,31 @@ measure_text_size :: proc ( text : string, font : Font, font_size, spacing : f32 text_width : f32 = 0.0 temp_text_width : f32 = 0.0 // Used to counter longer text line width - text_height := cast(f32) font.baseSize - scale_factor := font_size / text_height + 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)); { + 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( font, letter ) + index = rl.GetGlyphIndex( rl_font, letter ) id += 1 if letter != rune('\n') { - if font.glyphs[index].advanceX != 0 { - text_width += f32(font.glyphs[index].advanceX) + if rl_font.glyphs[index].advanceX != 0 { + text_width += f32(rl_font.glyphs[index].advanceX) } else { - text_width += font.recs[index].width + f32(font.glyphs[index].offsetX) + text_width += rl_font.recs[index].width + f32(rl_font.glyphs[index].offsetX) } } else @@ -87,11 +122,9 @@ measure_text_size :: proc ( text : string, font : Font, font_size, spacing : f32 } } } - 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 diff --git a/code/tick_render.odin b/code/tick_render.odin index a8b2767..a738482 100644 --- a/code/tick_render.odin +++ b/code/tick_render.odin @@ -22,9 +22,9 @@ render :: proc() // Render Screenspace { fps_msg := fmt.tprint( "FPS:", rl.GetFPS() ) - fps_msg_width := measure_text_size( fps_msg, default_font, points_to_pixels(24.0), 0.0 ).x + fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } - debug_draw_text( fps_msg, fps_msg_pos, points_to_pixels(24.0), color = rl.GREEN ) + debug_draw_text( fps_msg, fps_msg_pos, 16.0, color = rl.GREEN ) debug_text :: proc( format : string, args : ..any ) { @@ -43,7 +43,7 @@ render :: proc() position.y += debug.draw_debug_text_y content := fmt.bprintf( draw_text_scratch[:], format, ..args ) - debug_draw_text( content, position, points_to_pixels(24.0) ) + debug_draw_text( content, position, 16.0 ) debug.draw_debug_text_y += 16 } @@ -100,6 +100,8 @@ render_mode_2d :: proc() { rl.DrawRectangleRec( rect, box.color ) } + debug_draw_text_world( "This is text in world space", { 0, 0 }, 16.0 ) + if debug.mouse_vis { // rl.DrawCircleV( screen_to_world(input.mouse.pos), 10, Color_GreyRed ) } diff --git a/code/tick_update.odin b/code/tick_update.odin index 11b99a1..6e59959 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -1,6 +1,7 @@ package sectr import "base:runtime" +import "core:math" import "core:fmt" import rl "vendor:raylib" @@ -136,11 +137,14 @@ update :: proc( delta_time : f64 ) -> b32 // Camera Manual Nav { digital_move_speed : f32 = 200.0 - zoom_sensitiviity : f32 = 3.5 + zoom_sensitiviity : f32 = 0.05 - cam := & project.workspace.cam - cam.zoom *= 1 + input.mouse.vertical_wheel * zoom_sensitiviity * f32(delta_time) - cam.zoom = clamp( cam.zoom, 0.05, 10.0 ) + cam := & project.workspace.cam + zoom_delta := input.mouse.vertical_wheel * zoom_sensitiviity + // zoom_delta *= f32(delta_time) + cam.zoom *= 1 + zoom_delta + cam.zoom = clamp( cam.zoom, 0.5, 10.0 ) + // cam.zoom = 2.0 move_velocity : Vec2 = { - cast(f32) i32(debug_actions.cam_move_left) + cast(f32) i32(debug_actions.cam_move_right), diff --git a/code/ui.odin b/code/ui.odin index 3465841..81503bd 100644 --- a/code/ui.odin +++ b/code/ui.odin @@ -12,6 +12,25 @@ Axis2 :: enum { Count, } +UI_IconKind :: enum u32 { + Null, + Arrow_Up, + Arrow_Left, + Arrow_Right, + Arrow_Down, + Caret_Up, + Caret_Left, + Caret_Right, + Caret_Down, + Check_Hollow, + Check_Filled, + Count, +} + +UI_IconInfo :: struct { + placehodler : int +} + UI_SizeKind :: enum u32 { Null, Pixels, @@ -19,6 +38,7 @@ UI_SizeKind :: enum u32 { TextContent, PercentOfParent, ChildrenSum, + Count, } UI_Size :: struct { @@ -95,6 +115,8 @@ UI_BoxFlag :: enum u64 { Has_Display_String, Has_Fuzzy_Match_Ranges, Round_Children_By_Parent, + + Count, } UI_BoxFlags :: bit_set[UI_BoxFlag; u64] @@ -123,7 +145,9 @@ UI_Box :: struct { // Note(rjf) : Per-frame info provided by builders flags : UI_BoxFlags, - semantic_size : [Axis2.Count]UI_Size, + display_str : string, + semantic_size : [Axis2.Count]UI_Size, + // Note(rjf) : Computed every frame computed_rel_pos : Vec2, diff --git a/ols.json b/ols.json index 2b55c69..249eff5 100644 --- a/ols.json +++ b/ols.json @@ -21,5 +21,6 @@ "enable_document_symbols": true, "enable_semantic_tokens": false, "enable_hover": true, - "enable_snippets": true + "enable_snippets": true, + "enable_format": false } \ No newline at end of file