pevhs.ch ~/prog/progle/

Progle journal

OpenGL attempts (and now progle journal)

It's been quite some time since I started thinking about making some sort of page talking about the progress I'm making with OpenGL. I find learning OpenGL very interesting. It is however full of new concepts to discover, like shaders, buffers, matrices, etc. I still don't have a good grasp on most of these things.

I didn't learn it by myself; I used countless tutorials and websites, some of which are listed below:

Note: Any date mentionned in this page relates to the time I worked on the program, not when I actually wrote the text. I'm never consistent.

around january 2021

Not knowing any other language at the time, I started this project with C#, using the OpenTK libraries. Having extremely shaky bases with C# (I didn't know how to create a class when I first started) and not knowing anything about OpenGL didn't help much. Nevertheless, after two weeks of working non-stop on the project, here is what I managed to do:

A 2D hill formed of green cubes.

It's a very, very basic platformer. There isn't any levels or points or anything, it's just one map with some green blocks that the player can jump on. You see the little square at the top of that sort of hill? That's the player. I would've loved to have some sort of video showing the player move around, but I'm sadly unable to show anything as the program doesn't work anymore. I left the project alone for 2-3 weeks and now it crashes every time I launch it. I don't consider it a huge loss though, since I remember this was the messiest code I've ever written and half of it comes from other's tutorial-like GitHub repositories.

For the second attempt, I chose C as the programming language. I can now say that it was a much better choice than the previous language as much more documentation is available online for C and C++ than C#.The project now uses the FreeGLUT and Glew libraries.

12 april

After learning a bit C and after a break, I was able to make a triangle appear. Making the program capable of rendering multiple objects was surprisingly time-consuming. However after playing a bit with vertex array buffers, I got something to work.

A white triangle. Two orange triangles overlapping each-other.

Now, to the most dreaded part I've ever encountered up to this point: Windows compatibility. I sometimes only have access to a Windows 10 machine. This unfortunate fact lead me to learn makefiles, how to link libraries and header files to your program and the fact that Microsoft doesn't really care. They have direct3d, it's more than enough for them. Oh well. Looking back, I can say it was not a waste of time.

During this learning process, after multiple failed attempts to make my program work on Windows, I considered changing libraries for my program and using glfw/glad in place of FreeGLUT and Glew, which is what I ended up doing. "Fortunately", my attempts weren't more successful and I eventually came back to the original libraries and figured it out! Thank you to Martin Payne for creating Windows binaries of FreeGLUT, especially.

Now. The big hurdle at the horizon is that I don't know much about C, either. I've started to learn it specifically to create a more "reliable" OpenGL project that shouldn't explode to my face at random times. That being said, while I do now think that C is a very useful programming language, by volumes more useful than what I've been using up until now, I still suck at it. Here's the thing: Tutorials on OpenGL teach you about OpenGL, its calls and whatnot, but not about how to create a program that makes use of them intelligently to create advanced scenes (at least, it is the case of the tutorials I've found). Now, much of my learning process will be around C as I cannot continue learning OpenGL without being able to use my knowledge in a real program.

9 june

After making some code cleanup and learning more about how C works, I was able to set up a very basic, basic program capable of rendering multiple forms using different shaders.

A green and an orange triangle.

Learning matrices sure isn't very straightforward, but I was able to create the necessary matrices to have my models converted from model space to world space, and from world space to camera space. It makes sense when you get the feel of how things work, but it's tricky to set up at first. I will take the time to say that you should always double-check your code. I spent 2 days trying to debug my view matrix, searching the web in search of an answer, when the problem was in fact a little typo I'd made in a function calculating the dot product of a vector. I wasn't sure if I wanted to feel happy or angry after finding out.

I'm able now to load multiple objects, in 3D, in my scene. The one thing missing here is some lighting, but I first need to create some sort of parser to more easily load models in my scene. I'm thinking about an OBJ format parser.

One thing to note here is that I can't decide the location of the objects, it's all done at the vertices' level. This means it's impossible for now to reuse the same object multiple times.

A prism and a triangle in 3D. The same prism and triangle, but of different colors. A house on a hill, without any shadows.

23 june

Hello again. A few days later I have a somewhat working OBJ parser. It was a lot less difficult than what I was thinking, so that was a happy surprise. Now that I have a more simple way of loading models, I was able to implement some lighting. Nothing really fancy but it helps breaking the "flat world" effect. It's simply a point light illuminating everything around it.

A house on a hill being illuminated by a light.

Something to remember is that making OpenGL light the scene for us this way consumes a lot of ressources, since it has to recalculate the light on every pixels each frame. No big deal here, but it can be a problem in more detailled scenes. The answer would be to bake lights directly into the texture or make a separate "lightmap" and to associate a texture with its "intensity texture" when rendering. But I'm not even close to have enough knowledge to do this, so this method will be fine for now.

