Lots of stuff (Big ones are cam and frame initial features)

There is now a 2D camera in the workspace!
We have a basic 'Frame' definition. It doesn't have any interaction yet...

I started to define spacial math, mostly for doing conversion and getting a grounding on pixel/points/cm reference. The world space is in cm.
This commit is contained in:
Edward R. Gonzalez 2024-02-10 03:40:53 -05:00
parent 2d698d22c9
commit f76ba4e9ba
13 changed files with 390 additions and 178 deletions

View File

@ -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

View File

@ -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 }

View File

@ -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,
}

27
code/frame.odin Normal file
View File

@ -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
}

View File

@ -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

View File

@ -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,

View File

@ -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()
}
}

View File

@ -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
}
}
}

View File

@ -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()

57
code/space.odin Normal file
View File

@ -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
}

View File

@ -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 );

85
code/tick_render.odin Normal file
View File

@ -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
}

134
code/tick_update.odin Normal file
View File

@ -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
}