Puhomir logo

Home / Oil-Rig

Raylib

Raylib is a C library and in order to use it with Jai I need bindings. Luckily Jai has the most powerful metaprogramming capabilities of any programming language. To build a Jai program I can write a Jai metaprogram, which is just a Jai program which runs at compile time. This metaprogram can do anything we want, and in my case I want to automatically generate Raylib bindings when necessary.

Inspired by another closed beta member I wrote the following function which will generate the Raylib module:

generate_raylib_module :: (module_name: string, raylib_path: string) {
    using options: Generate_Bindings_Options;

    // Add definitions for things that are missing in the { module.jai } header.
    header = #string MODULE_HEADER
        va_list :: *void;
    MODULE_HEADER

    // Link against system libraries we need in the { module.jai } footer (order matters).
    footer = #string MODULE_FOOTER
        user32  :: #library,system,link_always,no_dll "user32";
        gdi32   :: #library,system,link_always,no_dll "gdi32";
        shell32 :: #library,system,link_always,no_dll "shell32";
        winmm   :: #library,system,link_always,no_dll "winmm";
        raylib  :: #library,no_dll "lib/raylib";
    MODULE_FOOTER

    // Add { Raylib } files to the generator.
    raylib_lib_directory     := tprint("%/lib", raylib_path);
    raylib_include_directory := tprint("%/include", raylib_path);

    array_add(*libpaths, raylib_lib_directory);
    array_add(*libnames, "raylib");
    array_add(*include_paths, raylib_include_directory);
    array_add(*source_files, ..string.["raylib.h", "raymath.h", "rlgl.h"]);

    // Generate module directory.
    raylib_module_directory     := tprint("%/%", LOCAL_MODULE_IMPORT_DIRECTORY, module_name);
    raylib_module_lib_directory := tprint("%/lib", raylib_module_directory);
    raylib_module_file          := tprint("%/module.jai", raylib_module_directory);

    // Recursively create directories for the Raylib module:
    make_directory_if_it_does_not_exist(raylib_module_lib_directory, true);

    // Generate bindings and save them as module.jai.
    generate_bindings(options, raylib_module_file);

    // Copy over the lib file:
    copy_file(
        tprint("%/raylib.lib", raylib_lib_directory),
        tprint("%/raylib.lib", raylib_module_lib_directory)
    );
}

This function may be called at any point during the build to generate a Raylib module (library). I decided to turn Raylib into a module because it allows me to easily separate it from my project and namespace it like so:

rl :: #import "raylib_win64";

// Hello, Window example:
main :: () {
    rl.InitWindow(1920, 1080, "Hello, Sailor!");
    defer rl.CloseWindow();

    while !rl.WindowShouldClose() {
        rl.BeginDrawing();
        defer rl.EndDrawing();
        rl.ClearBackground(.{ 40, 80, 160, 255 });
    }
}

This funnily enough makes using Raylib with Jai much better than using it with C or C++, because Raylib collides with many symbols defined in Windows headers and working around those collisions is annoying.

When I compile and run this program I get what is surely the best looking platform window:

An empty platform window

This empty window is the canvas upon which I will project the horrors unimaginable. Stay tuned!


Next: Oil Rig : Card rendering - First pass of the card renderer.