pevhs.ch ~/prog/prol/

Prol journal

Prol: a (rudimentary) 3d engine

Welcome back! This is the second version and successor to progle.

After the experience I've gained making progle, I started to think I could build a second version. I wanted to do this mainly to throw away the huge mess of code progle had become. After wanting to go back working a bit on it, I've quickly noticed that I just couldn't get anything done. As of October 2022, this is the state of progle:

I'm proud of what progle has become, after working on it on and off for more than a year. But its design made me create all the functionalites into one big, chuncky codebase. This seriously limited code reuse and portability. I will try this time to compartmentalize every functionality early on in order to keep them reusable and hopefully portable. Everything is separated into different libraries to make sure that no ugly dependencies start creeping in.

The aim of this project is a bit different: I won't try to produce something as big as progle aimed to be, but will rather focus on simplicity and reusability. The ideal goal would be to have a project which I can easily port to other platforms (Windows, UNIXes, etc.) and to which I can easily add new ideas without modifying half of the source files. I wouldn't want to create a game, but to have the choice to easily do so. OpenGL isn't the main objective now, I'd much rather have a "renderer-agnostic" engine.

I won't keep a huge log like with the previous program, as I've found that it makes me write more sentences than needed to get the point accross. Still, I'll allow myself to write some entries from time to time.

Note: there is nothing "pro" about this program. The names I pick are just very bad and I don't want to rename the thing now.

8 october 2022

For this project I decided to use GLFW3 instead of FreeGLUT. After some days working on it, the program can load OBJ files and render multiple objects. No work has been done with the camera yet.

An orange shape and a red square.

The organisation of the project right now looks like this:

.
├── Makefile   <- one Makefile to rule them all
├── csv        <- to parse csv/tsv files
├── geo        <- contains vector/matrix functions
├── mdl        <- code to load models
├── ren        <- the renderer/callback handler (since rendering libraries often do this job as well)
└── prol
    ├── assets    <- models, shaders, etc.
    ├── lib       <- all the previous projects go here as libraries
    ├── Makefile  <- per-project Makefile
    └── src

Some libraries depend on others, like the renderer depends on the geo functions. But overall, the different aspects of the project are for now pretty well isolated.

11 october

There is now a proper 3D camera that can be controlled by the keyboard. It's a bit rudimentary, but it works. I'm trying to put into place an interface between GLFW and the rest of the program so that I could in the future swap GLFW with a new rendering library if I wanted to.

20 october

Although with around 5 seconds of wait, the program was able to load a 372MB .obj file and display its model correctly. Gone are the days where there was a hard-coded limit of 24000 vertices, we're talking about a dynamic allocation of 10,107,006 vertices now (no vertex reuse between triangles).

30 october

The last week or so has been entirely dedicated to code cleanup. Since a lot of code in different libraries make use of a "stack" to store structs, I'm creating (again) a library to handle just that. This should resolve the current situation where I have 5 different functions which do more or less the same job spread throughout the program.

4 february 2023

After trying to decouple the engine and the fps, I've stumbled upon a very interesting article by Glenn Fiedler. Thanks to his writing, I was able to understand and implement a solution that should do the trick, altough I can't really make sure that it works well right now since no movement has been implemented yet. Fps and engine speed was a really big issue in progle, as changing the fps there broke things and made some parts of the game faster or slower.

One thing I noticed is that the camera becomes increasingly harder to move the more the fps go up. I can't seem to find the reason why, but it only starts to be an issue at very high framerates (1000+). So I'm not too worried about that, although I would prefer to know what causes it.

5 february

A proper camera has been added which can freely navigate in the scene. The engine now interpolates in-between updates to make the frames look smoother. Before this change, the camera was very stuttery as I set the engine's "tick speed" to 30 per second, and the renderer was generating 60 images per second. With this change, one can set up virtually any fps and "tps" (tick/second) whithout seeing any stutter, providing that the fps is higher than the tps. Indeed, I found that having a higher tps can create visual glitches. It's no problem if it's only for a brief instant (e.g. in the case of high render load) but if it has been deliberately set up that way it's something that can happen. I'm not too worried about it though, because I think the cases where you may want to make the engine work harder than the renderer are few.

