diff --git a/code/api.odin b/code/api.odin index 32ccd47..e2ec5a6 100644 --- a/code/api.odin +++ b/code/api.odin @@ -21,8 +21,7 @@ ModuleAPI :: struct { startup : type_of( startup ), shutdown : type_of( sectr_shutdown), reload : type_of( reload ), - update : type_of( update ), - render : type_of( render ), + tick : type_of( tick ), clean_temp : type_of( clean_temp ), } @@ -66,14 +65,18 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ input_prev = & input_data[0] // Rough setup of window with rl stuff - screen_width = 1280 - screen_height = 1000 + screen_width = 1920 + screen_height = 1080 win_title : cstring = "Sectr Prototype" rl.InitWindow( screen_width, screen_height, win_title ) log( "Raylib initialized and window opened" ) + // We do not support non-uniform DPI. + screen_dpi_scale = rl.GetWindowScaleDPI().x + screen_dpc = os_default_dpc * screen_dpi_scale + // Determining current monitor and setting the target frametime based on it.. - monitor_id = rl.GetCurrentMonitor () + monitor_id = rl.GetCurrentMonitor() monitor_refresh_hz = rl.GetMonitorRefreshRate( monitor_id ) rl.SetTargetFPS( monitor_refresh_hz ) log( fmt.tprintf( "Set target FPS to: %v", monitor_refresh_hz ) ) @@ -90,13 +93,33 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ log( "Default font loaded" ) } - project.path = "./" - project.name = "First Project" - project.workspace.name = "First Workspace" + { + using project + path = "./" + name = "First Project" + workspace.name = "First Workspace" + { + using project.workspace + cam = { + target = { 0, 0 }, + offset = { f32(screen_width) / 2, f32(screen_height) / 2 }, + rotation = 0, + zoom = 1.0, + } + // cam = { + // position = { 0, 0, -100 }, + // target = { 0, 0, 0 }, + // up = { 0, 1, 0 }, + // fovy = 90, + // projection = rl.CameraProjection.ORTHOGRAPHIC, + // } - project_save( & project ) - project = {} - project_load( "./First Project.sectr_proj", & project ) + frame_1.color = Color_BG_TextBox + // Frame is getting interpreted as points (It doesn't have to be, I'm just doing it...) + frame_1.width = 400 + frame_1.height = 250 + } + } } // For some reason odin's symbols conflict with native foreign symbols... @@ -147,125 +170,11 @@ swap :: proc( a, b : ^ $Type ) -> ( ^ Type, ^ Type ) { } @export -update :: proc() -> b32 +tick :: proc ( delta_time : f64 ) -> b32 { - state := get_state(); using state - replay := & memory.replay - - state.input, state.input_prev = swap( state.input, state.input_prev ) - poll_input( state.input_prev, state.input ) - - debug_actions : DebugActions = {} - poll_debug_actions( & debug_actions, state.input ) - - // Input Replay - { - if debug_actions.record_replay { #partial switch replay.mode - { - case ReplayMode.Off : { - save_snapshot( & memory.snapshot[0] ) - replay_recording_begin( Path_Input_Replay ) - } - case ReplayMode.Record : { - replay_recording_end() - } - }} - - if debug_actions.play_replay { switch replay.mode - { - case ReplayMode.Off : { - if ! file_exists( Path_Input_Replay ) { - save_snapshot( & memory.snapshot[0] ) - replay_recording_begin( Path_Input_Replay ) - } - else { - load_snapshot( & memory.snapshot[0] ) - replay_playback_begin( Path_Input_Replay ) - } - } - case ReplayMode.Playback : { - replay_playback_end() - load_snapshot( & memory.snapshot[0] ) - } - case ReplayMode.Record : { - replay_recording_end() - load_snapshot( & memory.snapshot[0] ) - replay_playback_begin( Path_Input_Replay ) - } - }} - - if replay.mode == ReplayMode.Record { - record_input( replay.active_file, input ) - } - else if replay.mode == ReplayMode.Playback { - play_input( replay.active_file, input ) - } - } - - if debug_actions.show_mouse_pos { - debug.mouse_vis = !debug.mouse_vis - } - - debug.mouse_pos.basis = { input.mouse.X, input.mouse.Y } - - should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() - return should_shutdown -} - -@export -render :: proc() -{ - state := get_state(); using state - replay := & memory.replay - - rl.BeginDrawing() - rl.ClearBackground( Color_BG ) - defer { - rl.DrawFPS( 0, 0 ) - rl.EndDrawing() - // Note(Ed) : Polls input as well. - } - - draw_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 = 50 - } - - content := fmt.bprintf( draw_text_scratch[:], format, ..args ) - debug_text( content, 25, debug.draw_debug_text_y ) - - debug.draw_debug_text_y += 16 - } - - draw_text( "Screen Width : %v", rl.GetScreenWidth () ) - draw_text( "Screen Height: %v", rl.GetScreenHeight() ) - - if replay.mode == ReplayMode.Record { - draw_text( "Recording Input") - } - if replay.mode == ReplayMode.Playback { - draw_text( "Replaying Input") - } - - if debug.mouse_vis { - width : f32 = 32 - pos := debug.mouse_pos - - draw_text( "Position: %v", rl.GetMousePosition() ) - - mouse_rect : rl.Rectangle - mouse_rect.x = pos.x - width/2 - mouse_rect.y = pos.y - width/2 - mouse_rect.width = width - mouse_rect.height = width - rl.DrawRectangleRec( mouse_rect, Color_White ) - } - - debug.draw_debug_text_y = 50 + result := update( delta_time ) + render() + return result } @export diff --git a/code/colors.odin b/code/colors.odin index 2534269..36b1c11 100644 --- a/code/colors.odin +++ b/code/colors.odin @@ -6,6 +6,7 @@ Color :: rl.Color Color_White :: rl.WHITE +Color_Transparent :: Color { 0, 0, 0, 0 } Color_BG :: Color { 41, 41, 45, 255 } Color_BG_TextBox :: Color { 32, 32, 32, 255 } Color_Frame_Hover :: Color { 122, 122, 125, 255 } diff --git a/code/env.odin b/code/env.odin index babc89d..2950f17 100644 --- a/code/env.odin +++ b/code/env.odin @@ -49,8 +49,10 @@ State :: struct { project : Project, - screen_width : i32, - screen_height : i32, + screen_width : i32, + screen_height : i32, + screen_dpi_scale : f32, + screen_dpc : f32, // Pixels per cm monitor_id : i32, monitor_refresh_hz : i32, @@ -75,7 +77,10 @@ Project :: struct { } Workspace :: struct { - name : string + name : string, + + cam : Camera, + frame_1 : Frame } DebugData :: struct { @@ -85,27 +90,5 @@ DebugData :: struct { draw_debug_text_y : f32, mouse_vis : b32, - mouse_pos : vec2, -} - -DebugActions :: struct { - pause_renderer : b32, - - load_auto_snapshot : b32, - record_replay : b32, - play_replay : b32, - - show_mouse_pos : b32, -} - -poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) -{ - using actions - using input - - base_replay_bind := keyboard.right_alt.ended_down && pressed( keyboard.L) - record_replay = base_replay_bind && keyboard.right_shift.ended_down - play_replay = base_replay_bind && ! keyboard.right_shift.ended_down - - show_mouse_pos = keyboard.right_alt.ended_down && pressed(keyboard.M) + mouse_pos : Vec2, } diff --git a/code/frame.odin b/code/frame.odin new file mode 100644 index 0000000..c948e5c --- /dev/null +++ b/code/frame.odin @@ -0,0 +1,27 @@ +package sectr + +Frame :: struct { + position : Vec2, + width, height : f32, + color : Color +} + +get_bounds :: proc( frame : ^ Frame ) -> Bounds2 { + half_width := frame.width / 2 + half_height := frame.height / 2 + bottom_left := Vec2 { -half_width, -half_height } + top_right := Vec2 { half_width, half_height } + return { bottom_left, top_right } +} + +get_rect :: proc ( frame : ^ Frame ) -> Rectangle { + half_width := frame.width / 2 + half_height := frame.height / 2 + rect : Rectangle = { + x = frame.position.x - half_width, + y = frame.position.y - half_height, + width = frame.width, + height = frame.height, + } + return rect +} diff --git a/code/grime.odin b/code/grime.odin index 2937f9b..7c34f71 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -36,9 +36,9 @@ slice_ptr :: mem.slice_ptr Tracking_Allocator :: mem.Tracking_Allocator tracking_allocator :: mem.tracking_allocator tracking_allocator_init :: mem.tracking_allocator_init +OS_Type :: type_of(ODIN_OS) import rl "vendor:raylib" -Font :: rl.Font - - +Font :: rl.Font +Rectangle :: rl.Rectangle diff --git a/code/host/host.odin b/code/host/host.odin index 2793b36..dc2004b 100644 --- a/code/host/host.odin +++ b/code/host/host.odin @@ -126,16 +126,14 @@ load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI startup := cast( type_of( sectr.startup )) dynlib.symbol_address( lib, "startup" ) shutdown := cast( type_of( sectr.sectr_shutdown )) dynlib.symbol_address( lib, "sectr_shutdown" ) reload := cast( type_of( sectr.reload )) dynlib.symbol_address( lib, "reload" ) - update := cast( type_of( sectr.update )) dynlib.symbol_address( lib, "update" ) - render := cast( type_of( sectr.render )) dynlib.symbol_address( lib, "render" ) + tick := cast( type_of( sectr.tick )) dynlib.symbol_address( lib, "tick" ) clean_temp := cast( type_of( sectr.clean_temp )) dynlib.symbol_address( lib, "clean_temp" ) missing_symbol : b32 = false if startup == nil do fmt.println("Failed to load sectr.startup symbol") if shutdown == nil do fmt.println("Failed to load sectr.shutdown symbol") if reload == nil do fmt.println("Failed to load sectr.reload symbol") - if update == nil do fmt.println("Failed to load sectr.update symbol") - if render == nil do fmt.println("Failed to load sectr.render symbol") + if tick == nil do fmt.println("Failed to load sectr.tick symbol") if clean_temp == nil do fmt.println("Failed to load sector.clean_temp symbol") if missing_symbol { runtime.debug_trap() @@ -151,8 +149,7 @@ load_sectr_api :: proc ( version_id : i32 ) -> sectr.ModuleAPI startup = startup, shutdown = shutdown, reload = reload, - update = update, - render = render, + tick = tick, clean_temp = clean_temp, } return loaded_module @@ -240,15 +237,20 @@ main :: proc() sectr_api = sectr_api sectr_api.startup( memory.sectr_live, memory.sectr_snapshot, & logger ) + delta_ns : time.Duration + // TODO(Ed) : This should have an end status so that we know the reason the engine stopped. for ; running ; { + start_tick := time.tick_now() + // Hot-Reload sync_sectr_api( & sectr_api, & memory, & logger ) - running = sectr_api.update() - sectr_api.render() - sectr_api.clean_temp() + running = sectr_api.tick( time.duration_seconds( delta_ns ) ) + sectr_api.clean_temp() + + delta_ns = time.tick_lap_time( & start_tick ) } // Determine how the run_cyle completed, if it failed due to an error, diff --git a/code/input.odin b/code/input.odin index 3f4d6db..559f843 100644 --- a/code/input.odin +++ b/code/input.odin @@ -345,6 +345,7 @@ poll_input :: proc( old, new : ^ InputState ) new.mouse.X = mouse_pos.x new.mouse.Y = mouse_pos.y + new.mouse.vertical_wheel = rl.GetMouseWheelMove() } } diff --git a/code/math.odin b/code/math.odin index 98e82dd..7b525a7 100644 --- a/code/math.odin +++ b/code/math.odin @@ -1,25 +1,37 @@ package sectr +import "core:math/linalg" + +Vec2 :: linalg.Vector2f32 +Vec3 :: linalg.Vector3f32 + + + + + +when false { // TODO(Ed) : Evaluate if this is needed -vec2 :: vec2_f32 -vec2_f32 :: struct #raw_union { +Vec2 :: Vec2_f32 +Vec2_f32 :: struct #raw_union { basis : [2] f32, using components : struct { x, y : f32 } } -vec3 :: vec3_f32 -vec3_f32 :: struct #raw_union { +// make_vec2 :: proc ( x, y : f32 ) { + +// } + +Vec3 :: Vec3_f32 +Vec3_f32 :: struct #raw_union { basis : [3] f32, using components : struct { x, y, z : f32 } } - - - +} diff --git a/code/serialize.odin b/code/serialize.odin index d59cc97..1b97a91 100644 --- a/code/serialize.odin +++ b/code/serialize.odin @@ -20,7 +20,7 @@ archive_init_temp :: proc () -> ^ ArchiveData { } state_serialize :: proc ( archive : ^ ArchiveData = nil ) { - + // TODO(Ed): We'll need this for a better save/load snapshot setup. } project_serialize :: proc ( project : ^ Project, archive : ^ ArchiveData, is_writting : b32 = true ) @@ -83,7 +83,8 @@ project_save :: proc ( project : ^ Project, archive : ^ ArchiveData = nil ) os.write_entire_file( fmt.tprint( project.path, project.name, ".sectr_proj", sep = ""), archive.data ) } -project_load :: proc ( path : string, project : ^ Project, archive : ^ ArchiveData = nil ) { +project_load :: proc ( path : string, project : ^ Project, archive : ^ ArchiveData = nil ) +{ archive := archive if archive == nil { archive = archive_init_temp() diff --git a/code/space.odin b/code/space.odin new file mode 100644 index 0000000..9a903d4 --- /dev/null +++ b/code/space.odin @@ -0,0 +1,57 @@ +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 centimetres. + +Inches_To_Centimetre :: cast(f32) 2.54 +Points_Per_Centimetre := cast(f32) 28.3465 +Centimetres_Per_Point :: cast(f32) 1.0 / 28.3465 // Precalculated reciprocal for multiplication +DPT_DPC :: cast(f32) 72.0 * Inches_To_Centimetre + +when ODIN_OS == OS_Type.Windows { + os_default_dpc :: 96 * Inches_To_Centimetre + // 1 inch = 2.54 cm, 96 inch * 2.54 = 243.84 DPC +} + +f32_cm_to_pixels :: proc ( cm : f32 ) -> f32 { + state := get_state(); using state + return cm * screen_dpc +} + +vec2_cm_to_pixels :: proc ( v : Vec2 ) -> Vec2 { + state := get_state(); using state + return v * screen_dpc +} + +points_to_pixels :: proc ( points : f32 ) -> f32 { + state := get_state(); using state + cm_per_pixel := 1.0 / screen_dpc + return points * DPT_DPC * cm_per_pixel +} + +pixels_to_points :: proc ( pixels : f32 ) -> f32 { + state := get_state(); using state + cm_per_pixel := 1.0 / screen_dpc + return pixels * cm_per_pixel * Points_Per_Centimetre +} + +Camera :: rl.Camera2D + +get_half_screen :: proc() -> AreaSize { + state := get_state(); using state + return { + f32(screen_width) / 2, + f32(screen_height) / 2, + } +} + +Bounds2 :: struct { + bottom_left, top_right : Vec2 +} + +AreaSize :: struct { + width, height : f32 +} diff --git a/code/text.odin b/code/text.odin index e95239c..c70794c 100644 --- a/code/text.odin +++ b/code/text.odin @@ -3,7 +3,7 @@ package sectr import "core:unicode/utf8" import rl "vendor:raylib" -debug_text :: proc( content : string, x, y : f32, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = {} ) +debug_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = {} ) { if len( content ) == 0 { return @@ -17,7 +17,7 @@ debug_text :: proc( content : string, x, y : f32, size : f32 = 16.0, color : rl. rl.DrawTextCodepoints( font, raw_data(runes), cast(i32) len(runes), - position = rl.Vector2 { x, y }, + position = transmute(rl.Vector2) pos, fontSize = size, spacing = 0.0, tint = color ); diff --git a/code/tick_render.odin b/code/tick_render.odin new file mode 100644 index 0000000..713c7e9 --- /dev/null +++ b/code/tick_render.odin @@ -0,0 +1,85 @@ +package sectr + +import "core:fmt" + +import rl "vendor:raylib" + +render :: proc() +{ + state := get_state(); using state + replay := & memory.replay + + half_screen_width := f32(screen_width) / 2 + half_screen_height := f32(screen_height) / 2 + + rl.BeginDrawing() + rl.ClearBackground( Color_BG ) + rl.BeginMode2D( project.workspace.cam ) + // rl.BeginMode3D( project.workspace.cam ) + defer { + fps_msg := fmt.tprint( "FPS:", rl.GetFPS() ) + debug_text( fps_msg, { -half_screen_width, -half_screen_height }, color = rl.GREEN ) + + rl.EndMode2D() + // rl.EndMode3D() + rl.EndDrawing() + // Note(Ed) : Polls input as well. + } + + // Frame 1 + { + frame_1 := & project.workspace.frame_1 + rect := get_rect( frame_1 ) + rect.width = points_to_pixels( rect.width ) + rect.height = points_to_pixels( rect.height ) + rect.x = points_to_pixels( rect.x ) + rect.y = points_to_pixels( rect.y ) + + rl.DrawRectangleRec( rect, frame_1.color ) + // rl.DrawRectangleV( frame_1.position, { frame_1.width, frame_1.height }, frame_1.color ) + // rl.DrawRectanglePro( rect, frame_1.position, 0, frame_1.color ) + } + + debug_draw_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 = 50 + } + + content := fmt.bprintf( draw_text_scratch[:], format, ..args ) + debug_text( content, { 25, debug.draw_debug_text_y } ) + + debug.draw_debug_text_y += 16 + } + + // Debug Text + { + debug_draw_text( "Screen Width : %v", rl.GetScreenWidth () ) + debug_draw_text( "Screen Height: %v", rl.GetScreenHeight() ) + if replay.mode == ReplayMode.Record { + debug_draw_text( "Recording Input") + } + if replay.mode == ReplayMode.Playback { + debug_draw_text( "Replaying Input") + } + } + + if debug.mouse_vis { + width : f32 = 32 + pos := debug.mouse_pos + + debug_draw_text( "Position: %v", rl.GetMousePosition() ) + + mouse_rect : rl.Rectangle + mouse_rect.x = pos.x - width/2 + mouse_rect.y = pos.y - width/2 + mouse_rect.width = width + mouse_rect.height = width + // rl.DrawRectangleRec( mouse_rect, Color_White ) + } + + debug.draw_debug_text_y = 50 +} \ No newline at end of file diff --git a/code/tick_update.odin b/code/tick_update.odin new file mode 100644 index 0000000..71b1c78 --- /dev/null +++ b/code/tick_update.odin @@ -0,0 +1,134 @@ +package sectr + +import "core:fmt" + +import rl "vendor:raylib" + +DebugActions :: struct { + load_project : b32, + save_project : b32, + pause_renderer : b32, + + load_auto_snapshot : b32, + record_replay : b32, + play_replay : b32, + + show_mouse_pos : b32, + + cam_move_up : b32, + cam_move_left : b32, + cam_move_down : b32, + cam_move_right : b32, +} + +poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) +{ + using actions + using input + + load_project = keyboard.left_control.ended_down && pressed( keyboard.O ) + save_project = keyboard.left_control.ended_down && pressed( keyboard.S ) + + base_replay_bind := keyboard.right_alt.ended_down && pressed( keyboard.L) + record_replay = base_replay_bind && keyboard.right_shift.ended_down + play_replay = base_replay_bind && ! keyboard.right_shift.ended_down + + show_mouse_pos = keyboard.right_alt.ended_down && pressed(keyboard.M) + + cam_move_up = keyboard.W.ended_down + cam_move_left = keyboard.A.ended_down + cam_move_down = keyboard.S.ended_down + cam_move_right = keyboard.D.ended_down +} + +update :: proc( delta_time : f64 ) -> b32 +{ + state := get_state(); using state + replay := & memory.replay + + state.input, state.input_prev = swap( state.input, state.input_prev ) + poll_input( state.input_prev, state.input ) + + debug_actions : DebugActions = {} + poll_debug_actions( & debug_actions, state.input ) + + // Saving & Loading + { + if debug_actions.save_project { + project_save( & project ) + } + if debug_actions.load_project { + project_load( fmt.tprint( project.path, project.name, ".sectr_proj", sep = "" ), & project ) + } + } + + // Input Replay + { + if debug_actions.record_replay { #partial switch replay.mode + { + case ReplayMode.Off : { + save_snapshot( & memory.snapshot[0] ) + replay_recording_begin( Path_Input_Replay ) + } + case ReplayMode.Record : { + replay_recording_end() + } + }} + + if debug_actions.play_replay { switch replay.mode + { + case ReplayMode.Off : { + if ! file_exists( Path_Input_Replay ) { + save_snapshot( & memory.snapshot[0] ) + replay_recording_begin( Path_Input_Replay ) + } + else { + load_snapshot( & memory.snapshot[0] ) + replay_playback_begin( Path_Input_Replay ) + } + } + case ReplayMode.Playback : { + replay_playback_end() + load_snapshot( & memory.snapshot[0] ) + } + case ReplayMode.Record : { + replay_recording_end() + load_snapshot( & memory.snapshot[0] ) + replay_playback_begin( Path_Input_Replay ) + } + }} + + if replay.mode == ReplayMode.Record { + record_input( replay.active_file, input ) + } + else if replay.mode == ReplayMode.Playback { + play_input( replay.active_file, input ) + } + } + + if debug_actions.show_mouse_pos { + debug.mouse_vis = !debug.mouse_vis + } + + debug.mouse_pos = { input.mouse.X, input.mouse.Y } + + // Camera Manual Nav + { + move_speed : f32 = 200.0 + zoom_sensitiviity : f32 = 3.5 + + 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 ) + + move_velocity : Vec2 = { + - cast(f32) i32(debug_actions.cam_move_left) + cast(f32) i32(debug_actions.cam_move_right), + - cast(f32) i32(debug_actions.cam_move_up) + cast(f32) i32(debug_actions.cam_move_down), + } + move_velocity *= move_speed * f32(delta_time) + cam.target += move_velocity + } + + should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() + return should_shutdown +}