Learning OpenGL with the Superbible Book

Well, the previous thread was a bit awkward to introduce this series, and let’s do this in a more formal and organized way.

I am currently learning OpenGL using the OpenGL Superbible7th edition, and trying to program some of their examples using zig and some of the available zig dependencies. To make it have the look and feel to the original examples from the book, I have ported the built-in sb7.h framework into zig so that it offers some ways to “override” the functions from the framework although the lack of class in zig.

Hopefully through these projects, these could help people to learn OpenGL using the book with an alternative besides C++.

In addition, I know the current solution of using function pointer as a substitution to classes and inheritance is not the most ideal solution. Feel free to point that out if anyone have some better solutions and ideas.

Here are the examples:

I will add more examples when I make some more progress to other chapters, and I will update the code from time to time for a better explanation.

1 Like

I’d suggest to take a step back and think if you really need it like this. Most of the time any sort of vtable or function pointer approach is not necessary. Think about who you are programming this for. In your example I see absolutely no reason why you couldn’t just call e.g. main.render instead of storing it in a function pointer.

Furthermore if you do want to keep the framework separate and its own thing without calling into main, I’d suggest to look towards a more direct abstraction, like

pub fn main() {
    app.init();
    defer app.deinit();

    startup();
    defer shutdown();

    while(!app.shouldClose()) {
        app.processEvents();
        render();
    }
}

This also makes it easier to understand what is actually going on instead of being a black box that somehow peridically calls the render function for you.

1 Like

I know this is definitely the question people would ask and in fact what you have mention is more straightforward and you clearly saw that I was not satisfied with the current implementation, but the problem was a bit complicated, so let me show you the “simplest” example from that original C++ code:

#include <sb7.h>

class simpleclear_app : public sb7::application
{
    void init()
    {
        static const char title[] = "OpenGL SuperBible - Simple Clear";

        sb7::application::init();

        memcpy(info.title, title, sizeof(title));
    }

    virtual void render(double currentTime)
    {
        static const GLfloat red[] = { 1.0f, 0.0f, 0.0f, 1.0f };
        glClearBufferfv(GL_COLOR, 0, red);
    }
};

DECLARE_MAIN(simpleclear_app)

The example of the book is really like what you have mentioned, which is a complete black box and I had no idea how sb7.h and DECLARE_MAIN() works in the first place, not to mention some other “in-house” libraries hide under the sb7.h that have already been replaced with some zig dependencies, and it has no documentation (as far as I have read the book) about that part in the book and it kept on telling me just call and override the function in sb7.h. I have read an earlier edition of the book in a school library (no longer have the access) where it didn’t have such encrapsulation and I do think that is clearer than the latest edition; however, 7th edition is the only version I have, and it is also the only version that is available on the book store, so I have to stick with what I have.

As a result, instead of writing the code in a more optimal and more idiomatic way, I have to write code aligning to the book so that not only I can understand what sb7.h is really doing, but it also offer a smoother transition for migrating the code from C++ to zig without rewrite all the configuration from sketch for every projects, so I did the function pointers.

I want to write what you have mentioned which I did in my first triangle as well, but your example will end up putting all the code inside the original run() function back to the main, flooding with full of all the original configurations for glfw instead of showing the gl part that actually draws the shapes. Perhaps I will find a way to expose the main event loop only, but try to have a clearer separation between gl and glfw in the later version. For the “virtual” functions, I will find a better way because their callback have a strict input parameter typed with C convention, and I prefer to list the input and output type of the callback instead of repetitively correcting and guessing the callback function based on the compiler.

Some of the practice is not necessary and I definitely need some time to get rid of the OOP part of the original C++ implementation, but now I know how it works and I have a working zig solution, so It will be easier for me to improve or rewrite the code from a bad implementation.

I have added a disclaimer in my project because I don’t find a way to write better version that aligned to the content of the book for now, while I have many other things to learn in a short amount of time.

I will also rename that “framework” project into just triangle, so that to make it less encourage to use that as a framework. If some LLMs use that code as an example, good luck to the LLM users, unless I somehow find a better alternative to the current solution.

I will be hugely appreciated if someone manages to port “sb7.h” with idiomatic zig practice that is not breaking the tutorial.