Of course, the amount of fps doesn't impact the engine speed anymore, but changing the tps does. I personally set the tps to 30 as I think it's a nice compromise between smoothness and responsiveness (for comparison, Minecraft's tps is 20).

9 february

Of course, being restricted to the amount of tps also felt off, and so the next few days were spent trying to figure out how to make the player movement independent from those as well. Making the position update independently from tps was easy enough (pos += vel * deltatime * mult), mult being an int of 128 to increase the result a bit (it's working with a delta time of 0.0333, the result can't be seen otherwise). But implementing the friction to reduce the velocity was another matter (you can't multiply the velocity by the friction itself multiplied by delta time). But I eventually figured out a way to do it:
vel *= pow(friction /*0-1*/, deltatime * mult)
Now I get consistent movement whatever the tps is (10, 60, 9999...) and I can change mult to modify the speed of the engine without having to sacrifice precision or performance.

There's not anything to see, but here's a screenshot of me testing stuff.

Some models floating on a green background.

13 february

I've been trying to implement a crude scripting language into the engine. After around 2 days, I got something that works decently, considering that the whole logic behind it is a single switch. I wanted to create is because I though that it could be a nice way to implement trivial things by writing scripts instead of hard-coding them in the engine. Scripts will never match the speed of actual code though, so I should use this sparingly.

Not many commands are done right now, so this is simply a proof of concept. This script spawns an error model at 10 x, 10 y, 10 z.

# Prol scripting
echo "===========================\n"
echo "=       Test script       =\n"
echo "=       prol engine       =\n"
echo "===========================\n\n\n"

var m error
var p 10 2 10
var e (mdladd @m@ 0 0)
mdlpos @e@ @p@

echo "model @m@ \(id @e@\) appeared at pos {@p@}!!\n"

Edit: After a complete rewrite of the script code, it's now possible to only execute, say, 5 commands at a time and continue the execution of the script later. Since all the infos are now stored in structs, it's possible to run parts of multiple scripts one after another, which could be a good thing if the scripts are big so as to not make the engine hang.

I had the idea to add support for int, float and vector variables and things like if/else, while, etc., but after reconsideration I've abandonned those. Partly because it's way too difficult for me, and also because I thought that if someone wanted to write advanced stuff like that, they might as well use a real scripting language or simply code what they want to do. I would like to add the capacity for the engine to receive commands through a text file or something like that.

19 february

Gah, making a scripting language sure is hard

If you even can call it a scripting language, as you can't really write anything more complicated than aliases. But after a good week of rewrite and re-rewrite, I've managed to make something usable. The code was at first relying on the called command to parse the given arguments (with sscanf() for example), but I've decided to mimic the behavior of shells and provide **argv and argc variables. This way, commands don't have to worry about missing arguments as it's up to the parser to properly separate the given string. This has introduced another layer of complexity though, since the parser has to worry about spaces, quoted strings and so-on. I'm not really happy with the code as it stands but I won't spend another week on it. Hopefully I will come back to it periodically and make improvements whenever I'm able to have a clearer head about what a shell is supposed to do. The code is in a separate library to keep things clean and reusable.

But, with the power of aliases and arguments, it's now really easy to create commands. The internal alias and prol-specific bind have been created, allowing to create user-defined commands and to execute commands on keypresses. an example of the two working together:

alias zoomin 'fov 20;bind +c zoomout'
alias zoomout 'fov @fov@;bind +c zoomin'
bind +c zoomin

This creates a simple "zoom" effect when pressing C. As you can guess, all movement is now handled by commands which greatly simplifies the addition of new behavior. I find binds and aliases very useful as I don't need to worry about what key does what, as I can just handle that with a couple of lines in a script that auto-executes at startup. Neato.

And in the future, programs that use the prol library will be able to simply send their own, custom list of commands to the parser and rely on scripts for (simple) stuff. I'll probably write some sort of manual in this directory to explain the script syntax. Not before I make some sources of this project public though.

7 march

After a while, I've created a way to add gravity to the scene. Entities now are attracted to gravity points. It's still a work-in-progress though, so it's not really useful in any way (collisions aren't implemented). It still is pretty to see.