Adding textures was a bit tricky, but the program can now display textured models. Texture support was made possible thanks to this article which presents the basics of using textures with OpenGL: https://www.opengl-tutorial.org/beginners-tutorials/tutorial-5-a-textured-cube/ Although it lacks some crucial information related to the actual loading of the texture, most of the holes can be filled by reading the complete code over at their GitHub repository: https://github.com/opengl-tutorials/ogl

A house on a hill with textures.

9 july

I picked up the project after a couple of days and added some (very new and clunky) ways of loading models. A file (which can be considered the "map") is parsed and the program can now place the same object at different places in the scene. I gave its own model matrix to each loaded object, and the program now switch the shader's model matrix to the to-be-rendered object's matrix before rendering it. This is done for each object in the scene which makes me doubt that this is the best way to handle the problem of having the same model at different places, altough currently I don't see a better way. Also, the program doesn't reuse loaded models and reloads any model that has to be placed a second time.

Multiple trunks at different places. This model seems familiar.

14 july

After some head scratching I found a way to make the program smarter about how it loads models. It now checks if the model its about to load hasn't been loaded already, and if it has, it reuses it. It sounds a lot more simple than in reality though. Another bug I discoverd is that the scene seems to flicker (objects are not being rendered) at random times when there is a lot of objects to be drawn. This bug however doesn't occur if I lower the framerate to 58 rather than 60, or if I ask the renderer to immediatly render the next image as soon as it finished the previous one (So on my computer, the framerate climbs at 800+ fps). This is really weird, and I don't seem to be able to find a way to fix it. For now the framerate will be stuck at 58.

A lot of objects being rendered all around the place.

7 august

Maps now have a proper skybox. it's simply a cube whose shaders make so that it's always displayed behind any objects. The tricky part here was to make the skybox static, so that the player wouldn't be able to go and fly through it. After some failed attempts at making some shenanigans with shaders, I simply made it so that the skybox is always at the same position as the player.

Here's a bug I got: Video

9 august

After a big rewrite of how models are loaded, I was able to throw away over 50 lines of code and greatly simplify the process. Now, To be able to implement collision detection, I first have to calculate each models' bounding box. Since it's still full of spaghetti code, it took me a while to implement this function but I eventually got it working.

A tree surrounded by 8 black points.

A day and a big code overhaul later, things are starting to look much better to start toying with collision detection.

15 august

It took a lot of trial and error to get it working, but the program can now detect if the player's bounding box is intersecting with any other object's bounding box. Quite a lot of code had to be added/rewritten in order to implement this functionality. It's still a little hacky, though.

Here's a video of the system in action: Link

7 september

Oh crap, collision response is a pain in the ass. That's probably one of the most unintuitive things I've done up until now and I have no idea how I got it working. A big chunk of the added code wasn't written by me, as I have absolutely no idea how to do something this complex.

I tried to implement Kasper Fauerby's collision detection and response algorithm available here, but after spending literal weeks on it, the implementation I got is bugged. But honestly, I don't want to work on it anymore so I'm going to put it aside in order to work on something more enjoyable for a while.

A video of the collision detection system.

19 september

I'm now in the process of trying to implement a user interface to the program. I have some ideas that I want to do but I can't work on them without some buttons, so I first have to go ahead and do the "boring" stuff and try to implement something that would suit my needs. It was a hell of a lot tougher than I thought to get to the point that you can see in the screenshot below. Since I'm trying to implement a UI based on configuration files, I had to implement a couple of functions that scrape them and return the values needed. Spoilers: variadic functions are SO fun to implement.

Pink boxes on a screen.

1 january 2022

Happy new year!

I haven't made a lot of progress recently due to a lack of motivation. I picked up the project for a day and found a way to circumvent a rather annoying problem that made the player move slower when climbing or going down a slope. The movement feels more natural now.

20 january

Time for a change of pace. I've been doing a lot of OpenGL the last year, so I'm coming with something different this time. I recently had the chance to work on this program much more than usual, and I'm happy to say that I've managed to cram up multiplayer support in it! It's a very early form of multiplayer and a very ugly one if you look at the code, but the fact that I was able to put it together really blows my mind. This was only made possible thanks to the incredibly helpful guide by Brian Hall that you can see here. Thank you.

The code differs a tiny bit between Windows and Linux, but the effort to make it portable between those two platforms was incredibly low. The next updates will probably be about cleaning up the mess I made in the code.

2 green players.

22 january

The progress made these past two days was mainly focused on making the code a bit more bearable. Players that close the game will now disappear, instead of leaving their lifeless corpse float at the last place they were. Also, they now look at the direction their user is looking at which makes them look a bit less like statues. I mean, they don't have animations.

28 january

Getting multiplayer into a program is one thing, making it work well is a radically different story. I faced multiple segmentation faults, SIGPIPE signals (which I've never encountered before) and a bunch of other nasty bugs that just wouldn't go away. Buuut, I ironed out the edges of everything and I believe I made the network stuff much more stable. On 127.0.0.1, it's able to handle up to 20 players (server included) without drops in performance! More tests need to be done with real players on a LAN to test it's stability, but right now, I'm pretty happy of the work I've put out :)

