From e082ab49918eff1153d5840525fc75be9aca35d8 Mon Sep 17 00:00:00 2001 From: RobinsAviary Date: Wed, 15 Oct 2025 19:24:52 -0400 Subject: [PATCH 1/5] Add Calling Odin from Lua example --- .github/workflows/check.yml | 1 + .../call_odin_from_lua.odin | 65 +++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 lua/call_odin_from_lua/call_odin_from_lua.odin diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a11d7ca..04686bf 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -64,6 +64,7 @@ jobs: odin check lua/global_variables $FLAGS odin check lua/hellope_lua $FLAGS + odin check lua/call_odin_from_lua $FLAGS odin check math/noise/draw_texture $FLAGS odin check math/rand/markov $FLAGS diff --git a/lua/call_odin_from_lua/call_odin_from_lua.odin b/lua/call_odin_from_lua/call_odin_from_lua.odin new file mode 100644 index 0000000..8ac1430 --- /dev/null +++ b/lua/call_odin_from_lua/call_odin_from_lua.odin @@ -0,0 +1,65 @@ +package call_odin_from_lua + +import lua "vendor:lua/5.4" +import "core:fmt" + +// The code the Lua VM will run +CODE :: "print(add(2, 2))" + +// Since Lua is a C library, it expects procedures with the "c" calling convention +// Odin takes advantage of an overall implied context that is implicitly passed with each procedure when it is called +// As such, calling procedures with the Odin calling convention (the regular one) inside of a "c" procedure will require you to include "base:runtime" and "context = runtime.default_context()" at the beginning of the "c" procedure +// There is a compiler check for this, and a note, so this issue is very easy to catch + +// Lua expects a specific type of procedure to be defined, with the argument and return value seen below +// This procedure signature is also defined inside of the binding as CFunction, so they can easily be passed into functions +// State is the Lua state, which contains the passed arguments, and can/will contain return value(s) +add :: proc "c" (state: ^lua.State) -> i32 { + // Check to see if both arguments that were passed are integers, and store them in variables + // If these fail, Lua automatically pushes an error to the stack, and terminates the code it is currently executing + a := lua.L_checkinteger(state, 1) + b := lua.L_checkinteger(state, 2) + + // Since the integer type that the Lua library uses is a distinct copy of an i32 (because it is a C library), basic math operations are supported by default + result := a + b + + // Push the result onto the stack + lua.pushinteger(state, result) + + // Lua manages the stack itself once it regains control, flushing any leftover argument values (or otherwise), so there is no explicit memory management required (Within reason) + // To make things simpler and avoid over-managing memory, Lua initalizes the stack to be able to store at least 20 values before overflowing, though it is a constant defined in "lua.MINSTACK", to be specific + // To go beyond this number, use the function "lua.checkstack()" + + // It then takes the top number of elements of the stack, returning them to the end user + // Return the integer that was pushed on top of the stack + return 1 +} + +main :: proc() { + // Create new Lua state + state := lua.L_newstate() + + // Open the base libraries (print, etc...) + lua.open_base(state) + + // This is a macro to push a CFunction to the stack, and then popping and using it to set a global value in the Lua VM's global table + lua.register(state, "add", add) + + // Here is the extended version, for reference: + /* + lua.pushcfunction(state, add) + lua.setglobal(state, "add") + */ + + // Run code and check if it succeeded + if lua.L_dostring(state, CODE) != 0 { + // Get the error string from the top of the stack and print it + error := lua.tostring(state, -1) + fmt.println(error) + // Pop the error off of the stack + lua.pop(state, 1) + } + + // Closes the Lua VM, deallocating all memory + lua.close(state) +} \ No newline at end of file From 64abac383c29a58d3d4e4b8176c8754c548bdf5a Mon Sep 17 00:00:00 2001 From: RobinsAviary Date: Mon, 27 Oct 2025 16:37:26 -0400 Subject: [PATCH 2/5] added checkstack --- lua/call_odin_from_lua/call_odin_from_lua.odin | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lua/call_odin_from_lua/call_odin_from_lua.odin b/lua/call_odin_from_lua/call_odin_from_lua.odin index 8ac1430..1e637c2 100644 --- a/lua/call_odin_from_lua/call_odin_from_lua.odin +++ b/lua/call_odin_from_lua/call_odin_from_lua.odin @@ -23,6 +23,8 @@ add :: proc "c" (state: ^lua.State) -> i32 { // Since the integer type that the Lua library uses is a distinct copy of an i32 (because it is a C library), basic math operations are supported by default result := a + b + // Make room for the integer we're about to push onto the stack + lua.checkstack(state, 1) // Push the result onto the stack lua.pushinteger(state, result) From c038dc4e7ae2d024250c3eb5ca6f5879083a14b7 Mon Sep 17 00:00:00 2001 From: RobinsAviary Date: Wed, 19 Nov 2025 21:30:34 -0500 Subject: [PATCH 3/5] removed unnecessary check, clarified some language --- lua/call_odin_from_lua/call_odin_from_lua.odin | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lua/call_odin_from_lua/call_odin_from_lua.odin b/lua/call_odin_from_lua/call_odin_from_lua.odin index 1e637c2..7ee39bf 100644 --- a/lua/call_odin_from_lua/call_odin_from_lua.odin +++ b/lua/call_odin_from_lua/call_odin_from_lua.odin @@ -6,7 +6,7 @@ import "core:fmt" // The code the Lua VM will run CODE :: "print(add(2, 2))" -// Since Lua is a C library, it expects procedures with the "c" calling convention +// Because Lua is a C library, it expects procedures with the "c" calling convention // Odin takes advantage of an overall implied context that is implicitly passed with each procedure when it is called // As such, calling procedures with the Odin calling convention (the regular one) inside of a "c" procedure will require you to include "base:runtime" and "context = runtime.default_context()" at the beginning of the "c" procedure // There is a compiler check for this, and a note, so this issue is very easy to catch @@ -23,8 +23,6 @@ add :: proc "c" (state: ^lua.State) -> i32 { // Since the integer type that the Lua library uses is a distinct copy of an i32 (because it is a C library), basic math operations are supported by default result := a + b - // Make room for the integer we're about to push onto the stack - lua.checkstack(state, 1) // Push the result onto the stack lua.pushinteger(state, result) @@ -47,7 +45,7 @@ main :: proc() { // This is a macro to push a CFunction to the stack, and then popping and using it to set a global value in the Lua VM's global table lua.register(state, "add", add) - // Here is the extended version, for reference: + // The extended version, for reference: /* lua.pushcfunction(state, add) lua.setglobal(state, "add") From 1b8fa47f216477870494075da434a8c680047b79 Mon Sep 17 00:00:00 2001 From: RobinsAviary Date: Sat, 29 Nov 2025 06:55:11 -0500 Subject: [PATCH 4/5] added clock example --- .github/workflows/check.yml | 1 + raylib/clock/clock.odin | 140 ++++++++++++++++++++++++++++++++++++ 2 files changed, 141 insertions(+) create mode 100644 raylib/clock/clock.odin diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 04686bf..21fd3ae 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -72,6 +72,7 @@ jobs: odin check raylib/game_of_life $FLAGS odin check raylib/log $FLAGS odin check raylib/microui $FLAGS + odin check raylib/clock $FLAGS odin check raylib/ports/examples_template.odin -file $FLAGS odin check raylib/ports/shaders/shaders_mesh_instancing.odin -file $FLAGS odin check raylib/ports/shapes/shapes_basic_shapes.odin -file $FLAGS diff --git a/raylib/clock/clock.odin b/raylib/clock/clock.odin new file mode 100644 index 0000000..dacfdca --- /dev/null +++ b/raylib/clock/clock.odin @@ -0,0 +1,140 @@ +package clock + +import rl "vendor:raylib" +import "core:time" +import "core:time/datetime" +import "core:time/timezone" +import "core:os" +import "core:math" +import "core:fmt" +import "core:strings" + +// The time of day +Time_Period :: enum { + AM, + PM, +} + +Error :: enum { + LocalTimeFailed, + TimeToDateTimeFailed, + DateTimeToTimezoneFailed, + DateTimeToTimeFailed, +} + +// Create a vector with a specific direction and magnitude +distance_angle :: proc(distance: f32, angle: f32) -> (vector: rl.Vector2) { + vector = {math.sin_f32(angle), -math.cos_f32(angle)} * distance + + return +} + +throw_error :: proc(ok: bool, error: Error) { + if !ok { + fmt.printfln("An error occured: %s", error) + os.exit(int(error)) + } +} + +main :: proc() { + // Load the local timezone for the user + tz, ok := timezone.region_load("local") + throw_error(ok, .LocalTimeFailed) + + // Turn on anti-aliasing + rl.SetConfigFlags({.MSAA_4X_HINT}) + // Create window + rl.InitWindow(640, 480, "Clock") + + // Get the center of the screen + screen_center := rl.Vector2 {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2} + + rl.SetTargetFPS(60) + + for !rl.WindowShouldClose() { + rl.BeginDrawing() + + rl.ClearBackground(rl.RAYWHITE) + + date_time: datetime.DateTime + // Basic error handling + // Get the current time, convert it to DateTime + date_time, ok = time.time_to_datetime(time.now()) + throw_error(ok, .TimeToDateTimeFailed) + + // Get the current DateTime, convert it to the user's local timezone + date_time, ok = timezone.datetime_to_tz(date_time, tz) + throw_error(ok, .DateTimeToTimezoneFailed) + + // Convert the DateTime back to a regular Time struct + regular_time: time.Time + regular_time, ok = time.datetime_to_time(date_time) + throw_error(ok, .DateTimeToTimeFailed) + + // Determine if it's AM or PM + period: Time_Period + hour, min, sec, nanos := time.precise_clock_from_time(regular_time) + if hour > 12 { + hour -= 12 + period = .PM + } + millis := nanos / int(time.Millisecond) + + // Determine the radius of the clock (not including outline) + radius := f32(rl.GetScreenHeight()) / 2.5 + + // The number of points in the clock's outer circles + point_count: i32 = 128 + rl.DrawCircleSector(screen_center, radius + 2, 0, 360, point_count, rl.BLACK) + rl.DrawCircleSector(screen_center, radius, 0, 360, point_count, rl.LIGHTGRAY) + + sec_scalar := (f32(sec) + (f32(millis) / 1000)) / 60 + min_scalar := (f32(min) + sec_scalar) / 60 + hour_scalar := (f32(hour) + min_scalar) / 12 + + // Draw the hour hand + rl.DrawLineEx(screen_center, screen_center + distance_angle(radius * .7, hour_scalar * math.TAU), 4, rl.DARKGREEN) + // Draw the minute hand + rl.DrawLineEx(screen_center, screen_center + distance_angle(radius * .75, min_scalar * math.TAU), 4, rl.BLACK) + // Draw the second hand + rl.DrawLineEx(screen_center, screen_center + distance_angle(radius * .5, sec_scalar * math.TAU), 4, rl.RED) + + rl.DrawCircleV(screen_center, 6, {40, 40, 40, 255}) + + // Draw the dashes on the clock + for i := 0; i < 60; i += 1 { + angle := (f32(i) / 60) * math.TAU + + inside_distance: f32 = .8 + thickness: f32 = 1.5 + + if i % 5 == 0 { + // Hour markings + inside_distance = .775 + thickness = 3 + } + + rl.DrawLineEx(screen_center + distance_angle(radius * inside_distance, angle), screen_center + distance_angle(radius * .95, angle), thickness, rl.DARKGRAY) + } + + // Format time into a string + str := fmt.aprintfln("%2d:%2d:%2d %s", hour, min, sec, period, allocator = context.temp_allocator) + c_str := strings.clone_to_cstring(str, context.temp_allocator) + + // Center and draw text + default_font := rl.GetFontDefault() + text_size := rl.MeasureTextEx(default_font, c_str, 30, f32(default_font.glyphPadding) + 1) + + rl.DrawTextEx(default_font, c_str, {screen_center.x - (text_size.x / 2), f32(rl.GetScreenHeight()) - (30 * 1.25)}, 30 * 1.0001, f32(default_font.glyphPadding) + 1, rl.DARKGRAY) + + rl.EndDrawing() + + // Garbage collection + free_all(context.temp_allocator) + } + + rl.CloseWindow() + + // Unload user's local timezone + timezone.region_destroy(tz) +} \ No newline at end of file From f40f5bc8ef417f81374d3b393c26e1b89bbdac10 Mon Sep 17 00:00:00 2001 From: RobinsAviary Date: Sat, 29 Nov 2025 06:57:19 -0500 Subject: [PATCH 5/5] Revert "added clock example" This reverts commit 1b8fa47f216477870494075da434a8c680047b79. --- .github/workflows/check.yml | 1 - raylib/clock/clock.odin | 140 ------------------------------------ 2 files changed, 141 deletions(-) delete mode 100644 raylib/clock/clock.odin diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 21fd3ae..04686bf 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -72,7 +72,6 @@ jobs: odin check raylib/game_of_life $FLAGS odin check raylib/log $FLAGS odin check raylib/microui $FLAGS - odin check raylib/clock $FLAGS odin check raylib/ports/examples_template.odin -file $FLAGS odin check raylib/ports/shaders/shaders_mesh_instancing.odin -file $FLAGS odin check raylib/ports/shapes/shapes_basic_shapes.odin -file $FLAGS diff --git a/raylib/clock/clock.odin b/raylib/clock/clock.odin deleted file mode 100644 index dacfdca..0000000 --- a/raylib/clock/clock.odin +++ /dev/null @@ -1,140 +0,0 @@ -package clock - -import rl "vendor:raylib" -import "core:time" -import "core:time/datetime" -import "core:time/timezone" -import "core:os" -import "core:math" -import "core:fmt" -import "core:strings" - -// The time of day -Time_Period :: enum { - AM, - PM, -} - -Error :: enum { - LocalTimeFailed, - TimeToDateTimeFailed, - DateTimeToTimezoneFailed, - DateTimeToTimeFailed, -} - -// Create a vector with a specific direction and magnitude -distance_angle :: proc(distance: f32, angle: f32) -> (vector: rl.Vector2) { - vector = {math.sin_f32(angle), -math.cos_f32(angle)} * distance - - return -} - -throw_error :: proc(ok: bool, error: Error) { - if !ok { - fmt.printfln("An error occured: %s", error) - os.exit(int(error)) - } -} - -main :: proc() { - // Load the local timezone for the user - tz, ok := timezone.region_load("local") - throw_error(ok, .LocalTimeFailed) - - // Turn on anti-aliasing - rl.SetConfigFlags({.MSAA_4X_HINT}) - // Create window - rl.InitWindow(640, 480, "Clock") - - // Get the center of the screen - screen_center := rl.Vector2 {f32(rl.GetScreenWidth()) / 2, f32(rl.GetScreenHeight()) / 2} - - rl.SetTargetFPS(60) - - for !rl.WindowShouldClose() { - rl.BeginDrawing() - - rl.ClearBackground(rl.RAYWHITE) - - date_time: datetime.DateTime - // Basic error handling - // Get the current time, convert it to DateTime - date_time, ok = time.time_to_datetime(time.now()) - throw_error(ok, .TimeToDateTimeFailed) - - // Get the current DateTime, convert it to the user's local timezone - date_time, ok = timezone.datetime_to_tz(date_time, tz) - throw_error(ok, .DateTimeToTimezoneFailed) - - // Convert the DateTime back to a regular Time struct - regular_time: time.Time - regular_time, ok = time.datetime_to_time(date_time) - throw_error(ok, .DateTimeToTimeFailed) - - // Determine if it's AM or PM - period: Time_Period - hour, min, sec, nanos := time.precise_clock_from_time(regular_time) - if hour > 12 { - hour -= 12 - period = .PM - } - millis := nanos / int(time.Millisecond) - - // Determine the radius of the clock (not including outline) - radius := f32(rl.GetScreenHeight()) / 2.5 - - // The number of points in the clock's outer circles - point_count: i32 = 128 - rl.DrawCircleSector(screen_center, radius + 2, 0, 360, point_count, rl.BLACK) - rl.DrawCircleSector(screen_center, radius, 0, 360, point_count, rl.LIGHTGRAY) - - sec_scalar := (f32(sec) + (f32(millis) / 1000)) / 60 - min_scalar := (f32(min) + sec_scalar) / 60 - hour_scalar := (f32(hour) + min_scalar) / 12 - - // Draw the hour hand - rl.DrawLineEx(screen_center, screen_center + distance_angle(radius * .7, hour_scalar * math.TAU), 4, rl.DARKGREEN) - // Draw the minute hand - rl.DrawLineEx(screen_center, screen_center + distance_angle(radius * .75, min_scalar * math.TAU), 4, rl.BLACK) - // Draw the second hand - rl.DrawLineEx(screen_center, screen_center + distance_angle(radius * .5, sec_scalar * math.TAU), 4, rl.RED) - - rl.DrawCircleV(screen_center, 6, {40, 40, 40, 255}) - - // Draw the dashes on the clock - for i := 0; i < 60; i += 1 { - angle := (f32(i) / 60) * math.TAU - - inside_distance: f32 = .8 - thickness: f32 = 1.5 - - if i % 5 == 0 { - // Hour markings - inside_distance = .775 - thickness = 3 - } - - rl.DrawLineEx(screen_center + distance_angle(radius * inside_distance, angle), screen_center + distance_angle(radius * .95, angle), thickness, rl.DARKGRAY) - } - - // Format time into a string - str := fmt.aprintfln("%2d:%2d:%2d %s", hour, min, sec, period, allocator = context.temp_allocator) - c_str := strings.clone_to_cstring(str, context.temp_allocator) - - // Center and draw text - default_font := rl.GetFontDefault() - text_size := rl.MeasureTextEx(default_font, c_str, 30, f32(default_font.glyphPadding) + 1) - - rl.DrawTextEx(default_font, c_str, {screen_center.x - (text_size.x / 2), f32(rl.GetScreenHeight()) - (30 * 1.25)}, 30 * 1.0001, f32(default_font.glyphPadding) + 1, rl.DARKGRAY) - - rl.EndDrawing() - - // Garbage collection - free_all(context.temp_allocator) - } - - rl.CloseWindow() - - // Unload user's local timezone - timezone.region_destroy(tz) -} \ No newline at end of file