├── .gitignore ├── LICENSE ├── README.md ├── chipmunk.dll ├── examples ├── bunnymark │ ├── README.md │ └── bunnymark.odin ├── chipmunk │ └── example_chipmunk.odin ├── chipmunk_walker │ └── example_chipmunk.odin ├── example_raymath │ └── example_raymath.odin ├── gui │ └── example_gui.odin ├── live_reload_demo │ ├── README.md │ ├── game.odin │ └── host.odin ├── physac │ └── example_physac.odin ├── shared │ ├── chipmunk_ext │ │ └── chipmunk_ext.odin │ ├── debug_console │ │ └── debug_console.odin │ ├── game_math │ │ └── game_math.odin │ ├── json_ext │ │ └── unmarshal.odin │ ├── plugin │ │ └── plugin.odin │ ├── reloader_thread │ │ └── reloader_thread.odin │ └── sprite │ │ └── sprite.odin ├── simple_demo │ └── simple_demo.odin └── sprites │ ├── README.md │ ├── game.odin │ └── host.odin ├── ext ├── README.md ├── chipmunk │ ├── bridge │ │ └── chipmunk_bridge.odin │ ├── chipmunk_bindings.odin │ └── types │ │ └── chipmunk_types.odin ├── lib │ ├── chipmunk.lib │ ├── physac.lib │ ├── raygui.lib │ └── raymath.lib ├── physac │ ├── README.md │ ├── bridge │ │ └── physac_bridge.odin │ ├── build.bat │ ├── physac-preprocessed.h │ ├── physac.c │ ├── physac.h │ ├── physac_bindings.odin │ ├── preprocess.bat │ └── types │ │ └── physac_types.odin ├── raygui │ ├── bridge │ │ └── raygui_bridge.odin │ ├── build.bat │ ├── generate_raygui_header.py │ ├── raygui-preprocessed.h │ ├── raygui.c │ ├── raygui.h │ ├── raygui_bindings.odin │ ├── ricons.h │ └── types │ │ └── raygui_types.odin ├── raylib │ ├── include │ │ └── raylib.h │ └── lib │ │ ├── cmake │ │ └── raylib │ │ │ ├── raylib-config-version.cmake │ │ │ └── raylib-config.cmake │ │ ├── pkgconfig │ │ └── raylib.pc │ │ ├── raylib.lib │ │ └── raylib_static.lib └── raymath │ ├── bridge │ └── raymath_bridge.odin │ ├── build.bat │ ├── generate_raymath_header.py │ ├── raymath-preprocessed.h │ ├── raymath.c │ ├── raymath.h │ ├── raymath_bindings.odin │ └── types │ └── raymath_types.odin ├── generator ├── README.md ├── bindgen │ ├── c-parser-evaluate.odin │ ├── c-parser-helpers.odin │ ├── c-parser-nodes.odin │ ├── c-parser.odin │ ├── errors.odin │ ├── generator-clean.odin │ ├── generator-export.odin │ ├── generator-helpers.odin │ └── generator.odin ├── generate_bindings.odin └── preprocessed │ ├── aux_data │ └── aux_data.odin │ ├── raylib-preprocessed.h │ └── raylib.h ├── raylib.dll ├── raylib ├── bridge │ └── raylib_bridge.odin ├── raylib_bindings.odin └── types │ └── raylib_types.odin ├── resources ├── bunnymark │ └── wabbit_alpha.png ├── cat.png ├── live-reload.gif ├── meow.wav ├── scarfy.png ├── screenshots │ ├── example_bunnymark.png │ ├── example_live_reload.png │ ├── example_simple_demo.png │ └── physac.png └── tanatana.ogg ├── scripts ├── build_live_reload_host.bat ├── build_live_reload_plugin.bat ├── build_sprites_host.bat ├── build_sprites_plugin.bat ├── copy_raylib_libs.bat ├── debug.bat ├── generate_raylib_bindings.bat ├── run_live_reload_demo.bat ├── run_simple_demo.bat └── run_sprites_demo.bat └── todo.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.dll 2 | *.exp 3 | *.pdb 4 | *.obj 5 | *.exe 6 | *.bc 7 | *.ll 8 | .vs/ 9 | .vscode/ 10 | temp/ 11 | bin/ 12 | game.lib 13 | game.exp 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # odin-raylib 2 | 3 | ## !! 4 | 5 | ## NEWER AND MAINTAINED REPO HERE: [ftommasi/raylib-odin](https://github.com/ftommasi/raylib-odin) 6 | 7 | ## !! 8 | 9 | Bindings for [raylib](http://www.raylib.com) 3.0.0, a small C gamedev library, for the [odin programming language](https://odin.handmade.network/). 10 | 11 | These are usable, but in a pre-alpha state and definitely a work in progress. 12 | 13 | from [Kevin Watters](https://kev.town) 14 | 15 | ## Prerequisites 16 | 17 | - Windows 10 (for now) 18 | - Visual Studio 2017 19 | - A command-line prompt opened with the "x64 Native Tools Command Prompt for VS 2017" shortcut 20 | - `odin` on your PATH (built from odin's `master` branch is good) 21 | 22 | ## Running the examples 23 | 24 | ### `examples/simple_demo` 25 | 26 | To run a simple demo: 27 | 28 | ``` 29 | odin run examples/simple_demo 30 | ``` 31 | 32 | You should see this: 33 | 34 | ![a screenshot of a simple demo](resources/screenshots/example_simple_demo.png) 35 | 36 | ### `examples/live_reload_demo` 37 | 38 | To run the live reload demo: 39 | 40 | ``` 41 | scripts\run_live_reload_demo 42 | ``` 43 | 44 | You should see the demo appear. It runs a background thread watching for changes in the source directory. When a change happens, it will rebuild the `bin/game.dll` file--which will then get automatically reloaded by the host process. 45 | 46 | ![a screenshot of the live reload demo](resources/screenshots/example_live_reload.png) 47 | 48 | Try editing some of the values in `examples/live_reload_demo/game.odin` and saving the file to see the changes instantly. 49 | 50 | ![live reload example](resources/live-reload.gif) 51 | 52 | ### `examples/bunnymark` 53 | 54 | ``` 55 | odin run examples/bunnymark 56 | ``` 57 | 58 | ![a screenshot of the bunnymark demo](resources/screenshots/example_bunnymark.png) 59 | 60 | ### `examples/physac` 61 | 62 | ``` 63 | odin run examples/physac 64 | ``` 65 | 66 | ![a screenshot of the physac demo](resources/screenshots/physac.png) 67 | 68 | ## Modules 69 | 70 | Bindings for `raygui` and `raymath` are in an alpha state. See `examples/gui` and `examples/example_raymath`. 71 | 72 | ## Bindings 73 | 74 | To rebuild the bindings to raylib, make sure you have raylib checked out into the directory above raylib-odin, and run `scripts\generate_raylib_bindings.bat`. 75 | 76 | ## TODO 77 | 78 | - fix up bindgen code so that raylib.h doesn't need to be hand-tweaked 79 | - make everything work on OSX and Linux 80 | 81 | ## Acknowledgements 82 | 83 | - Thanks to [@TheElkantor](https://twitter.com/theelkantor) for their article ["How to add hot reload when using raylib"](https://www.developing-stuff.com/how-to-add-hot-reload-when-using-raylib/) - for pointing out that passing function pointers to a plugin was probably the easiest way to get a live reload harness going. 84 | 85 | -------------------------------------------------------------------------------- /chipmunk.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/chipmunk.dll -------------------------------------------------------------------------------- /examples/bunnymark/README.md: -------------------------------------------------------------------------------- 1 | # bunnymark 2 | 3 | ![a screenshot of the bunnymark demo](../../resources/screenshots/example_bunnymark.png) 4 | -------------------------------------------------------------------------------- /examples/bunnymark/bunnymark.odin: -------------------------------------------------------------------------------- 1 | package bunnymark 2 | 3 | import "core:fmt" 4 | import "core:strings" 5 | import "core:math/linalg" 6 | 7 | import rl "../../raylib" 8 | 9 | MAX_BUNNIES :: 100000; 10 | 11 | Bunny :: struct { 12 | position : linalg.Vector2, 13 | speed : linalg.Vector2, 14 | color : rl.Color, 15 | } 16 | 17 | get_random_color :: inline proc() -> rl.Color { 18 | return rl.Color { 19 | cast(u8)rl.get_random_value(0, 255), 20 | cast(u8)rl.get_random_value(0, 255), 21 | cast(u8)rl.get_random_value(0, 255), 22 | 255 23 | }; 24 | } 25 | 26 | main :: proc() { 27 | using rl; 28 | 29 | screen_width :: 1280; 30 | screen_height :: 960; 31 | 32 | init_window(screen_width, screen_height, "raylib-odin example :: Bunnymark"); 33 | defer close_window(); 34 | 35 | tex_bunny := load_texture("resources/bunnymark/wabbit_alpha.png"); 36 | 37 | bunnies := make([]Bunny, MAX_BUNNIES); 38 | 39 | bunnies_count := 0; 40 | 41 | set_target_fps(60); 42 | 43 | for !window_should_close() { 44 | if is_mouse_button_down(.LEFT_BUTTON) { 45 | for _ in 0..100 { 46 | new_bunny := &bunnies[bunnies_count]; 47 | bunnies_count += 1; 48 | 49 | using new_bunny; 50 | position = get_mouse_position(); 51 | speed.x = f32(get_random_value(250, 500)) / f32(60); 52 | speed.y = f32(get_random_value(250, 500) - 500) / f32(60); 53 | color = get_random_color(); 54 | } 55 | } 56 | 57 | for i in 0..bunnies_count { 58 | bunny := &bunnies[i]; 59 | using bunny; 60 | 61 | position.x += speed.x; 62 | position.y += speed.y; 63 | 64 | if position.x > cast(f32)get_screen_width() || position.x < 0 do speed.x *= -1; 65 | if position.y > cast(f32)get_screen_height() || position.y < 0 do speed.y *= -1; 66 | } 67 | 68 | begin_drawing(); 69 | defer end_drawing(); 70 | 71 | clear_background(RAYWHITE); 72 | 73 | for i in 0..bunnies_count { 74 | draw_texture(tex_bunny, cast(i32)bunnies[i].position.x, cast(i32)bunnies[i].position.y, bunnies[i].color); 75 | } 76 | 77 | draw_rectangle(0, 0, screen_width, 40, LIGHTGRAY); 78 | draw_text("raylib-odin bunnymark", 10, 10, 20, DARKGRAY); 79 | s := strings.clone_to_cstring(fmt.tprint("bunnies:", bunnies_count)); defer delete(s); 80 | draw_text(s, 400, 10, 20, RED); 81 | draw_fps(260, 10); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /examples/chipmunk/example_chipmunk.odin: -------------------------------------------------------------------------------- 1 | package example_chipmunk 2 | 3 | import "../../raylib" 4 | 5 | import cp "../../ext/chipmunk" 6 | import "core:math" 7 | import "core:fmt" 8 | import "core:math/rand" 9 | 10 | import "../shared/chipmunk_ext" 11 | 12 | NUM_VERTS :: 5; 13 | 14 | Accumulator := 0.0; 15 | MAX_FRAME_TIME := 0.2; 16 | FIXED_DT := 1.0 / 60.0; 17 | 18 | main :: proc() { 19 | space := cp.space_new(); 20 | 21 | cp.space_set_iterations(space, 5); 22 | cp.space_set_gravity(space, cpv(0, -100)); 23 | 24 | body := cp.space_get_static_body(space); 25 | static_body := body; 26 | shape : ^cp.Shape; 27 | 28 | tris : [3]cp.Vect = { 29 | cpv(-15, -15), 30 | cpv(0, 10), 31 | cpv(15, -15) 32 | }; 33 | 34 | for i in 0..10 { 35 | for j in 0..7 { 36 | stagger := (j % 2) * 40.0; 37 | offset := cpv(i*80 - 320 + stagger, j*70 - 240); 38 | shape = cp.space_add_shape(space, cp.poly_shape_new(static_body, len(tris), &tris[0], TransformTranslate(offset), 0.0)); 39 | cp.shape_set_elasticity(shape, 1); 40 | cp.shape_set_friction(shape, 1); 41 | cp.shape_set_filter(shape, NOT_GRABBABLE_FILTER); 42 | } 43 | } 44 | 45 | verts : [NUM_VERTS]cp.Vect; 46 | for _, i in verts { 47 | angle := -2.0 * math.PI * cast(f32)i / f32(NUM_VERTS); 48 | verts[i] = cpv(10.0*math.cos(angle), 10.0*math.sin(angle)); 49 | } 50 | 51 | pentagon_mass := 1.0; 52 | pentagon_moment := cp.moment_for_poly(1.0, NUM_VERTS, &verts[0], cpv(0, 0), 0.0); 53 | 54 | for _ in 0..300 { 55 | body = cp.space_add_body(space, cp.body_new(pentagon_mass, pentagon_moment)); 56 | x := rand.float32() * 640 - 320; 57 | cp.body_set_position(body, cpv(x, 350)); 58 | 59 | shape = cp.space_add_shape(space, cp.poly_shape_new(body, NUM_VERTS, &verts[0], TransformIdentity, 0)); 60 | cp.shape_set_elasticity(shape, 0); 61 | cp.shape_set_friction(shape, 0.4); 62 | } 63 | 64 | 65 | screenWidth, screenHeight:i32 = 800, 450; 66 | init_window(screenWidth, screenHeight, "odin-chipmunk :: Plink"); 67 | defer close_window(); 68 | 69 | camera := Camera2D { math.Vec2{}, math.Vec2{280, 320/2}, 180, .65 }; 70 | 71 | set_target_fps(60); 72 | for !window_should_close() { // Detect window close button or ESC key 73 | { 74 | // Fixed update time loop for chipmunk 75 | delta_time := cast(f64)get_frame_time(); 76 | if delta_time > MAX_FRAME_TIME do delta_time = MAX_FRAME_TIME; 77 | for Accumulator += delta_time; Accumulator > FIXED_DT; Accumulator -= FIXED_DT { 78 | cp.space_step(space, FIXED_DT); 79 | } 80 | } 81 | 82 | begin_drawing(); 83 | defer end_drawing(); 84 | 85 | clear_background(BLACK); 86 | 87 | begin_mode_2d(camera); 88 | space_debug_draw(space); 89 | end_mode_2d(); 90 | 91 | draw_fps(5, 5); 92 | } 93 | 94 | free_space_children(space); 95 | cp.space_free(space); 96 | } 97 | 98 | -------------------------------------------------------------------------------- /examples/chipmunk_walker/example_chipmunk.odin: -------------------------------------------------------------------------------- 1 | package example_chipmunk 2 | 3 | import "../../raylib" 4 | 5 | import cp "../../ext/chipmunk" 6 | import "core:math" 7 | import "core:math/rand" 8 | 9 | import "../shared/chipmunk_ext" 10 | import "../../../shmup/src/input" 11 | 12 | NUM_VERTS :: 5; 13 | 14 | Accumulator := 0.0; 15 | MAX_FRAME_TIME := 0.2; 16 | FIXED_DT := 1.0 / 180.0; 17 | 18 | @private seg_radius :: 3.0; 19 | 20 | make_leg :: proc(space: ^cp.Space, side, offset: cp.Float, chassis, crank: ^cp.Body, anchor: cp.Vect) { 21 | leg_mass := 1.0; 22 | 23 | // make leg 24 | a := cpv(0, 0); 25 | b := cpv(0.0, side); 26 | 27 | upper_leg := cp.space_add_body(space, cp.body_new(leg_mass, cp.moment_for_segment(leg_mass, a, b, 0.0))); 28 | cp.body_set_position(upper_leg, cpv(offset, 0.0)); 29 | 30 | shape := cp.space_add_shape(space, cp.segment_shape_new(upper_leg, a, b, seg_radius)); 31 | cp.shape_set_filter(shape, cp.ShapeFilter {1, ALL_CATEGORIES, ALL_CATEGORIES} ); 32 | 33 | cp.space_add_constraint(space, cp.pivot_joint_new_2(chassis, upper_leg, cpv(offset, 0.0), cpv(0, 0))); 34 | 35 | // lower leg 36 | a = cpv(0, 0); 37 | b = cpv(0.0, -1.0 * side); 38 | lower_leg := cp.space_add_body(space, cp.body_new(leg_mass, cp.moment_for_segment(leg_mass, a, b, 0.0))); 39 | cp.body_set_position(lower_leg, cpv(offset, -side)); 40 | 41 | shape = cp.space_add_shape(space, cp.segment_shape_new(lower_leg, a, b, seg_radius)); 42 | cp.shape_set_filter(shape, cp.ShapeFilter{1, ALL_CATEGORIES, ALL_CATEGORIES}); 43 | 44 | shape = cp.space_add_shape(space, cp.circle_shape_new(lower_leg, seg_radius * 2.0, b)); 45 | cp.shape_set_filter(shape, cp.ShapeFilter{1, ALL_CATEGORIES, ALL_CATEGORIES}); 46 | cp.shape_set_elasticity(shape, 0); 47 | cp.shape_set_friction(shape, 1); 48 | 49 | cp.space_add_constraint(space, cp.pin_joint_new(chassis, lower_leg, cpv(offset, 0.0), cpv(0, 0))); 50 | 51 | cp.space_add_constraint(space, cp.gear_joint_new(upper_leg, lower_leg, 0.0, 1.0)); 52 | 53 | diag := math.sqrt(side * side + offset * offset); 54 | 55 | constraint := cp.space_add_constraint(space, cp.pin_joint_new(crank, upper_leg, anchor, cpv(0.0, side))); 56 | cp.pin_joint_set_dist(constraint, diag); 57 | 58 | constraint = cp.space_add_constraint(space, cp.pin_joint_new(crank, lower_leg, anchor, cpv(0, 0))); 59 | cp.pin_joint_set_dist(constraint, diag); 60 | } 61 | 62 | main :: proc() { 63 | space := cp.space_new(); 64 | defer cp.space_free(space); 65 | defer free_space_children(space); 66 | 67 | cp.space_set_iterations(space, 20); 68 | cp.space_set_gravity(space, cpv(0, -500)); 69 | 70 | static_body := cp.space_get_static_body(space); 71 | shape : ^cp.Shape; 72 | 73 | // Create segments around the edge of the screen. 74 | shape = cp.space_add_shape(space, cp.segment_shape_new(static_body, cpv(-320, -240), cpv(-320, 240), 0)); 75 | cp.shape_set_elasticity(shape, 1); 76 | cp.shape_set_friction(shape, 1); 77 | cp.shape_set_filter(shape, NOT_GRABBABLE_FILTER); 78 | 79 | shape = cp.space_add_shape(space, cp.segment_shape_new(static_body, cpv(320, -240), cpv(320,240), 0)); 80 | cp.shape_set_elasticity(shape, 1); 81 | cp.shape_set_friction(shape, 1); 82 | cp.shape_set_filter(shape, NOT_GRABBABLE_FILTER); 83 | 84 | shape = cp.space_add_shape(space, cp.segment_shape_new(static_body, cpv(-320, -240), cpv(320, -240), 0)); 85 | cp.shape_set_elasticity(shape, 1); 86 | cp.shape_set_friction(shape, 1); 87 | cp.shape_set_filter(shape, NOT_GRABBABLE_FILTER); 88 | 89 | make_walker :: proc(space: ^cp.Space, pos: cp.Vect) -> ^cp.Constraint { 90 | 91 | offset := 30.0; 92 | 93 | // make chassis 94 | chassis_mass := 2.0; 95 | a, b := cpv(-offset, 0.0), cpv(offset, 0.0); 96 | chassis := cp.space_add_body(space, cp.body_new(chassis_mass, cp.moment_for_segment(chassis_mass, a, b, 0))); 97 | 98 | shape := cp.space_add_shape(space, cp.segment_shape_new(chassis, a, b, seg_radius)); 99 | cp.shape_set_filter(shape, cp.ShapeFilter{1, ALL_CATEGORIES, ALL_CATEGORIES}); 100 | 101 | // make crank 102 | crank_mass := 1.0; 103 | crank_radius := rand.float64() * 43.0; 104 | //crank_radius := 13.0; 105 | crank := cp.space_add_body(space, cp.body_new(crank_mass, cp.moment_for_circle(crank_mass, crank_radius, 0, cpv(0, 0)))); 106 | 107 | shape = cp.space_add_shape(space, cp.circle_shape_new(crank, crank_radius, cpv(0, 0))); 108 | cp.shape_set_filter(shape, cp.ShapeFilter{1, ALL_CATEGORIES, ALL_CATEGORIES}); 109 | 110 | cp.space_add_constraint(space, cp.pivot_joint_new_2(chassis, crank, cpv(0, 0), cpv(0, 0))); 111 | 112 | side := 30.0; 113 | num_legs := 2; 114 | 115 | cpvmult :: inline proc(v: cp.Vect, s: cp.Float) -> cp.Vect { return cpv(v.x * s, v.y * s); } 116 | cpvforangle :: inline proc(a: cp.Float) -> cp.Vect { return cpv(math.cos(a), math.sin(a)); } 117 | 118 | for i in 0..num_legs-1 { 119 | make_leg(space, side, offset, chassis, crank, cpvmult(cpvforangle(cast(cp.Float)(2*i+0)/cast(cp.Float)num_legs*math.PI), crank_radius)); 120 | make_leg(space, side, -offset, chassis, crank, cpvmult(cpvforangle(cast(cp.Float)(2*i+1)/cast(cp.Float)num_legs*math.PI), crank_radius)); 121 | } 122 | 123 | motor := cp.space_add_constraint(space, cp.simple_motor_new(chassis, crank, 6.0)); 124 | 125 | return motor; 126 | } 127 | 128 | motor1 := make_walker(space, cpv(0, 0)); 129 | motor2 := make_walker(space, cpv(0, 0)); 130 | 131 | // 132 | 133 | screenWidth, screenHeight:i32 = 800, 450; 134 | windowWidth, windowHeight:i32 = screenWidth * 2, screenHeight * 2; 135 | init_window(windowWidth, windowHeight, "odin-chipmunk :: TheoJansen"); 136 | defer close_window(); 137 | 138 | screen_rt := load_render_texture(screenWidth, screenHeight); 139 | defer unload_render_texture(screen_rt); 140 | 141 | camera := Camera2D { math.Vec2{}, math.Vec2{200, 320/2 - 80}, 180, 1.2 }; 142 | 143 | set_target_fps(60); 144 | for !window_should_close() { // Detect window close button or ESC key 145 | { 146 | // Fixed update time loop for chipmunk 147 | delta_time := cast(f64)get_frame_time(); 148 | if delta_time > MAX_FRAME_TIME do delta_time = MAX_FRAME_TIME; 149 | for Accumulator += delta_time; Accumulator > FIXED_DT; Accumulator -= FIXED_DT { 150 | 151 | { 152 | keys := input.keyboard_xy_player(0); 153 | rate := f64(-keys.x * 10.0 * (2.0 + keys.y) / 3.0); 154 | cp.simple_motor_set_rate(motor1, rate); 155 | cp.constraint_set_max_force(motor1, rate != 0.0 ? 100000.0 : 0.0); 156 | } 157 | { 158 | keys := input.keyboard_xy_player(1); 159 | rate := f64(-keys.x * 10.0 * (2.0 + keys.y) / 3.0); 160 | cp.simple_motor_set_rate(motor2, rate); 161 | cp.constraint_set_max_force(motor2, rate != 0.0 ? 100000.0 : 0.0); 162 | } 163 | 164 | cp.space_step(space, FIXED_DT); 165 | } 166 | } 167 | 168 | { 169 | begin_drawing(); 170 | defer end_drawing(); 171 | 172 | { 173 | begin_texture_mode(screen_rt); 174 | defer end_texture_mode(); 175 | 176 | clear_background(RAYWHITE); 177 | 178 | { 179 | begin_mode_2d(camera); 180 | defer end_mode_2d(); 181 | space_debug_draw(space); 182 | } 183 | } 184 | 185 | src_rect := Rectangle { 0, 0, cast(f32)screenWidth, -cast(f32)screenHeight }; 186 | dest_rect := Rectangle { 0, 0, cast(f32)windowWidth, cast(f32)windowHeight }; 187 | draw_texture_pro(screen_rt.texture, src_rect, dest_rect, Vector2 { 0, 0 }, 0, WHITE); 188 | 189 | draw_fps(5, 5); 190 | } 191 | } 192 | } 193 | 194 | -------------------------------------------------------------------------------- /examples/example_raymath/example_raymath.odin: -------------------------------------------------------------------------------- 1 | package example_raymath 2 | 3 | import "../../raylib" 4 | 5 | import raymath "../../ext/raymath" 6 | 7 | Particle :: struct { 8 | pos: Vector2, 9 | speed: Vector2, 10 | } 11 | 12 | NUM_PARTICLES :: 300; 13 | DRAG := Vector2 { 0.55, 0.55 }; 14 | 15 | main :: proc() { 16 | screenWidth:f32 = 800.0; 17 | screenHeight:f32 = 450.0; 18 | init_window(cast(i32)screenWidth, cast(i32)screenHeight, "raylib-odin :: raymath example"); 19 | set_target_fps(60); 20 | 21 | particles : [NUM_PARTICLES]Particle; 22 | 23 | for i in 0..NUM_PARTICLES-1 { 24 | particles[i].pos = Vector2{ 25 | cast(f32)get_random_value(0, cast(i32)screenWidth), 26 | cast(f32)get_random_value(0, cast(i32)screenHeight), 27 | }; 28 | particles[i].speed = Vector2 { 29 | cast(f32)get_random_value(-300, 300), 30 | cast(f32)get_random_value(-300, 300), 31 | }; 32 | } 33 | 34 | for !window_should_close() { 35 | for i in 0..NUM_PARTICLES-1 { 36 | p := &particles[i]; 37 | using p; 38 | pos = raymath.vector2_add(pos, raymath.vector2_scale(speed, get_frame_time())); 39 | if pos.x < 0 || pos.x > screenWidth { 40 | pos.x = raymath.clamp(pos.x, 0, screenWidth); 41 | speed.x = -speed.x; 42 | speed = raymath.vector2_multiply_v(speed, DRAG); 43 | } 44 | if pos.y < 0 || pos.y > screenHeight { 45 | pos.y = raymath.clamp(pos.y, 0, screenHeight); 46 | speed.y = -speed.y; 47 | speed = raymath.vector2_multiply_v(speed, DRAG); 48 | } 49 | } 50 | 51 | begin_drawing(); 52 | defer end_drawing(); 53 | 54 | clear_background(RAYWHITE); 55 | for i in 0..NUM_PARTICLES-1 { 56 | draw_circle_v(particles[i].pos, 5, RED); 57 | } 58 | } 59 | 60 | close_window(); 61 | } 62 | -------------------------------------------------------------------------------- /examples/gui/example_gui.odin: -------------------------------------------------------------------------------- 1 | package example_gui 2 | 3 | import "core:fmt" 4 | 5 | import "../../raylib" 6 | 7 | import raygui "../../ext/raygui" 8 | 9 | main :: proc() { 10 | init_window(800, 450, "odin-raylib :: gui"); 11 | 12 | for !window_should_close() { 13 | begin_drawing(); 14 | clear_background(BLACK); 15 | if (raygui.gui_button(Rectangle { 30, 320, 115, 30 }, "NORMAL")) { 16 | fmt.println("pressed"); 17 | } 18 | end_drawing(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/live_reload_demo/README.md: -------------------------------------------------------------------------------- 1 | # Live Reload Example 2 | 3 | ![a screenshot of the live reload demo](../../resources/screenshots/example_live_reload.png) 4 | 5 | ## `host.odin` 6 | 7 | This is the "harness" that contains the `main` proc that stays running while you iterate on the game. 8 | 9 | ## `game.odin` 10 | 11 | This is the "plugin" code that gets recompiled and reloaded by the host harness. 12 | 13 | ## `plugin` 14 | 15 | A small library providing the live-reload/plugin functionality. 16 | -------------------------------------------------------------------------------- /examples/live_reload_demo/game.odin: -------------------------------------------------------------------------------- 1 | package live_reload_demo 2 | 3 | import "core:math/linalg" 4 | import "core:os" 5 | import "core:fmt" 6 | import serializer "core:encoding/json" 7 | import rl "../../raylib/bridge" 8 | 9 | import "../shared/json_ext" 10 | 11 | // RAYLIB_EXTRA 12 | unload :: proc { 13 | rl.unload_texture, 14 | rl.unload_sound 15 | }; 16 | 17 | import "../shared/game_math" 18 | import "../shared/plugin" 19 | import "../shared/debug_console" 20 | 21 | state_json_dir :: "temp"; 22 | state_json_path :: "temp/state.json"; 23 | 24 | when os.OS == "windows" { 25 | import "core:sys/win32" 26 | 27 | mkdir_if_not_exist :: proc(dir: string) -> os.Errno { 28 | dir_wstr := win32.utf8_to_wstring(dir, context.temp_allocator); 29 | if win32.Bool(false) == win32.create_directory_w(dir_wstr, nil) do return os.Errno(win32.get_last_error()); 30 | return os.ERROR_NONE; 31 | } 32 | } 33 | 34 | // This state persists during a plugin reload. 35 | State :: struct { 36 | currentFrame : int, 37 | framesCounter : int, 38 | position : rl.Vector2, 39 | cat_position: rl.Vector2, 40 | cat_velocity: f32, 41 | } 42 | 43 | Transient_State :: struct { 44 | bg : rl.Texture, 45 | frameRec : rl.Rectangle, 46 | num_frames : int, 47 | framesSpeed : int, 48 | bg2 : rl.Texture, 49 | cat : rl.Texture, 50 | scarfy : rl.Texture, 51 | meow : rl.Sound, 52 | console: debug_console.Debug_Console, 53 | did_play: bool, 54 | } 55 | 56 | state : State; 57 | transient_state : Transient_State; 58 | 59 | @(export) 60 | on_load :: proc(funcs: ^rl.raylib_Funcs) { 61 | rl.bridge_init(funcs); 62 | 63 | using transient_state; 64 | 65 | scarfy = rl.load_texture("resources/scarfy.png"); 66 | cat = rl.load_texture("resources/cat.png"); 67 | meow = rl.load_sound("resources/tanatana.ogg"); 68 | 69 | num_frames = 6; 70 | frameRec = rl.Rectangle { 0, 0, cast(f32)scarfy.width / cast(f32)num_frames, cast(f32)scarfy.height }; 71 | framesSpeed = 9; 72 | 73 | { 74 | bg_img := rl.gen_image_gradient_v(850, 450, rl.PURPLE, rl.RAYWHITE); 75 | defer rl.unload_image(bg_img); 76 | bg = rl.load_texture_from_image(bg_img); 77 | 78 | bg2_img := rl.gen_image_gradient_h(850, 450, rl.Color { 0, 0, 0, 0, }, rl.Color { 255, 190, 200, 60 }); 79 | defer rl.unload_image(bg2_img); 80 | 81 | bg2 = rl.load_texture_from_image(bg2_img); 82 | } 83 | 84 | if !json_ext.unmarshal_file(&state, state_json_path) { 85 | fmt.println("error unmarshalling json"); 86 | } 87 | 88 | debug_console.init(&console); 89 | debug_console.log(&console, "game.dll loaded at time=", rl.get_time(), " seconds"); 90 | } 91 | 92 | @(export) 93 | on_unload :: proc() { 94 | rl.bridge_deinit(); 95 | 96 | using state; 97 | using transient_state; 98 | 99 | if state_bytes, err := serializer.marshal(state); err == .None { 100 | mkdir_if_not_exist(state_json_dir); 101 | os.write_entire_file(state_json_path, state_bytes); 102 | } else { 103 | s := ""; 104 | #partial switch err { 105 | case .Unsupported_Type: s = "Unsupported_Type"; 106 | case: s = "TODO"; 107 | } 108 | fmt.eprintln("error serializing state to bytes: ", s); 109 | } 110 | 111 | debug_console.destroy(&console); 112 | unload(scarfy); 113 | unload(bg); 114 | unload(bg2); 115 | unload(meow); 116 | } 117 | 118 | @(export) 119 | update_and_draw :: proc() -> plugin.Request { 120 | using state; 121 | using transient_state; 122 | using rl; 123 | 124 | request := plugin.Request.None; 125 | 126 | if !did_play && is_audio_device_ready() { 127 | //play_sound(meow); 128 | did_play = true; 129 | } 130 | 131 | // UPDATE 132 | width_of_one_frame := cast(f32)scarfy.width / cast(f32)num_frames; 133 | { 134 | framesCounter += 1; 135 | delta_time := get_frame_time(); 136 | player_move_pixels_per_second:f32 = 400.0; 137 | speed := delta_time * player_move_pixels_per_second; 138 | 139 | // move the player with the arrow or WASD keys 140 | if is_key_down(.RIGHT) || is_key_down(.D) do position.x += speed; 141 | if is_key_down(.LEFT) || is_key_down(.A) do position.x -= speed; 142 | if is_key_down(.UP) || is_key_down(.W) do position.y -= speed; 143 | if is_key_down(.DOWN) || is_key_down(.S) do position.y += speed; 144 | 145 | if is_key_pressed(.L) do debug_console.log(&console, "this is a test"); 146 | 147 | if is_key_pressed(.R) do request = .Reload; 148 | if is_key_pressed(.Q) do request = .Quit; 149 | 150 | // click to move the player as well 151 | if is_mouse_button_down(.LEFT_BUTTON) { 152 | mouse_pos := get_mouse_position(); 153 | to_pos := linalg.length(linalg.Vector2 { mouse_pos.x - position.x, mouse_pos.y - position.y }); 154 | if to_pos > 20 { 155 | mouse_pos.x -= f32(width_of_one_frame) * .5; 156 | mouse_pos.y -= f32(scarfy.height) * .5; 157 | if mouse_pos.x > position.x do position.x = min(mouse_pos.x, position.x + speed); 158 | else if mouse_pos.x < position.x do position.x = max(mouse_pos.x, position.x - speed); 159 | if mouse_pos.y > position.y do position.y = min(mouse_pos.y, position.y + speed); 160 | else if mouse_pos.y < position.y do position.y = max(mouse_pos.y, position.y - speed); 161 | } 162 | } 163 | 164 | // update the player sprite 165 | if framesCounter >= (60.0 / framesSpeed) { 166 | framesCounter = 0; 167 | currentFrame += 1; 168 | if currentFrame >= num_frames do currentFrame = 0.0; 169 | frameRec.x = cast(f32)currentFrame * width_of_one_frame; 170 | } 171 | 172 | // smoothly move the cat towards the player 173 | smooth_time:f32 = 0.4; 174 | 175 | cat_position.x = game_math.smooth_damp(cat_position.x, position.x - 40, &cat_velocity, smooth_time, delta_time); 176 | cat_position.y = 230; 177 | } 178 | 179 | player_rect := rl.Rectangle { position.x, position.y, width_of_one_frame, f32(scarfy.height) }; 180 | cat_rect := rl.Rectangle { cat_position.x, cat_position.y, f32(cat.width), f32(cat.height) }; 181 | cat_color := check_collision_recs(player_rect, cat_rect) ? RED : WHITE; 182 | 183 | // DRAW 184 | { 185 | begin_drawing(); 186 | defer end_drawing(); 187 | 188 | clear_background(RAYWHITE); 189 | draw_texture(bg, 0, 0, WHITE); 190 | { 191 | begin_blend_mode(BlendMode.ADDITIVE); 192 | defer end_blend_mode(); 193 | 194 | draw_texture(bg2, 0, 0, WHITE); 195 | } 196 | 197 | draw_texture_rec(scarfy, frameRec, position, WHITE); 198 | cat_rotation:f32 = 0.0; 199 | cat_scale:f32 = 1.0; 200 | draw_texture_ex(cat, cat_position, cat_rotation, cat_scale, cat_color); 201 | { 202 | begin_blend_mode(BlendMode.MULTIPLIED); 203 | defer end_blend_mode(); 204 | 205 | draw_circle_v(get_mouse_position(), 15, RED); 206 | } 207 | 208 | debug_console.update_and_draw(&console); 209 | } 210 | 211 | return request; 212 | } 213 | -------------------------------------------------------------------------------- /examples/live_reload_demo/host.odin: -------------------------------------------------------------------------------- 1 | package live_reload_demo 2 | 3 | import rl "../../raylib" 4 | 5 | import "core:fmt" 6 | import "core:os" 7 | 8 | import "../shared/plugin" 9 | 10 | when os.OS == "windows" do import "../shared/reloader_thread" 11 | 12 | screenWidth :i32 = 800; 13 | screenHeight :i32 = 450; 14 | 15 | main :: proc() { 16 | using rl; 17 | using plugin; 18 | 19 | // Create the window 20 | set_config_flags( 21 | // TODO: bindings could remove FLAG_ and the like from enums 22 | ConfigFlag.MSAA_4X_HINT 23 | | ConfigFlag.VSYNC_HINT 24 | //| ConfigFlag.FULLSCREEN_MODE 25 | ); 26 | 27 | init_window(screenWidth, screenHeight, "raylib-odin :: live reload example"); 28 | defer close_window(); 29 | 30 | set_window_position(40, 40); 31 | set_target_fps(60); 32 | init_audio_device(); 33 | defer close_audio_device(); 34 | 35 | // Load the plugin 36 | plugin_funcs : raylib_Funcs; 37 | rl.get_function_pointers(&plugin_funcs); 38 | 39 | plugin: Plugin; 40 | if !plugin_load(&plugin, "bin/game.dll", &plugin_funcs) { 41 | fmt.println("error loading bin/game.dll"); 42 | return; 43 | } 44 | defer plugin_unload(&plugin); 45 | 46 | // kick off live reload watcher thread 47 | when os.OS == "windows" { 48 | reloader := reloader_thread.start("cmd.exe /c scripts\\build_live_reload_plugin.bat", "examples\\live_reload_demo"); 49 | defer reloader_thread.finish(reloader); 50 | } 51 | 52 | // Game loop 53 | RELOAD_INTERVAL_MS:f32 : 0.25; 54 | reload_timer := RELOAD_INTERVAL_MS; 55 | for !window_should_close() { 56 | force_reload := false; 57 | #partial switch plugin.update_and_draw_proc() { 58 | case .Reload: force_reload = true; 59 | case .Quit: return; 60 | } 61 | 62 | needs_reload_check := false; 63 | if !force_reload { 64 | reload_timer -= get_frame_time(); 65 | for reload_timer < 0 { 66 | reload_timer += RELOAD_INTERVAL_MS; 67 | needs_reload_check = true; 68 | } 69 | } 70 | 71 | if needs_reload_check || force_reload { 72 | plugin_maybe_reload(&plugin, &plugin_funcs, force_reload); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /examples/physac/example_physac.odin: -------------------------------------------------------------------------------- 1 | package example_physac 2 | 3 | import "core:thread" 4 | 5 | import "../../raylib" 6 | 7 | import physac "../../ext/physac" 8 | 9 | _physics_loop :: proc(^thread.Thread) -> int { 10 | physac.physics_loop(nil); 11 | return 0; 12 | } 13 | 14 | main :: proc() { 15 | screenWidth:f32 = 800.0; 16 | screenHeight:f32 = 450.0; 17 | init_window(cast(i32)screenWidth, cast(i32)screenHeight, "raylib-odin :: physac example"); 18 | set_target_fps(60); 19 | 20 | physac.init_physics(); 21 | 22 | t := thread.create(_physics_loop); 23 | thread.start(t); 24 | 25 | defer physac.close_physics(); 26 | 27 | floor := physac.create_physics_body_rectangle({ screenWidth / 2.0, screenHeight}, 500, 100, 10); 28 | floor.enabled = false; 29 | 30 | circle := physac.create_physics_body_circle({ screenWidth / 2.0, screenHeight / 2.0}, 45, 10); 31 | circle.enabled = false; 32 | 33 | for !window_should_close() { 34 | // Physics body creation inputs 35 | if is_mouse_button_pressed(.LEFT_BUTTON) do physac.create_physics_body_polygon(get_mouse_position(), cast(f32)get_random_value(20, 80), get_random_value(3, 8), 10); 36 | else if is_mouse_button_pressed(.RIGHT_BUTTON) do physac.create_physics_body_circle(get_mouse_position(), cast(f32)get_random_value(10, 45), 10); 37 | 38 | // Destroy falling physics bodies 39 | { 40 | bodies_count := physac.get_physics_bodies_count(); 41 | for i in bodies_count-1..0 { 42 | body := physac.get_physics_body(i); 43 | if body != nil && body.position.y > screenHeight*2 do physac.destroy_physics_body(body); 44 | } 45 | } 46 | 47 | begin_drawing(); 48 | defer end_drawing(); 49 | 50 | clear_background(RAYWHITE); 51 | 52 | bodies_count := physac.get_physics_bodies_count(); 53 | for i in 0..bodies_count - 1 { 54 | body := physac.get_physics_body(i); 55 | if body == nil do continue; 56 | 57 | vertex_count := physac.get_physics_shape_vertices_count(i); 58 | for j in 0..vertex_count - 1 { 59 | vertex_a := physac.get_physics_shape_vertex(body, j); 60 | 61 | jj := ((j + 1) < vertex_count) ? (j + 1) : 0; 62 | 63 | vertex_b := physac.get_physics_shape_vertex(body, jj); 64 | 65 | draw_line_v(vertex_a, vertex_b, GREEN); 66 | } 67 | 68 | } 69 | } 70 | 71 | close_window(); 72 | } 73 | -------------------------------------------------------------------------------- /examples/shared/chipmunk_ext/chipmunk_ext.odin: -------------------------------------------------------------------------------- 1 | // TODO: remove cp.transform_translate and other static inline funcs from the bindings 2 | // and add their odin equivalents to an "ext" file that sits in the bindings package 3 | 4 | package chipmunk_ext 5 | 6 | import "core:math" 7 | import "core:fmt" 8 | import "core:mem" 9 | 10 | import cp "../../../ext/chipmunk" 11 | import "../../../raylib" 12 | 13 | cpv :: proc(a, b: $T) -> cp.Vect { return cp.Vect{cast(f64)a,cast(f64)b}; } 14 | 15 | NO_GROUP :: 0; 16 | NOT_GRABBABLE_FILTER := cp.ShapeFilter {NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT}; 17 | 18 | ALL_CATEGORIES :: ~u32(0); 19 | 20 | //cpShapeFilter NOT_GRABBABLE_FILTER = {CP_NO_GROUP, ~GRABBABLE_MASK_BIT, ~GRABBABLE_MASK_BIT}; 21 | 22 | CollisionTypes :: enum { 23 | ONE_WAY = 1, 24 | } 25 | 26 | TransformIdentity := cp.Transform { 1, 0, 0, 1, 0, 0, }; 27 | 28 | GRABBABLE_MASK_BIT :u32 = 1<<31; 29 | 30 | One_Way_Platform :: struct { 31 | n: cp.Vect, 32 | } 33 | 34 | platform_instance : One_Way_Platform; 35 | 36 | 37 | to_color :: inline proc(c: $C) -> Color { 38 | return Color { 39 | cast(u8)(c.r * 255), 40 | cast(u8)(c.g * 255), 41 | cast(u8)(c.b * 255), 42 | cast(u8)(c.a * 255), 43 | }; 44 | } 45 | to_vec2 :: inline proc(v: $V) -> math.Vec2 { 46 | return math.Vec2 { cast(f32)v.x, cast(f32)v.y }; 47 | } 48 | 49 | free_space_children :: proc(space: ^cp.Space) { 50 | _post_shape_free :: proc "c" (shape: ^cp.Shape, space: ^cp.Space) { 51 | _shape_free_wrap :: proc(space: ^cp.Space, shape: ^cp.Shape, unused: rawptr) { 52 | cp.space_remove_shape(space, shape); 53 | cp.shape_free(shape); 54 | } 55 | cp.space_add_post_step_callback(space, cast(cp.PostStepFunc)_shape_free_wrap, shape, nil); 56 | } 57 | 58 | _post_constraint_free :: proc "c" (constraint: ^cp.Constraint, space: ^cp.Space) { 59 | _constraint_free_wrap :: proc(space: ^cp.Space, constraint: ^cp.Constraint, unused: rawptr) { 60 | cp.space_remove_constraint(space, constraint); 61 | cp.constraint_free(constraint); 62 | } 63 | 64 | cp.space_add_post_step_callback(space, cast(cp.PostStepFunc)_constraint_free_wrap, constraint, nil); 65 | } 66 | 67 | cp.space_each_shape(space, cast(cp.SpaceShapeIteratorFunc)_post_shape_free, space); 68 | cp.space_each_constraint(space, cast(cp.SpaceConstraintIteratorFunc)_post_constraint_free, space); 69 | } 70 | 71 | _draw_circle :: proc "c" (p: cp.Vect, a: cp.Float, outline, fill: cp.SpaceDebugColor, data: cp.DataPointer) { 72 | draw_circle_v(to_vec2(p), cast(f32)a, to_color(fill)); 73 | } 74 | 75 | _draw_segment :: proc "c" (a, b: cp.Vect, color: cp.SpaceDebugColor, data: cp.DataPointer) { 76 | draw_line_v(to_vec2(a), to_vec2(b), to_color(color)); 77 | } 78 | 79 | _draw_fat_segment :: proc "c" (a, b: cp.Vect, r: cp.Float, outline, fill: cp.SpaceDebugColor, data: cp.DataPointer) { 80 | draw_line_ex(to_vec2(a), to_vec2(b), cast(f32)r, to_color(fill)); 81 | } 82 | 83 | _draw_polygon :: proc "c" (count: i32, verts: ^cp.Vect, r: cp.Float, outline, fill: cp.SpaceDebugColor, data: cp.DataPointer) { 84 | assert(count > 0 && count < 200000); 85 | 86 | points := make([]Vector2, count + 1, context.temp_allocator); 87 | 88 | verts_slice := mem.slice_ptr(verts, cast(int)count); 89 | 90 | for vert, i in verts_slice do points[i] = to_vec2(vert); 91 | 92 | points[count] = to_vec2(verts_slice[0]); // wrap back around 93 | 94 | draw_poly_ex(&points[0], count+1, to_color(fill)); 95 | draw_poly_ex_lines(&points[0], count+1, to_color(outline)); 96 | } 97 | 98 | _draw_dot :: proc "c" (size: cp.Float, pos: cp.Vect, color: cp.SpaceDebugColor, data: cp.DataPointer) { 99 | draw_pixel_v(to_vec2(pos), to_color(color)); 100 | } 101 | 102 | LAColor :: inline proc(l, a: f32) -> cp.SpaceDebugColor do return cp.SpaceDebugColor {l, l, l, a}; 103 | RGBAColor :: inline proc(r,g,b,a: f32) -> cp.SpaceDebugColor do return cp.SpaceDebugColor {r, g, b, a}; 104 | 105 | color_for_shape :: proc "stdcall" (shape: ^cp.Shape, data: ^cp.DataPointer) -> cp.SpaceDebugColor { 106 | if cp.shape_get_sensor(shape) != 0 { 107 | return LAColor(1.0, 0.1); 108 | } 109 | 110 | body := cp.shape_get_body(shape); 111 | 112 | if cp.body_is_sleeping(body) != 0 { 113 | return LAColor(0.2, 1.0); 114 | } 115 | 116 | if body.sleeping.idle_time > shape.space.sleep_time_threshold { 117 | return LAColor(0.66, 1.0); 118 | } 119 | 120 | val:u32 = cast(u32)shape.hashid; 121 | 122 | // scramble the bits up using Robert Jenkins' 32 bit integer hash function 123 | val = (val+0x7ed55d16) + (val<<12); 124 | val = (val ~ 0xc761c23c) ~ (val>>19); 125 | val = (val+0x165667b1) + (val<<5); 126 | val = (val+0xd3a2646c) ~ (val<<9); 127 | val = (val+0xfd7046c5) + (val<<3); 128 | val = (val ~ 0xb55a4f09) ~ (val>>16); 129 | 130 | r := cast(f64)((val>>0) & 0xFF); 131 | g := cast(f64)((val>>8) & 0xFF); 132 | b := cast(f64)((val>>16) & 0xFF); 133 | 134 | max_val := max(max(r, g), b); 135 | min_val := min(min(r, g), b); 136 | intensity := cp.body_get_type(body) == .STATIC ? 0.15 : 0.75; 137 | 138 | // Saturate and scale the color 139 | if min_val == max_val { 140 | return RGBAColor(cast(f32)intensity, 0.0, 0.0, 1.0); 141 | } 142 | 143 | coef := cast(f64)intensity/(max_val - min_val); 144 | return RGBAColor( 145 | f32((r - min_val)*coef), 146 | f32((g - min_val)*coef), 147 | f32((b - min_val)*coef), 148 | 1.0 149 | ); 150 | } 151 | 152 | space_debug_draw :: proc(space: ^cp.Space) { 153 | options := cp.SpaceDebugDrawOptions { 154 | auto_cast _draw_circle, 155 | auto_cast _draw_segment, 156 | auto_cast _draw_fat_segment, 157 | auto_cast _draw_polygon, 158 | auto_cast _draw_dot, 159 | 160 | cp.SpaceDebugDrawFlags.SHAPES | .CONSTRAINTS | .COLLISION_POINTS, 161 | 162 | {200.0/255.0, 210.0/255.0, 230.0/255.0, 1.0}, 163 | 0, // extra padding 164 | color_for_shape, 165 | {0, 0.75, 0.0, 1.0}, 166 | {1.0, 0.0, 0.0, 1.0}, 167 | nil, 168 | }; 169 | 170 | cp.space_debug_draw(space, &options); 171 | } 172 | 173 | TransformNewTranspose :: inline proc(a, c, tx, b, d, ty: cp.Float) -> cp.Transform { 174 | return cp.Transform {a, b, c, d, tx, ty}; 175 | } 176 | 177 | TransformTranslate :: inline proc(translate: cp.Vect) -> cp.Transform { 178 | return TransformNewTranspose( 179 | 1.0, 0.0, translate.x, 180 | 0.0, 1.0, translate.y 181 | ); 182 | } 183 | 184 | -------------------------------------------------------------------------------- /examples/shared/debug_console/debug_console.odin: -------------------------------------------------------------------------------- 1 | package live_reload_demo 2 | 3 | import "core:strings" 4 | import "core:fmt" 5 | 6 | import "../game_math" 7 | 8 | import rl "../../../raylib/bridge" 9 | 10 | Console_Entry :: struct { 11 | message: cstring, 12 | time: f64, 13 | } 14 | 15 | Debug_Console :: struct { 16 | entries: [dynamic]Console_Entry, 17 | } 18 | 19 | init :: proc(debug_console: ^Debug_Console) { 20 | } 21 | 22 | destroy :: proc(debug_console: ^Debug_Console) { 23 | } 24 | 25 | log :: proc(using debug_console: ^Debug_Console, format_args: ..any) { 26 | entry := Console_Entry {}; 27 | entry.time = rl.get_time(); 28 | entry.message = strings.clone_to_cstring(fmt.tprint(..format_args)); 29 | 30 | append(&entries, entry); 31 | } 32 | 33 | update_and_draw :: proc(using debug_console: ^Debug_Console) { 34 | if len(entries) == 0 do return; 35 | 36 | font_size:i32 = 20; 37 | 38 | new_entries: [dynamic]Console_Entry; 39 | 40 | now := rl.get_time(); 41 | 42 | y:i32 = 10; 43 | x:i32 = 10; 44 | 45 | color := rl.WHITE; 46 | shadow_color := rl.BLACK; 47 | 48 | time_visible :: 2.0; 49 | 50 | alpha_for_time :: inline proc(elapsed: f64) -> u8 { 51 | return u8(255.0 * (1.0 - game_math.saturate(game_math.unlerp(elapsed, time_visible - 0.5, time_visible)))); 52 | } 53 | 54 | for entry in entries { 55 | elapsed := now - entry.time; 56 | if elapsed >= time_visible { 57 | delete(entry.message); 58 | continue; 59 | } 60 | 61 | alpha := alpha_for_time(elapsed); 62 | shadow_color.a = alpha; 63 | color.a = alpha; 64 | rl.draw_text(entry.message, x+1, y+1, font_size, shadow_color); 65 | rl.draw_text(entry.message, x, y, font_size, color); 66 | y += i32(f32(font_size) * 1.2); 67 | 68 | append(&new_entries, entry); 69 | } 70 | 71 | entries = new_entries; 72 | } 73 | -------------------------------------------------------------------------------- /examples/shared/game_math/game_math.odin: -------------------------------------------------------------------------------- 1 | package game_math 2 | 3 | import "core:math" 4 | 5 | INFINITY :: math.F32_MAX + 1; 6 | 7 | saturate :: proc(f: f64) -> f64 { 8 | if f < 0 do return 0; 9 | if f > 1 do return 1; 10 | return f; 11 | } 12 | 13 | repeat_scalar :: proc(t, length: f32) -> f32 { 14 | return clamp(t - math.floor(t / length) * length, 0.0, length); 15 | } 16 | 17 | repeat_vec :: proc(t, length: $T/[$N]$E) -> T { 18 | res: T; 19 | for i in 0..N-1 do res[i] = clamp(t[i] - math.floor(t[i] / length[i]) * length[i], 0.0, length[i]); 20 | return res; 21 | } 22 | 23 | repeat :: proc { repeat_scalar, repeat_vec }; 24 | 25 | clamp :: proc(val, min_val, max_val: f32) -> f32 { 26 | return min(max_val, max(min_val, val)); 27 | } 28 | 29 | is_nonzero :: inline proc(v: $T/[$N]$E) -> bool { 30 | for i in 0..N-1 { 31 | if v[i] > 0 || v[i] < 0 { 32 | return true; 33 | } 34 | } 35 | 36 | return false; 37 | } 38 | 39 | add_to :: inline proc(a: ^$T/[$N]$E, b: T) { 40 | for i in 0..N-1 do a[i] = a[i] + b[i]; 41 | } 42 | 43 | 44 | scale_vec :: proc(a, b: $T/[$N]$E) -> T { 45 | res: T; 46 | for i in 0..N-1 do res[i] = a[i] * b[i]; 47 | return res; 48 | } 49 | 50 | scale_scalar :: proc(a: $T/[$N]$E, scalar: f32) -> T { 51 | res: T; 52 | for i in 0..N-1 do res[i] = a[i] * scalar; 53 | return res; 54 | } 55 | 56 | scale :: proc { scale_vec, scale_scalar }; 57 | 58 | max_vec :: proc(a, b: $T/[$N]$E) -> T { 59 | res: T; 60 | for i in 0..N-1 do res[i] = max(a[i], b[i]); 61 | return res; 62 | } 63 | 64 | min_vec :: proc(a, b: $T/[$N]$E) -> T { 65 | res: T; 66 | for i in 0..N-1 do res[i] = min(a[i], b[i]); 67 | return res; 68 | } 69 | 70 | clamp_vec :: proc(val, min_val, max_val: $R) -> R { 71 | return min_vec(max_val, max_vec(min_val, val)); 72 | } 73 | 74 | 75 | unlerp :: proc(ax, a1, a2: f64) -> f64 { 76 | return (ax - a1) / (a2 - a1); 77 | } 78 | 79 | // thanks Unity decompiled 80 | smooth_damp :: proc( 81 | current: f32, 82 | target_: f32, 83 | current_velocity: ^f32, 84 | smooth_time_: f32, 85 | delta_time: f32, 86 | max_speed: f32 = INFINITY, 87 | ) -> f32 { 88 | target := target_; 89 | smooth_time := max(0.0001, smooth_time_); 90 | num:f32 = 2.0 / smooth_time; 91 | num2:f32 = num * delta_time; 92 | num3:f32 = 1.0 / (1.0 + num2 + 0.48 * num2 * num2 + 0.235 * num2 * num2 * num2); 93 | num4:f32 = current - target; 94 | num5:f32 = target; 95 | num6:f32 = max_speed * smooth_time; 96 | num4 = clamp(num4, -num6, num6); 97 | target = current - num4; 98 | num7:f32 = (current_velocity^ + num * num4) * delta_time; 99 | current_velocity^ = (current_velocity^ - num * num7) * num3; 100 | num8:f32 = target + (num4 + num7) * num3; 101 | if (num5 - current > 0.0) == (num8 > num5) { 102 | num8 = num5; 103 | current_velocity^ = (num8 - num5) / delta_time; 104 | } 105 | return num8; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /examples/shared/json_ext/unmarshal.odin: -------------------------------------------------------------------------------- 1 | package json_ext 2 | 3 | import "core:encoding/json" 4 | import "core:fmt" 5 | import "core:mem" 6 | import "core:os" 7 | import "core:reflect" 8 | import "core:runtime" 9 | 10 | unmarshal :: proc{unmarshal_value_to_struct_ptr, unmarshal_value_to_any, unmarshal_value_to_type}; 11 | 12 | unmarshal_value_to_struct_ptr :: proc(p: ^$T, value: json.Value, spec := json.Specification.JSON) -> bool { 13 | a := any{rawptr(p), typeid_of(T)}; 14 | return unmarshal(a, value, spec); 15 | } 16 | 17 | unmarshal_value_to_any :: proc(data: any, value: json.Value, spec := json.Specification.JSON) -> bool { 18 | type_info := reflect.type_info_base(type_info_of(data.id)); 19 | type_info = reflect.type_info_base(type_info); // @todo: dirty fucking hack, won't hold up 20 | 21 | #partial switch v in value.value { 22 | case json.Object: 23 | #partial switch variant in type_info.variant { 24 | case runtime.Type_Info_Struct: 25 | for field, i in variant.names { 26 | // @todo: stricter type checking and by-order instead of by-name as an option 27 | a := any{rawptr(uintptr(data.data) + uintptr(variant.offsets[i])), variant.types[i].id}; 28 | if !unmarshal(a, v[field], spec) do return false; // @error 29 | } 30 | 31 | return true; 32 | 33 | case runtime.Type_Info_Map: 34 | // @todo: implement. ask bill about this, maps are a daunting prospect because they're fairly opaque 35 | fmt.eprintln("TODO: Type_Info_Map not implemented yet"); 36 | return false; 37 | 38 | case: 39 | fmt.eprintln("JSON error: unhandled type_info.variant", variant); 40 | return false; 41 | } 42 | 43 | case json.Array: 44 | #partial switch variant in type_info.variant { 45 | case runtime.Type_Info_Array: 46 | if len(v) > variant.count { 47 | fmt.eprintln("len of array wrong"); 48 | return false; // @error 49 | } 50 | 51 | for i in 0..variant.count-1 { 52 | a := any{rawptr(uintptr(data.data) + uintptr(variant.elem_size * i)), variant.elem.id}; 53 | if !unmarshal(a, v[i], spec) do return false; // @error 54 | } 55 | 56 | return true; 57 | 58 | case runtime.Type_Info_Slice: 59 | array := (^mem.Raw_Slice)(data.data); 60 | 61 | if len(v) > array.len { 62 | fmt.eprintln("slice length wrong"); 63 | return false; // @error 64 | } 65 | array.len = len(v); 66 | 67 | for i in 0..array.len { 68 | a := any{rawptr(uintptr(array.data) + uintptr(variant.elem_size * i)), variant.elem.id}; 69 | if !unmarshal(a, v[i], spec) do return false; // @error 70 | } 71 | 72 | return true; 73 | 74 | case runtime.Type_Info_Dynamic_Array: 75 | array := (^mem.Raw_Dynamic_Array)(data.data); 76 | 77 | if array.cap == 0 { 78 | array.data = mem.alloc(len(v)*variant.elem_size); 79 | array.cap = len(v); 80 | array.allocator = context.allocator; 81 | } 82 | 83 | if len(v) > array.cap { 84 | context = mem.context_from_allocator(array.allocator); 85 | mem.resize(array.data, array.cap, len(v)*variant.elem_size); 86 | } 87 | 88 | array.len = len(v); 89 | 90 | for i in 0..array.len-1 { 91 | a := any{rawptr(uintptr(array.data) + uintptr(variant.elem_size * i)), variant.elem.id}; 92 | if !unmarshal(a, v[i], spec) do return false; // @error 93 | } 94 | 95 | return true; 96 | 97 | case: 98 | fmt.eprintln("unhandled variant for Array:", variant); 99 | return false; // @error 100 | } 101 | 102 | case json.String: 103 | #partial switch variant in type_info.variant { 104 | case runtime.Type_Info_String: 105 | tmp := string(v); 106 | mem.copy(data.data, &tmp, size_of(string)); 107 | 108 | return true; 109 | 110 | case runtime.Type_Info_Enum: 111 | for name, i in variant.names { 112 | if name == string(v) { 113 | val := variant.values[i]; 114 | mem.copy(data.data, &val, size_of(val)); 115 | return true; 116 | } 117 | } 118 | case: 119 | fmt.eprintln("unhandled String variant", variant); 120 | return false; 121 | } 122 | 123 | case json.Integer: 124 | #partial switch variant in type_info.variant { 125 | case runtime.Type_Info_Integer: 126 | switch type_info.size { 127 | case 8: 128 | tmp := i64(v); 129 | mem.copy(data.data, &tmp, type_info.size); 130 | 131 | case 4: 132 | tmp := i32(v); 133 | mem.copy(data.data, &tmp, type_info.size); 134 | 135 | case 2: 136 | tmp := i16(v); 137 | mem.copy(data.data, &tmp, type_info.size); 138 | 139 | case 1: 140 | tmp := i8(v); 141 | mem.copy(data.data, &tmp, type_info.size); 142 | 143 | case: 144 | fmt.eprintln("unhandled integer size", type_info.size); 145 | return false; // @error 146 | } 147 | 148 | return true; 149 | 150 | case runtime.Type_Info_Enum: 151 | return unmarshal(any{data.data, variant.base.id}, value, spec); 152 | 153 | case: 154 | fmt.eprintln("Unhandled Integer variant", variant); 155 | return false; 156 | } 157 | 158 | case json.Float: 159 | if _, ok := type_info.variant.(runtime.Type_Info_Float); ok { 160 | switch type_info.size { 161 | case 8: 162 | tmp := f64(v); 163 | mem.copy(data.data, &tmp, type_info.size); 164 | 165 | case 4: 166 | tmp := f32(v); 167 | mem.copy(data.data, &tmp, type_info.size); 168 | 169 | case: 170 | fmt.eprintln("Unhandled Float type_info.size", type_info.size); 171 | return false; // @error 172 | } 173 | 174 | return true; 175 | } 176 | 177 | fmt.eprintln("expected a float in the struct"); 178 | return false; // @error 179 | 180 | case json.Boolean: 181 | if _, ok := type_info.variant.(runtime.Type_Info_Boolean); ok { 182 | tmp := bool(v); 183 | mem.copy(data.data, &tmp, type_info.size); 184 | 185 | return true; 186 | } 187 | 188 | fmt.eprintln("expected a boolean in the struct"); 189 | return false; // @error 190 | 191 | case json.Null: 192 | mem.set(data.data, 0, type_info.size); 193 | return true; 194 | 195 | case: 196 | fmt.eprintln("unhandled value type", v); 197 | return false; // @error 198 | } 199 | 200 | panic("Unreachable code."); 201 | } 202 | 203 | unmarshal_value_to_type :: inline proc($T: typeid, value: json.Value, spec := json.Specification.JSON) -> (T, bool) { 204 | tmp: T; 205 | ok := unmarshal(tmp, value, spec); 206 | return tmp, ok; 207 | } 208 | 209 | parse_file :: inline proc(path: string, spec := json.Specification.JSON) -> (value: json.Value, ok: bool) { 210 | if bytes, ok2 := os.read_entire_file(path); ok2 { 211 | val, err := json.parse(bytes, spec); 212 | value = val; 213 | ok = err == .None; 214 | return; 215 | } 216 | 217 | fmt.eprintln("could not read_entire_file", path); 218 | value = json.Value {}; 219 | ok = false; 220 | return; 221 | } 222 | 223 | unmarshal_file :: proc{unmarshal_file_to_struct_ptr, unmarshal_file_to_any, unmarshal_file_to_type}; 224 | 225 | unmarshal_file_to_struct_ptr :: inline proc(p: ^$T, path: string, spec := json.Specification.JSON) -> bool { 226 | if value, ok := parse_file(path, spec); ok { 227 | return unmarshal(p, value, spec); 228 | } 229 | return false; 230 | } 231 | 232 | unmarshal_file_to_any :: inline proc(data: any, path: string, spec := json.Specification.JSON) -> bool { 233 | if value, ok := parse_file(path, spec); ok { 234 | return unmarshal(data, value, spec); 235 | } 236 | return false; 237 | } 238 | 239 | unmarshal_file_to_type :: inline proc($T: typeid, path: string, spec := json.Specification.JSON) -> (T, bool) { 240 | if value, ok := parse_file(path, spec); ok { 241 | return unmarshal(T, value, spec); 242 | } 243 | 244 | return T{}, false; 245 | } 246 | 247 | -------------------------------------------------------------------------------- /examples/shared/plugin/plugin.odin: -------------------------------------------------------------------------------- 1 | package plugin 2 | 3 | import "core:fmt" 4 | import "core:os" 5 | import "core:strings" 6 | 7 | Request :: enum { 8 | None, 9 | Reload, 10 | Quit, 11 | } 12 | 13 | when os.OS == "windows" { 14 | import "core:sys/win32" 15 | 16 | _odin_to_wchar_string :: proc(str : string) -> win32.Wstring { 17 | olen := i32(len(str) * size_of(byte)); 18 | cstr := strings.clone_to_cstring(str); 19 | wlen := win32.multi_byte_to_wide_char(win32.CP_UTF8, 0, cstr, olen, nil, 0); 20 | buf := make([]u16, int(wlen * size_of(u16) + 1)); 21 | ptr := win32.Wstring(&buf[0]); 22 | win32.multi_byte_to_wide_char(win32.CP_UTF8, 0, cstr, olen, ptr, wlen); 23 | return ptr; 24 | } 25 | 26 | _copy_file :: proc(src, dest : string, force := false) -> bool { 27 | src_wc := _odin_to_wchar_string(src); defer free(src_wc); 28 | dst_wc := _odin_to_wchar_string(dest); defer free(dst_wc); 29 | res := win32.copy_file_w(src_wc, dst_wc, win32.Bool(!force)); 30 | return bool(res); 31 | } 32 | 33 | _file_exists :: proc(filename: string) -> bool { 34 | wide_path := win32.utf8_to_wstring(filename); 35 | data: win32.File_Attribute_Data; 36 | 37 | if !win32.get_file_attributes_ex_w(wide_path, win32.GetFileExInfoStandard, &data) { 38 | return false; 39 | } 40 | return true; 41 | } 42 | } 43 | 44 | get_file_time :: proc(filename: string) -> (bool, os.File_Time) { 45 | fd, err := os.open(filename); 46 | if err != os.ERROR_NONE do return false, 0; 47 | defer os.close(fd); 48 | 49 | file_time, err2 := os.last_write_time(fd); 50 | if err2 != os.ERROR_NONE do return false, 0; 51 | 52 | return true, file_time; 53 | } 54 | 55 | On_Load_Proc :: #type proc(userdata: rawptr); 56 | On_Unload_Proc :: #type proc(); 57 | Update_And_Draw_Proc :: #type proc() -> Request; 58 | 59 | Plugin :: struct { 60 | name: string, 61 | path_on_disk: string, 62 | module: win32.Hmodule, 63 | last_write_time: os.File_Time, 64 | 65 | on_load_proc: On_Load_Proc, 66 | on_unload_proc: On_Unload_Proc, 67 | update_and_draw_proc: Update_And_Draw_Proc, 68 | } 69 | 70 | plugin_load :: proc(plugin: ^Plugin, name: string, userdata: rawptr) -> bool { 71 | temp_path :: "bin/temp/temp.dll"; 72 | temp_pdb_path :: "bin/temp/temp.pdb"; 73 | 74 | // copy dll to temp location 75 | if !_copy_file(name, temp_path, true) { 76 | fmt.println("ERR IN COPY FILE"); 77 | fmt.eprintln("could not copy", name, "to", temp_path); 78 | return false; 79 | } 80 | 81 | if !_copy_file("bin/game.pdb", temp_pdb_path, true) { 82 | fmt.println("ERROR: cannot copy pdb from bin/game.pdb to bin/temp/temp.pdb"); 83 | } 84 | 85 | // load dll 86 | new_dll := win32.load_library_a(temp_path); 87 | if new_dll == nil { 88 | fmt.println("ERR IN LOAD_LIBRARY"); 89 | fmt.eprintln("could not load library", name); 90 | return false; 91 | } 92 | 93 | // load functions 94 | on_load_proc : On_Load_Proc = cast(On_Load_Proc)win32.get_proc_address(new_dll, "on_load"); 95 | if on_load_proc == nil { 96 | fmt.eprintln("error: could not load on_load proc"); 97 | return false; 98 | } 99 | 100 | on_unload_proc : On_Unload_Proc = cast(On_Unload_Proc)win32.get_proc_address(new_dll, "on_unload"); 101 | if on_unload_proc == nil { 102 | fmt.eprintln("error: could not load on_unload proc"); 103 | return false; 104 | } 105 | 106 | update_and_draw_proc : Update_And_Draw_Proc = cast(Update_And_Draw_Proc)win32.get_proc_address(new_dll, "update_and_draw"); 107 | if update_and_draw_proc == nil { 108 | fmt.eprintln("error: could not load update_and_draw proc"); 109 | return false; 110 | } 111 | 112 | { 113 | ok, file_time := get_file_time(name); 114 | if !ok { 115 | fmt.println("error getting write time"); 116 | fmt.eprintln("could not read DLL write time:", temp_path); 117 | return false; 118 | } 119 | plugin.last_write_time = file_time; 120 | } 121 | 122 | plugin.name = name; 123 | plugin.module = new_dll; 124 | plugin.path_on_disk = name; 125 | plugin.on_load_proc = on_load_proc; 126 | plugin.on_unload_proc = on_unload_proc; 127 | plugin.update_and_draw_proc = update_and_draw_proc; 128 | 129 | plugin.on_load_proc(userdata); 130 | 131 | return true; 132 | } 133 | 134 | plugin_unload :: proc(plugin: ^Plugin) { 135 | if plugin.module == nil do return; 136 | 137 | plugin.on_unload_proc(); 138 | win32.free_library(plugin.module); 139 | plugin.module = nil; 140 | } 141 | 142 | plugin_maybe_reload :: proc(plugin: ^Plugin, userdata: rawptr, force_reload: bool = false) { 143 | ok, file_time := get_file_time(plugin.path_on_disk); 144 | if !ok { 145 | //fmt.eprintln("could not get file time of plugin:", plugin.path_on_disk); 146 | return; 147 | } 148 | 149 | if !force_reload && file_time == plugin.last_write_time do return; 150 | 151 | plugin.last_write_time = file_time; 152 | 153 | plugin_unload(plugin); 154 | plugin_load(plugin, plugin.name, userdata); 155 | } 156 | 157 | plugin_force_reload :: proc(plugin: ^Plugin, userdata: rawptr) { 158 | plugin_maybe_reload(plugin, userdata, true); 159 | } 160 | -------------------------------------------------------------------------------- /examples/shared/reloader_thread/reloader_thread.odin: -------------------------------------------------------------------------------- 1 | package reloader_thread 2 | 3 | import "core:thread" 4 | import "core:fmt" 5 | import "core:strings" 6 | 7 | import "core:sys/win32" 8 | 9 | compile_game_dll :: proc() -> bool { 10 | startup_info : win32.Startup_Info; 11 | startup_info.cb = size_of(win32.Startup_Info); 12 | 13 | process_information: win32.Process_Information; 14 | 15 | if ok := win32.create_process_a(nil, _recompile_script, nil, nil, false, 0, nil, nil, &startup_info, &process_information); !ok { 16 | fmt.eprintln("could not invoke build script"); 17 | return false; 18 | } 19 | 20 | if win32.WAIT_OBJECT_0 != win32.wait_for_single_object(process_information.process, win32.INFINITE) { 21 | fmt.eprintln("ERROR invoking build batch file"); 22 | return false; 23 | } 24 | 25 | // TODO: something like win32.destroy_handle(process_information.process); 26 | 27 | return true; 28 | } 29 | 30 | watcher_thread_proc :: proc(^thread.Thread) { 31 | fmt.println("watching for changes in", _directory_to_watch); 32 | 33 | watch_subtree:win32.Bool : true; 34 | filter:u32 = win32.FILE_NOTIFY_CHANGE_LAST_WRITE; 35 | FALSE:win32.Bool : false; 36 | 37 | handle := win32.find_first_change_notification_a(_directory_to_watch, watch_subtree, filter); 38 | if handle == win32.INVALID_HANDLE { 39 | fmt.eprintln("FindFirstChangeNotification failed"); 40 | return; 41 | } 42 | 43 | next_timeout_ms:u32 = win32.INFINITE; 44 | did_get_change := false; 45 | 46 | for { 47 | wait_status := win32.wait_for_single_object(handle, next_timeout_ms); 48 | 49 | switch wait_status { 50 | case win32.WAIT_OBJECT_0: 51 | // when we get a file change notification, it's often immediately followed by another one. 52 | // so we'll lower our timeout and use that as a signal to actually recompile, to coalesce 53 | // multiple updates into one. 54 | next_timeout_ms = 150; 55 | did_get_change = true; 56 | case win32.WAIT_TIMEOUT: 57 | if !did_get_change do 58 | panic("error: infinite timeout triggered"); 59 | 60 | // actually recompile the game.dll 61 | did_get_change = false; 62 | next_timeout_ms = win32.INFINITE; 63 | if ok := compile_game_dll(); !ok { 64 | fmt.eprintln("result:", ok); 65 | } 66 | case: 67 | fmt.eprintln("unhandled wait_status", wait_status); 68 | return; 69 | } 70 | 71 | if win32.find_next_change_notification(handle) == FALSE { 72 | fmt.eprintln("error in find_next_change_notification"); 73 | return; 74 | } 75 | } 76 | 77 | return; 78 | } 79 | 80 | _recompile_script: cstring; 81 | _directory_to_watch: cstring; 82 | 83 | start :: proc(recompile_script: string, directory_to_watch: string) -> ^thread.Thread { 84 | assert(_recompile_script == nil, "only one reloader thread can exist at once"); 85 | 86 | _recompile_script = strings.clone_to_cstring(recompile_script); 87 | _directory_to_watch = strings.clone_to_cstring(directory_to_watch); 88 | 89 | watcher_thread := thread.create(watcher_thread_proc); 90 | thread.start(watcher_thread); 91 | return watcher_thread; 92 | } 93 | 94 | finish :: proc(watcher_thread: ^thread.Thread) { 95 | // TODO: signal to thread it should exit gracefully with CreateEvent like https://docs.microsoft.com/en-us/windows/desktop/sync/using-event-objects 96 | } 97 | 98 | -------------------------------------------------------------------------------- /examples/shared/sprite/sprite.odin: -------------------------------------------------------------------------------- 1 | package sprite 2 | 3 | import "core:fmt" 4 | 5 | import rl "../../../raylib/bridge" 6 | 7 | import "../../shared/game_math" 8 | 9 | import "core:math/linalg" 10 | 11 | Sprite :: struct { 12 | pos: linalg.Vector2, 13 | rotation: f32, 14 | scale: f32, 15 | origin: linalg.Vector2, 16 | tint: rl.Color, 17 | flip_x: bool, 18 | flip_y: bool, 19 | animations: []Anim, 20 | current_anim: ^Anim, 21 | } 22 | 23 | Anim :: struct { 24 | name: string, 25 | texture: rl.Texture, 26 | rects: []rl.Rectangle, 27 | fps: f32, 28 | current_time: f32, 29 | } 30 | 31 | Anim_destroy :: proc(using anim: ^Anim) { 32 | delete(rects); 33 | } 34 | 35 | Sprite_destroy :: proc(using spr: ^Sprite) { 36 | for _, i in animations do Anim_destroy(&animations[i]); 37 | delete(animations); 38 | } 39 | 40 | Rectangle_scale :: proc(rec: rl.Rectangle, xscale, yscale: f32) -> rl.Rectangle { 41 | return rl.Rectangle {rec.x, rec.y, rec.width*xscale, rec.height*yscale}; 42 | } 43 | 44 | destroy :: proc(using spr: ^Sprite) { 45 | Sprite_destroy(spr); 46 | } 47 | 48 | destroy_many :: proc(sprites: []Sprite) { 49 | for _, i in sprites { 50 | destroy(&sprites[i]); 51 | } 52 | 53 | delete(sprites); 54 | } 55 | 56 | update_many :: proc(sprites: []Sprite, delta_time: f32) { 57 | for _, i in sprites { 58 | if sprites[i].current_anim != nil { 59 | sprites[i].current_anim.current_time += delta_time; 60 | } 61 | } 62 | } 63 | 64 | rects_from_horizontal_texture :: proc(width: f32, height: f32, num_frames: uint) -> []rl.Rectangle { 65 | 66 | rects := make([]rl.Rectangle, num_frames); 67 | 68 | x :f32 = 0.0; 69 | w := width / cast(f32)num_frames; 70 | 71 | for i in 0..num_frames-1 { 72 | rects[i] = rl.Rectangle {x, 0, w, height}; 73 | x += w; 74 | } 75 | 76 | return rects; 77 | } 78 | 79 | current_rect_index :: inline proc(using sprite: ^Sprite) -> int { 80 | return cast(int)game_math.repeat( 81 | current_anim.current_time * current_anim.fps, cast(f32)len(current_anim.rects)); 82 | } 83 | 84 | current_rect :: proc(using sprite: ^Sprite) -> rl.Rectangle { 85 | rect_index := current_rect_index(sprite); 86 | return current_anim.rects[rect_index]; 87 | } 88 | 89 | draw_many :: proc(sprites: []Sprite) { 90 | for _, i in sprites { 91 | sprite := &sprites[i]; 92 | using sprite; 93 | 94 | source_rect := current_anim.rects[current_rect_index(sprite)]; 95 | dest_rect := rl.Rectangle { pos.x, pos.y, source_rect.width * scale, source_rect.height * scale }; 96 | 97 | scale_x := f32(flip_x ? -1 : 1); 98 | scale_y := f32(flip_y ? -1 : 1); 99 | 100 | source_rect = Rectangle_scale(source_rect, scale_x, scale_y); 101 | 102 | rl.draw_texture_pro( 103 | current_anim.texture, 104 | source_rect, 105 | dest_rect, 106 | origin, 107 | rotation, 108 | tint); 109 | 110 | } 111 | } 112 | 113 | play_anim :: proc(using sprite: ^Sprite, anim_name: string) { 114 | anim: ^Anim; 115 | found := false; 116 | 117 | for _, i in animations { 118 | if animations[i].name == anim_name { 119 | anim = &animations[i]; 120 | found = true; 121 | break; 122 | } 123 | } 124 | 125 | if !found { 126 | fmt.eprintln("WARNING: animation", anim_name, "doesn't exist"); 127 | return; 128 | } 129 | 130 | current_anim = anim; 131 | } 132 | -------------------------------------------------------------------------------- /examples/simple_demo/simple_demo.odin: -------------------------------------------------------------------------------- 1 | package demo 2 | 3 | import "../../raylib" 4 | 5 | main :: proc() 6 | { 7 | using raylib; 8 | 9 | // Initialization 10 | //-------------------------------------------------------------------------------------- 11 | screenWidth :i32 = 800; 12 | screenHeight :i32 = 450; 13 | 14 | init_window(screenWidth, screenHeight, "game"); 15 | 16 | set_target_fps(60); 17 | //-------------------------------------------------------------------------------------- 18 | 19 | ball_color := DARKBLUE; 20 | 21 | scarfy := load_texture("resources/scarfy.png"); 22 | 23 | num_frames := 6; 24 | 25 | frameRec : Rectangle; 26 | frameRec.x = 0; 27 | frameRec.y = 0; 28 | frameRec.width = cast(f32)scarfy.width / cast(f32)num_frames; 29 | frameRec.height = cast(f32)scarfy.height; 30 | 31 | position := Vector2 { 350.0, 280.0 }; 32 | 33 | currentFrame := 0; 34 | framesCounter := 0; 35 | framesSpeed := 8; // Number of spritesheet frames shown by second 36 | 37 | // Main game loop 38 | for !window_should_close() // Detect window close button or ESC key 39 | { 40 | framesCounter += 1; 41 | 42 | if framesCounter >= (60.0 / framesSpeed) { 43 | framesCounter = 0; 44 | currentFrame += 1; 45 | 46 | if currentFrame >= num_frames do currentFrame = 0.0; 47 | 48 | frameRec.x = cast(f32)currentFrame * cast(f32)scarfy.width / cast(f32)num_frames; 49 | } 50 | 51 | ball_position := get_mouse_position(); 52 | 53 | if is_mouse_button_pressed(.LEFT_BUTTON) do ball_color = MAROON; 54 | else if is_mouse_button_pressed(.MIDDLE_BUTTON) do ball_color = LIME; 55 | else if is_mouse_button_pressed(.RIGHT_BUTTON) do ball_color = DARKBLUE; 56 | 57 | { 58 | begin_drawing(); 59 | defer end_drawing(); 60 | 61 | draw_texture(scarfy, 15, 40, WHITE); 62 | draw_circle_v(ball_position, 40, ball_color); 63 | 64 | clear_background(RAYWHITE); 65 | 66 | draw_texture_rec(scarfy, frameRec, position, WHITE); // Draw part of the texture 67 | 68 | draw_text("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY); 69 | } 70 | //---------------------------------------------------------------------------------- 71 | } 72 | 73 | // De-Initialization 74 | //-------------------------------------------------------------------------------------- 75 | close_window(); // Close window and OpenGL context 76 | //-------------------------------------------------------------------------------------- 77 | } 78 | -------------------------------------------------------------------------------- /examples/sprites/README.md: -------------------------------------------------------------------------------- 1 | # Live Reload Example 2 | 3 | ![a screenshot of the live reload demo](../../resources/screenshots/example_live_reload.png) 4 | 5 | ## `host.odin` 6 | 7 | This is the "harness" that contains the `main` proc that stays running while you iterate on the game. 8 | 9 | ## `game.odin` 10 | 11 | This is the "plugin" code that gets recompiled and reloaded by the host harness. 12 | 13 | ## `plugin` 14 | 15 | A small library providing the live-reload/plugin functionality. 16 | -------------------------------------------------------------------------------- /examples/sprites/game.odin: -------------------------------------------------------------------------------- 1 | package live_reload_demo 2 | 3 | import "core:os" 4 | import "core:fmt" 5 | import "core:math/linalg" 6 | import serializer "core:encoding/json" 7 | 8 | import rl "../../raylib/bridge" 9 | 10 | import "../shared/json_ext" 11 | 12 | import "../shared/sprite" 13 | 14 | // RAYLIB_EXTRA 15 | unload :: proc { 16 | rl.unload_texture, 17 | rl.unload_sound 18 | }; 19 | 20 | //import "../shared/game_math" 21 | import "../shared/plugin" 22 | import "../shared/debug_console" 23 | 24 | state_json_dir :: "temp"; 25 | state_json_path :: "temp/state.json"; 26 | 27 | when os.OS == "windows" { 28 | import "core:sys/win32" 29 | 30 | mkdir_if_not_exist :: proc(dir: string) -> os.Errno { 31 | dir_wstr := win32.utf8_to_wstring(dir, context.temp_allocator); 32 | if win32.Bool(false) == win32.create_directory_w(dir_wstr, nil) do return os.Errno(win32.get_last_error()); 33 | return os.ERROR_NONE; 34 | } 35 | } 36 | 37 | // This state persists during a plugin reload. 38 | State :: struct { 39 | currentFrame : int, 40 | framesCounter : int, 41 | position : linalg.Vector2, 42 | } 43 | 44 | 45 | Transient_State :: struct { 46 | bg : rl.Texture, 47 | num_frames : int, 48 | bg2 : rl.Texture, 49 | scarfy : rl.Texture, 50 | console: debug_console.Debug_Console, 51 | 52 | sprs : []sprite.Sprite, 53 | } 54 | 55 | state : State; 56 | transient_state : Transient_State; 57 | 58 | @(export) 59 | on_load :: proc(funcs: ^rl.raylib_Funcs) { 60 | using rl; 61 | 62 | bridge_init(funcs); 63 | 64 | using transient_state; 65 | 66 | scarfy = load_texture("resources/scarfy.png"); 67 | 68 | sprs = make([]sprite.Sprite, 51); 69 | 70 | for i in 0..49 { 71 | bg_spr := &sprs[i]; 72 | using bg_spr; 73 | 74 | pos = Vector2 { 75 | cast(f32)get_random_value(0, get_screen_width()), 76 | cast(f32)get_random_value(0, get_screen_height()), 77 | }; 78 | rotation = cast(f32)get_random_value(0, 360); 79 | tint = Color { 255, 255, 255, cast(u8)get_random_value(30, 100) }; 80 | animations = make([]sprite.Anim, 1); 81 | current_anim = &animations[0]; 82 | current_anim.name = "walk_right"; 83 | current_anim.fps = cast(f32)get_random_value(1, 15); 84 | current_anim.texture = scarfy; 85 | current_anim.rects = sprite.rects_from_horizontal_texture(cast(f32)scarfy.width, cast(f32)scarfy.height, 6); 86 | } 87 | 88 | spr := &sprs[50]; 89 | { 90 | using spr; 91 | pos = Vector2 { 50.0, 50.0 }; 92 | rotation = 0.0; 93 | tint = WHITE; 94 | animations = make([]sprite.Anim, 1); 95 | current_anim = &animations[0]; 96 | current_anim.name = "walk_right"; 97 | current_anim.fps = 10.0; 98 | current_anim.texture = scarfy; 99 | current_anim.rects = sprite.rects_from_horizontal_texture(cast(f32)scarfy.width, cast(f32)scarfy.height, 6); 100 | } 101 | 102 | num_frames = 6; 103 | 104 | { 105 | bg_img := gen_image_gradient_v(850, 450, PURPLE, RAYWHITE); 106 | defer unload_image(bg_img); 107 | bg = load_texture_from_image(bg_img); 108 | 109 | bg2_img := gen_image_gradient_h(850, 450, Color { 0, 0, 0, 0, }, Color { 255, 190, 200, 60 }); 110 | defer unload_image(bg2_img); 111 | 112 | bg2 = load_texture_from_image(bg2_img); 113 | } 114 | 115 | if !json_ext.unmarshal_file(&state, state_json_path) { 116 | fmt.println("error unmarshalling json"); 117 | } 118 | 119 | debug_console.init(&console); 120 | debug_console.log(&console, "game.dll loaded at time=", get_time(), " seconds"); 121 | } 122 | 123 | @(export) 124 | on_unload :: proc() { 125 | using rl; 126 | 127 | bridge_deinit(); 128 | 129 | using state; 130 | using transient_state; 131 | 132 | if state_bytes, err := serializer.marshal(state); err == .None { 133 | mkdir_if_not_exist(state_json_dir); 134 | os.write_entire_file(state_json_path, state_bytes); 135 | } else { 136 | s := ""; 137 | #partial switch err { 138 | case .Unsupported_Type: s = "Unsupported_Type"; 139 | case: s = "TODO"; 140 | } 141 | fmt.eprintln("error serializing state to bytes: ", s); 142 | } 143 | 144 | debug_console.destroy(&console); 145 | unload(scarfy); 146 | unload(bg); 147 | unload(bg2); 148 | } 149 | 150 | @(export) 151 | update_and_draw :: proc() -> plugin.Request { 152 | using state; 153 | using transient_state; 154 | using rl; 155 | 156 | request := plugin.Request.None; 157 | 158 | spr := &sprs[50]; 159 | 160 | // UPDATE 161 | spr_rect := sprite.current_rect(spr); 162 | width_of_one_frame := spr_rect.width; 163 | { 164 | framesCounter += 1; 165 | delta_time := get_frame_time(); 166 | player_move_pixels_per_second:f32 = 400.0; 167 | speed := delta_time * player_move_pixels_per_second; 168 | 169 | prev_position := position; 170 | 171 | // debug key L - log a message 172 | if is_key_pressed(.L) do debug_console.log(&console, "this is a test"); 173 | 174 | // debug key R - reload the plugin 175 | if is_key_pressed(.R) do request = .Reload; 176 | 177 | // debug key Q - quit 178 | if is_key_pressed(.Q) do request = .Quit; 179 | 180 | // move the player with the arrow or WASD keys 181 | if is_key_down(.RIGHT) || is_key_down(.D) do position.x += speed; 182 | if is_key_down(.LEFT) || is_key_down(.A) do position.x -= speed; 183 | if is_key_down(.UP) || is_key_down(.W) do position.y -= speed; 184 | if is_key_down(.DOWN) || is_key_down(.S) do position.y += speed; 185 | 186 | // click to move the player as well 187 | if is_mouse_button_down(.LEFT_BUTTON) { 188 | mouse_pos := get_mouse_position(); 189 | to_pos := linalg.length(linalg.Vector2 { mouse_pos.x - position.x, mouse_pos.y - position.y }); 190 | if to_pos > 20 { 191 | mouse_pos.x -= f32(width_of_one_frame) * .5; 192 | mouse_pos.y -= f32(spr_rect.height) * .5; 193 | if mouse_pos.x > position.x do position.x = min(mouse_pos.x, position.x + speed); 194 | else if mouse_pos.x < position.x do position.x = max(mouse_pos.x, position.x - speed); 195 | if mouse_pos.y > position.y do position.y = min(mouse_pos.y, position.y + speed); 196 | else if mouse_pos.y < position.y do position.y = max(mouse_pos.y, position.y - speed); 197 | } 198 | } 199 | 200 | // TODO: these flip values get lost on reload 201 | if prev_position.x < position.x do spr.flip_x = false; 202 | else if prev_position.x > position.x do spr.flip_x = true; 203 | 204 | spr.pos = position; 205 | sprite.update_many(sprs, delta_time); 206 | } 207 | 208 | // DRAW 209 | { 210 | begin_drawing(); 211 | defer end_drawing(); 212 | 213 | clear_background(RAYWHITE); 214 | draw_texture(bg, 0, 0, WHITE); 215 | { 216 | begin_blend_mode(BlendMode.ADDITIVE); 217 | defer end_blend_mode(); 218 | draw_texture(bg2, 0, 0, WHITE); 219 | } 220 | 221 | sprite.draw_many(sprs); 222 | 223 | { 224 | // mouse cursor 225 | begin_blend_mode(BlendMode.MULTIPLIED); 226 | defer end_blend_mode(); 227 | draw_circle_v(get_mouse_position(), 15, RED); 228 | } 229 | 230 | debug_console.update_and_draw(&console); 231 | } 232 | 233 | return request; 234 | } 235 | -------------------------------------------------------------------------------- /examples/sprites/host.odin: -------------------------------------------------------------------------------- 1 | package live_reload_demo 2 | 3 | import rl "../../raylib" 4 | 5 | import "core:fmt" 6 | import "core:os" 7 | 8 | import "../shared/plugin" 9 | 10 | when os.OS == "windows" do import "../shared/reloader_thread" 11 | 12 | screenWidth :i32 = 800; 13 | screenHeight :i32 = 450; 14 | 15 | main :: proc() { 16 | using rl; 17 | 18 | // Create the window 19 | set_config_flags( 20 | // TODO: bindings could remove FLAG_ and the like from enums 21 | ConfigFlag.MSAA_4X_HINT 22 | | ConfigFlag.VSYNC_HINT 23 | //| ConfigFlag.FULLSCREEN_MODE 24 | ); 25 | 26 | init_window(screenWidth, screenHeight, "raylib-odin :: sprite example"); 27 | defer close_window(); 28 | 29 | set_window_position(40, 40); 30 | set_target_fps(60); 31 | init_audio_device(); 32 | defer close_audio_device(); 33 | 34 | // Load the plugin 35 | plugin_funcs : raylib_Funcs; 36 | rl.get_function_pointers(&plugin_funcs); 37 | 38 | game_dll: plugin.Plugin; 39 | if !plugin.plugin_load(&game_dll, "bin/sprites_game.dll", &plugin_funcs) { 40 | fmt.println("error loading bin/sprites_game.dll"); 41 | return; 42 | } 43 | defer plugin.plugin_unload(&game_dll); 44 | 45 | // kick off live reload watcher thread 46 | when os.OS == "windows" { 47 | reloader := reloader_thread.start("cmd.exe /c scripts\\build_sprites_plugin.bat", "examples"); 48 | defer reloader_thread.finish(reloader); 49 | } 50 | 51 | // Game loop 52 | RELOAD_INTERVAL_MS:f32 : 0.25; 53 | reload_timer := RELOAD_INTERVAL_MS; 54 | for !window_should_close() { 55 | force_reload := false; 56 | #partial switch game_dll.update_and_draw_proc() { 57 | case .Reload: force_reload = true; 58 | case .Quit: return; 59 | } 60 | 61 | needs_reload_check := false; 62 | if !force_reload { 63 | reload_timer -= get_frame_time(); 64 | for reload_timer < 0 { 65 | reload_timer += RELOAD_INTERVAL_MS; 66 | needs_reload_check = true; 67 | } 68 | } 69 | 70 | if needs_reload_check || force_reload { 71 | plugin.plugin_maybe_reload(&game_dll, &plugin_funcs, force_reload); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /ext/README.md: -------------------------------------------------------------------------------- 1 | ## raylib 2 | 3 | The raylib here is version 3.0.0 from: 4 | 5 | https://github.com/raysan5/raylib/releases/download/3.0.0/raylib-3.0.0-Win64-msvc15.zip -------------------------------------------------------------------------------- /ext/lib/chipmunk.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/ext/lib/chipmunk.lib -------------------------------------------------------------------------------- /ext/lib/physac.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/ext/lib/physac.lib -------------------------------------------------------------------------------- /ext/lib/raygui.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/ext/lib/raygui.lib -------------------------------------------------------------------------------- /ext/lib/raymath.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/ext/lib/raymath.lib -------------------------------------------------------------------------------- /ext/physac/README.md: -------------------------------------------------------------------------------- 1 | ## Prerequisites 2 | 3 | ``` 4 | pip install pcpp 5 | ``` 6 | 7 | -------------------------------------------------------------------------------- /ext/physac/bridge/physac_bridge.odin: -------------------------------------------------------------------------------- 1 | 2 | package physac 3 | 4 | import raylib_types "../types" 5 | 6 | physac_Funcs :: raylib_types.physac_Funcs; 7 | 8 | // re-export everything from ./types for convienience 9 | PHYSAC_H :: raylib_types.PHYSAC_H; 10 | PHYSAC_MAX_BODIES :: raylib_types.PHYSAC_MAX_BODIES; 11 | PHYSAC_MAX_MANIFOLDS :: raylib_types.PHYSAC_MAX_MANIFOLDS; 12 | PHYSAC_MAX_VERTICES :: raylib_types.PHYSAC_MAX_VERTICES; 13 | PHYSAC_CIRCLE_VERTICES :: raylib_types.PHYSAC_CIRCLE_VERTICES; 14 | PHYSAC_DESIRED_DELTATIME :: raylib_types.PHYSAC_DESIRED_DELTATIME; 15 | PHYSAC_MAX_TIMESTEP :: raylib_types.PHYSAC_MAX_TIMESTEP; 16 | PHYSAC_COLLISION_ITERATIONS :: raylib_types.PHYSAC_COLLISION_ITERATIONS; 17 | PHYSAC_PENETRATION_ALLOWANCE :: raylib_types.PHYSAC_PENETRATION_ALLOWANCE; 18 | PHYSAC_PENETRATION_CORRECTION :: raylib_types.PHYSAC_PENETRATION_CORRECTION; 19 | PHYSAC_PI :: raylib_types.PHYSAC_PI; 20 | PHYSAC_DEG2RAD :: raylib_types.PHYSAC_DEG2RAD; 21 | 22 | PhysicsBody :: raylib_types.PhysicsBody; 23 | 24 | PhysicsBodyData :: raylib_types.PhysicsBodyData; 25 | Mat2 :: raylib_types.Mat2; 26 | PolygonData :: raylib_types.PolygonData; 27 | PhysicsShape :: raylib_types.PhysicsShape; 28 | PhysicsManifoldData :: raylib_types.PhysicsManifoldData; 29 | PhysicsShapeType :: raylib_types.PhysicsShapeType; 30 | 31 | 32 | import _c "core:c" 33 | 34 | bridge_init :: proc(funcs: ^physac_Funcs) { 35 | assert(funcs != nil); 36 | assert(funcs.init_physics != nil); 37 | 38 | init_physics = funcs.init_physics; 39 | is_physics_enabled = funcs.is_physics_enabled; 40 | set_physics_gravity = funcs.set_physics_gravity; 41 | create_physics_body_circle = funcs.create_physics_body_circle; 42 | create_physics_body_rectangle = funcs.create_physics_body_rectangle; 43 | create_physics_body_polygon = funcs.create_physics_body_polygon; 44 | physics_add_force = funcs.physics_add_force; 45 | physics_add_torque = funcs.physics_add_torque; 46 | physics_shatter = funcs.physics_shatter; 47 | get_physics_bodies_count = funcs.get_physics_bodies_count; 48 | get_physics_body = funcs.get_physics_body; 49 | get_physics_shape_type = funcs.get_physics_shape_type; 50 | get_physics_shape_vertices_count = funcs.get_physics_shape_vertices_count; 51 | get_physics_shape_vertex = funcs.get_physics_shape_vertex; 52 | set_physics_body_rotation = funcs.set_physics_body_rotation; 53 | destroy_physics_body = funcs.destroy_physics_body; 54 | reset_physics = funcs.reset_physics; 55 | close_physics = funcs.close_physics; 56 | physics_loop = funcs.physics_loop; 57 | } 58 | 59 | bridge_deinit :: proc() { 60 | } 61 | 62 | init_physics : proc "c" (); 63 | is_physics_enabled : proc "c" () -> bool; 64 | set_physics_gravity : proc "c" ( 65 | x : _c.float, 66 | y : _c.float 67 | ); 68 | create_physics_body_circle : proc "c" ( 69 | pos : Vector2, 70 | radius : _c.float, 71 | density : _c.float 72 | ) -> PhysicsBody; 73 | create_physics_body_rectangle : proc "c" ( 74 | pos : Vector2, 75 | width : _c.float, 76 | height : _c.float, 77 | density : _c.float 78 | ) -> PhysicsBody; 79 | create_physics_body_polygon : proc "c" ( 80 | pos : Vector2, 81 | radius : _c.float, 82 | sides : _c.int, 83 | density : _c.float 84 | ) -> PhysicsBody; 85 | physics_add_force : proc "c" ( 86 | body : PhysicsBody, 87 | force : Vector2 88 | ); 89 | physics_add_torque : proc "c" ( 90 | body : PhysicsBody, 91 | amount : _c.float 92 | ); 93 | physics_shatter : proc "c" ( 94 | body : PhysicsBody, 95 | position : Vector2, 96 | force : _c.float 97 | ); 98 | get_physics_bodies_count : proc "c" () -> _c.int; 99 | get_physics_body : proc "c" (index : _c.int) -> PhysicsBody; 100 | get_physics_shape_type : proc "c" (index : _c.int) -> _c.int; 101 | get_physics_shape_vertices_count : proc "c" (index : _c.int) -> _c.int; 102 | get_physics_shape_vertex : proc "c" ( 103 | body : PhysicsBody, 104 | vertex : _c.int 105 | ) -> Vector2; 106 | set_physics_body_rotation : proc "c" ( 107 | body : PhysicsBody, 108 | radians : _c.float 109 | ); 110 | destroy_physics_body : proc "c" (body : PhysicsBody); 111 | reset_physics : proc "c" (); 112 | close_physics : proc "c" (); 113 | physics_loop : proc "c" (data : rawptr) -> rawptr; 114 | -------------------------------------------------------------------------------- /ext/physac/build.bat: -------------------------------------------------------------------------------- 1 | @setlocal 2 | @set INPUT_FILE=physac.c 3 | @set OUTPUT_FILE=..\lib\physac.lib 4 | @echo. 5 | @echo Compiling... 6 | @echo. 7 | cl /nologo /I. /I ..\..\..\raylib\src /O2 /c physac.c && lib /nologo physac.obj /out:"%OUTPUT_FILE%" || exit /b 1 8 | @echo. 9 | @echo Created "%OUTPUT_FILE%" 10 | 11 | -------------------------------------------------------------------------------- /ext/physac/physac-preprocessed.h: -------------------------------------------------------------------------------- 1 | #line 71 "physac.h" 2 | /********************************************************************************************** 3 | * 4 | * Physac v1.0 - 2D Physics library for videogames 5 | * 6 | * DESCRIPTION: 7 | * 8 | * Physac is a small 2D physics engine written in pure C. The engine uses a fixed time-step thread loop 9 | * to simluate physics. A physics step contains the following phases: get collision information, 10 | * apply dynamics, collision solving and position correction. It uses a very simple struct for physic 11 | * bodies with a position vector to be used in any 3D rendering API. 12 | * 13 | * CONFIGURATION: 14 | * 15 | * #define PHYSAC_IMPLEMENTATION 16 | * Generates the implementation of the library into the included file. 17 | * If not defined, the library is in header only mode and can be included in other headers 18 | * or source files without problems. But only ONE file should hold the implementation. 19 | * 20 | * #define PHYSAC_STATIC (defined by default) 21 | * The generated implementation will stay private inside implementation file and all 22 | * internal symbols and functions will only be visible inside that file. 23 | * 24 | * #define PHYSAC_NO_THREADS 25 | * The generated implementation won't include pthread library and user must create a secondary thread to call PhysicsThread(). 26 | * It is so important that the thread where PhysicsThread() is called must not have v-sync or any other CPU limitation. 27 | * 28 | * #define PHYSAC_STANDALONE 29 | * Avoid raylib.h header inclusion in this file. Data types defined on raylib are defined 30 | * internally in the library and input management and drawing functions must be provided by 31 | * the user (check library implementation for further details). 32 | * 33 | * #define PHYSAC_DEBUG 34 | * Traces log messages when creating and destroying physics bodies and detects errors in physics 35 | * calculations and reference exceptions; it is useful for debug purposes 36 | * 37 | * #define PHYSAC_MALLOC() 38 | * #define PHYSAC_FREE() 39 | * You can define your own malloc/free implementation replacing stdlib.h malloc()/free() functions. 40 | * Otherwise it will include stdlib.h and use the C standard library malloc()/free() function. 41 | * 42 | * 43 | * NOTE 1: Physac requires multi-threading, when InitPhysics() a second thread is created to manage physics calculations. 44 | * NOTE 2: Physac requires static C library linkage to avoid dependency on MinGW DLL (-static -lpthread) 45 | * 46 | * Use the following code to compile: 47 | * gcc -o $(NAME_PART).exe $(FILE_NAME) -s icon\physac_icon -static -lraylib -lpthread -lopengl32 -lgdi32 -std=c99 48 | * 49 | * VERY THANKS TO: 50 | * Ramon Santamaria (github: @raysan5) 51 | * 52 | * 53 | * LICENSE: zlib/libpng 54 | * 55 | * Copyright (c) 2016-2018 Victor Fisac (github: @victorfisac) 56 | * 57 | * This software is provided "as-is", without any express or implied warranty. In no event 58 | * will the authors be held liable for any damages arising from the use of this software. 59 | * 60 | * Permission is granted to anyone to use this software for any purpose, including commercial 61 | * applications, and to alter it and redistribute it freely, subject to the following restrictions: 62 | * 63 | * 1. The origin of this software must not be misrepresented; you must not claim that you 64 | * wrote the original software. If you use this software in a product, an acknowledgment 65 | * in the product documentation would be appreciated but is not required. 66 | * 67 | * 2. Altered source versions must be plainly marked as such, and must not be misrepresented 68 | * as being the original software. 69 | * 70 | * 3. This notice may not be removed or altered from any source distribution. 71 | * 72 | **********************************************************************************************/ 73 | 74 | 75 | #define PHYSAC_H 76 | 77 | // #define PHYSAC_STATIC 78 | // #define PHYSAC_NO_THREADS 79 | // #define PHYSAC_STANDALONE 80 | // #define PHYSAC_DEBUG 81 | #line 87 "physac.h" 82 | #define PHYSACDEF extern // Functions visible from other files 83 | 84 | 85 | 86 | //---------------------------------------------------------------------------------- 87 | // Defines and Macros 88 | //---------------------------------------------------------------------------------- 89 | #define PHYSAC_MAX_BODIES 64 90 | #define PHYSAC_MAX_MANIFOLDS 4096 91 | #define PHYSAC_MAX_VERTICES 24 92 | #define PHYSAC_CIRCLE_VERTICES 24 93 | 94 | #define PHYSAC_DESIRED_DELTATIME 1.0/60.0 95 | #define PHYSAC_MAX_TIMESTEP 0.02 96 | #define PHYSAC_COLLISION_ITERATIONS 100 97 | #define PHYSAC_PENETRATION_ALLOWANCE 0.05f 98 | #define PHYSAC_PENETRATION_CORRECTION 0.4f 99 | 100 | #define PHYSAC_PI 3.14159265358979323846 101 | #define PHYSAC_DEG2RAD (PHYSAC_PI/180.0f) 102 | 103 | #define PHYSAC_MALLOC(size) malloc(size) 104 | #define PHYSAC_FREE(ptr) free(ptr) 105 | 106 | //---------------------------------------------------------------------------------- 107 | // Types and Structures Definition 108 | // NOTE: Below types are required for PHYSAC_STANDALONE usage 109 | //---------------------------------------------------------------------------------- 110 | #line 129 "physac.h" 111 | typedef enum PhysicsShapeType { PHYSICS_CIRCLE, PHYSICS_POLYGON } PhysicsShapeType; 112 | 113 | // Previously defined to be used in PhysicsShape struct as circular dependencies 114 | typedef struct PhysicsBodyData *PhysicsBody; 115 | 116 | // Mat2 type (used for polygon shape rotation matrix) 117 | typedef struct Mat2 { 118 | float m00; 119 | float m01; 120 | float m10; 121 | float m11; 122 | } Mat2; 123 | 124 | typedef struct PolygonData { 125 | unsigned int vertexCount; // Current used vertex and normals count 126 | Vector2 positions[24]; // Polygon vertex positions vectors 127 | Vector2 normals[24]; // Polygon vertex normals vectors 128 | } PolygonData; 129 | 130 | typedef struct PhysicsShape { 131 | PhysicsShapeType type; // Physics shape type (circle or polygon) 132 | PhysicsBody body; // Shape physics body reference 133 | float radius; // Circle shape radius (used for circle shapes) 134 | Mat2 transform; // Vertices transform matrix 2x2 135 | PolygonData vertexData; // Polygon shape vertices position and normals data (just used for polygon shapes) 136 | } PhysicsShape; 137 | 138 | typedef struct PhysicsBodyData { 139 | unsigned int id; // Reference unique identifier 140 | bool enabled; // Enabled dynamics state (collisions are calculated anyway) 141 | Vector2 position; // Physics body shape pivot 142 | Vector2 velocity; // Current linear velocity applied to position 143 | Vector2 force; // Current linear force (reset to 0 every step) 144 | float angularVelocity; // Current angular velocity applied to orient 145 | float torque; // Current angular force (reset to 0 every step) 146 | float orient; // Rotation in radians 147 | float inertia; // Moment of inertia 148 | float inverseInertia; // Inverse value of inertia 149 | float mass; // Physics body mass 150 | float inverseMass; // Inverse value of mass 151 | float staticFriction; // Friction when the body has not movement (0 to 1) 152 | float dynamicFriction; // Friction when the body has movement (0 to 1) 153 | float restitution; // Restitution coefficient of the body (0 to 1) 154 | bool useGravity; // Apply gravity force to dynamics 155 | bool isGrounded; // Physics grounded on other body state 156 | bool freezeOrient; // Physics rotation constraint 157 | PhysicsShape shape; // Physics body shape information (type, radius, vertices, normals) 158 | } PhysicsBodyData; 159 | 160 | typedef struct PhysicsManifoldData { 161 | unsigned int id; // Reference unique identifier 162 | PhysicsBody bodyA; // Manifold first physics body reference 163 | PhysicsBody bodyB; // Manifold second physics body reference 164 | float penetration; // Depth of penetration from collision 165 | Vector2 normal; // Normal direction vector from 'a' to 'b' 166 | Vector2 contacts[2]; // Points of contact during collision 167 | unsigned int contactsCount; // Current collision number of contacts 168 | float restitution; // Mixed restitution during collision 169 | float dynamicFriction; // Mixed dynamic friction during collision 170 | float staticFriction; // Mixed static friction during collision 171 | } PhysicsManifoldData, *PhysicsManifold; 172 | 173 | 174 | 175 | 176 | 177 | //---------------------------------------------------------------------------------- 178 | // Module Functions Declaration 179 | //---------------------------------------------------------------------------------- 180 | PHYSACDEF void InitPhysics(void); // Initializes physics values, pointers and creates physics loop thread 181 | PHYSACDEF bool IsPhysicsEnabled(void); // Returns true if physics thread is currently enabled 182 | PHYSACDEF void SetPhysicsGravity(float x, float y); // Sets physics global gravity force 183 | PHYSACDEF PhysicsBody CreatePhysicsBodyCircle(Vector2 pos, float radius, float density); // Creates a new circle physics body with generic parameters 184 | PHYSACDEF PhysicsBody CreatePhysicsBodyRectangle(Vector2 pos, float width, float height, float density); // Creates a new rectangle physics body with generic parameters 185 | PHYSACDEF PhysicsBody CreatePhysicsBodyPolygon(Vector2 pos, float radius, int sides, float density); // Creates a new polygon physics body with generic parameters 186 | PHYSACDEF void PhysicsAddForce(PhysicsBody body, Vector2 force); // Adds a force to a physics body 187 | PHYSACDEF void PhysicsAddTorque(PhysicsBody body, float amount); // Adds an angular force to a physics body 188 | PHYSACDEF void PhysicsShatter(PhysicsBody body, Vector2 position, float force); // Shatters a polygon shape physics body to little physics bodies with explosion force 189 | PHYSACDEF int GetPhysicsBodiesCount(void); // Returns the current amount of created physics bodies 190 | PHYSACDEF PhysicsBody GetPhysicsBody(int index); // Returns a physics body of the bodies pool at a specific index 191 | PHYSACDEF int GetPhysicsShapeType(int index); // Returns the physics body shape type (PHYSICS_CIRCLE or PHYSICS_POLYGON) 192 | PHYSACDEF int GetPhysicsShapeVerticesCount(int index); // Returns the amount of vertices of a physics body shape 193 | PHYSACDEF Vector2 GetPhysicsShapeVertex(PhysicsBody body, int vertex); // Returns transformed position of a body shape (body position + vertex transformed position) 194 | PHYSACDEF void SetPhysicsBodyRotation(PhysicsBody body, float radians); // Sets physics body shape transform based on radians parameter 195 | PHYSACDEF void DestroyPhysicsBody(PhysicsBody body); // Unitializes and destroy a physics body 196 | PHYSACDEF void ResetPhysics(void); // Destroys created physics bodies and manifolds and resets global values 197 | PHYSACDEF void ClosePhysics(void); // Unitializes physics pointers and closes physics loop thread 198 | PHYSACDEF void* PhysicsLoop(void* data); 199 | #line 228 "physac.h" 200 | /*********************************************************************************** 201 | * 202 | * PHYSAC IMPLEMENTATION 203 | * 204 | ************************************************************************************/ 205 | -------------------------------------------------------------------------------- /ext/physac/physac.c: -------------------------------------------------------------------------------- 1 | #define PHYSAC_IMPLEMENTATION 2 | #define PHYSAC_NO_THREADS 3 | #define PHYSAC_DEBUG 4 | #include "raylib.h" 5 | #include "physac.h" 6 | -------------------------------------------------------------------------------- /ext/physac/physac_bindings.odin: -------------------------------------------------------------------------------- 1 | 2 | // 3 | // THIS FILE WAS AUTOGENERATED 4 | // 5 | 6 | package physac_bindings 7 | 8 | foreign import physac_native "physac.lib" 9 | 10 | import _c "core:c" 11 | import "../../raylib/types" 12 | 13 | 14 | import physac_types "./types" 15 | 16 | physac_Funcs :: physac_types.physac_Funcs; 17 | 18 | // re-export everything from ./types for convienience 19 | PHYSAC_H :: physac_types.PHYSAC_H; 20 | PHYSAC_MAX_BODIES :: physac_types.PHYSAC_MAX_BODIES; 21 | PHYSAC_MAX_MANIFOLDS :: physac_types.PHYSAC_MAX_MANIFOLDS; 22 | PHYSAC_MAX_VERTICES :: physac_types.PHYSAC_MAX_VERTICES; 23 | PHYSAC_CIRCLE_VERTICES :: physac_types.PHYSAC_CIRCLE_VERTICES; 24 | PHYSAC_DESIRED_DELTATIME :: physac_types.PHYSAC_DESIRED_DELTATIME; 25 | PHYSAC_MAX_TIMESTEP :: physac_types.PHYSAC_MAX_TIMESTEP; 26 | PHYSAC_COLLISION_ITERATIONS :: physac_types.PHYSAC_COLLISION_ITERATIONS; 27 | PHYSAC_PENETRATION_ALLOWANCE :: physac_types.PHYSAC_PENETRATION_ALLOWANCE; 28 | PHYSAC_PENETRATION_CORRECTION :: physac_types.PHYSAC_PENETRATION_CORRECTION; 29 | PHYSAC_PI :: physac_types.PHYSAC_PI; 30 | PHYSAC_DEG2RAD :: physac_types.PHYSAC_DEG2RAD; 31 | 32 | PhysicsBody :: physac_types.PhysicsBody; 33 | 34 | PhysicsBodyData :: physac_types.PhysicsBodyData; 35 | Mat2 :: physac_types.Mat2; 36 | PolygonData :: physac_types.PolygonData; 37 | PhysicsShape :: physac_types.PhysicsShape; 38 | PhysicsManifoldData :: physac_types.PhysicsManifoldData; 39 | PhysicsShapeType :: physac_types.PhysicsShapeType; 40 | 41 | get_function_pointers :: proc(funcs: ^physac_types.physac_Funcs) { 42 | funcs.init_physics = init_physics; 43 | funcs.is_physics_enabled = is_physics_enabled; 44 | funcs.set_physics_gravity = set_physics_gravity; 45 | funcs.create_physics_body_circle = create_physics_body_circle; 46 | funcs.create_physics_body_rectangle = create_physics_body_rectangle; 47 | funcs.create_physics_body_polygon = create_physics_body_polygon; 48 | funcs.physics_add_force = physics_add_force; 49 | funcs.physics_add_torque = physics_add_torque; 50 | funcs.physics_shatter = physics_shatter; 51 | funcs.get_physics_bodies_count = get_physics_bodies_count; 52 | funcs.get_physics_body = get_physics_body; 53 | funcs.get_physics_shape_type = get_physics_shape_type; 54 | funcs.get_physics_shape_vertices_count = get_physics_shape_vertices_count; 55 | funcs.get_physics_shape_vertex = get_physics_shape_vertex; 56 | funcs.set_physics_body_rotation = set_physics_body_rotation; 57 | funcs.destroy_physics_body = destroy_physics_body; 58 | funcs.reset_physics = reset_physics; 59 | funcs.close_physics = close_physics; 60 | funcs.physics_loop = physics_loop; 61 | } 62 | 63 | @(default_calling_convention="c") 64 | foreign physac_native { 65 | 66 | @(link_name="InitPhysics") 67 | init_physics :: proc() ---; 68 | 69 | @(link_name="IsPhysicsEnabled") 70 | is_physics_enabled :: proc() -> bool ---; 71 | 72 | @(link_name="SetPhysicsGravity") 73 | set_physics_gravity :: proc( 74 | x : _c.float, 75 | y : _c.float 76 | ) ---; 77 | 78 | @(link_name="CreatePhysicsBodyCircle") 79 | create_physics_body_circle :: proc( 80 | pos : Vector2, 81 | radius : _c.float, 82 | density : _c.float 83 | ) -> PhysicsBody ---; 84 | 85 | @(link_name="CreatePhysicsBodyRectangle") 86 | create_physics_body_rectangle :: proc( 87 | pos : Vector2, 88 | width : _c.float, 89 | height : _c.float, 90 | density : _c.float 91 | ) -> PhysicsBody ---; 92 | 93 | @(link_name="CreatePhysicsBodyPolygon") 94 | create_physics_body_polygon :: proc( 95 | pos : Vector2, 96 | radius : _c.float, 97 | sides : _c.int, 98 | density : _c.float 99 | ) -> PhysicsBody ---; 100 | 101 | @(link_name="PhysicsAddForce") 102 | physics_add_force :: proc( 103 | body : PhysicsBody, 104 | force : Vector2 105 | ) ---; 106 | 107 | @(link_name="PhysicsAddTorque") 108 | physics_add_torque :: proc( 109 | body : PhysicsBody, 110 | amount : _c.float 111 | ) ---; 112 | 113 | @(link_name="PhysicsShatter") 114 | physics_shatter :: proc( 115 | body : PhysicsBody, 116 | position : Vector2, 117 | force : _c.float 118 | ) ---; 119 | 120 | @(link_name="GetPhysicsBodiesCount") 121 | get_physics_bodies_count :: proc() -> _c.int ---; 122 | 123 | @(link_name="GetPhysicsBody") 124 | get_physics_body :: proc(index : _c.int) -> PhysicsBody ---; 125 | 126 | @(link_name="GetPhysicsShapeType") 127 | get_physics_shape_type :: proc(index : _c.int) -> _c.int ---; 128 | 129 | @(link_name="GetPhysicsShapeVerticesCount") 130 | get_physics_shape_vertices_count :: proc(index : _c.int) -> _c.int ---; 131 | 132 | @(link_name="GetPhysicsShapeVertex") 133 | get_physics_shape_vertex :: proc( 134 | body : PhysicsBody, 135 | vertex : _c.int 136 | ) -> Vector2 ---; 137 | 138 | @(link_name="SetPhysicsBodyRotation") 139 | set_physics_body_rotation :: proc( 140 | body : PhysicsBody, 141 | radians : _c.float 142 | ) ---; 143 | 144 | @(link_name="DestroyPhysicsBody") 145 | destroy_physics_body :: proc(body : PhysicsBody) ---; 146 | 147 | @(link_name="ResetPhysics") 148 | reset_physics :: proc() ---; 149 | 150 | @(link_name="ClosePhysics") 151 | close_physics :: proc() ---; 152 | 153 | @(link_name="PhysicsLoop") 154 | physics_loop :: proc(data : rawptr) -> rawptr ---; 155 | 156 | } 157 | -------------------------------------------------------------------------------- /ext/physac/preprocess.bat: -------------------------------------------------------------------------------- 1 | pcpp -N PHYSACDEF --passthru-defines --passthru-comments --passthru-unfound-includes -o physac-preprocessed.h physac.h 2 | -------------------------------------------------------------------------------- /ext/physac/types/physac_types.odin: -------------------------------------------------------------------------------- 1 | // 2 | // generated by bindgen (https://github.com/Breush/odin-binding-generator) 3 | // 4 | 5 | package physac_types 6 | 7 | import _c "core:c" 8 | 9 | import "../../../raylib/types" 10 | PHYSAC_H :: 1; 11 | PHYSAC_MAX_BODIES :: 64; 12 | PHYSAC_MAX_MANIFOLDS :: 4096; 13 | PHYSAC_MAX_VERTICES :: 24; 14 | PHYSAC_CIRCLE_VERTICES :: 24; 15 | PHYSAC_DESIRED_DELTATIME :: 0.017; 16 | PHYSAC_MAX_TIMESTEP :: 0.020; 17 | PHYSAC_COLLISION_ITERATIONS :: 100; 18 | PHYSAC_PENETRATION_ALLOWANCE :: 0.050; 19 | PHYSAC_PENETRATION_CORRECTION :: 0.400; 20 | PHYSAC_PI :: 3.142; 21 | PHYSAC_DEG2RAD :: 0.017; 22 | 23 | PhysicsBody :: ^PhysicsBodyData; 24 | 25 | PhysicsShapeType :: enum i32 { 26 | PHYSICS_CIRCLE, 27 | PHYSICS_POLYGON, 28 | }; 29 | 30 | PhysicsBodyData :: struct { 31 | id : _c.uint, 32 | enabled : bool, 33 | position : Vector2, 34 | velocity : Vector2, 35 | force : Vector2, 36 | angular_velocity : _c.float, 37 | torque : _c.float, 38 | orient : _c.float, 39 | inertia : _c.float, 40 | inverse_inertia : _c.float, 41 | mass : _c.float, 42 | inverse_mass : _c.float, 43 | static_friction : _c.float, 44 | dynamic_friction : _c.float, 45 | restitution : _c.float, 46 | use_gravity : bool, 47 | is_grounded : bool, 48 | freeze_orient : bool, 49 | shape : PhysicsShape, 50 | }; 51 | 52 | Mat2 :: struct { 53 | m00 : _c.float, 54 | m01 : _c.float, 55 | m10 : _c.float, 56 | m11 : _c.float, 57 | }; 58 | 59 | PolygonData :: struct { 60 | vertex_count : _c.uint, 61 | positions : [24]Vector2, 62 | normals : [24]Vector2, 63 | }; 64 | 65 | PhysicsShape :: struct { 66 | type : PhysicsShapeType, 67 | body : PhysicsBody, 68 | radius : _c.float, 69 | transform : Mat2, 70 | vertex_data : PolygonData, 71 | }; 72 | 73 | PhysicsManifoldData :: struct { 74 | id : _c.uint, 75 | body_a : PhysicsBody, 76 | body_b : PhysicsBody, 77 | penetration : _c.float, 78 | normal : Vector2, 79 | contacts : [2]Vector2, 80 | contacts_count : _c.uint, 81 | restitution : _c.float, 82 | dynamic_friction : _c.float, 83 | static_friction : _c.float, 84 | }; 85 | 86 | physac_Funcs :: struct { 87 | init_physics : proc "c" (), 88 | is_physics_enabled : proc "c" () -> bool, 89 | set_physics_gravity : proc "c" ( 90 | x : _c.float, 91 | y : _c.float 92 | ), 93 | create_physics_body_circle : proc "c" ( 94 | pos : Vector2, 95 | radius : _c.float, 96 | density : _c.float 97 | ) -> PhysicsBody, 98 | create_physics_body_rectangle : proc "c" ( 99 | pos : Vector2, 100 | width : _c.float, 101 | height : _c.float, 102 | density : _c.float 103 | ) -> PhysicsBody, 104 | create_physics_body_polygon : proc "c" ( 105 | pos : Vector2, 106 | radius : _c.float, 107 | sides : _c.int, 108 | density : _c.float 109 | ) -> PhysicsBody, 110 | physics_add_force : proc "c" ( 111 | body : PhysicsBody, 112 | force : Vector2 113 | ), 114 | physics_add_torque : proc "c" ( 115 | body : PhysicsBody, 116 | amount : _c.float 117 | ), 118 | physics_shatter : proc "c" ( 119 | body : PhysicsBody, 120 | position : Vector2, 121 | force : _c.float 122 | ), 123 | get_physics_bodies_count : proc "c" () -> _c.int, 124 | get_physics_body : proc "c" (index : _c.int) -> PhysicsBody, 125 | get_physics_shape_type : proc "c" (index : _c.int) -> _c.int, 126 | get_physics_shape_vertices_count : proc "c" (index : _c.int) -> _c.int, 127 | get_physics_shape_vertex : proc "c" ( 128 | body : PhysicsBody, 129 | vertex : _c.int 130 | ) -> Vector2, 131 | set_physics_body_rotation : proc "c" ( 132 | body : PhysicsBody, 133 | radians : _c.float 134 | ), 135 | destroy_physics_body : proc "c" (body : PhysicsBody), 136 | reset_physics : proc "c" (), 137 | close_physics : proc "c" (), 138 | physics_loop : proc "c" (data : rawptr) -> rawptr, 139 | } 140 | 141 | -------------------------------------------------------------------------------- /ext/raygui/bridge/raygui_bridge.odin: -------------------------------------------------------------------------------- 1 | 2 | package raygui 3 | 4 | import raylib_types "../types" 5 | 6 | raygui_Funcs :: raylib_types.raygui_Funcs; 7 | 8 | // re-export everything from ./types for convienience 9 | RAYGUI_H :: raylib_types.RAYGUI_H; 10 | RAYGUI_VERSION :: raylib_types.RAYGUI_VERSION; 11 | RAYGUI_RICONS_SUPPORT :: raylib_types.RAYGUI_RICONS_SUPPORT; 12 | TEXTEDIT_CURSOR_BLINK_FRAMES :: raylib_types.TEXTEDIT_CURSOR_BLINK_FRAMES; 13 | NUM_CONTROLS :: raylib_types.NUM_CONTROLS; 14 | NUM_PROPS_DEFAULT :: raylib_types.NUM_PROPS_DEFAULT; 15 | NUM_PROPS_EXTENDED :: raylib_types.NUM_PROPS_EXTENDED; 16 | 17 | 18 | 19 | 20 | import _c "core:c" 21 | 22 | bridge_init :: proc(funcs: ^raygui_Funcs) { 23 | assert(funcs != nil); 24 | assert(funcs.gui_list_element != nil); 25 | 26 | gui_list_element = funcs.gui_list_element; 27 | convert_hs_vto_rgb = funcs.convert_hs_vto_rgb; 28 | convert_rg_bto_hsv = funcs.convert_rg_bto_hsv; 29 | get_text_width = funcs.get_text_width; 30 | get_text_bounds = funcs.get_text_bounds; 31 | get_text_icon = funcs.get_text_icon; 32 | gui_draw_text = funcs.gui_draw_text; 33 | gui_text_split = funcs.gui_text_split; 34 | gui_enable = funcs.gui_enable; 35 | gui_disable = funcs.gui_disable; 36 | gui_lock = funcs.gui_lock; 37 | gui_unlock = funcs.gui_unlock; 38 | gui_state = funcs.gui_state; 39 | gui_font = funcs.gui_font; 40 | gui_fade = funcs.gui_fade; 41 | gui_set_style = funcs.gui_set_style; 42 | gui_get_style = funcs.gui_get_style; 43 | gui_window_box = funcs.gui_window_box; 44 | gui_group_box = funcs.gui_group_box; 45 | gui_line = funcs.gui_line; 46 | gui_panel = funcs.gui_panel; 47 | gui_scroll_panel = funcs.gui_scroll_panel; 48 | gui_label = funcs.gui_label; 49 | gui_button = funcs.gui_button; 50 | gui_label_button = funcs.gui_label_button; 51 | gui_image_button = funcs.gui_image_button; 52 | gui_image_button_ex = funcs.gui_image_button_ex; 53 | gui_toggle = funcs.gui_toggle; 54 | gui_toggle_group = funcs.gui_toggle_group; 55 | gui_check_box = funcs.gui_check_box; 56 | gui_combo_box = funcs.gui_combo_box; 57 | gui_dropdown_box = funcs.gui_dropdown_box; 58 | gui_spinner = funcs.gui_spinner; 59 | gui_value_box = funcs.gui_value_box; 60 | gui_text_box = funcs.gui_text_box; 61 | gui_text_box_multi = funcs.gui_text_box_multi; 62 | gui_slider_pro = funcs.gui_slider_pro; 63 | gui_slider = funcs.gui_slider; 64 | gui_slider_bar = funcs.gui_slider_bar; 65 | gui_progress_bar = funcs.gui_progress_bar; 66 | gui_status_bar = funcs.gui_status_bar; 67 | gui_dummy_rec = funcs.gui_dummy_rec; 68 | gui_scroll_bar = funcs.gui_scroll_bar; 69 | gui_list_view = funcs.gui_list_view; 70 | gui_list_view_ex = funcs.gui_list_view_ex; 71 | gui_color_panel = funcs.gui_color_panel; 72 | gui_color_bar_alpha = funcs.gui_color_bar_alpha; 73 | gui_color_bar_hue = funcs.gui_color_bar_hue; 74 | gui_color_picker = funcs.gui_color_picker; 75 | gui_message_box = funcs.gui_message_box; 76 | gui_grid = funcs.gui_grid; 77 | gui_load_style = funcs.gui_load_style; 78 | gui_load_style_props = funcs.gui_load_style_props; 79 | gui_load_style_default = funcs.gui_load_style_default; 80 | gui_update_style_complete = funcs.gui_update_style_complete; 81 | gui_icon_text = funcs.gui_icon_text; 82 | } 83 | 84 | bridge_deinit :: proc() { 85 | } 86 | 87 | gui_list_element : proc "c" ( 88 | bounds : Rectangle, 89 | text : cstring, 90 | active : bool, 91 | edit_mode : bool 92 | ) -> bool; 93 | convert_hs_vto_rgb : proc "c" (hsv : Vector3) -> Vector3; 94 | convert_rg_bto_hsv : proc "c" (rgb : Vector3) -> Vector3; 95 | get_text_width : proc "c" (text : cstring) -> _c.int; 96 | get_text_bounds : proc "c" ( 97 | control : _c.int, 98 | bounds : Rectangle 99 | ) -> Rectangle; 100 | get_text_icon : proc "c" ( 101 | text : cstring, 102 | icon_id : ^_c.int 103 | ) -> cstring; 104 | gui_draw_text : proc "c" ( 105 | text : cstring, 106 | bounds : Rectangle, 107 | alignment : _c.int, 108 | tint : Color 109 | ); 110 | gui_text_split : proc "c" ( 111 | text : cstring, 112 | count : ^_c.int, 113 | text_row : ^_c.int 114 | ) -> ^cstring; 115 | gui_enable : proc "c" (); 116 | gui_disable : proc "c" (); 117 | gui_lock : proc "c" (); 118 | gui_unlock : proc "c" (); 119 | gui_state : proc "c" (state : _c.int); 120 | gui_font : proc "c" (font : Font); 121 | gui_fade : proc "c" (alpha : _c.float); 122 | gui_set_style : proc "c" ( 123 | control : _c.int, 124 | property : _c.int, 125 | value : _c.int 126 | ); 127 | gui_get_style : proc "c" ( 128 | control : _c.int, 129 | property : _c.int 130 | ) -> _c.int; 131 | gui_window_box : proc "c" ( 132 | bounds : Rectangle, 133 | text : cstring 134 | ) -> bool; 135 | gui_group_box : proc "c" ( 136 | bounds : Rectangle, 137 | text : cstring 138 | ); 139 | gui_line : proc "c" ( 140 | bounds : Rectangle, 141 | text : cstring 142 | ); 143 | gui_panel : proc "c" (bounds : Rectangle); 144 | gui_scroll_panel : proc "c" ( 145 | bounds : Rectangle, 146 | content : Rectangle, 147 | scroll : ^Vector2 148 | ) -> Rectangle; 149 | gui_label : proc "c" ( 150 | bounds : Rectangle, 151 | text : cstring 152 | ); 153 | gui_button : proc "c" ( 154 | bounds : Rectangle, 155 | text : cstring 156 | ) -> bool; 157 | gui_label_button : proc "c" ( 158 | bounds : Rectangle, 159 | text : cstring 160 | ) -> bool; 161 | gui_image_button : proc "c" ( 162 | bounds : Rectangle, 163 | texture : Texture2D 164 | ) -> bool; 165 | gui_image_button_ex : proc "c" ( 166 | bounds : Rectangle, 167 | texture : Texture2D, 168 | tex_source : Rectangle, 169 | text : cstring 170 | ) -> bool; 171 | gui_toggle : proc "c" ( 172 | bounds : Rectangle, 173 | text : cstring, 174 | active : bool 175 | ) -> bool; 176 | gui_toggle_group : proc "c" ( 177 | bounds : Rectangle, 178 | text : cstring, 179 | active : _c.int 180 | ) -> _c.int; 181 | gui_check_box : proc "c" ( 182 | bounds : Rectangle, 183 | text : cstring, 184 | checked : bool 185 | ) -> bool; 186 | gui_combo_box : proc "c" ( 187 | bounds : Rectangle, 188 | text : cstring, 189 | active : _c.int 190 | ) -> _c.int; 191 | gui_dropdown_box : proc "c" ( 192 | bounds : Rectangle, 193 | text : cstring, 194 | active : ^_c.int, 195 | edit_mode : bool 196 | ) -> bool; 197 | gui_spinner : proc "c" ( 198 | bounds : Rectangle, 199 | value : ^_c.int, 200 | min_value : _c.int, 201 | max_value : _c.int, 202 | edit_mode : bool 203 | ) -> bool; 204 | gui_value_box : proc "c" ( 205 | bounds : Rectangle, 206 | value : ^_c.int, 207 | min_value : _c.int, 208 | max_value : _c.int, 209 | edit_mode : bool 210 | ) -> bool; 211 | gui_text_box : proc "c" ( 212 | bounds : Rectangle, 213 | text : cstring, 214 | text_size : _c.int, 215 | edit_mode : bool 216 | ) -> bool; 217 | gui_text_box_multi : proc "c" ( 218 | bounds : Rectangle, 219 | text : cstring, 220 | text_size : _c.int, 221 | edit_mode : bool 222 | ) -> bool; 223 | gui_slider_pro : proc "c" ( 224 | bounds : Rectangle, 225 | text : cstring, 226 | value : _c.float, 227 | min_value : _c.float, 228 | max_value : _c.float, 229 | slider_width : _c.int, 230 | show_value : bool 231 | ) -> _c.float; 232 | gui_slider : proc "c" ( 233 | bounds : Rectangle, 234 | text : cstring, 235 | value : _c.float, 236 | min_value : _c.float, 237 | max_value : _c.float, 238 | show_value : bool 239 | ) -> _c.float; 240 | gui_slider_bar : proc "c" ( 241 | bounds : Rectangle, 242 | text : cstring, 243 | value : _c.float, 244 | min_value : _c.float, 245 | max_value : _c.float, 246 | show_value : bool 247 | ) -> _c.float; 248 | gui_progress_bar : proc "c" ( 249 | bounds : Rectangle, 250 | text : cstring, 251 | value : _c.float, 252 | min_value : _c.float, 253 | max_value : _c.float, 254 | show_value : bool 255 | ) -> _c.float; 256 | gui_status_bar : proc "c" ( 257 | bounds : Rectangle, 258 | text : cstring 259 | ); 260 | gui_dummy_rec : proc "c" ( 261 | bounds : Rectangle, 262 | text : cstring 263 | ); 264 | gui_scroll_bar : proc "c" ( 265 | bounds : Rectangle, 266 | value : _c.int, 267 | min_value : _c.int, 268 | max_value : _c.int 269 | ) -> _c.int; 270 | gui_list_view : proc "c" ( 271 | bounds : Rectangle, 272 | text : cstring, 273 | active : ^_c.int, 274 | scroll_index : ^_c.int, 275 | edit_mode : bool 276 | ) -> bool; 277 | gui_list_view_ex : proc "c" ( 278 | bounds : Rectangle, 279 | text : ^cstring, 280 | count : _c.int, 281 | enabled : ^_c.int, 282 | active : ^_c.int, 283 | focus : ^_c.int, 284 | scroll_index : ^_c.int, 285 | edit_mode : bool 286 | ) -> bool; 287 | gui_color_panel : proc "c" ( 288 | bounds : Rectangle, 289 | color : Color 290 | ) -> Color; 291 | gui_color_bar_alpha : proc "c" ( 292 | bounds : Rectangle, 293 | alpha : _c.float 294 | ) -> _c.float; 295 | gui_color_bar_hue : proc "c" ( 296 | bounds : Rectangle, 297 | hue : _c.float 298 | ) -> _c.float; 299 | gui_color_picker : proc "c" ( 300 | bounds : Rectangle, 301 | color : Color 302 | ) -> Color; 303 | gui_message_box : proc "c" ( 304 | bounds : Rectangle, 305 | window_title : cstring, 306 | message : cstring, 307 | buttons : cstring 308 | ) -> _c.int; 309 | gui_grid : proc "c" ( 310 | bounds : Rectangle, 311 | spacing : _c.float, 312 | subdivs : _c.int 313 | ) -> Vector2; 314 | gui_load_style : proc "c" (file_name : cstring); 315 | gui_load_style_props : proc "c" ( 316 | props : ^_c.int, 317 | count : _c.int 318 | ); 319 | gui_load_style_default : proc "c" (); 320 | gui_update_style_complete : proc "c" (); 321 | gui_icon_text : proc "c" ( 322 | icon_id : _c.int, 323 | text : cstring 324 | ) -> cstring; 325 | -------------------------------------------------------------------------------- /ext/raygui/build.bat: -------------------------------------------------------------------------------- 1 | @setlocal 2 | @set OUTPUT_FILE=..\..\lib\raygui.lib 3 | @echo. 4 | @echo Compiling... 5 | @echo. 6 | cl /nologo /I. /I ..\..\..\raylib\src /c raygui.c && lib /nologo raygui.obj /out:"%OUTPUT_FILE%" 7 | @echo. 8 | @echo Created "%OUTPUT_FILE%" 9 | 10 | -------------------------------------------------------------------------------- /ext/raygui/generate_raygui_header.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | this_dir = os.path.realpath(os.path.dirname(__file__)) 4 | skipping = False 5 | 6 | with open(os.path.join(this_dir, "raygui.h")) as f: 7 | with open(os.path.join(this_dir, "raygui-preprocessed.h"), "w") as out: 8 | for line in f: 9 | if line == "#include ": 10 | continue 11 | if line == "#if defined(RAYGUI_STANDALONE)\n": 12 | skipping = True 13 | elif line == "#endif // RAYGUI_STANDALONE\n": 14 | skipping = False 15 | else: 16 | if not skipping: 17 | out.write(line) 18 | 19 | -------------------------------------------------------------------------------- /ext/raygui/raygui.c: -------------------------------------------------------------------------------- 1 | #define RAYGUI_IMPLEMENTATION 2 | 3 | #include "raygui.h" 4 | 5 | -------------------------------------------------------------------------------- /ext/raygui/types/raygui_types.odin: -------------------------------------------------------------------------------- 1 | // 2 | // generated by bindgen (https://github.com/Breush/odin-binding-generator) 3 | // 4 | 5 | package raygui_types 6 | 7 | import _c "core:c" 8 | 9 | import "../../raylib/types" 10 | import "core:math/linalg" 11 | RAYGUI_H :: 1; 12 | RAYGUI_VERSION :: "2.0-dev"; 13 | RAYGUI_RICONS_SUPPORT :: 1; 14 | TEXTEDIT_CURSOR_BLINK_FRAMES :: 20; 15 | NUM_CONTROLS :: 13; 16 | NUM_PROPS_DEFAULT :: 16; 17 | NUM_PROPS_EXTENDED :: 8; 18 | 19 | 20 | raygui_Funcs :: struct { 21 | gui_list_element : proc "c" ( 22 | bounds : Rectangle, 23 | text : cstring, 24 | active : bool, 25 | edit_mode : bool 26 | ) -> bool, 27 | convert_hs_vto_rgb : proc "c" (hsv : Vector3) -> Vector3, 28 | convert_rg_bto_hsv : proc "c" (rgb : Vector3) -> Vector3, 29 | get_text_width : proc "c" (text : cstring) -> _c.int, 30 | get_text_bounds : proc "c" ( 31 | control : _c.int, 32 | bounds : Rectangle 33 | ) -> Rectangle, 34 | get_text_icon : proc "c" ( 35 | text : cstring, 36 | icon_id : ^_c.int 37 | ) -> cstring, 38 | gui_draw_text : proc "c" ( 39 | text : cstring, 40 | bounds : Rectangle, 41 | alignment : _c.int, 42 | tint : Color 43 | ), 44 | gui_text_split : proc "c" ( 45 | text : cstring, 46 | count : ^_c.int, 47 | text_row : ^_c.int 48 | ) -> ^cstring, 49 | gui_enable : proc "c" (), 50 | gui_disable : proc "c" (), 51 | gui_lock : proc "c" (), 52 | gui_unlock : proc "c" (), 53 | gui_state : proc "c" (state : _c.int), 54 | gui_font : proc "c" (font : Font), 55 | gui_fade : proc "c" (alpha : _c.float), 56 | gui_set_style : proc "c" ( 57 | control : _c.int, 58 | property : _c.int, 59 | value : _c.int 60 | ), 61 | gui_get_style : proc "c" ( 62 | control : _c.int, 63 | property : _c.int 64 | ) -> _c.int, 65 | gui_window_box : proc "c" ( 66 | bounds : Rectangle, 67 | text : cstring 68 | ) -> bool, 69 | gui_group_box : proc "c" ( 70 | bounds : Rectangle, 71 | text : cstring 72 | ), 73 | gui_line : proc "c" ( 74 | bounds : Rectangle, 75 | text : cstring 76 | ), 77 | gui_panel : proc "c" (bounds : Rectangle), 78 | gui_scroll_panel : proc "c" ( 79 | bounds : Rectangle, 80 | content : Rectangle, 81 | scroll : ^Vector2 82 | ) -> Rectangle, 83 | gui_label : proc "c" ( 84 | bounds : Rectangle, 85 | text : cstring 86 | ), 87 | gui_button : proc "c" ( 88 | bounds : Rectangle, 89 | text : cstring 90 | ) -> bool, 91 | gui_label_button : proc "c" ( 92 | bounds : Rectangle, 93 | text : cstring 94 | ) -> bool, 95 | gui_image_button : proc "c" ( 96 | bounds : Rectangle, 97 | texture : Texture2D 98 | ) -> bool, 99 | gui_image_button_ex : proc "c" ( 100 | bounds : Rectangle, 101 | texture : Texture2D, 102 | tex_source : Rectangle, 103 | text : cstring 104 | ) -> bool, 105 | gui_toggle : proc "c" ( 106 | bounds : Rectangle, 107 | text : cstring, 108 | active : bool 109 | ) -> bool, 110 | gui_toggle_group : proc "c" ( 111 | bounds : Rectangle, 112 | text : cstring, 113 | active : _c.int 114 | ) -> _c.int, 115 | gui_check_box : proc "c" ( 116 | bounds : Rectangle, 117 | text : cstring, 118 | checked : bool 119 | ) -> bool, 120 | gui_combo_box : proc "c" ( 121 | bounds : Rectangle, 122 | text : cstring, 123 | active : _c.int 124 | ) -> _c.int, 125 | gui_dropdown_box : proc "c" ( 126 | bounds : Rectangle, 127 | text : cstring, 128 | active : ^_c.int, 129 | edit_mode : bool 130 | ) -> bool, 131 | gui_spinner : proc "c" ( 132 | bounds : Rectangle, 133 | value : ^_c.int, 134 | min_value : _c.int, 135 | max_value : _c.int, 136 | edit_mode : bool 137 | ) -> bool, 138 | gui_value_box : proc "c" ( 139 | bounds : Rectangle, 140 | value : ^_c.int, 141 | min_value : _c.int, 142 | max_value : _c.int, 143 | edit_mode : bool 144 | ) -> bool, 145 | gui_text_box : proc "c" ( 146 | bounds : Rectangle, 147 | text : cstring, 148 | text_size : _c.int, 149 | edit_mode : bool 150 | ) -> bool, 151 | gui_text_box_multi : proc "c" ( 152 | bounds : Rectangle, 153 | text : cstring, 154 | text_size : _c.int, 155 | edit_mode : bool 156 | ) -> bool, 157 | gui_slider_pro : proc "c" ( 158 | bounds : Rectangle, 159 | text : cstring, 160 | value : _c.float, 161 | min_value : _c.float, 162 | max_value : _c.float, 163 | slider_width : _c.int, 164 | show_value : bool 165 | ) -> _c.float, 166 | gui_slider : proc "c" ( 167 | bounds : Rectangle, 168 | text : cstring, 169 | value : _c.float, 170 | min_value : _c.float, 171 | max_value : _c.float, 172 | show_value : bool 173 | ) -> _c.float, 174 | gui_slider_bar : proc "c" ( 175 | bounds : Rectangle, 176 | text : cstring, 177 | value : _c.float, 178 | min_value : _c.float, 179 | max_value : _c.float, 180 | show_value : bool 181 | ) -> _c.float, 182 | gui_progress_bar : proc "c" ( 183 | bounds : Rectangle, 184 | text : cstring, 185 | value : _c.float, 186 | min_value : _c.float, 187 | max_value : _c.float, 188 | show_value : bool 189 | ) -> _c.float, 190 | gui_status_bar : proc "c" ( 191 | bounds : Rectangle, 192 | text : cstring 193 | ), 194 | gui_dummy_rec : proc "c" ( 195 | bounds : Rectangle, 196 | text : cstring 197 | ), 198 | gui_scroll_bar : proc "c" ( 199 | bounds : Rectangle, 200 | value : _c.int, 201 | min_value : _c.int, 202 | max_value : _c.int 203 | ) -> _c.int, 204 | gui_list_view : proc "c" ( 205 | bounds : Rectangle, 206 | text : cstring, 207 | active : ^_c.int, 208 | scroll_index : ^_c.int, 209 | edit_mode : bool 210 | ) -> bool, 211 | gui_list_view_ex : proc "c" ( 212 | bounds : Rectangle, 213 | text : ^cstring, 214 | count : _c.int, 215 | enabled : ^_c.int, 216 | active : ^_c.int, 217 | focus : ^_c.int, 218 | scroll_index : ^_c.int, 219 | edit_mode : bool 220 | ) -> bool, 221 | gui_color_panel : proc "c" ( 222 | bounds : Rectangle, 223 | color : Color 224 | ) -> Color, 225 | gui_color_bar_alpha : proc "c" ( 226 | bounds : Rectangle, 227 | alpha : _c.float 228 | ) -> _c.float, 229 | gui_color_bar_hue : proc "c" ( 230 | bounds : Rectangle, 231 | hue : _c.float 232 | ) -> _c.float, 233 | gui_color_picker : proc "c" ( 234 | bounds : Rectangle, 235 | color : Color 236 | ) -> Color, 237 | gui_message_box : proc "c" ( 238 | bounds : Rectangle, 239 | window_title : cstring, 240 | message : cstring, 241 | buttons : cstring 242 | ) -> _c.int, 243 | gui_grid : proc "c" ( 244 | bounds : Rectangle, 245 | spacing : _c.float, 246 | subdivs : _c.int 247 | ) -> Vector2, 248 | gui_load_style : proc "c" (file_name : cstring), 249 | gui_load_style_props : proc "c" ( 250 | props : ^_c.int, 251 | count : _c.int 252 | ), 253 | gui_load_style_default : proc "c" (), 254 | gui_update_style_complete : proc "c" (), 255 | gui_icon_text : proc "c" ( 256 | icon_id : _c.int, 257 | text : cstring 258 | ) -> cstring, 259 | } 260 | 261 | -------------------------------------------------------------------------------- /ext/raylib/lib/cmake/raylib/raylib-config-version.cmake: -------------------------------------------------------------------------------- 1 | set(PACKAGE_VERSION "3.0.0") 2 | 3 | if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION) 4 | set(PACKAGE_VERSION_EXACT TRUE) 5 | endif() 6 | if(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) 7 | set(PACKAGE_VERSION_COMPATIBLE TRUE) 8 | else(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) 9 | set(PACKAGE_VERSION_UNSUITABLE TRUE) 10 | endif(NOT PACKAGE_FIND_VERSION VERSION_GREATER PACKAGE_VERSION) 11 | 12 | # if the installed or the using project don't have CMAKE_SIZEOF_VOID_P set, ignore it: 13 | if("${CMAKE_SIZEOF_VOID_P}" STREQUAL "" OR "8" STREQUAL "") 14 | return() 15 | endif() 16 | 17 | if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") 18 | math(EXPR installedBits "8 * 8") 19 | set(PACKAGE_VERSION "${PACKAGE_VERSION} (${installedBits}bit)") 20 | set(PACKAGE_VERSION_UNSUITABLE TRUE) 21 | endif() 22 | -------------------------------------------------------------------------------- /ext/raylib/lib/cmake/raylib/raylib-config.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find raylib 2 | # Options: 3 | # raylib_USE_STATIC_LIBS - OFF by default 4 | # raylib_VERBOSE - OFF by default 5 | # Once done, this defines a raylib target that can be passed to 6 | # target_link_libraries as well as following variables: 7 | # 8 | # raylib_FOUND - System has raylib installed 9 | # raylib_INCLUDE_DIRS - The include directories for the raylib header(s) 10 | # raylib_LIBRARIES - The libraries needed to use raylib 11 | # raylib_LDFLAGS - The linker flags needed with raylib 12 | # raylib_DEFINITIONS - Compiler switches required for using raylib 13 | 14 | set(XPREFIX PC_RAYLIB) 15 | 16 | find_package(PkgConfig QUIET) 17 | pkg_check_modules(${XPREFIX} QUIET raylib) 18 | 19 | if (raylib_USE_STATIC_LIBS) 20 | set(XPREFIX ${XPREFIX}_STATIC) 21 | endif() 22 | 23 | set(raylib_DEFINITIONS ${${XPREFIX}_CFLAGS}) 24 | 25 | find_path(raylib_INCLUDE_DIR 26 | NAMES raylib.h 27 | HINTS ${${XPREFIX}_INCLUDE_DIRS} 28 | ) 29 | 30 | set(RAYLIB_NAMES raylib) 31 | 32 | if (raylib_USE_STATIC_LIBS) 33 | set(RAYLIB_NAMES libraylib.a raylib.lib ${RAYLIB_NAMES}) 34 | endif() 35 | 36 | find_library(raylib_LIBRARY 37 | NAMES ${RAYLIB_NAMES} 38 | HINTS ${${XPREFIX}_LIBRARY_DIRS} 39 | ) 40 | 41 | set(raylib_LIBRARIES ${raylib_LIBRARY}) 42 | set(raylib_LIBRARY_DIRS ${${XPREFIX}_LIBRARY_DIRS}) 43 | set(raylib_LIBRARY_DIR ${raylib_LIBRARY_DIRS}) 44 | set(raylib_INCLUDE_DIRS ${raylib_INCLUDE_DIR}) 45 | set(raylib_LDFLAGS ${${XPREFIX}_LDFLAGS}) 46 | 47 | include(FindPackageHandleStandardArgs) 48 | find_package_handle_standard_args(raylib DEFAULT_MSG 49 | raylib_LIBRARY 50 | raylib_INCLUDE_DIR 51 | ) 52 | 53 | mark_as_advanced(raylib_LIBRARY raylib_INCLUDE_DIR) 54 | 55 | if (raylib_USE_STATIC_LIBS) 56 | add_library(raylib STATIC IMPORTED GLOBAL) 57 | else() 58 | add_library(raylib SHARED IMPORTED GLOBAL) 59 | endif() 60 | string (REPLACE ";" " " raylib_LDFLAGS "${raylib_LDFLAGS}") 61 | 62 | set_target_properties(raylib 63 | PROPERTIES 64 | IMPORTED_LOCATION "${raylib_LIBRARIES}" 65 | INTERFACE_INCLUDE_DIRECTORIES "${raylib_INCLUDE_DIRS}" 66 | INTERFACE_LINK_LIBRARIES "${raylib_LDFLAGS}" 67 | INTERFACE_COMPILE_OPTIONS "${raylib_DEFINITIONS}" 68 | ) 69 | 70 | if (raylib_VERBOSE) 71 | message(STATUS "raylib_FOUND: ${raylib_FOUND}") 72 | message(STATUS "raylib_INCLUDE_DIRS: ${raylib_INCLUDE_DIRS}") 73 | message(STATUS "raylib_LIBRARIES: ${raylib_LIBRARIES}") 74 | message(STATUS "raylib_LDFLAGS: ${raylib_LDFLAGS}") 75 | message(STATUS "raylib_DEFINITIONS: ${raylib_DEFINITIONS}") 76 | endif() 77 | -------------------------------------------------------------------------------- /ext/raylib/lib/pkgconfig/raylib.pc: -------------------------------------------------------------------------------- 1 | prefix=C:/Program Files/Project 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/lib 4 | includedir=${prefix}/include 5 | 6 | Name: raylib 7 | Description: Simple and easy-to-use library to enjoy videogames programming 8 | URL: http://github.com/raysan5/raylib 9 | Version: 3.0.0 10 | Libs: -L${libdir} -lraylib 11 | Libs.private: -lwinmm -lgdi32 12 | Requires.private: 13 | Cflags: -I${includedir} 14 | -------------------------------------------------------------------------------- /ext/raylib/lib/raylib.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/ext/raylib/lib/raylib.lib -------------------------------------------------------------------------------- /ext/raylib/lib/raylib_static.lib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/ext/raylib/lib/raylib_static.lib -------------------------------------------------------------------------------- /ext/raymath/bridge/raymath_bridge.odin: -------------------------------------------------------------------------------- 1 | 2 | package raymath 3 | 4 | import raylib_types "../types" 5 | 6 | raymath_Funcs :: raylib_types.raymath_Funcs; 7 | 8 | // re-export everything from ./types for convienience 9 | RAYMATH_H :: raylib_types.RAYMATH_H; 10 | 11 | 12 | Float3 :: raylib_types.Float3; 13 | Float16 :: raylib_types.Float16; 14 | 15 | 16 | import _c "core:c" 17 | 18 | bridge_init :: proc(funcs: ^raymath_Funcs) { 19 | assert(funcs != nil); 20 | assert(funcs.clamp != nil); 21 | 22 | clamp = funcs.clamp; 23 | lerp = funcs.lerp; 24 | vector2_zero = funcs.vector2_zero; 25 | vector2_one = funcs.vector2_one; 26 | vector2_add = funcs.vector2_add; 27 | vector2_subtract = funcs.vector2_subtract; 28 | vector2_length = funcs.vector2_length; 29 | vector2_dot_product = funcs.vector2_dot_product; 30 | vector2_distance = funcs.vector2_distance; 31 | vector2_angle = funcs.vector2_angle; 32 | vector2_scale = funcs.vector2_scale; 33 | vector2_multiply_v = funcs.vector2_multiply_v; 34 | vector2_negate = funcs.vector2_negate; 35 | vector2_divide = funcs.vector2_divide; 36 | vector2_divide_v = funcs.vector2_divide_v; 37 | vector2_normalize = funcs.vector2_normalize; 38 | vector2_lerp = funcs.vector2_lerp; 39 | vector3_zero = funcs.vector3_zero; 40 | vector3_one = funcs.vector3_one; 41 | vector3_add = funcs.vector3_add; 42 | vector3_subtract = funcs.vector3_subtract; 43 | vector3_multiply = funcs.vector3_multiply; 44 | vector3_multiply_v = funcs.vector3_multiply_v; 45 | vector3_cross_product = funcs.vector3_cross_product; 46 | vector3_perpendicular = funcs.vector3_perpendicular; 47 | vector3_length = funcs.vector3_length; 48 | vector3_dot_product = funcs.vector3_dot_product; 49 | vector3_distance = funcs.vector3_distance; 50 | vector3_scale = funcs.vector3_scale; 51 | vector3_negate = funcs.vector3_negate; 52 | vector3_divide = funcs.vector3_divide; 53 | vector3_divide_v = funcs.vector3_divide_v; 54 | vector3_normalize = funcs.vector3_normalize; 55 | vector3_ortho_normalize = funcs.vector3_ortho_normalize; 56 | vector3_transform = funcs.vector3_transform; 57 | vector3_rotate_by_quaternion = funcs.vector3_rotate_by_quaternion; 58 | vector3_lerp = funcs.vector3_lerp; 59 | vector3_reflect = funcs.vector3_reflect; 60 | vector3_min = funcs.vector3_min; 61 | vector3_max = funcs.vector3_max; 62 | vector3_barycenter = funcs.vector3_barycenter; 63 | vector3_to_float_v = funcs.vector3_to_float_v; 64 | matrix_determinant = funcs.matrix_determinant; 65 | matrix_trace = funcs.matrix_trace; 66 | matrix_transpose = funcs.matrix_transpose; 67 | matrix_invert = funcs.matrix_invert; 68 | matrix_normalize = funcs.matrix_normalize; 69 | matrix_identity = funcs.matrix_identity; 70 | matrix_add = funcs.matrix_add; 71 | matrix_subtract = funcs.matrix_subtract; 72 | matrix_translate = funcs.matrix_translate; 73 | matrix_rotate = funcs.matrix_rotate; 74 | matrix_rotate_x = funcs.matrix_rotate_x; 75 | matrix_rotate_y = funcs.matrix_rotate_y; 76 | matrix_rotate_z = funcs.matrix_rotate_z; 77 | matrix_scale = funcs.matrix_scale; 78 | matrix_multiply = funcs.matrix_multiply; 79 | matrix_frustum = funcs.matrix_frustum; 80 | matrix_perspective = funcs.matrix_perspective; 81 | matrix_ortho = funcs.matrix_ortho; 82 | matrix_look_at = funcs.matrix_look_at; 83 | matrix_to_float_v = funcs.matrix_to_float_v; 84 | quaternion_identity = funcs.quaternion_identity; 85 | quaternion_length = funcs.quaternion_length; 86 | quaternion_normalize = funcs.quaternion_normalize; 87 | quaternion_invert = funcs.quaternion_invert; 88 | quaternion_multiply = funcs.quaternion_multiply; 89 | quaternion_lerp = funcs.quaternion_lerp; 90 | quaternion_nlerp = funcs.quaternion_nlerp; 91 | quaternion_slerp = funcs.quaternion_slerp; 92 | quaternion_from_vector3_to_vector3 = funcs.quaternion_from_vector3_to_vector3; 93 | quaternion_from_matrix = funcs.quaternion_from_matrix; 94 | quaternion_to_matrix = funcs.quaternion_to_matrix; 95 | quaternion_from_axis_angle = funcs.quaternion_from_axis_angle; 96 | quaternion_to_axis_angle = funcs.quaternion_to_axis_angle; 97 | quaternion_from_euler = funcs.quaternion_from_euler; 98 | quaternion_to_euler = funcs.quaternion_to_euler; 99 | quaternion_transform = funcs.quaternion_transform; 100 | } 101 | 102 | bridge_deinit :: proc() { 103 | } 104 | 105 | clamp : proc "c" ( 106 | value : _c.float, 107 | min : _c.float, 108 | max : _c.float 109 | ) -> _c.float; 110 | lerp : proc "c" ( 111 | start : _c.float, 112 | end : _c.float, 113 | amount : _c.float 114 | ) -> _c.float; 115 | vector2_zero : proc "c" () -> Vector2; 116 | vector2_one : proc "c" () -> Vector2; 117 | vector2_add : proc "c" ( 118 | v1 : Vector2, 119 | v2 : Vector2 120 | ) -> Vector2; 121 | vector2_subtract : proc "c" ( 122 | v1 : Vector2, 123 | v2 : Vector2 124 | ) -> Vector2; 125 | vector2_length : proc "c" (v : Vector2) -> _c.float; 126 | vector2_dot_product : proc "c" ( 127 | v1 : Vector2, 128 | v2 : Vector2 129 | ) -> _c.float; 130 | vector2_distance : proc "c" ( 131 | v1 : Vector2, 132 | v2 : Vector2 133 | ) -> _c.float; 134 | vector2_angle : proc "c" ( 135 | v1 : Vector2, 136 | v2 : Vector2 137 | ) -> _c.float; 138 | vector2_scale : proc "c" ( 139 | v : Vector2, 140 | scale : _c.float 141 | ) -> Vector2; 142 | vector2_multiply_v : proc "c" ( 143 | v1 : Vector2, 144 | v2 : Vector2 145 | ) -> Vector2; 146 | vector2_negate : proc "c" (v : Vector2) -> Vector2; 147 | vector2_divide : proc "c" ( 148 | v : Vector2, 149 | div : _c.float 150 | ) -> Vector2; 151 | vector2_divide_v : proc "c" ( 152 | v1 : Vector2, 153 | v2 : Vector2 154 | ) -> Vector2; 155 | vector2_normalize : proc "c" (v : Vector2) -> Vector2; 156 | vector2_lerp : proc "c" ( 157 | v1 : Vector2, 158 | v2 : Vector2, 159 | amount : _c.float 160 | ) -> Vector2; 161 | vector3_zero : proc "c" () -> Vector3; 162 | vector3_one : proc "c" () -> Vector3; 163 | vector3_add : proc "c" ( 164 | v1 : Vector3, 165 | v2 : Vector3 166 | ) -> Vector3; 167 | vector3_subtract : proc "c" ( 168 | v1 : Vector3, 169 | v2 : Vector3 170 | ) -> Vector3; 171 | vector3_multiply : proc "c" ( 172 | v : Vector3, 173 | scalar : _c.float 174 | ) -> Vector3; 175 | vector3_multiply_v : proc "c" ( 176 | v1 : Vector3, 177 | v2 : Vector3 178 | ) -> Vector3; 179 | vector3_cross_product : proc "c" ( 180 | v1 : Vector3, 181 | v2 : Vector3 182 | ) -> Vector3; 183 | vector3_perpendicular : proc "c" (v : Vector3) -> Vector3; 184 | vector3_length : proc "c" (v : Vector3) -> _c.float; 185 | vector3_dot_product : proc "c" ( 186 | v1 : Vector3, 187 | v2 : Vector3 188 | ) -> _c.float; 189 | vector3_distance : proc "c" ( 190 | v1 : Vector3, 191 | v2 : Vector3 192 | ) -> _c.float; 193 | vector3_scale : proc "c" ( 194 | v : Vector3, 195 | scale : _c.float 196 | ) -> Vector3; 197 | vector3_negate : proc "c" (v : Vector3) -> Vector3; 198 | vector3_divide : proc "c" ( 199 | v : Vector3, 200 | div : _c.float 201 | ) -> Vector3; 202 | vector3_divide_v : proc "c" ( 203 | v1 : Vector3, 204 | v2 : Vector3 205 | ) -> Vector3; 206 | vector3_normalize : proc "c" (v : Vector3) -> Vector3; 207 | vector3_ortho_normalize : proc "c" ( 208 | v1 : ^Vector3, 209 | v2 : ^Vector3 210 | ); 211 | vector3_transform : proc "c" ( 212 | v : Vector3, 213 | mat : Matrix 214 | ) -> Vector3; 215 | vector3_rotate_by_quaternion : proc "c" ( 216 | v : Vector3, 217 | q : Quaternion 218 | ) -> Vector3; 219 | vector3_lerp : proc "c" ( 220 | v1 : Vector3, 221 | v2 : Vector3, 222 | amount : _c.float 223 | ) -> Vector3; 224 | vector3_reflect : proc "c" ( 225 | v : Vector3, 226 | normal : Vector3 227 | ) -> Vector3; 228 | vector3_min : proc "c" ( 229 | v1 : Vector3, 230 | v2 : Vector3 231 | ) -> Vector3; 232 | vector3_max : proc "c" ( 233 | v1 : Vector3, 234 | v2 : Vector3 235 | ) -> Vector3; 236 | vector3_barycenter : proc "c" ( 237 | p : Vector3, 238 | a : Vector3, 239 | b : Vector3, 240 | c : Vector3 241 | ) -> Vector3; 242 | vector3_to_float_v : proc "c" (v : Vector3) -> Float3; 243 | matrix_determinant : proc "c" (mat : Matrix) -> _c.float; 244 | matrix_trace : proc "c" (mat : Matrix) -> _c.float; 245 | matrix_transpose : proc "c" (mat : Matrix) -> Matrix; 246 | matrix_invert : proc "c" (mat : Matrix) -> Matrix; 247 | matrix_normalize : proc "c" (mat : Matrix) -> Matrix; 248 | matrix_identity : proc "c" () -> Matrix; 249 | matrix_add : proc "c" ( 250 | left : Matrix, 251 | right : Matrix 252 | ) -> Matrix; 253 | matrix_subtract : proc "c" ( 254 | left : Matrix, 255 | right : Matrix 256 | ) -> Matrix; 257 | matrix_translate : proc "c" ( 258 | x : _c.float, 259 | y : _c.float, 260 | z : _c.float 261 | ) -> Matrix; 262 | matrix_rotate : proc "c" ( 263 | axis : Vector3, 264 | angle : _c.float 265 | ) -> Matrix; 266 | matrix_rotate_x : proc "c" (angle : _c.float) -> Matrix; 267 | matrix_rotate_y : proc "c" (angle : _c.float) -> Matrix; 268 | matrix_rotate_z : proc "c" (angle : _c.float) -> Matrix; 269 | matrix_scale : proc "c" ( 270 | x : _c.float, 271 | y : _c.float, 272 | z : _c.float 273 | ) -> Matrix; 274 | matrix_multiply : proc "c" ( 275 | left : Matrix, 276 | right : Matrix 277 | ) -> Matrix; 278 | matrix_frustum : proc "c" ( 279 | left : _c.double, 280 | right : _c.double, 281 | bottom : _c.double, 282 | top : _c.double, 283 | near : _c.double, 284 | far : _c.double 285 | ) -> Matrix; 286 | matrix_perspective : proc "c" ( 287 | fovy : _c.double, 288 | aspect : _c.double, 289 | near : _c.double, 290 | far : _c.double 291 | ) -> Matrix; 292 | matrix_ortho : proc "c" ( 293 | left : _c.double, 294 | right : _c.double, 295 | bottom : _c.double, 296 | top : _c.double, 297 | near : _c.double, 298 | far : _c.double 299 | ) -> Matrix; 300 | matrix_look_at : proc "c" ( 301 | eye : Vector3, 302 | target : Vector3, 303 | up : Vector3 304 | ) -> Matrix; 305 | matrix_to_float_v : proc "c" (mat : Matrix) -> Float16; 306 | quaternion_identity : proc "c" () -> Quaternion; 307 | quaternion_length : proc "c" (q : Quaternion) -> _c.float; 308 | quaternion_normalize : proc "c" (q : Quaternion) -> Quaternion; 309 | quaternion_invert : proc "c" (q : Quaternion) -> Quaternion; 310 | quaternion_multiply : proc "c" ( 311 | q1 : Quaternion, 312 | q2 : Quaternion 313 | ) -> Quaternion; 314 | quaternion_lerp : proc "c" ( 315 | q1 : Quaternion, 316 | q2 : Quaternion, 317 | amount : _c.float 318 | ) -> Quaternion; 319 | quaternion_nlerp : proc "c" ( 320 | q1 : Quaternion, 321 | q2 : Quaternion, 322 | amount : _c.float 323 | ) -> Quaternion; 324 | quaternion_slerp : proc "c" ( 325 | q1 : Quaternion, 326 | q2 : Quaternion, 327 | amount : _c.float 328 | ) -> Quaternion; 329 | quaternion_from_vector3_to_vector3 : proc "c" ( 330 | from : Vector3, 331 | to : Vector3 332 | ) -> Quaternion; 333 | quaternion_from_matrix : proc "c" (mat : Matrix) -> Quaternion; 334 | quaternion_to_matrix : proc "c" (q : Quaternion) -> Matrix; 335 | quaternion_from_axis_angle : proc "c" ( 336 | axis : Vector3, 337 | angle : _c.float 338 | ) -> Quaternion; 339 | quaternion_to_axis_angle : proc "c" ( 340 | q : Quaternion, 341 | out_axis : ^Vector3, 342 | out_angle : ^_c.float 343 | ); 344 | quaternion_from_euler : proc "c" ( 345 | roll : _c.float, 346 | pitch : _c.float, 347 | yaw : _c.float 348 | ) -> Quaternion; 349 | quaternion_to_euler : proc "c" (q : Quaternion) -> Vector3; 350 | quaternion_transform : proc "c" ( 351 | q : Quaternion, 352 | mat : Matrix 353 | ) -> Quaternion; 354 | -------------------------------------------------------------------------------- /ext/raymath/build.bat: -------------------------------------------------------------------------------- 1 | @setlocal 2 | @set OUTPUT_FILE=..\lib\raymath.lib 3 | @echo. 4 | @echo Compiling... 5 | @echo. 6 | cl /nologo /I. /I ..\..\..\raylib\src /c raymath.c && lib /nologo raymath.obj /out:"%OUTPUT_FILE%" 7 | @echo. 8 | @echo Created "%OUTPUT_FILE%" 9 | 10 | -------------------------------------------------------------------------------- /ext/raymath/generate_raymath_header.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | 3 | this_dir = os.path.realpath(os.path.dirname(__file__)) 4 | skipping = False 5 | 6 | with open(os.path.join(this_dir, "raymath.h")) as f: 7 | with open(os.path.join(this_dir, "raymath-preprocessed.h"), "w") as out: 8 | for line in f: 9 | if line == "#include ": 10 | continue 11 | if line == "#include ": 12 | continue 13 | if line == "#if defined(RAYMATH_STANDALONE)\n": 14 | skipping = True 15 | elif skipping and line == "#endif\n": 16 | skipping = False 17 | else: 18 | if not skipping: 19 | out.write(line) 20 | 21 | -------------------------------------------------------------------------------- /ext/raymath/raymath.c: -------------------------------------------------------------------------------- 1 | #define RAYMATH_IMPLEMENTATION 2 | 3 | #include "raymath.h" 4 | -------------------------------------------------------------------------------- /ext/raymath/types/raymath_types.odin: -------------------------------------------------------------------------------- 1 | // 2 | // generated by bindgen (https://github.com/Breush/odin-binding-generator) 3 | // 4 | 5 | package raymath_types 6 | 7 | import _c "core:c" 8 | 9 | import "../../../raylib/types" 10 | RAYMATH_H :: 1; 11 | 12 | 13 | Float3 :: struct { 14 | v : [3]_c.float, 15 | }; 16 | 17 | Float16 :: struct { 18 | v : [16]_c.float, 19 | }; 20 | 21 | raymath_Funcs :: struct { 22 | clamp : proc "c" ( 23 | value : _c.float, 24 | min : _c.float, 25 | max : _c.float 26 | ) -> _c.float, 27 | lerp : proc "c" ( 28 | start : _c.float, 29 | end : _c.float, 30 | amount : _c.float 31 | ) -> _c.float, 32 | vector2_zero : proc "c" () -> Vector2, 33 | vector2_one : proc "c" () -> Vector2, 34 | vector2_add : proc "c" ( 35 | v1 : Vector2, 36 | v2 : Vector2 37 | ) -> Vector2, 38 | vector2_subtract : proc "c" ( 39 | v1 : Vector2, 40 | v2 : Vector2 41 | ) -> Vector2, 42 | vector2_length : proc "c" (v : Vector2) -> _c.float, 43 | vector2_dot_product : proc "c" ( 44 | v1 : Vector2, 45 | v2 : Vector2 46 | ) -> _c.float, 47 | vector2_distance : proc "c" ( 48 | v1 : Vector2, 49 | v2 : Vector2 50 | ) -> _c.float, 51 | vector2_angle : proc "c" ( 52 | v1 : Vector2, 53 | v2 : Vector2 54 | ) -> _c.float, 55 | vector2_scale : proc "c" ( 56 | v : Vector2, 57 | scale : _c.float 58 | ) -> Vector2, 59 | vector2_multiply_v : proc "c" ( 60 | v1 : Vector2, 61 | v2 : Vector2 62 | ) -> Vector2, 63 | vector2_negate : proc "c" (v : Vector2) -> Vector2, 64 | vector2_divide : proc "c" ( 65 | v : Vector2, 66 | div : _c.float 67 | ) -> Vector2, 68 | vector2_divide_v : proc "c" ( 69 | v1 : Vector2, 70 | v2 : Vector2 71 | ) -> Vector2, 72 | vector2_normalize : proc "c" (v : Vector2) -> Vector2, 73 | vector2_lerp : proc "c" ( 74 | v1 : Vector2, 75 | v2 : Vector2, 76 | amount : _c.float 77 | ) -> Vector2, 78 | vector3_zero : proc "c" () -> Vector3, 79 | vector3_one : proc "c" () -> Vector3, 80 | vector3_add : proc "c" ( 81 | v1 : Vector3, 82 | v2 : Vector3 83 | ) -> Vector3, 84 | vector3_subtract : proc "c" ( 85 | v1 : Vector3, 86 | v2 : Vector3 87 | ) -> Vector3, 88 | vector3_multiply : proc "c" ( 89 | v : Vector3, 90 | scalar : _c.float 91 | ) -> Vector3, 92 | vector3_multiply_v : proc "c" ( 93 | v1 : Vector3, 94 | v2 : Vector3 95 | ) -> Vector3, 96 | vector3_cross_product : proc "c" ( 97 | v1 : Vector3, 98 | v2 : Vector3 99 | ) -> Vector3, 100 | vector3_perpendicular : proc "c" (v : Vector3) -> Vector3, 101 | vector3_length : proc "c" (v : Vector3) -> _c.float, 102 | vector3_dot_product : proc "c" ( 103 | v1 : Vector3, 104 | v2 : Vector3 105 | ) -> _c.float, 106 | vector3_distance : proc "c" ( 107 | v1 : Vector3, 108 | v2 : Vector3 109 | ) -> _c.float, 110 | vector3_scale : proc "c" ( 111 | v : Vector3, 112 | scale : _c.float 113 | ) -> Vector3, 114 | vector3_negate : proc "c" (v : Vector3) -> Vector3, 115 | vector3_divide : proc "c" ( 116 | v : Vector3, 117 | div : _c.float 118 | ) -> Vector3, 119 | vector3_divide_v : proc "c" ( 120 | v1 : Vector3, 121 | v2 : Vector3 122 | ) -> Vector3, 123 | vector3_normalize : proc "c" (v : Vector3) -> Vector3, 124 | vector3_ortho_normalize : proc "c" ( 125 | v1 : ^Vector3, 126 | v2 : ^Vector3 127 | ), 128 | vector3_transform : proc "c" ( 129 | v : Vector3, 130 | mat : Matrix 131 | ) -> Vector3, 132 | vector3_rotate_by_quaternion : proc "c" ( 133 | v : Vector3, 134 | q : Quaternion 135 | ) -> Vector3, 136 | vector3_lerp : proc "c" ( 137 | v1 : Vector3, 138 | v2 : Vector3, 139 | amount : _c.float 140 | ) -> Vector3, 141 | vector3_reflect : proc "c" ( 142 | v : Vector3, 143 | normal : Vector3 144 | ) -> Vector3, 145 | vector3_min : proc "c" ( 146 | v1 : Vector3, 147 | v2 : Vector3 148 | ) -> Vector3, 149 | vector3_max : proc "c" ( 150 | v1 : Vector3, 151 | v2 : Vector3 152 | ) -> Vector3, 153 | vector3_barycenter : proc "c" ( 154 | p : Vector3, 155 | a : Vector3, 156 | b : Vector3, 157 | c : Vector3 158 | ) -> Vector3, 159 | vector3_to_float_v : proc "c" (v : Vector3) -> Float3, 160 | matrix_determinant : proc "c" (mat : Matrix) -> _c.float, 161 | matrix_trace : proc "c" (mat : Matrix) -> _c.float, 162 | matrix_transpose : proc "c" (mat : Matrix) -> Matrix, 163 | matrix_invert : proc "c" (mat : Matrix) -> Matrix, 164 | matrix_normalize : proc "c" (mat : Matrix) -> Matrix, 165 | matrix_identity : proc "c" () -> Matrix, 166 | matrix_add : proc "c" ( 167 | left : Matrix, 168 | right : Matrix 169 | ) -> Matrix, 170 | matrix_subtract : proc "c" ( 171 | left : Matrix, 172 | right : Matrix 173 | ) -> Matrix, 174 | matrix_translate : proc "c" ( 175 | x : _c.float, 176 | y : _c.float, 177 | z : _c.float 178 | ) -> Matrix, 179 | matrix_rotate : proc "c" ( 180 | axis : Vector3, 181 | angle : _c.float 182 | ) -> Matrix, 183 | matrix_rotate_x : proc "c" (angle : _c.float) -> Matrix, 184 | matrix_rotate_y : proc "c" (angle : _c.float) -> Matrix, 185 | matrix_rotate_z : proc "c" (angle : _c.float) -> Matrix, 186 | matrix_scale : proc "c" ( 187 | x : _c.float, 188 | y : _c.float, 189 | z : _c.float 190 | ) -> Matrix, 191 | matrix_multiply : proc "c" ( 192 | left : Matrix, 193 | right : Matrix 194 | ) -> Matrix, 195 | matrix_frustum : proc "c" ( 196 | left : _c.double, 197 | right : _c.double, 198 | bottom : _c.double, 199 | top : _c.double, 200 | near : _c.double, 201 | far : _c.double 202 | ) -> Matrix, 203 | matrix_perspective : proc "c" ( 204 | fovy : _c.double, 205 | aspect : _c.double, 206 | near : _c.double, 207 | far : _c.double 208 | ) -> Matrix, 209 | matrix_ortho : proc "c" ( 210 | left : _c.double, 211 | right : _c.double, 212 | bottom : _c.double, 213 | top : _c.double, 214 | near : _c.double, 215 | far : _c.double 216 | ) -> Matrix, 217 | matrix_look_at : proc "c" ( 218 | eye : Vector3, 219 | target : Vector3, 220 | up : Vector3 221 | ) -> Matrix, 222 | matrix_to_float_v : proc "c" (mat : Matrix) -> Float16, 223 | quaternion_identity : proc "c" () -> Quaternion, 224 | quaternion_length : proc "c" (q : Quaternion) -> _c.float, 225 | quaternion_normalize : proc "c" (q : Quaternion) -> Quaternion, 226 | quaternion_invert : proc "c" (q : Quaternion) -> Quaternion, 227 | quaternion_multiply : proc "c" ( 228 | q1 : Quaternion, 229 | q2 : Quaternion 230 | ) -> Quaternion, 231 | quaternion_lerp : proc "c" ( 232 | q1 : Quaternion, 233 | q2 : Quaternion, 234 | amount : _c.float 235 | ) -> Quaternion, 236 | quaternion_nlerp : proc "c" ( 237 | q1 : Quaternion, 238 | q2 : Quaternion, 239 | amount : _c.float 240 | ) -> Quaternion, 241 | quaternion_slerp : proc "c" ( 242 | q1 : Quaternion, 243 | q2 : Quaternion, 244 | amount : _c.float 245 | ) -> Quaternion, 246 | quaternion_from_vector3_to_vector3 : proc "c" ( 247 | from : Vector3, 248 | to : Vector3 249 | ) -> Quaternion, 250 | quaternion_from_matrix : proc "c" (mat : Matrix) -> Quaternion, 251 | quaternion_to_matrix : proc "c" (q : Quaternion) -> Matrix, 252 | quaternion_from_axis_angle : proc "c" ( 253 | axis : Vector3, 254 | angle : _c.float 255 | ) -> Quaternion, 256 | quaternion_to_axis_angle : proc "c" ( 257 | q : Quaternion, 258 | out_axis : ^Vector3, 259 | out_angle : ^_c.float 260 | ), 261 | quaternion_from_euler : proc "c" ( 262 | roll : _c.float, 263 | pitch : _c.float, 264 | yaw : _c.float 265 | ) -> Quaternion, 266 | quaternion_to_euler : proc "c" (q : Quaternion) -> Vector3, 267 | quaternion_transform : proc "c" ( 268 | q : Quaternion, 269 | mat : Matrix 270 | ) -> Quaternion, 271 | } 272 | 273 | -------------------------------------------------------------------------------- /generator/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This directory contains a modified version of [odin-binding-generator](https://github.com/Breush/odin-binding-generator/). 4 | -------------------------------------------------------------------------------- /generator/bindgen/c-parser-evaluate.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | import "core:fmt" 4 | import "core:strconv" 5 | 6 | // Evaluates an expression to a i64, without checking. 7 | evaluate_i64 :: proc(data : ^ParserData) -> i64 { 8 | ok : bool; 9 | value : LiteralValue; 10 | 11 | value, ok = evaluate(data); 12 | return value.(i64); 13 | } 14 | 15 | // Evaluate an expression, returns whether it succeeded. 16 | evaluate :: proc(data : ^ParserData) -> (LiteralValue, bool) { 17 | return evaluate_level_4(data); 18 | } 19 | 20 | // @note Evaluate levels numbers are based on 21 | // https://en.cppreference.com/w/c/language/operator_precedence. 22 | 23 | // Additive level. 24 | evaluate_level_4 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { 25 | value, ok = evaluate_level_3(data); 26 | if !ok do return; 27 | 28 | token := peek_token(data); 29 | if token == "+" { 30 | v : LiteralValue; 31 | eat_token(data); 32 | v, ok = evaluate_level_4(data); 33 | if is_i64(v) do value = value.(i64) + v.(i64); 34 | else if is_f64(v) do value = value.(f64) + v.(f64); 35 | } 36 | else if token == "-" { 37 | v : LiteralValue; 38 | eat_token(data); 39 | v, ok = evaluate_level_4(data); 40 | if is_i64(v) do value = value.(i64) - v.(i64); 41 | else if is_f64(v) do value = value.(f64) - v.(f64); 42 | } 43 | 44 | return; 45 | } 46 | 47 | // Multiplicative level. 48 | evaluate_level_3 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { 49 | value, ok = evaluate_level_2(data); 50 | if !ok do return; 51 | 52 | token := peek_token(data); 53 | if token == "*" { 54 | v : LiteralValue; 55 | eat_token(data); 56 | v, ok = evaluate_level_3(data); 57 | if is_i64(v) do value = value.(i64) * v.(i64); 58 | else if is_f64(v) do value = value.(f64) * v.(f64); 59 | } 60 | else if token == "/" { 61 | v : LiteralValue; 62 | eat_token(data); 63 | v, ok = evaluate_level_3(data); 64 | if is_i64(v) do value = value.(i64) / v.(i64); 65 | else if is_f64(v) do value = value.(f64) / v.(f64); 66 | } 67 | 68 | return; 69 | } 70 | 71 | // Prefix level. 72 | evaluate_level_2 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { 73 | token := peek_token(data); 74 | 75 | // Bitwise not 76 | if token == "~" { 77 | check_and_eat_token(data, "~"); 78 | value, ok = evaluate_level_2(data); 79 | value = ~value.(i64); 80 | } 81 | else { 82 | // @note Should call evaluate_level_1, but we don't have that because we do not dereferenciation. 83 | value, ok = evaluate_level_0(data); 84 | } 85 | 86 | return; 87 | } 88 | 89 | // Does not try to compose with arithmetics, it just evaluates one single expression. 90 | evaluate_level_0 :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { 91 | ok = true; 92 | value = 0; 93 | token := peek_token(data); 94 | 95 | // Parentheses 96 | if token == "(" { 97 | value, ok = evaluate_parentheses(data); 98 | } 99 | // Number literal 100 | else if (token[0] == '-') || (token[0] >= '0' && token[0] <= '9') { 101 | value = evaluate_number_literal(data); 102 | } 103 | // String literal 104 | else if token[0] == '"' { 105 | value = evaluate_string_literal(data); 106 | } 107 | // Function-like 108 | else if token == "sizeof" { 109 | value = evaluate_sizeof(data); 110 | } 111 | // Knowned literal 112 | else if token in data.knownedLiterals { 113 | value = evaluate_knowned_literal(data); 114 | } 115 | // Custom expression 116 | else if token in data.options.customExpressionHandlers { 117 | value = data.options.customExpressionHandlers[token](data); 118 | } 119 | else { 120 | line, _ := get_line_column(data); 121 | print_warning("Unknown token ", token, " (at line ", line, ") for expression evaluation."); 122 | ok = false; 123 | } 124 | 125 | return; 126 | } 127 | 128 | evaluate_sizeof :: proc(data : ^ParserData) -> LiteralValue { 129 | print_warning("Using 'sizeof()'. Currently not able to precompute that. Please check generated code."); 130 | 131 | check_and_eat_token(data, "sizeof"); 132 | check_and_eat_token(data, "("); 133 | for data.bytes[data.offset] != ')' { 134 | data.offset += 1; 135 | } 136 | check_and_eat_token(data, ")"); 137 | return 1; 138 | } 139 | 140 | evaluate_parentheses :: proc(data : ^ParserData) -> (value : LiteralValue, ok : bool) { 141 | check_and_eat_token(data, "("); 142 | 143 | // Cast to int (via "(int)" syntax) 144 | token := peek_token(data); 145 | if token == "int" { 146 | check_and_eat_token(data, "int"); 147 | check_and_eat_token(data, ")"); 148 | value, ok = evaluate(data); 149 | return; 150 | } 151 | 152 | value, ok = evaluate(data); 153 | check_and_eat_token(data, ")"); 154 | return; 155 | } 156 | 157 | evaluate_number_literal :: proc(data : ^ParserData) -> (value_: LiteralValue) { 158 | value := value_; 159 | 160 | token := parse_any(data); 161 | 162 | // Check if any point or scientific notation in number 163 | foundPointOrExp := false; 164 | for c in token { 165 | if c == '.' || c == 'e' || c == 'E' { 166 | foundPointOrExp = true; 167 | break; 168 | } 169 | } 170 | 171 | isHexadecimal := len(token) >= 2 && token[:1] == "0x"; 172 | 173 | // Floating point 174 | if !isHexadecimal && (foundPointOrExp || token[len(token)-1] == 'f') { 175 | value, _ = strconv.parse_f64(token); 176 | } 177 | // Integer 178 | else { 179 | value, _ = strconv.parse_i64(token); 180 | } 181 | 182 | return value; 183 | } 184 | 185 | evaluate_string_literal :: proc(data : ^ParserData) -> string { 186 | token := parse_any(data); 187 | return token; 188 | } 189 | 190 | evaluate_knowned_literal :: proc(data : ^ParserData) -> LiteralValue { 191 | token := parse_any(data); 192 | return data.knownedLiterals[token]; 193 | } 194 | 195 | is_i64 :: proc(value : LiteralValue) -> (ok : bool) { 196 | v : i64; 197 | v, ok = value.(i64); 198 | return ok; 199 | } 200 | 201 | is_f64 :: proc(value : LiteralValue) -> (ok : bool) { 202 | v : f64; 203 | v, ok = value.(f64); 204 | return ok; 205 | } 206 | -------------------------------------------------------------------------------- /generator/bindgen/c-parser-helpers.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | import "core:os" 4 | import "core:fmt" 5 | import "core:strings" 6 | import "core:strconv" 7 | 8 | // Extract from start (included) to end (excluded) offsets 9 | extract_string :: proc(data : ^ParserData, startOffset : u32, endOffset : u32) -> string { 10 | return strings.string_from_ptr(&data.bytes[startOffset], cast(int) (endOffset - startOffset)); 11 | } 12 | 13 | // Peek the end offset of the next token 14 | peek_token_end :: proc(data : ^ParserData) -> u32 { 15 | offset : u32; 16 | 17 | for true { 18 | eat_whitespaces_and_comments(data); 19 | if data.offset >= data.bytesLength { 20 | return data.bytesLength; 21 | } 22 | offset = data.offset; 23 | 24 | // Identifier 25 | if (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || 26 | (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || 27 | (data.bytes[offset] == '_') { 28 | offset += 1; 29 | for (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'z') || 30 | (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'Z') || 31 | (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || 32 | (data.bytes[offset] == '_') { 33 | offset += 1; 34 | } 35 | } 36 | if offset != data.offset { 37 | // Nothing to do: we found an identifier 38 | } 39 | // Number literal 40 | else if (data.bytes[offset] == '-') || 41 | (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { 42 | offset += 1; 43 | // Hexademical literal 44 | if data.bytes[offset - 1] == '0' && data.bytes[offset] == 'x' { 45 | offset += 1; 46 | for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || 47 | (data.bytes[offset] >= 'a' && data.bytes[offset] <= 'f') || 48 | (data.bytes[offset] >= 'A' && data.bytes[offset] <= 'F') { 49 | offset += 1; 50 | } 51 | } 52 | // Basic number literal 53 | else { 54 | for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') || 55 | data.bytes[offset] == '.' { 56 | offset += 1; 57 | } 58 | 59 | if (data.bytes[offset] == 'e' || data.bytes[offset] == 'E') { 60 | offset += 1; 61 | if data.bytes[offset] == '-' { 62 | offset += 1; 63 | } 64 | } 65 | 66 | for (data.bytes[offset] >= '0' && data.bytes[offset] <= '9') { 67 | offset += 1; 68 | } 69 | } 70 | 71 | // Number suffix? 72 | for (data.bytes[offset] == 'u' || data.bytes[offset] == 'U') || 73 | (data.bytes[offset] == 'l' || data.bytes[offset] == 'L') || 74 | (data.bytes[offset] == 'f') { 75 | offset += 1; 76 | } 77 | } 78 | // String literal 79 | else if data.bytes[offset] == '"' { 80 | offset += 1; 81 | for data.bytes[offset-1] == '\\' || data.bytes[offset] != '"' { 82 | offset += 1; 83 | } 84 | offset += 1; 85 | } 86 | // Single character 87 | else { 88 | offset += 1; 89 | } 90 | 91 | token := extract_string(data, data.offset, offset); 92 | 93 | // Ignore __attribute__ 94 | if token == "__attribute__" { 95 | print_warning("__attribute__ is ignored."); 96 | 97 | for data.bytes[offset] != '(' { 98 | offset += 1; 99 | } 100 | 101 | parenthesesCount := 1; 102 | for true { 103 | offset += 1; 104 | if data.bytes[offset] == '(' do parenthesesCount += 1; 105 | else if data.bytes[offset] == ')' do parenthesesCount -= 1; 106 | if parenthesesCount == 0 do break; 107 | } 108 | offset += 1; 109 | 110 | data.offset = offset; 111 | } 112 | 113 | // Ignore certain keywords 114 | else if (token == "inline" || token == "__inline" || token == "static" 115 | || token == "restrict" || token == "__restrict" 116 | || token == "volatile" 117 | || token == "__extension__") { 118 | data.offset = offset; 119 | } 120 | 121 | // Ignore ignored tokens ;) 122 | else { 123 | for ignoredToken in data.options.ignoredTokens { 124 | if token == ignoredToken { 125 | data.offset = offset; 126 | break; 127 | } 128 | } 129 | } 130 | 131 | if data.offset != offset { 132 | break; 133 | } 134 | } 135 | 136 | return offset; 137 | } 138 | 139 | // Peek the next token (just eating whitespaces and comment) 140 | peek_token :: proc(data : ^ParserData) -> string { 141 | tokenEnd := peek_token_end(data); 142 | if tokenEnd == data.bytesLength { 143 | return "EOF"; 144 | } 145 | return extract_string(data, data.offset, tokenEnd); 146 | } 147 | 148 | // Find the end of the define directive (understanding endline backslashes) 149 | // @note Tricky cases like comments hiding a backslash effect are not handled. 150 | peek_define_end :: proc(data : ^ParserData) -> u32 { 151 | defineEndOffset := data.offset; 152 | for data.bytes[defineEndOffset-1] == '\\' || data.bytes[defineEndOffset] != '\n' { 153 | defineEndOffset += 1; 154 | } 155 | return defineEndOffset; 156 | } 157 | 158 | eat_comment :: proc(data : ^ParserData) { 159 | if data.offset >= data.bytesLength || data.bytes[data.offset] != '/' { 160 | return; 161 | } 162 | 163 | // Line comment 164 | if data.bytes[data.offset + 1] == '/' { 165 | eat_line(data); 166 | } 167 | // Range comment 168 | else if data.bytes[data.offset + 1] == '*' { 169 | data.offset += 2; 170 | for data.bytes[data.offset] != '*' || data.bytes[data.offset + 1] != '/' { 171 | data.offset += 1; 172 | } 173 | data.offset += 2; 174 | } 175 | } 176 | 177 | // Eat whitespaces 178 | eat_whitespaces :: proc(data : ^ParserData) { 179 | // Effective whitespace 180 | for data.offset < data.bytesLength && 181 | (data.bytes[data.offset] == ' ' || data.bytes[data.offset] == '\t' || 182 | data.bytes[data.offset] == '\r' || data.bytes[data.offset] == '\n') { 183 | if data.bytes[data.offset] == '\n' && data.bytes[data.offset] != '\\' { 184 | data.foundFullReturn = true; 185 | } 186 | data.offset += 1; 187 | } 188 | } 189 | 190 | // Removes whitespaces and comments 191 | eat_whitespaces_and_comments :: proc(data : ^ParserData) { 192 | startOffset : u32 = 0xFFFFFFFF; 193 | for startOffset != data.offset { 194 | startOffset = data.offset; 195 | eat_whitespaces(data); 196 | eat_comment(data); 197 | } 198 | } 199 | 200 | // Eat full line 201 | eat_line :: proc(data : ^ParserData) { 202 | for ; data.bytes[data.offset] != '\n'; data.offset += 1 { 203 | } 204 | } 205 | 206 | // Eat a line, and repeat if it ends with a backslash 207 | eat_define_lines :: proc(data : ^ParserData) { 208 | for data.bytes[data.offset-1] == '\\' || data.bytes[data.offset] != '\n' { 209 | data.offset += 1; 210 | } 211 | } 212 | 213 | // Eat next token 214 | eat_token :: proc(data : ^ParserData) { 215 | data.offset = peek_token_end(data); 216 | } 217 | 218 | // Eat next token 219 | check_and_eat_token :: proc(data : ^ParserData, expectedToken : string, loc := #caller_location) { 220 | token := peek_token(data); 221 | if token != expectedToken { 222 | print_error(data, loc, "Expected ", expectedToken, " but found ", token, "."); 223 | } 224 | data.offset += cast(u32) len(token); 225 | } 226 | 227 | // Check whether the next token is outside #define range 228 | is_define_end :: proc(data : ^ParserData) -> bool { 229 | defineEnd := peek_define_end(data); 230 | tokenEnd := peek_token_end(data); 231 | 232 | return (defineEnd < tokenEnd); 233 | } 234 | 235 | // Check if the current #define is a macro definition 236 | is_define_macro :: proc(data : ^ParserData) -> bool { 237 | 238 | startOffset := data.offset; 239 | defer data.offset = startOffset; 240 | 241 | token := parse_any(data); 242 | if token != "(" do return false; 243 | 244 | 245 | // Find the other parenthesis 246 | parenthesesCount := 1; 247 | for parenthesesCount != 0 { 248 | token = parse_any(data); 249 | if token == "(" do parenthesesCount += 1; 250 | else if token == ")" do parenthesesCount -= 1; 251 | } 252 | 253 | // Its a macro if after the parentheses, it's not the end 254 | return !is_define_end(data); 255 | } 256 | 257 | // @note Very slow function to get line number, 258 | // use only for errors. 259 | // @todo Well, this does not seem to work properly, UTF-8 problem? 260 | get_line_column :: proc(data : ^ParserData) -> (u32, u32) { 261 | line : u32 = 1; 262 | column : u32 = 0; 263 | for i : u32 = 0; i < data.offset; i += 1 { 264 | if data.bytes[i] == '\n' { 265 | column = 0; 266 | line += 1; 267 | } 268 | else { 269 | column += 1; 270 | } 271 | } 272 | return line, column; 273 | } 274 | -------------------------------------------------------------------------------- /generator/bindgen/c-parser-nodes.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | DefineNode :: struct { 4 | name : string, 5 | value : LiteralValue, 6 | is_variable : bool, 7 | } 8 | 9 | StructDefinitionNode :: struct { 10 | name : string, 11 | members : [dynamic]StructOrUnionMember, 12 | forwardDeclared : bool, 13 | } 14 | 15 | UnionDefinitionNode :: struct { 16 | name : string, 17 | members : [dynamic]StructOrUnionMember, 18 | } 19 | 20 | EnumDefinitionNode :: struct { 21 | name : string, 22 | members : [dynamic]EnumMember, 23 | } 24 | 25 | FunctionDeclarationNode :: struct { 26 | name : string, 27 | returnType : Type, 28 | parameters : [dynamic]FunctionParameter, 29 | } 30 | 31 | TypedefNode :: struct { 32 | name : string, 33 | sourceType : Type, 34 | } 35 | 36 | Nodes :: struct { 37 | defines : [dynamic]DefineNode, 38 | enumDefinitions : [dynamic]EnumDefinitionNode, 39 | unionDefinitions : [dynamic]UnionDefinitionNode, 40 | structDefinitions : [dynamic]StructDefinitionNode, 41 | functionDeclarations : [dynamic]FunctionDeclarationNode, 42 | typedefs : [dynamic]TypedefNode, 43 | } 44 | 45 | LiteralValue :: union { 46 | i64, 47 | f64, 48 | string, 49 | } 50 | 51 | Type :: union { 52 | BuiltinType, 53 | StandardType, 54 | PointerType, 55 | IdentifierType, 56 | FunctionPointerType, 57 | } 58 | 59 | BuiltinType :: enum { 60 | Unknown, 61 | Void, 62 | Int, 63 | UInt, 64 | LongInt, 65 | ULongInt, 66 | LongLongInt, 67 | ULongLongInt, 68 | ShortInt, 69 | UShortInt, 70 | Char, 71 | SChar, 72 | UChar, 73 | Float, 74 | Double, 75 | LongDouble, 76 | } 77 | 78 | // Not defined by C language but in 79 | StandardType :: enum { 80 | Unknown, 81 | Int8, 82 | Int16, 83 | Int32, 84 | Int64, 85 | UInt8, 86 | UInt16, 87 | UInt32, 88 | UInt64, 89 | Size, 90 | SSize, 91 | PtrDiff, 92 | UIntPtr, 93 | IntPtr, 94 | Bool, 95 | } 96 | 97 | PointerType :: struct { 98 | type : ^Type, // Pointer is there to prevent definition cycle. Null means void. 99 | } 100 | 101 | IdentifierType :: struct { 102 | name : string, 103 | } 104 | 105 | FunctionPointerType :: struct { 106 | name : string, 107 | returnType : ^Type, // Pointer is there to prevent definition cycle. Null means void. 108 | parameters : [dynamic]FunctionParameter, 109 | } 110 | 111 | EnumMember :: struct { 112 | name : string, 113 | value : i64, 114 | hasValue : bool, 115 | } 116 | 117 | StructOrUnionMember :: struct { 118 | name : string, 119 | type : Type, 120 | dimensions : [dynamic]u64, // Array dimensions 121 | } 122 | 123 | FunctionParameter :: struct { 124 | name : string, 125 | type : Type, 126 | dimensions : [dynamic]u64, // Array dimensions 127 | } 128 | -------------------------------------------------------------------------------- /generator/bindgen/errors.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | import "core:fmt" 4 | import "core:os" 5 | 6 | seenWarnings : map[string]bool; 7 | 8 | print_warning :: proc(args : ..any) { 9 | message := fmt.tprint(..args); 10 | 11 | if !seenWarnings[message] { 12 | fmt.eprint("[bindgen] Warning: ", message, "\n"); 13 | seenWarnings[message] = true; 14 | } 15 | } 16 | 17 | print_error :: proc(data : ^ParserData, loc := #caller_location, args : ..any) { 18 | message := fmt.tprint(..args); 19 | 20 | min : u32 = 0; 21 | for i := data.offset - 1; i > 0; i -= 1 { 22 | if data.bytes[i] == '\n' { 23 | min = i + 1; 24 | break; 25 | } 26 | } 27 | 28 | max := min + 200; 29 | for i := min + 1; i < max; i += 1 { 30 | if data.bytes[i] == '\n' { 31 | max = i; 32 | break; 33 | } 34 | } 35 | 36 | line, _ := get_line_column(data); 37 | 38 | fmt.eprint("[bindgen] Error: ", message, "\n"); 39 | fmt.eprint("[bindgen] ... from ", loc.procedure, "\n"); 40 | fmt.eprint("[bindgen] ... at line ", line, " within this context:\n"); 41 | fmt.eprint("> ", extract_string(data, min, max), "\n"); 42 | 43 | os.exit(1); 44 | } 45 | -------------------------------------------------------------------------------- /generator/bindgen/generator-clean.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | import "core:fmt" 4 | 5 | // Prevent keywords clashes and other tricky cases 6 | clean_identifier :: proc(name_: string) -> string { 7 | name := name_; 8 | 9 | if name == "" { 10 | return name; 11 | } 12 | 13 | // Starting with _? Try removing that. 14 | for true { 15 | if name[0] == '_' { 16 | name = name[1:]; 17 | } 18 | else { 19 | break; 20 | } 21 | } 22 | 23 | // Number 24 | if name[0] >= '0' && name[0] <= '9' { 25 | return fmt.tprint("_", name); 26 | } 27 | 28 | // Keywords clash 29 | else if name == "map" || name == "proc" || name == "opaque" || name == "in" { 30 | return fmt.tprint("_", name); 31 | } 32 | 33 | return name; 34 | } 35 | 36 | clean_variable_name :: proc(name_: string, options : ^GeneratorOptions) -> string { 37 | name := change_case(name_, options.variableCase); 38 | return clean_identifier(name); 39 | } 40 | 41 | clean_pseudo_type_name :: proc(structName_: string, options : ^GeneratorOptions) -> string { 42 | structName := structName_; 43 | /* 44 | replacement, found := options.typeReplacements[structName]; 45 | if found { 46 | return replacement; 47 | } else { 48 | */ 49 | structName = remove_postfixes(structName, options.pseudoTypePostfixes, options.pseudoTypeTransparentPostfixes); 50 | structName = remove_prefixes(structName, options.pseudoTypePrefixes, options.pseudoTypeTransparentPrefixes); 51 | structName = change_case(structName, options.pseudoTypeCase); 52 | return structName; 53 | //} 54 | } 55 | 56 | // Clean up the enum name so that it can be used to remove the prefix from enum values. 57 | clean_enum_name_for_prefix_removal :: proc(enumName_: string, options : ^GeneratorOptions) -> (string, [dynamic]string) { 58 | enumName := enumName_; 59 | 60 | if !options.enumValueNameRemove { 61 | return enumName, nil; 62 | } 63 | 64 | // Remove postfix and use same case convention as the enum values 65 | removedPostfixes : [dynamic]string; 66 | enumName, removedPostfixes = remove_postfixes_with_removed(enumName, options.enumValueNameRemovePostfixes); 67 | enumName = change_case(enumName, options.enumValueCase); 68 | return enumName, removedPostfixes; 69 | } 70 | 71 | clean_enum_value_name :: proc(valueName_: string, enumName : string, postfixes : []string, options : ^GeneratorOptions) -> string { 72 | valueName := valueName_; 73 | valueName = remove_prefixes(valueName, options.enumValuePrefixes, options.enumValueTransparentPrefixes); 74 | valueName = remove_postfixes(valueName, postfixes, options.enumValueTransparentPostfixes); 75 | valueName = change_case(valueName, options.enumValueCase); 76 | 77 | if options.enumValueNameRemove { 78 | valueName = remove_prefixes(valueName, []string{enumName}); 79 | } 80 | 81 | return clean_identifier(valueName); 82 | } 83 | 84 | clean_function_name :: proc(functionName_: string, options : ^GeneratorOptions) -> string { 85 | functionName := functionName_; 86 | functionName = remove_prefixes(functionName, options.functionPrefixes, options.functionTransparentPrefixes); 87 | functionName = remove_postfixes(functionName, options.definePostfixes, options.defineTransparentPostfixes); 88 | functionName = change_case(functionName, options.functionCase); 89 | return functionName; 90 | } 91 | 92 | clean_define_name :: proc(defineName_: string, options : ^GeneratorOptions) -> string { 93 | defineName := defineName_; 94 | defineName = remove_prefixes(defineName, options.definePrefixes, options.defineTransparentPrefixes); 95 | defineName = remove_postfixes(defineName, options.definePostfixes, options.defineTransparentPostfixes); 96 | defineName = change_case(defineName, options.defineCase); 97 | return defineName; 98 | } 99 | 100 | // Convert to Odin's types 101 | clean_type :: proc(type : Type, options : ^GeneratorOptions, baseTab : string = "") -> string { 102 | if _type, ok1 := type.(BuiltinType); ok1 { 103 | if _type == BuiltinType.Void do return ""; 104 | else if _type == BuiltinType.Int do return "_c.int"; 105 | else if _type == BuiltinType.UInt do return "_c.uint"; 106 | else if _type == BuiltinType.LongInt do return "_c.long"; 107 | else if _type == BuiltinType.ULongInt do return "_c.ulong"; 108 | else if _type == BuiltinType.LongLongInt do return "_c.longlong"; 109 | else if _type == BuiltinType.ULongLongInt do return "_c.ulonglong"; 110 | else if _type == BuiltinType.ShortInt do return "_c.short"; 111 | else if _type == BuiltinType.UShortInt do return "_c.ushort"; 112 | else if _type == BuiltinType.Char do return "_c.char"; 113 | else if _type == BuiltinType.SChar do return "_c.schar"; 114 | else if _type == BuiltinType.UChar do return "_c.uchar"; 115 | else if _type == BuiltinType.Float do return "_c.float"; 116 | else if _type == BuiltinType.Double do return "_c.double"; 117 | else if _type == BuiltinType.LongDouble { 118 | print_warning("Found long double which is currently not supported. Fallback to double in generated code."); 119 | return "_c.double"; 120 | } 121 | } 122 | else if _type, ok2 := type.(StandardType); ok2 { 123 | if _type == StandardType.Int8 do return "i8"; 124 | else if _type == StandardType.Int16 do return "i16"; 125 | else if _type == StandardType.Int32 do return "i32"; 126 | else if _type == StandardType.Int64 do return "i64"; 127 | else if _type == StandardType.UInt8 do return "u8"; 128 | else if _type == StandardType.UInt16 do return "u16"; 129 | else if _type == StandardType.UInt32 do return "u32"; 130 | else if _type == StandardType.UInt64 do return "u64"; 131 | else if _type == StandardType.Bool do return "bool"; 132 | else if _type == StandardType.Size do return "_c.size_t"; 133 | else if _type == StandardType.SSize do return "_c.ssize_t"; 134 | else if _type == StandardType.PtrDiff do return "_c.ptrdiff_t"; 135 | else if _type == StandardType.UIntPtr do return "_c.uintptr_t"; 136 | else if _type == StandardType.IntPtr do return "_c.intptr_t"; 137 | } 138 | else if _type, ok3 := type.(PointerType); ok3 { 139 | if __type, ok4 := _type.type.(BuiltinType); ok4 { 140 | if __type == BuiltinType.Void do return "rawptr"; 141 | else if __type == BuiltinType.Char do return "cstring"; 142 | } 143 | name := clean_type(_type.type^, options, baseTab); 144 | return fmt.tprint("^", name); 145 | } 146 | else if _type, ok5 := type.(IdentifierType); ok5 { 147 | return clean_pseudo_type_name(_type.name, options); 148 | } 149 | else if _type, ok6 := type.(FunctionPointerType); ok6 { 150 | output := "#type proc("; 151 | parameters := clean_function_parameters(_type.parameters, options, baseTab); 152 | output = fmt.tprint(output, parameters, ")"); 153 | // @fixme And return value!? 154 | return output; 155 | } 156 | 157 | return ""; 158 | } 159 | 160 | clean_function_parameters :: proc(parameters : [dynamic]FunctionParameter, options : ^GeneratorOptions, baseTab : string) -> string { 161 | output := ""; 162 | 163 | // Special case: function(void) does not really have a parameter 164 | if len(parameters) == 1 { 165 | if _type, ok := parameters[0].type.(BuiltinType); ok { 166 | if _type == BuiltinType.Void { 167 | return ""; 168 | } 169 | } 170 | } 171 | 172 | tab := ""; 173 | if (len(parameters) > 1) { 174 | output = fmt.tprint(output, "\n"); 175 | tab = fmt.tprint(baseTab, " "); 176 | } 177 | 178 | unamedParametersCount := 0; 179 | for parameter, i in parameters { 180 | type := clean_type(parameter.type, options); 181 | 182 | name : string; 183 | if len(parameter.name) != 0 { 184 | name = clean_variable_name(parameter.name, options); 185 | } else { 186 | name = fmt.tprint("unamed", unamedParametersCount); 187 | unamedParametersCount += 1; 188 | } 189 | 190 | output = fmt.tprint(output, tab, name, " : "); 191 | for dimension in parameter.dimensions { 192 | output = fmt.tprint(output, "[", dimension, "]"); 193 | } 194 | output = fmt.tprint(output, type); 195 | if i != len(parameters) - 1 { 196 | output = fmt.tprint(output, ",\n"); 197 | } 198 | } 199 | 200 | if (len(parameters) > 1) { 201 | output = fmt.tprint(output, "\n", baseTab); 202 | } 203 | 204 | return output; 205 | } 206 | -------------------------------------------------------------------------------- /generator/bindgen/generator-export.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | import "core:os" 4 | import "core:fmt" 5 | 6 | export_defines :: proc(data : ^GeneratorData) { 7 | for node in data.nodes.defines { 8 | defineName := clean_define_name(node.name, data.options); 9 | 10 | // @fixme fprint of float numbers are pretty badly handled, 11 | // just has a 10^-3 precision. 12 | fmt.fprint(data.handle, defineName, 13 | node.is_variable ? " := " : " :: ", node.value, ";\n"); 14 | } 15 | fmt.fprint(data.handle, "\n"); 16 | } 17 | 18 | export_typedefs :: proc(data : ^GeneratorData) { 19 | for node in data.nodes.typedefs { 20 | aliasName := clean_pseudo_type_name(node.name, data.options); 21 | sourceType := clean_type(node.sourceType, data.options); 22 | if aliasName == sourceType do continue; 23 | fmt.fprint(data.handle, aliasName, " :: ", sourceType, ";\n"); 24 | } 25 | fmt.fprint(data.handle, "\n"); 26 | } 27 | 28 | export_enums :: proc(data : ^GeneratorData) { 29 | for node in data.nodes.enumDefinitions { 30 | enumName := clean_pseudo_type_name(node.name, data.options); 31 | fmt.fprint(data.handle, enumName, " :: enum i32 {"); 32 | 33 | postfixes : [dynamic]string; 34 | enumName, postfixes = clean_enum_name_for_prefix_removal(enumName, data.options); 35 | 36 | // Changing the case of postfixes to the enum value one, 37 | // so that they can be removed. 38 | enumValueCase := find_case(node.members[0].name); 39 | for postfix, i in postfixes { 40 | postfixes[i] = change_case(postfix, enumValueCase); 41 | } 42 | 43 | // Merging enum value postfixes with postfixes that have been removed from the enum name. 44 | for postfix in data.options.enumValuePostfixes { 45 | append(&postfixes, postfix); 46 | } 47 | 48 | export_enum_members(data, node.members, enumName, postfixes[:]); 49 | fmt.fprint(data.handle, "};\n"); 50 | fmt.fprint(data.handle, "\n"); 51 | } 52 | } 53 | 54 | export_structs :: proc(data : ^GeneratorData) { 55 | for node in data.nodes.structDefinitions { 56 | structName := clean_pseudo_type_name(node.name, data.options); 57 | 58 | replacement, found := data.options.typeReplacements[structName]; 59 | if found { 60 | fmt.fprint(data.handle, structName, " :: ", replacement, ";\n"); 61 | } else { 62 | fmt.fprint(data.handle, structName, " :: struct {"); 63 | export_struct_or_union_members(data, node.members); 64 | fmt.fprint(data.handle, "};\n"); 65 | fmt.fprint(data.handle, "\n"); 66 | } 67 | } 68 | } 69 | 70 | export_unions :: proc(data : ^GeneratorData) { 71 | for node in data.nodes.unionDefinitions { 72 | unionName := clean_pseudo_type_name(node.name, data.options); 73 | fmt.fprint(data.handle, unionName, " :: struct #raw_union {"); 74 | export_struct_or_union_members(data, node.members); 75 | fmt.fprint(data.handle, "};\n"); 76 | fmt.fprint(data.handle, "\n"); 77 | } 78 | } 79 | 80 | Export_Functions_Mode :: enum { 81 | Top_Level, 82 | Pointer_In_Struct, 83 | Plugin_Pointers, 84 | } 85 | 86 | should_skip_function_node :: proc(data: ^GeneratorData, node: FunctionDeclarationNode) -> bool { 87 | for fnName in data.options.removeFunctions { 88 | if fnName == node.name do return true; 89 | } 90 | return false; 91 | } 92 | 93 | export_functions :: proc(data : ^GeneratorData, mode: Export_Functions_Mode = Export_Functions_Mode.Top_Level) { 94 | using Export_Functions_Mode; 95 | 96 | for node in data.nodes.functionDeclarations { 97 | functionName := clean_function_name(node.name, data.options); 98 | if should_skip_function_node(data, node) { 99 | fmt.println("SKIPPING FUNCTION", functionName); 100 | continue; 101 | } 102 | 103 | if mode == Top_Level { 104 | fmt.fprint(data.handle, " @(link_name=\"", node.name, "\")\n"); 105 | } 106 | fmt.fprint(data.handle, (mode == Plugin_Pointers ? "" : " "), functionName); 107 | if mode == Top_Level { 108 | fmt.fprint(data.handle, " :: "); 109 | } else if mode == Pointer_In_Struct || mode == Plugin_Pointers { 110 | fmt.fprint(data.handle, " : "); 111 | } 112 | fmt.fprint(data.handle, mode != Top_Level ? "proc \"c\" (" : "proc("); 113 | parameters := clean_function_parameters(node.parameters, data.options, " "); 114 | fmt.fprint(data.handle, parameters, ")"); 115 | returnType := clean_type(node.returnType, data.options); 116 | if len(returnType) > 0 { 117 | fmt.fprint(data.handle, " -> ", returnType); 118 | } 119 | if mode == Top_Level { 120 | fmt.fprint(data.handle, " ---;\n"); 121 | } else if mode == Pointer_In_Struct { 122 | fmt.fprint(data.handle, ","); 123 | } else if mode == Plugin_Pointers { 124 | fmt.fprint(data.handle, ";"); 125 | } 126 | fmt.fprint(data.handle, "\n"); 127 | } 128 | } 129 | 130 | 131 | export_enum_members :: proc(data : ^GeneratorData, members : [dynamic]EnumMember, enumName : string, postfixes : []string) { 132 | if (len(members) > 0) { 133 | fmt.fprint(data.handle, "\n"); 134 | } 135 | for member in members { 136 | name := clean_enum_value_name(member.name, enumName, postfixes, data.options); 137 | if len(name) == 0 do continue; 138 | fmt.fprint(data.handle, " ", name); 139 | if member.hasValue { 140 | fmt.fprint(data.handle, " = ", member.value); 141 | } 142 | fmt.fprint(data.handle, ",\n"); 143 | } 144 | } 145 | 146 | export_struct_or_union_members :: proc(data : ^GeneratorData, members : [dynamic]StructOrUnionMember) { 147 | if (len(members) > 0) { 148 | fmt.fprint(data.handle, "\n"); 149 | } 150 | for member in members { 151 | type := clean_type(member.type, data.options, " "); 152 | name := clean_variable_name(member.name, data.options); 153 | fmt.fprint(data.handle, " ", name, " : "); 154 | for dimension in member.dimensions { 155 | fmt.fprint(data.handle, "[", dimension, "]"); 156 | } 157 | fmt.fprint(data.handle, type, ",\n"); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /generator/bindgen/generator-helpers.odin: -------------------------------------------------------------------------------- 1 | package bindgen 2 | 3 | import "core:fmt" 4 | 5 | Case :: enum { 6 | Unknown, 7 | Camel, 8 | Constant, 9 | Kebab, 10 | Pascal, 11 | Snake, 12 | } 13 | 14 | WordCase :: enum { 15 | Unknown, 16 | Up, 17 | Low, 18 | FirstUp, 19 | // When first upping, numbers are followed always by a capital 20 | FirstUpNumberReset, 21 | } 22 | 23 | // Change a character to a capital. 24 | to_uppercase :: proc(c_: rune) -> rune { 25 | c := c_; 26 | if c >= 'a' && c <= 'z' { 27 | c = c - 'a' + 'A'; 28 | } 29 | return c; 30 | } 31 | 32 | // Change a character to lowercase. 33 | to_lowercase :: proc(c_: rune) -> rune { 34 | c := c_; 35 | if c >= 'A' && c <= 'Z' { 36 | c = c - 'A' + 'a'; 37 | } 38 | return c; 39 | } 40 | 41 | // Change the case convention of a word. 42 | change_word_case :: proc(str : string, targetCase : WordCase) -> string { 43 | newStr : string; 44 | if targetCase == WordCase.Up { 45 | for c in str { 46 | newStr = fmt.tprint(newStr, to_uppercase(c)); 47 | } 48 | } 49 | else if targetCase == WordCase.Low { 50 | for c in str { 51 | newStr = fmt.tprint(newStr, to_lowercase(c)); 52 | } 53 | } 54 | else if targetCase == WordCase.FirstUp { 55 | for c, i in str { 56 | if i == 0 { 57 | newStr = fmt.tprint(newStr, to_uppercase(c)); 58 | } else { 59 | newStr = fmt.tprint(newStr, to_lowercase(c)); 60 | } 61 | } 62 | } 63 | else if targetCase == WordCase.FirstUpNumberReset { 64 | for c, i in str { 65 | if i == 0 || (str[i - 1] >= '0' && str[i - 1] <= '9') { 66 | newStr = fmt.tprint(newStr, to_uppercase(c)); 67 | } else { 68 | newStr = fmt.tprint(newStr, to_lowercase(c)); 69 | } 70 | } 71 | } 72 | return newStr; 73 | } 74 | 75 | // Change the case convention of a string by detecting original convention, 76 | // then splitting it into words. 77 | change_case :: proc(str : string, targetCase : Case) -> string { 78 | if targetCase == Case.Unknown { 79 | return str; 80 | } 81 | 82 | // Split 83 | parts := autosplit_string(str); 84 | 85 | // Join 86 | newStr : string; 87 | if targetCase == Case.Pascal { 88 | for part, _ in parts { 89 | newStr = fmt.tprint(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); 90 | } 91 | } 92 | else if targetCase == Case.Snake { 93 | for part, i in parts { 94 | newStr = fmt.tprint(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "_" : ""); 95 | } 96 | } 97 | else if targetCase == Case.Kebab { 98 | for part, i in parts { 99 | newStr = fmt.tprint(newStr, change_word_case(part, WordCase.Low), (i != len(parts) - 1) ? "-" : ""); 100 | } 101 | } 102 | else if targetCase == Case.Camel { 103 | for part, i in parts { 104 | if i == 0 { 105 | newStr = fmt.tprint(newStr, change_word_case(part, WordCase.Low)); 106 | } else { 107 | newStr = fmt.tprint(newStr, change_word_case(part, WordCase.FirstUpNumberReset)); 108 | } 109 | } 110 | } 111 | else if targetCase == Case.Constant { 112 | for part, i in parts { 113 | newStr = fmt.tprint(newStr, change_word_case(part, WordCase.Up), (i != len(parts) - 1) ? "_" : ""); 114 | } 115 | } 116 | 117 | return newStr; 118 | } 119 | 120 | // Identify the case of the provided string. 121 | // Full lowercase with no separator is identified as camelCase. 122 | find_case :: proc(str : string) -> Case { 123 | refuted : bool; 124 | 125 | // CONSTANT_CASE 126 | refuted = false; 127 | for c in str { 128 | if (c != '_') && (c < 'A' || c > 'Z') && (c < '0' || c > '9') { 129 | refuted = true; 130 | break; 131 | } 132 | } 133 | if !refuted do return Case.Constant; 134 | 135 | for c in str { 136 | // snake_case 137 | if c == '_' { 138 | return Case.Snake; 139 | } 140 | // kebab-case 141 | else if c == '-' { 142 | return Case.Kebab; 143 | } 144 | } 145 | 146 | // PascalCase 147 | if str[0] >= 'A' && str[0] <= 'Z' { 148 | return Case.Pascal; 149 | } 150 | 151 | // camelCase 152 | return Case.Camel; 153 | } 154 | 155 | // Splits the string according to detected case. 156 | // HeyBuddy -> {"Hey", "Buddy"} 157 | // hey-buddy -> {"hey", "buddy"} 158 | // and such... 159 | autosplit_string :: proc(str : string) -> [dynamic]string { 160 | lowCount := 0; 161 | upCount := 0; 162 | for c in str { 163 | // If any '_', split according to that (CONSTANT_CASE or snake_case) 164 | if c == '_' { 165 | return split_from_separator(str, '_'); 166 | } 167 | // If any '-', split according to that (kebab-case) 168 | else if c == '-' { 169 | return split_from_separator(str, '-'); 170 | } 171 | else if c >= 'a' && c <= 'z' { 172 | lowCount += 1; 173 | } 174 | else if c >= 'A' && c <= 'Z' { 175 | upCount += 1; 176 | } 177 | } 178 | 179 | // If it seems to be only one word 180 | if lowCount == 0 || upCount == 0 { 181 | parts : [dynamic]string; 182 | append(&parts, str); 183 | return parts; 184 | } 185 | 186 | // Split at each uppercase letter (PascalCase or camelCase) 187 | return split_from_capital(str); 188 | } 189 | 190 | split_from_separator :: proc(str : string, sep : rune) -> [dynamic]string { 191 | parts : [dynamic]string; 192 | 193 | // Ignore non letter prefix 194 | lastI := 0; 195 | for c in str { 196 | if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { 197 | lastI += 1; 198 | } 199 | else { 200 | break; 201 | } 202 | } 203 | 204 | for c, i in str { 205 | if i > lastI + 1 && c == sep { 206 | append(&parts, str[lastI:i]); 207 | lastI = i + 1; 208 | } 209 | } 210 | 211 | append(&parts, str[lastI:]); 212 | 213 | return parts; 214 | } 215 | 216 | split_from_capital :: proc(str : string) -> [dynamic]string { 217 | parts : [dynamic]string; 218 | 219 | // Ignore non letter prefix 220 | lastI := 0; 221 | for c in str { 222 | if (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') { 223 | lastI += 1; 224 | } 225 | else { 226 | break; 227 | } 228 | } 229 | 230 | is_sep :: inline proc(c: u8) -> bool { 231 | return (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); 232 | } 233 | 234 | is_number :: inline proc(c: u8) -> bool { 235 | return c >= '0' && c <= '9'; 236 | } 237 | 238 | chk :: proc(s: string, n: u8) -> bool { 239 | return s == "Vector" && is_number(n); 240 | } 241 | 242 | 243 | // We want to handle: 244 | // myBrainIsCRAZY -> my Brain Is Crazy 245 | // myCRAZYBrain -> my CRAZY Brain 246 | // SOLO -> SOLO 247 | 248 | // Do split 249 | for i := 1; i < len(str); i += 1 { 250 | if !is_sep(str[i]) || chk(str[lastI:i], str[i]) do continue; 251 | 252 | // Do not split too much if it seems to be a capitalized word 253 | if (lastI == i - 1) && is_sep(str[lastI]) { 254 | for ; i + 1 < len(str); i += 1 { 255 | if str[i + 1] < 'A' || str[i + 1] > 'Z' { 256 | break; 257 | } 258 | } 259 | if (i + 1 == len(str)) && is_sep(str[i]) { 260 | i += 1; 261 | } 262 | } 263 | 264 | append(&parts, str[lastI:i]); 265 | lastI = i; 266 | } 267 | 268 | if lastI != len(str) { 269 | append(&parts, str[lastI:]); 270 | } 271 | 272 | return parts; 273 | } 274 | 275 | // Check if str if prefixed with any of the provided strings, 276 | // even combinaisons of those, and remove them. 277 | remove_prefixes :: proc(str_: string, prefixes : []string, transparentPrefixes : []string = nil) -> string { 278 | str := str_; 279 | transparentStr := ""; 280 | 281 | found := true; 282 | for found { 283 | found = false; 284 | 285 | // Remove effective prefixes 286 | for prefix in prefixes { 287 | if len(str) >= len(prefix) && 288 | str[:len(prefix)] == prefix { 289 | str = str[len(prefix):]; 290 | if len(str) != 0 && (str[0] == '_' || str[0] == '-') { 291 | str = str[1:]; 292 | } 293 | found = true; 294 | break; 295 | } 296 | } 297 | 298 | if found do continue; 299 | 300 | // Remove transparent ones, only one by one, 301 | // as we want effective ones to be fully removed. 302 | for prefix in transparentPrefixes { 303 | if len(str) >= len(prefix) && 304 | str[:len(prefix)] == prefix { 305 | str = str[len(prefix):]; 306 | transparentStr = fmt.tprint(transparentStr, prefix); 307 | if len(str) != 0 && (str[0] == '_' || str[0] == '-') { 308 | str = str[1:]; 309 | transparentStr = fmt.tprint(transparentStr, '_'); 310 | } 311 | found = true; 312 | break; 313 | } 314 | } 315 | } 316 | 317 | return fmt.tprint(transparentStr, str); 318 | } 319 | 320 | // Check if str if postfixes with any of the provided strings, 321 | // even combinaisons of those, and remove them. 322 | remove_postfixes_with_removed :: proc( 323 | str_: string, 324 | postfixes : []string, 325 | transparentPostfixes : []string = nil 326 | ) -> (string, [dynamic]string) { 327 | str := str_; 328 | removedPostfixes : [dynamic]string; 329 | transparentStr := ""; 330 | 331 | found := true; 332 | for found { 333 | found = false; 334 | 335 | // Remove effective postfixes 336 | for postfix in postfixes { 337 | if len(str) >= len(postfix) && 338 | str[len(str) - len(postfix):] == postfix { 339 | str = str[:len(str) - len(postfix)]; 340 | if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { 341 | str = str[:len(str)-1]; 342 | } 343 | append(&removedPostfixes, postfix); 344 | found = true; 345 | break; 346 | } 347 | } 348 | 349 | if found do continue; 350 | 351 | // Remove transparent ones, only one by one, 352 | // as we want effective ones to be fully removed. 353 | for postfix in transparentPostfixes { 354 | if len(str) >= len(postfix) && 355 | str[len(str) - len(postfix):] == postfix { 356 | str = str[:len(str) - len(postfix)]; 357 | transparentStr = fmt.tprint(postfix, transparentStr); 358 | if len(str) != 0 && (str[len(str)-1] == '_' || str[len(str)-1] == '-') { 359 | str = str[:len(str)-1]; 360 | transparentStr = fmt.tprint('_', transparentStr); 361 | } 362 | found = true; 363 | break; 364 | } 365 | } 366 | } 367 | 368 | return fmt.tprint(str, transparentStr), removedPostfixes; 369 | } 370 | 371 | remove_postfixes :: proc( 372 | str_: string, 373 | postfixes : []string, 374 | transparentPostfixes : []string = nil 375 | ) -> string { 376 | str := str_; 377 | removedPostfixes : [dynamic]string; 378 | str, removedPostfixes = remove_postfixes_with_removed(str, postfixes, transparentPostfixes); 379 | return str; 380 | } 381 | -------------------------------------------------------------------------------- /generator/bindgen/generator.odin: -------------------------------------------------------------------------------- 1 | /** 2 | * Odin binding generator from C header data. 3 | */ 4 | 5 | package bindgen 6 | 7 | import "core:os" 8 | import "core:strings" 9 | import "core:fmt" 10 | import "core:runtime" 11 | 12 | GeneratorOptions :: struct { 13 | // Variable 14 | variableCase : Case, 15 | 16 | // Defines 17 | definePrefixes : []string, 18 | defineTransparentPrefixes : []string, 19 | definePostfixes : []string, 20 | defineTransparentPostfixes : []string, 21 | defineCase : Case, 22 | 23 | // Pseudo-types 24 | pseudoTypePrefixes : []string, 25 | pseudoTypeTransparentPrefixes : []string, 26 | pseudoTypePostfixes : []string, 27 | pseudoTypeTransparentPostfixes : []string, 28 | pseudoTypeCase : Case, 29 | 30 | // Functions 31 | functionPrefixes : []string, 32 | functionTransparentPrefixes : []string, 33 | functionPostfixes : []string, 34 | functionTransparentPostfixes : []string, 35 | functionCase : Case, 36 | 37 | // Enum values 38 | enumValuePrefixes : []string, 39 | enumValueTransparentPrefixes : []string, 40 | enumValuePostfixes : []string, 41 | enumValueTransparentPostfixes : []string, 42 | enumValueCase : Case, 43 | enumValueNameRemove : bool, 44 | enumValueNameRemovePostfixes : []string, 45 | 46 | odin_includes: []string, 47 | odin_using_includes: []string, 48 | typeReplacements: map[string]string, 49 | extra_type_string_lines: []string, 50 | removeFunctions: []string, 51 | 52 | parserOptions : ParserOptions, 53 | } 54 | 55 | GeneratorData :: struct { 56 | handle : os.Handle, 57 | nodes : Nodes, 58 | 59 | // References 60 | options : ^GeneratorOptions, 61 | } 62 | 63 | Enum_Args :: [dynamic]string; 64 | Enum_Args_Map :: map[string]Enum_Args; 65 | 66 | generate :: proc( 67 | packageName : string, 68 | foreignLibrary : string, 69 | outputFile : string, 70 | typesFile : string, 71 | bridgeFile : string, 72 | headerFiles : []string, 73 | options_: GeneratorOptions, 74 | enum_args_map : Enum_Args_Map, 75 | ) -> bool { 76 | options := options_; 77 | 78 | data : GeneratorData; 79 | data.options = &options; 80 | 81 | options.parserOptions.enum_args_map = enum_args_map; 82 | 83 | // Parsing header files 84 | for headerFile in headerFiles { 85 | bytes, ok := os.read_entire_file(headerFile); 86 | if !ok { 87 | fmt.eprint("[bindgen] Unable to read file ", headerFile, "\n"); 88 | return false; 89 | } 90 | 91 | // We fuse the SOAs 92 | headerNodes := parse(bytes, options.parserOptions); 93 | merge_generic_nodes(&data.nodes.defines, &headerNodes.defines); 94 | merge_generic_nodes(&data.nodes.enumDefinitions, &headerNodes.enumDefinitions); 95 | merge_generic_nodes(&data.nodes.unionDefinitions, &headerNodes.unionDefinitions); 96 | merge_forward_declared_nodes(&data.nodes.structDefinitions, &headerNodes.structDefinitions); 97 | merge_generic_nodes(&data.nodes.functionDeclarations, &headerNodes.functionDeclarations); 98 | merge_generic_nodes(&data.nodes.typedefs, &headerNodes.typedefs); 99 | } 100 | 101 | // Outputing odin "types" file 102 | { 103 | errno : os.Errno; 104 | data.handle, errno = os.open(typesFile, os.O_WRONLY | os.O_CREATE | os.O_TRUNC); 105 | if errno != 0 { 106 | fmt.eprint("[bindgen] Unable to write to output file ", typesFile, " (", errno ,")\n"); 107 | return false; 108 | } 109 | defer os.close(data.handle); 110 | 111 | fmt.fprintln(data.handle, "//"); 112 | fmt.fprintln(data.handle, "// generated by bindgen (https://github.com/Breush/odin-binding-generator)"); 113 | fmt.fprintln(data.handle, "//"); 114 | fmt.fprint(data.handle, "\n"); 115 | 116 | fmt.fprint(data.handle, "package ", packageName, "_types\n"); 117 | fmt.fprint(data.handle, "\n"); 118 | fmt.fprint(data.handle, "import _c \"core:c\"\n"); 119 | fmt.fprint(data.handle, "\n"); 120 | 121 | for include in options.odin_includes { 122 | fmt.fprintf(data.handle, "import \"%s\"\n", include); 123 | } 124 | for include in options.odin_using_includes { 125 | fmt.fprintf(data.handle, "import \"../%s\"\n", include); 126 | } 127 | 128 | // Exporting 129 | export_defines(&data); 130 | export_typedefs(&data); 131 | export_enums(&data); 132 | export_structs(&data); 133 | export_unions(&data); 134 | 135 | fmt.fprint(data.handle, packageName, "_Funcs :: struct {\n"); 136 | export_functions(&data, Export_Functions_Mode.Pointer_In_Struct); 137 | fmt.fprint(data.handle, "}\n\n"); 138 | 139 | } 140 | 141 | // Outputing odin "bindings" file 142 | { 143 | errno : os.Errno; 144 | data.handle, errno = os.open(outputFile, os.O_WRONLY | os.O_CREATE | os.O_TRUNC); 145 | if errno != 0 { 146 | fmt.eprint("[bindgen] Unable to write to output file ", outputFile, " (", errno ,")\n"); 147 | return false; 148 | } 149 | defer os.close(data.handle); 150 | 151 | builder : = strings.make_builder(); 152 | /* 153 | removed includes from here because they were going unused 154 | for include in options.odin_includes { 155 | fmt.sbprintf(&builder, "import \"%s\"\n", include); 156 | } 157 | */ 158 | for include in options.odin_using_includes { 159 | fmt.sbprintf(&builder, "import \"%s\"\n", include); 160 | } 161 | additional_includes_string := strings.to_string(builder); 162 | 163 | foreignLibrarySimple := fmt.tprintf("%s_native", simplify_library_name(foreignLibrary)); 164 | assert(len(foreignLibrarySimple) > 0, fmt.tprint("simplified a foreign library name to a zero-length string: ", foreignLibrary)); 165 | 166 | fmt.fprintf(data.handle, ` 167 | // 168 | // THIS FILE WAS AUTOGENERATED 169 | // 170 | 171 | package %s_bindings 172 | 173 | foreign import %s "%s" 174 | 175 | import _c "core:c" 176 | %s 177 | 178 | import %s_types "./types" 179 | 180 | %s_Funcs :: %s_types.%s_Funcs; 181 | 182 | `, packageName, foreignLibrarySimple, foreignLibrary, additional_includes_string, packageName, packageName, packageName, packageName); 183 | 184 | reexport_types(data, packageName); 185 | 186 | fmt.fprintf(data.handle, ` 187 | get_function_pointers :: proc(funcs: ^%s_types.%s_Funcs) {{ 188 | `, packageName, packageName); 189 | 190 | // assign incoming func pointers to struct 191 | for node in data.nodes.functionDeclarations { 192 | if should_skip_function_node(&data, node) do continue; 193 | function_name := clean_function_name(node.name, data.options); 194 | fmt.fprintf(data.handle, " funcs.%s = %s;\n", function_name, function_name); 195 | } 196 | 197 | fmt.fprint(data.handle, "}\n\n"); 198 | 199 | // Foreign block for functions 200 | fmt.fprint(data.handle, "@(default_calling_convention=\"c\")\n"); 201 | fmt.fprint(data.handle, "foreign ", foreignLibrarySimple, " {\n"); 202 | fmt.fprint(data.handle, "\n"); 203 | 204 | export_functions(&data); 205 | 206 | fmt.fprint(data.handle, "}\n"); 207 | } 208 | 209 | // bridge "plugin" file 210 | { 211 | errno : os.Errno; 212 | data.handle, errno = os.open(bridgeFile, os.O_WRONLY | os.O_CREATE | os.O_TRUNC); 213 | if errno != 0 { 214 | fmt.eprint("[bindgen] Unable to write to output file ", bridgeFile, " (", errno ,")\n"); 215 | return false; 216 | } 217 | defer os.close(data.handle); 218 | 219 | fmt.fprintf(data.handle, ` 220 | package %s 221 | 222 | import raylib_types "../types" 223 | 224 | %s_Funcs :: raylib_types.%s_Funcs; 225 | 226 | `, packageName, packageName, packageName); 227 | 228 | reexport_types(data, "raylib"); 229 | 230 | fmt.fprintf(data.handle, ` 231 | 232 | import _c "core:c" 233 | 234 | bridge_init :: proc(funcs: ^%s_Funcs) {{ 235 | `, packageName); 236 | 237 | count := 0; 238 | 239 | // assign incoming struct function pointers to package level function pointers 240 | for node in data.nodes.functionDeclarations { 241 | if should_skip_function_node(&data, node) do continue; 242 | function_name := clean_function_name(node.name, data.options); 243 | if count == 0 { 244 | fmt.fprint(data.handle, " assert(funcs != nil);\n"); 245 | fmt.fprintf(data.handle, " assert(funcs.%s != nil);\n\n", function_name); 246 | } 247 | fmt.fprintf(data.handle, " %s = funcs.%s;\n", function_name, function_name); 248 | count += 1; 249 | } 250 | 251 | fmt.fprintf(data.handle, `} 252 | 253 | bridge_deinit :: proc() {{ 254 | }} 255 | 256 | `); 257 | export_functions(&data, Export_Functions_Mode.Plugin_Pointers); 258 | } 259 | 260 | return true; 261 | } 262 | 263 | // system:foo.lib -> foo 264 | simplify_library_name :: proc(libraryName : string) -> string { 265 | startOffset := 0; 266 | endOffset := len(libraryName); 267 | 268 | for c, i in libraryName { 269 | if startOffset == 0 && c == ':' { 270 | startOffset = i + 1; 271 | } 272 | else if c == '/' { 273 | startOffset = i + 1; 274 | } 275 | else if c == '.' { 276 | endOffset = i; 277 | } 278 | } 279 | 280 | return libraryName[startOffset:endOffset]; 281 | } 282 | 283 | merge_generic_nodes :: proc(nodes : ^$T, headerNodes : ^T) { 284 | for headerNode in headerNodes { 285 | // Check that there are no duplicated nodes (due to forward declaration or such) 286 | duplicatedIndex := -1; 287 | for i := 0; i < len(nodes); i += 1 { 288 | node := nodes[i]; 289 | if node.name == headerNode.name { 290 | duplicatedIndex = i; 291 | break; 292 | } 293 | } 294 | 295 | if duplicatedIndex < 0 { 296 | append(nodes, headerNode); 297 | } 298 | } 299 | } 300 | 301 | merge_forward_declared_nodes :: proc(nodes : ^$T, headerNodes : ^T) { 302 | for headerNode in headerNodes { 303 | // Check that there are no duplicated nodes (due to forward declaration or such) 304 | duplicatedIndex := -1; 305 | for i := 0; i < len(nodes); i += 1 { 306 | node := nodes[i]; 307 | if node.name == headerNode.name { 308 | duplicatedIndex = i; 309 | break; 310 | } 311 | } 312 | 313 | if duplicatedIndex < 0 { 314 | append(nodes, headerNode); 315 | } 316 | else if !headerNode.forwardDeclared { 317 | nodes[duplicatedIndex] = headerNode; 318 | } 319 | } 320 | } 321 | 322 | reexport_types :: proc(data: GeneratorData, packageName: string = "") { 323 | fmt.fprint(data.handle, "// re-export everything from ./types for convienience\n"); 324 | 325 | for line in data.options.extra_type_string_lines { 326 | fmt.fprintf(data.handle, "%s\n", line); 327 | } 328 | 329 | types_prefix := ""; 330 | if len(packageName) > 0 { 331 | types_prefix = fmt.tprint(packageName, "_types."); 332 | } 333 | 334 | // defines 335 | for node in data.nodes.defines { 336 | defineName := clean_define_name(node.name, data.options); 337 | fmt.fprint(data.handle, defineName, 338 | node.is_variable ? " := " : " :: ", 339 | types_prefix, defineName, ";\n"); 340 | } 341 | fmt.fprint(data.handle, "\n"); 342 | 343 | // typedefs 344 | for node in data.nodes.typedefs { 345 | aliasName := clean_pseudo_type_name(node.name, data.options); 346 | sourceType := clean_type(node.sourceType, data.options); 347 | if aliasName == sourceType do continue; 348 | fmt.fprint(data.handle, aliasName, " :: ", types_prefix, aliasName, ";\n"); 349 | } 350 | fmt.fprint(data.handle, "\n"); 351 | 352 | // structs 353 | for node in data.nodes.structDefinitions { 354 | structName := clean_pseudo_type_name(node.name, data.options); 355 | fmt.fprint(data.handle, structName, " :: ", types_prefix, structName, ";\n"); 356 | } 357 | 358 | // enums 359 | for node in data.nodes.enumDefinitions { 360 | enumName := clean_pseudo_type_name(node.name, data.options); 361 | 362 | // @Kevin Hack - don't rexport AnonymousEnumX 363 | anon := "AnonymousEnum"; 364 | if len(enumName) > len(anon) && enumName[:len(anon)] == anon { 365 | continue; 366 | } 367 | 368 | fmt.fprint(data.handle, enumName, " :: ", types_prefix, enumName, ";\n"); 369 | } 370 | } 371 | -------------------------------------------------------------------------------- /generator/preprocessed/aux_data/aux_data.odin: -------------------------------------------------------------------------------- 1 | package aux_data 2 | 3 | Enum_Args :: [dynamic]string; 4 | Enum_Args_Map :: map[string]Enum_Args; 5 | 6 | add_to_list :: proc(m: ^Enum_Args_Map, key, val: string) { 7 | list := m[key]; 8 | append(&list, val); 9 | m[key] = list; 10 | } 11 | 12 | get_enum_args :: proc() -> Enum_Args_Map { 13 | m : Enum_Args_Map; 14 | 15 | add_to_list(&m, "IsKeyPressed", "KeyboardKey"); 16 | add_to_list(&m, "IsKeyDown", "KeyboardKey"); 17 | add_to_list(&m, "IsKeyReleased", "KeyboardKey"); 18 | add_to_list(&m, "IsKeyUp", "KeyboardKey"); 19 | add_to_list(&m, "SetExitKey", "KeyboardKey"); 20 | 21 | add_to_list(&m, "SetConfigFlags", "ConfigFlag"); 22 | 23 | add_to_list(&m, "SetTraceLogLevel", "TraceLogType"); 24 | add_to_list(&m, "SetTraceLogExit", "TraceLogType"); 25 | add_to_list(&m, "TraceLog", "TraceLogType"); 26 | 27 | add_to_list(&m, "IsMouseButtonPressed", "MouseButton"); 28 | add_to_list(&m, "IsMouseButtonDown", "MouseButton"); 29 | add_to_list(&m, "IsMouseButtonReleased", "MouseButton"); 30 | add_to_list(&m, "IsMouseButtonUp", "MouseButton"); 31 | 32 | add_to_list(&m, "IsGamepadAvailable", "GamepadNumber"); 33 | add_to_list(&m, "IsGamepadName", "GamepadNumber"); 34 | add_to_list(&m, "GetGamepadName", "GamepadNumber"); 35 | add_to_list(&m, "IsGamepadButtonPressed", "GamepadNumber"); 36 | add_to_list(&m, "IsGamepadButtonDown", "GamepadNumber"); 37 | add_to_list(&m, "IsGamepadButtonReleased", "GamepadNumber"); 38 | add_to_list(&m, "IsGamepadButtonUp", "GamepadNumber"); 39 | add_to_list(&m, "GetGamepadAxisCount", "GamepadNumber"); 40 | add_to_list(&m, "GetGamepadAxisMovement", "GamepadNumber"); 41 | 42 | add_to_list(&m, "SetCameraMode", "CameraMode"); 43 | 44 | add_to_list(&m, "BeginBlendMode", "BlendMode"); 45 | 46 | return m; 47 | } 48 | -------------------------------------------------------------------------------- /raylib.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/raylib.dll -------------------------------------------------------------------------------- /resources/bunnymark/wabbit_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/bunnymark/wabbit_alpha.png -------------------------------------------------------------------------------- /resources/cat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/cat.png -------------------------------------------------------------------------------- /resources/live-reload.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/live-reload.gif -------------------------------------------------------------------------------- /resources/meow.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/meow.wav -------------------------------------------------------------------------------- /resources/scarfy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/scarfy.png -------------------------------------------------------------------------------- /resources/screenshots/example_bunnymark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/screenshots/example_bunnymark.png -------------------------------------------------------------------------------- /resources/screenshots/example_live_reload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/screenshots/example_live_reload.png -------------------------------------------------------------------------------- /resources/screenshots/example_simple_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/screenshots/example_simple_demo.png -------------------------------------------------------------------------------- /resources/screenshots/physac.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/screenshots/physac.png -------------------------------------------------------------------------------- /resources/tanatana.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kevinw/raylib-odin/8aa65e6ba82f87510d485266c9e3ee672f8e699c/resources/tanatana.ogg -------------------------------------------------------------------------------- /scripts/build_live_reload_host.bat: -------------------------------------------------------------------------------- 1 | @mkdir bin\temp 2> NUL 2 | @REM call scripts\build_raylib_bindings.bat && ^ 3 | odin build examples/live_reload_demo/game.odin -debug -build-mode=dll -out="bin/game.dll" && ^ 4 | odin build examples/live_reload_demo/host.odin -debug -out="bin/live_reload_host.exe" 5 | -------------------------------------------------------------------------------- /scripts/build_live_reload_plugin.bat: -------------------------------------------------------------------------------- 1 | odin build examples\live_reload_demo\game.odin -vet -build-mode=dll -debug -out="bin/game.dll" 2 | -------------------------------------------------------------------------------- /scripts/build_sprites_host.bat: -------------------------------------------------------------------------------- 1 | @mkdir bin\temp 2> NUL 2 | @REM call scripts\build_raylib_bindings.bat && ^ 3 | odin build examples/sprites/game.odin -debug -build-mode=dll -out="bin/sprites_game.dll" && ^ 4 | odin build examples/sprites/host.odin -debug -out="bin/sprites.exe" 5 | -------------------------------------------------------------------------------- /scripts/build_sprites_plugin.bat: -------------------------------------------------------------------------------- 1 | odin build examples\sprites\game.odin -vet -build-mode=dll -debug -out="bin/sprites_game.dll" 2 | -------------------------------------------------------------------------------- /scripts/copy_raylib_libs.bat: -------------------------------------------------------------------------------- 1 | set RAYLIB_BIN_DIR=..\raylib\projects\VS2017\x64\Release.DLL 2 | copy %RAYLIB_BIN_DIR%\raylib.lib .\lib 3 | copy %RAYLIB_BIN_DIR%\raylib.dll . 4 | -------------------------------------------------------------------------------- /scripts/debug.bat: -------------------------------------------------------------------------------- 1 | @setlocal 2 | 3 | set EXENAME=live_reload_host.exe 4 | 5 | copy bin\%EXENAME% . || exit /b 1 6 | devenv.exe %EXENAME% || exit /b 1 7 | -------------------------------------------------------------------------------- /scripts/generate_raylib_bindings.bat: -------------------------------------------------------------------------------- 1 | odin build generator/generate_bindings.odin -debug -out="bin/generate_bindings.exe" && ^ 2 | bin\generate_bindings || exit /b 1 3 | -------------------------------------------------------------------------------- /scripts/run_live_reload_demo.bat: -------------------------------------------------------------------------------- 1 | call scripts\build_live_reload_host.bat && bin\live_reload_host.exe 2 | -------------------------------------------------------------------------------- /scripts/run_simple_demo.bat: -------------------------------------------------------------------------------- 1 | @mkdir bin 2> NUL 2 | odin run examples\simple_demo\simple_demo.odin -debug -out="bin/simple_demo.exe" || exit /b 1 3 | -------------------------------------------------------------------------------- /scripts/run_sprites_demo.bat: -------------------------------------------------------------------------------- 1 | call scripts\build_sprites_host.bat && bin\sprites.exe 2 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | static inline functions in header files become unresolved symbols since they aren't actually exported as functions. 2 | what is the solution? a set of replacement functions in odin? 3 | 4 | C:\Users\Kevin\src\raylib-odin>odin run examples\chipmunk_walker 5 | chipmunk_walker.obj : error LNK2019: unresolved external symbol cpShapeFilterNew referenced in function example_chipmunk.make_leg 6 | C:\Users\Kevin\src\raylib-odin\chipmunk_walker.exe : fatal error LNK1120: 1 unresolved externals 7 | 8 | 2019-03-27 try getting a basic version of serialization working 9 | 2019-03-27 fixup enums so that you don't have "AnonymousEnum19" et al 10 | 2019-03-27 check for leaks on plugin load 11 | 12 | --------------------------------------------------------------------------------