An ominous picture of 20 players.

30 january

Two days later and the engine now has projectiles. You can rocket-jump and push other players around, but no collision can be done with players directly for now. It's still pretty to look at. A video of the projectiles.

2 february

Players can now strafe in the air, like in source games or Quake. It feels pretty different but you can now at least control where you are going when you are airborne. The code needed to implement it is absolutely not clear at first. I slept on it 2-3 nights, but eventually figured out how to make it work (it was a mixture of bad code written earlier and me not knowing what I was doing that made it hard to implement).

Also: Remember when I was complaining 7 months ago about the screen flickering at random times? Turns out, I wasn't in double-buffered mode at all! What this means is I was drawing directly on the screen instead of doing it in a separate "layer". So sometimes the screen would display a half drawn frame and objects seemed to be flickering (This was not happening on Windows, adding to my incomprehension). I wrongly thought that this mode was on by default as I recall this being the case in my earlier tests with OpenGL using C#. Oh well. Glad that it's fixed.

24 february

All of those models and entities are good and all, but the program was lacking something. I can't quite put my finger on it... Ah yes, it needed some text! The engine can now render text strings on the screen! This was one of the trickiest things to implement. it took only 2-3 days to code the whole thing, but it took weeks to wrap my head around what I wanted to do. At first the problem is absolutely not straightforward, and I'm glad I found a "mostly okay" (to my standards) way to do it.

I haven't coded everything yet but the implementation is I think for the most part finished. The only characters supported are from the ASCII table, unfortunately, so while it may seem good enough for english-speaking people, it's far from ideal. Maybe one day, a UTF-8 implementation will see the light of day.

A string saying \

26 february

When you suddenly find yourself being able to easily create strings, It's hard to resist the urge to spawn them everywhere. I went a little overboard and created a (local) chat system and a Vim-like interface to send commands to the program.

27 february

Players can now speak to each-other through the chat.

A showcase of the chat system.

22 may

No progress has been made this time. I don't have a lot of free time right now (and if I have some, I don't spend it on this project). But I have some time to think about the continuation of this project. The main things I'd like to do are: refactorization of some parts of the program, that were created when I had extremely little knowledge about C and now are barely standing up; and adding the ability to pass/receive commands to/from the server.

Why? Because I've started to think that having the program work as a client and as a server, like it does now, makes things a lot more complicated. Plus, all the functionality is hard-coded into the program, so if someone wanted to create a new ability or a new command, every client who would want to connect to their server whould have to have their version of the client. I want to be able to create multiples servers with different rules, but only one client to access them.

Splitting the server and the client would also have a performance benefit (even though that's not really my concern right now), because it wouldn't have to render anything, and wouldn't need OpenGL to work. This is a huge plus, as it would allow the server to run on a wide range of Operating systems that aren't supported now, like the BSDs, other UNIX-likes or Plan 9. In fact, I'd like to try to develop this server on 9front (a Plan 9 fork) and possibly have one running 24/7 on a VPS for anyone to join.

But I shouldn't think about that: I don't have the time right now. I'm thinking about a new "protocol" to pass data between the server and the client and new commands to implement. I'll post here text files containing the ideas I come up with. Until then, have a great day!

PS: I really should think about renaming this file, as it's not really solely about OpenGL anymore.

4 june

I've managed to develop a server for the game on the 9front OS. while I said that I wanted to create something cross-platform, I have to admit that I was more occupied with learning 9front than create something cross-platform (This thing is completely alien to me). Nevertheless, I managed to glue a server together. I didn't give up on the cross-platform part - Almost no-one uses 9front, or know about it's existence - but I want to make the code a little prettier before I tackle this task.

12 june

The server is a bit more stable now. It supports both 9front and Linux, and its internals are a bit more managable. While a lot more needs to be done, I'm starting to see that a lot of the code from the client needs to be reworked. If I'm lucky enough, I can backport the network code I wrote for the server without much trouble, but most of the work will be around deleting a ton of code that shouldn't be in a client in the first place. Right now, a lot of logic is hard-coded into the program like the projectiles, the health bar and so-on. This should all be handled server-side. Once the server is more feature-complete, I'll release the sources alongside the client.

There isn't really a point anymore to have an embedded server inside the client like it is right now, although it brings a certain level of convenience since there is no need to setup a separate program to play in multiplayer. But I'm pretty confident a couple of shell/batch scripts should be able to produce a similar experience.

8 october

This was a long ride. I stopped working on this project for a couple of months now, not having a lot of time to work on it. The general messiness of the code also prevented me to work on small things, since modifying a part of the program can have unexpected side effects. I've started working on a second version though, called prol. (Im still the best at naming things.) Some of the code from progle will be transferred to this new version (after some cleanup), so I won't be starting from scratch.

The download links are in the dl document.