diff --git a/code/api.odin b/code/api.odin index e2ec5a6..0ac8ec4 100644 --- a/code/api.odin +++ b/code/api.odin @@ -64,16 +64,22 @@ 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 } ) + // Rough setup of window with rl stuff - screen_width = 1920 - screen_height = 1080 + window_width : i32 = 1000 + window_height : i32 = 600 win_title : cstring = "Sectr Prototype" - rl.InitWindow( screen_width, screen_height, win_title ) + 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. - screen_dpi_scale = rl.GetWindowScaleDPI().x - screen_dpc = os_default_dpc * screen_dpi_scale + window.dpi_scale = rl.GetWindowScaleDPI().x + window.dpc = os_default_dpc * window.dpi_scale // Determining current monitor and setting the target frametime based on it.. monitor_id = rl.GetCurrentMonitor() @@ -102,7 +108,7 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ using project.workspace cam = { target = { 0, 0 }, - offset = { f32(screen_width) / 2, f32(screen_height) / 2 }, + offset = transmute(Vec2) window.extent, rotation = 0, zoom = 1.0, } @@ -116,8 +122,8 @@ startup :: proc( live_mem : virtual.Arena, snapshot_mem : []u8, host_logger : ^ 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 + box_set_size( & frame_1, { 400, 200 } ) + // frame_1.position = { 1000, 1000 } } } } diff --git a/code/collision.odin b/code/collision.odin new file mode 100644 index 0000000..2b9940c --- /dev/null +++ b/code/collision.odin @@ -0,0 +1,51 @@ +package sectr + +import "core:math/linalg" + + +// Not sure if I should in the future not do the radius check, +// As it maybe be better off as a general proc used in an iteration... +box_is_within_view :: proc( box : ^ Box2 ) -> b32 +{ + state := get_state(); using state + screen_extent := app_window.extent + + screen_bounds_radius := max(screen_extent.x, screen_extent.y) + box_bounds_radius := max(box.extent.x, box.extent.y) + + cam := project.workspace.cam + cam_box_distance := linalg.distance(cam.target, box.position) + acceptable_distance := box_bounds_radius + screen_bounds_radius + + if cam_box_distance > acceptable_distance { + return false + } + + screen_bounds := view_get_bounds() + box_bounds := box_get_bounds( box ) + + within_bounds : b32 = false + + when false { + within_x : b32 = box_bounds.top_left.x > screen_bounds.top_left.x + within_x &= box_bounds.top_left.x < screen_bounds.bottom_right.x + + within_y : b32 = box_bounds.top_left.y > screen_bounds.top_left.y + + state := get_state(); using state + screen_extent := state.app_window.extent + cam := & project.workspace.cam + within_x_bounds : b32 = pos.x >= -screen_extent.x && pos.x <= screen_extent.x + within_y_bounds : b32 = pos.y >= -screen_extent.y && pos.y <= screen_extent.y + } + return within_bounds +} + +is_within_screenspace :: proc( pos : Vec2 ) -> b32 { + state := get_state(); using state + screen_extent := state.app_window.extent + cam := & project.workspace.cam + within_x_bounds : b32 = pos.x >= -screen_extent.x && pos.x <= screen_extent.x + within_y_bounds : b32 = pos.y >= -screen_extent.y && pos.y <= screen_extent.y + return within_x_bounds && within_y_bounds +} diff --git a/code/entity_box2.odin b/code/entity_box2.odin new file mode 100644 index 0000000..b3931c0 --- /dev/null +++ b/code/entity_box2.odin @@ -0,0 +1,35 @@ +package sectr + +import "core:encoding/json" + +import rl "vendor:raylib" + +Box2 :: struct { + position : Vec2, + extent : Extents2, + color : Color +} + +box_size :: proc( box : ^ Box2 ) -> AreaSize { + return transmute(AreaSize) box.extent * 2.0 +} + +box_get_bounds :: proc( box : ^ Box2 ) -> Bounds2 { + top_left := box.position + Vec2 { -box.extent.x, box.extent.y } + bottom_right := box.position + Vec2 { box.extent.x, -box.extent.y } + return { top_left, bottom_right } +} + +box_set_size :: proc( box : ^ Box2, size : AreaSize ) { + box.extent = transmute(Extents2) size * 0.5 +} + +get_rl_rect :: proc ( box : ^ Box2 ) -> rl.Rectangle { + rect : rl.Rectangle = { + x = box.position.x - box.extent.x, + y = box.position.y - box.extent.y, + width = box.extent.x * 2.0, + height = box.extent.y * 2.0, + } + return rect +} diff --git a/code/env.odin b/code/env.odin index 2950f17..83b999f 100644 --- a/code/env.odin +++ b/code/env.odin @@ -26,11 +26,6 @@ Memory :: struct { } save_snapshot :: proc( snapshot : [^]u8 ) { - state := get_state() - - // state.font_rec_mono_semicasual_reg - // state.default_font - live_ptr := cast( ^ rawptr ) memory.live.curr_block.base mem.copy_non_overlapping( & snapshot[0], live_ptr, memory_chunk_size ) } @@ -40,6 +35,12 @@ load_snapshot :: proc( snapshot : [^]u8 ) { mem.copy_non_overlapping( live_ptr, snapshot, memory_chunk_size ) } +AppConfig :: struct { + resolution_width : uint, + resolution_height : uint, + refresh_rate : uint +} + State :: struct { input_data : [2] InputState, input_prev : ^ InputState, @@ -49,10 +50,8 @@ State :: struct { project : Project, - screen_width : i32, - screen_height : i32, - screen_dpi_scale : f32, - screen_dpc : f32, // Pixels per cm + config : AppConfig, + app_window : AppWindow, monitor_id : i32, monitor_refresh_hz : i32, @@ -64,10 +63,16 @@ State :: struct { default_font : Font, } -get_state :: proc() -> (^ State) { +get_state :: proc "contextless" () -> ^ State { return cast( ^ State ) raw_data( memory.persistent.backing.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 +} + Project :: struct { path : string, name : string, @@ -80,7 +85,7 @@ Workspace :: struct { name : string, cam : Camera, - frame_1 : Frame + frame_1 : Box2 } DebugData :: struct { @@ -89,6 +94,8 @@ DebugData :: struct { draw_debug_text_y : f32, - mouse_vis : b32, - mouse_pos : Vec2, + cursor_locked : b32, + cursor_unlock_pos : Vec2, // Raylib changes the mose position on lock, we want restore the position the user would be in on screen + mouse_vis : b32, + last_mouse_pos : Vec2, } diff --git a/code/frame.odin b/code/frame.odin deleted file mode 100644 index c948e5c..0000000 --- a/code/frame.odin +++ /dev/null @@ -1,27 +0,0 @@ -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 7c34f71..07370bc 100644 --- a/code/grime.odin +++ b/code/grime.odin @@ -40,5 +40,9 @@ OS_Type :: type_of(ODIN_OS) import rl "vendor:raylib" -Font :: rl.Font -Rectangle :: rl.Rectangle +Font :: rl.Font + +get_bounds :: proc { + box_get_bounds, + view_get_bounds, +} diff --git a/code/filesystem.odin b/code/grime_filesystem.odin similarity index 100% rename from code/filesystem.odin rename to code/grime_filesystem.odin diff --git a/code/memory.odin b/code/grime_memory.odin similarity index 100% rename from code/memory.odin rename to code/grime_memory.odin diff --git a/code/input.odin b/code/input.odin index 559f843..11fd456 100644 --- a/code/input.odin +++ b/code/input.odin @@ -1,6 +1,8 @@ // TODO(Ed) : This if its gets larget can be moved to its own package package sectr +import "base:runtime" + AnalogAxis :: f32 AnalogStick :: struct { X, Y : f32 @@ -267,9 +269,8 @@ MouseState :: struct { side, forward, back, extra : DigitalBtn } }, - X, Y, - vertical_wheel, - horizontal_wheel : AnalogAxis + pos, delta : Vec2, + vertical_wheel, horizontal_wheel : AnalogAxis } InputState :: struct { @@ -308,10 +309,6 @@ poll_input :: proc( old, new : ^ InputState ) key_id := cast(KeyboardKey) id is_down := cast(b32) rl.IsKeyDown( to_raylib_key(id) ) - if is_down { - nothing := true - nothing = false - } input_process_digital_btn( entry_old, entry_new, is_down ) } } @@ -337,14 +334,12 @@ poll_input :: proc( old, new : ^ InputState ) mouse_id := cast(MouseBtn) id - is_down := cast(b32) rl.IsMouseButtonPressed( to_raylib_mouse_btn(id) ) - input_process_digital_btn( & old.mouse.left, & new.mouse.left, is_down ) + is_down := cast(b32) rl.IsMouseButtonDown( to_raylib_mouse_btn(id) ) + input_process_digital_btn( old_btn, new_btn, is_down ) } - mouse_pos := rl.GetMousePosition() - - new.mouse.X = mouse_pos.x - new.mouse.Y = mouse_pos.y + new.mouse.pos = rl.GetMousePosition() - transmute(Vec2) get_state().app_window.extent + new.mouse.delta = rl.GetMouseDelta() new.mouse.vertical_wheel = rl.GetMouseWheelMove() } } diff --git a/code/math.odin b/code/math.odin index 7b525a7..838ccfd 100644 --- a/code/math.odin +++ b/code/math.odin @@ -5,9 +5,8 @@ import "core:math/linalg" Vec2 :: linalg.Vector2f32 Vec3 :: linalg.Vector3f32 - - - +Vec2i :: [2]i32 +Vec3i :: [3]i32 when false { // TODO(Ed) : Evaluate if this is needed diff --git a/code/serialize.odin b/code/serialize.odin index 1b97a91..e571948 100644 --- a/code/serialize.odin +++ b/code/serialize.odin @@ -3,8 +3,76 @@ package sectr import "core:encoding/json" import "core:fmt" import "core:os" +import "core:reflect" +import "core:runtime" import "core:strings" +@(private="file") +assign_int :: proc(val: any, i: $T) -> bool { + v := reflect.any_core(val) + switch &dst in v { + case i8: dst = i8 (i) + case i16: dst = i16 (i) + case i16le: dst = i16le (i) + case i16be: dst = i16be (i) + case i32: dst = i32 (i) + case i32le: dst = i32le (i) + case i32be: dst = i32be (i) + case i64: dst = i64 (i) + case i64le: dst = i64le (i) + case i64be: dst = i64be (i) + case i128: dst = i128 (i) + case i128le: dst = i128le (i) + case i128be: dst = i128be (i) + case u8: dst = u8 (i) + case u16: dst = u16 (i) + case u16le: dst = u16le (i) + case u16be: dst = u16be (i) + case u32: dst = u32 (i) + case u32le: dst = u32le (i) + case u32be: dst = u32be (i) + case u64: dst = u64 (i) + case u64le: dst = u64le (i) + case u64be: dst = u64be (i) + case u128: dst = u128 (i) + case u128le: dst = u128le (i) + case u128be: dst = u128be (i) + case int: dst = int (i) + case uint: dst = uint (i) + case uintptr: dst = uintptr(i) + case: return false + } + return true +} + +when false { +unmarshal_from_object :: proc( $Type: typeid, object : json.Object ) -> Type +{ + result : Type + type_info := type_info_of(Type) + #partial switch type in type_info.variant { + case runtime.Type_Info_Union: + ensure( true, "This proc doesn't support raw unions" ) + } + + base_ptr := uintptr( & result ) + + field_infos := reflect.struct_fields_zipped(Type) + for field_info in field_infos + { + field_type := field_info.type.id + field_ptr := cast(field_type) rawptr( base_ptr + field_info.offset ) + + #partial switch type in field_info.type.variant { + case runtime.Type_Info_Integer: + field_ptr = object[filed_info.name].(json.Integer) + } + } + + return result +} +} + Serializer_Version :: 1 Serializer_Loading :: false @@ -31,12 +99,14 @@ project_serialize :: proc ( project : ^ Project, archive : ^ ArchiveData, is_wri options.pretty = true options.use_spaces = false - if is_writting - { - marshal_archive : struct { + MarshalArchive :: struct { version : i32, project : Project - } + } + + if is_writting + { + marshal_archive : MarshalArchive marshal_archive.version = archive.version marshal_archive.project = project^ // TODO(Ed): In the future this will be more complicated, as serialization of workspaces and the code database won't be trivial @@ -55,12 +125,30 @@ project_serialize :: proc ( project : ^ Project, archive : ^ ArchiveData, is_wri archive_version : i32 = cast(i32) archive_json["version"].(json.Float) verify( Serializer_Version != archive_version, "Version mismatch on archive!" ) - project_json := archive_json["project"].(json.Object) - project.name = project_json["name"].(json.String) + // Note(Ed) : This works fine for now, but eventually it will most likely break with pointers... + // We'll most likely set things up so that all refs in the project & workspace are handles. + marshal_archive : MarshalArchive + json.unmarshal( archive.data, & marshal_archive, spec = json.Specification.MJSON, allocator = context.temp_allocator ) + if marshal_archive.version == Serializer_Version { + project^ = marshal_archive.project + } - // TODO(Ed) : Make this a separate proc - workspace_json := project_json["workspace"].(json.Object) - project.workspace.name = workspace_json["name"].(json.String) + // Manual unmarshal + when false + { + project_json := archive_json["project"].(json.Object) + project.name = project_json["name"].(json.String) + + // TODO(Ed) : Make this a separate proc + workspace_json := project_json["workspace"].(json.Object) + { + using project.workspace + name = workspace_json["name"].(json.String) + + // cam = unmarshal_from_object(Camera, workspace_json["camera"].(json.Object) ) + frame_1 = frame_json_unmarshal( & workspace_json["frame_1"] ) + } + } // DEBUG DUD options.use_spaces = false diff --git a/code/serialize_manual_unmarshal.odin b/code/serialize_manual_unmarshal.odin new file mode 100644 index 0000000..cad9b13 --- /dev/null +++ b/code/serialize_manual_unmarshal.odin @@ -0,0 +1,38 @@ +package sectr + +import "core:encoding/json" +import "core:reflect" + +// TODO(Ed) : Generic Unmarshling of json objects (There should be a way I believe todo this generically but the reflect library is not well documented) + +vec2_json_unmarshal :: proc ( value : ^ json.Value ) -> Vec2 { + json_v := value.(json.Array) + return { + f32(json_v[0].(json.Float)), + f32(json_v[1].(json.Float)), + } +} + +color_json_unmarshal :: proc ( value : ^ json.Value ) -> Color { + json_color := value.(json.Array) + r := u8(json_color[0].(json.Float)) + g := u8(json_color[1].(json.Float)) + b := u8(json_color[2].(json.Float)) + a := u8(json_color[3].(json.Float)) + return { r, g, b, a } +} + +box_json_unmarshal :: proc ( value : ^ json.Value ) -> Box2 { + object := value.(json.Object) + json_pos := object["position"].(json.Array) + + position := Vec2 { f32(json_pos[0].(json.Float)), f32(json_pos[1].(json.Float)) } + width := f32( object["width"] .(json.Float)) + height := f32( object["height"].(json.Float)) + + return { + position = position, + extent = { width, height }, + color = color_json_unmarshal( & object["color"] ), + }, +} diff --git a/code/space.odin b/code/space.odin index 9a903d4..91dc82a 100644 --- a/code/space.odin +++ b/code/space.odin @@ -16,42 +16,110 @@ when ODIN_OS == OS_Type.Windows { // 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 +cm_to_pixels :: proc( cm : f32 ) -> f32 { + screen_dpc := get_state().app_window.dpc return cm * screen_dpc } -vec2_cm_to_pixels :: proc ( v : Vec2 ) -> Vec2 { - state := get_state(); using state +vec2_cm_to_pixels :: proc( v : Vec2 ) -> Vec2 { + screen_dpc := get_state().app_window.dpc return v * screen_dpc } -points_to_pixels :: proc ( points : f32 ) -> f32 { - state := get_state(); using state +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 } -pixels_to_points :: proc ( pixels : f32 ) -> f32 { - state := get_state(); using state +pixels_to_points :: proc( pixels : f32 ) -> f32 { + screen_dpc := get_state().app_window.dpc cm_per_pixel := 1.0 / screen_dpc return pixels * cm_per_pixel * Points_Per_Centimetre } +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 +} + Camera :: rl.Camera2D -get_half_screen :: proc() -> AreaSize { - state := get_state(); using state - return { - f32(screen_width) / 2, - f32(screen_height) / 2, - } -} +AreaSize :: distinct Vec2 Bounds2 :: struct { - bottom_left, top_right : Vec2 + top_left, bottom_right : Vec2 } -AreaSize :: struct { - width, height : f32 +BoundsCorners2 :: struct { + top_left, top_right, bottom_left, bottom_right : Vec2 +} + +Extents2 :: distinct Vec2 +Extents2i :: distinct Vec2i + +bounds2_radius :: proc( bounds : Bounds2 ) -> f32 { + return max( bounds.bottom_right.x, bounds.top_left.y ) +} + +extent_from_size :: proc ( size : AreaSize ) -> Extents2 { + return transmute(Extents2) size * 2.0 +} + +screen_size :: proc "contextless" () -> AreaSize { + extent := get_state().app_window.extent + return transmute(AreaSize) (extent * 2.0) +} + +screen_get_corners :: proc() -> BoundsCorners2 { + 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 } +} + +view_get_bounds :: proc() -> Bounds2 { + 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 } +} + +view_get_corners :: proc() -> BoundsCorners2 { + 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_to_render :: proc( pos : Vec2 ) -> Vec2 { + 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 + 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 } +} + +world_to_screen_no_zoom :: proc( position : Vec2 ) -> Vec2 { + state := get_state(); using state + cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom + return { position.x, position.y * -1 } * cam_zoom_ratio } diff --git a/code/text.odin b/code/text.odin index c70794c..44aae72 100644 --- a/code/text.odin +++ b/code/text.odin @@ -3,18 +3,22 @@ package sectr import "core:unicode/utf8" import rl "vendor:raylib" -debug_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = {} ) +debug_draw_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl.Color = rl.WHITE, font : rl.Font = {} ) { + state := get_state(); using state + if len( content ) == 0 { return } runes := utf8.string_to_runes( content, context.temp_allocator ) font := font - if ( font.chars == nil ) { - font = get_state().default_font + if ( font.glyphs == nil ) { + font = default_font } + pos := screen_to_render(pos) + rl.DrawTextCodepoints( font, raw_data(runes), cast(i32) len(runes), position = transmute(rl.Vector2) pos, @@ -22,3 +26,74 @@ debug_text :: proc( content : string, pos : Vec2, size : f32 = 16.0, color : rl. 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 +{ + // This is a static var within raylib. We don't have getter access to it. + @static text_line_spacing : f32 = 15 + + text_size : AreaSize + + if 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) font.baseSize + scale_factor := font_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( font, letter ) + + id += 1 + + if letter != rune('\n') + { + if font.glyphs[index].advanceX != 0 { + text_width += f32(font.glyphs[index].advanceX) + } + else { + text_width += font.recs[index].width + f32(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 +} diff --git a/code/tick_render.odin b/code/tick_render.odin index 713c7e9..52c341b 100644 --- a/code/tick_render.odin +++ b/code/tick_render.odin @@ -8,78 +8,114 @@ render :: proc() { state := get_state(); using state replay := & memory.replay + cam := & project.workspace.cam + win_extent := state.app_window.extent - half_screen_width := f32(screen_width) / 2 - half_screen_height := f32(screen_height) / 2 + screen_top_left : Vec2 = { + -win_extent.x + cam.target.x, + -win_extent.y + cam.target.y, + } 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 ) + render_mode_2d() + { + fps_msg := fmt.tprint( "FPS:", rl.GetFPS() ) + 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, color = rl.GREEN ) - rl.EndMode2D() - // rl.EndMode3D() - rl.EndDrawing() - // Note(Ed) : Polls input as well. + 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 = 50 + } + + cam := & project.workspace.cam + screen_corners := screen_get_corners() + + position := screen_corners.top_right + position.x -= 300 + position.y += debug.draw_debug_text_y + + content := fmt.bprintf( draw_text_scratch[:], format, ..args ) + debug_draw_text( content, position ) + + debug.draw_debug_text_y += 16 + } + + // Debug Text + { + debug_text( "Screen Width : %v", rl.GetScreenWidth () ) + debug_text( "Screen Height: %v", rl.GetScreenHeight() ) + if replay.mode == ReplayMode.Record { + debug_text( "Recording Input") + } + if replay.mode == ReplayMode.Playback { + debug_text( "Replaying Input") + } + } + + if debug.mouse_vis { + debug_text( "Position: %v", input.mouse.pos ) + rect_pos := transmute(Vec2) state.app_window.extent + input.mouse.pos + + width : f32 = 32 + mouse_rect : rl.Rectangle + mouse_rect.x = rect_pos.x - width * 0.5 + mouse_rect.y = rect_pos.y - width * 0.5 + mouse_rect.width = width + mouse_rect.height = width + rl.DrawRectangleRec( mouse_rect, Color_White ) + } + + debug.draw_debug_text_y = 50 } + rl.EndDrawing() +} + +render_mode_2d :: proc() { + state := get_state(); using state + cam := & project.workspace.cam + win_extent := state.app_window.extent + + rl.BeginMode2D( project.workspace.cam ) // Frame 1 { frame_1 := & project.workspace.frame_1 - rect := get_rect( frame_1 ) + rect := get_rl_rect( frame_1 ) + screen_pos := world_to_screen_pos(frame_1.position) + 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 ) + rect.x = points_to_pixels( screen_pos.x ) + rect.y = points_to_pixels( screen_pos.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 + // Frame 2 + when false + { + frame_1 := & project.workspace.frame_1 + rect := get_rl_rect( frame_1 ) + screen_pos := world_to_screen_pos(frame_1.position) - state := get_state(); using state - if debug.draw_debug_text_y > 800 { - debug.draw_debug_text_y = 50 + rect.width = points_to_pixels( rect.width ) + rect.height = points_to_pixels( rect.height ) + rect.x = points_to_pixels( screen_pos.x ) + rect.y = points_to_pixels( screen_pos.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 ) } - 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 + rl.EndMode2D() +} diff --git a/code/tick_update.odin b/code/tick_update.odin index 71b1c78..fd8bc27 100644 --- a/code/tick_update.odin +++ b/code/tick_update.odin @@ -1,5 +1,6 @@ package sectr +import "base:runtime" import "core:fmt" import rl "vendor:raylib" @@ -19,6 +20,7 @@ DebugActions :: struct { cam_move_left : b32, cam_move_down : b32, cam_move_right : b32, + cam_mouse_pan : b32, } poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) @@ -26,6 +28,13 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) using actions using input + modifier_active := keyboard.right_alt.ended_down || + keyboard.right_control.ended_down || + keyboard.right_shift.ended_down || + keyboard.left_alt.ended_down || + keyboard.left_control.ended_down || + keyboard.left_shift.ended_down + load_project = keyboard.left_control.ended_down && pressed( keyboard.O ) save_project = keyboard.left_control.ended_down && pressed( keyboard.S ) @@ -35,10 +44,12 @@ poll_debug_actions :: proc( actions : ^ DebugActions, input : ^ InputState ) 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 + cam_move_up = keyboard.W.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) + cam_move_left = keyboard.A.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) + cam_move_down = keyboard.S.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) + cam_move_right = keyboard.D.ended_down && ( ! modifier_active || keyboard.left_shift.ended_down ) + + cam_mouse_pan = mouse.right.ended_down && ! pressed(mouse.right) } update :: proc( delta_time : f64 ) -> b32 @@ -46,6 +57,14 @@ update :: proc( delta_time : f64 ) -> b32 state := get_state(); using state replay := & memory.replay + 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 + } + state.input, state.input_prev = swap( state.input, state.input_prev ) poll_input( state.input_prev, state.input ) @@ -110,14 +129,12 @@ update :: proc( delta_time : f64 ) -> b32 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 + digital_move_speed : f32 = 200.0 + zoom_sensitiviity : f32 = 3.5 - cam := & project.workspace.cam + 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 ) @@ -125,10 +142,20 @@ update :: proc( delta_time : f64 ) -> b32 - 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) + move_velocity *= digital_move_speed * f32(delta_time) cam.target += move_velocity + + if debug_actions.cam_mouse_pan + { + if is_within_screenspace(input.mouse.pos) { + pan_velocity := input.mouse.delta * (1/cam.zoom) + cam.target -= pan_velocity + } + } } + debug.last_mouse_pos = input.mouse.pos + should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose() return should_shutdown } diff --git a/ols.json b/ols.json index 367da58..2b55c69 100644 --- a/ols.json +++ b/ols.json @@ -12,6 +12,10 @@ { "name": "code", "path": "C:/projects/SectrPrototype/code" + }, + { + "name": "ini", + "path": "C:/projects/SectrPrototype/thirdparty/ini" } ], "enable_document_symbols": true, diff --git a/scripts/update_deps.ps1 b/scripts/update_deps.ps1 index 321ec84..ae3d11e 100644 --- a/scripts/update_deps.ps1 +++ b/scripts/update_deps.ps1 @@ -5,8 +5,10 @@ $path_code = join-path $path_root 'code' $path_build = join-path $path_root 'build' $path_thirdparty = join-path $path_root 'thirdparty' -$url_odin_repo = 'https://github.com/Ed94/Odin.git' -$path_odin = join-path $path_thirdparty 'Odin' +$url_odin_repo = 'https://github.com/Ed94/Odin.git' +$url_ini_parser = 'https://github.com/laytan/odin-ini-parser.git' +$path_odin = join-path $path_thirdparty 'Odin' +$path_ini_parser = join-path $path_thirdparty 'ini' $incremental_checks = Join-Path $PSScriptRoot 'helpers/incremental_checks.ps1' . $incremental_checks @@ -25,21 +27,18 @@ if (Test-Path -Path $path_odin) # Get the latest local and remote commit hashes for the current branch $localCommit = git -C $path_odin rev-parse HEAD $remoteCommit = git -C $path_odin rev-parse '@{u}' - - # Compare local and remote commits - if ($localCommit -ne $remoteCommit) + if ($localCommit -ne $remoteCommit) { - Write-Host "Odin repository is out-of-date. Pulling changes and rebuilding..." - git -C $path_odin pull - push-location $path_odin - & .\build.bat - pop-location + Write-Host "Odin repository is out-of-date. Pulling changes and rebuilding..." + git -C $path_odin pull + push-location $path_odin + & .\build.bat + pop-location - $binaries_dirty = $true + $binaries_dirty = $true } - else - { - Write-Host "Odin repository is up-to-date. No need to rebuild." + else { + Write-Host "Odin repository is up-to-date. No need to rebuild." } } else @@ -54,6 +53,23 @@ else $binaries_dirty = $true } +if (Test-Path -Path $path_ini_parser) +{ + Write-Host "Checking for updates on the ini-parser" + $localCommit = git -C $path_ini_parser rev-parse HEAD + $remoteCommit = git -C $path_ini_parser rev-parse '@{u}' + if ($localCommit -ne $remoteCommit) + { + Write-Host "ini-parser repository is out-of-date. Pulling changes and rebuilding..." + git -C $path_ini_parser pull + } +} +else +{ + Write-Host "Cloning Odin repository..." + git clone $url_ini_parser $path_ini_parser +} + $path_vendor = join-path $path_odin 'vendor' $path_vendor_raylib = join-path $path_vendor 'raylib' $path_raylib_dlls = join-path $path_vendor_raylib 'windows'