Wolfenstein yes, Doom no. Wolfenstein 3D used a very simple raycasting method that just shot rays out from the player's position to render a 2D level in psuedo-3D. Doom, while still technically using raycasting, uses a much more complicated method that involves BSP trees.
For anyone interested in the inner workings of Doom[1], I can't recommend Fabien Sanglard's blog enough. He also released a book on Wolfenstein 3D which goes into tremendous detail on the code, company, and hardware of the time.
Granted, the code is not really readable. But you can see the awesomeness.
Boot Windows 3.0 and DOS with a 4kB emulator.
C compiler in 4k, which can compile itself.
Global illumination renderer in 4kB.
Flight simulator in 4kB.
...
I wrote a similar code a few years ago, although it also supported textures (including ground and skybox, unlike the original Wolfenstein). I don't know if it'll be more to your taste but there are afew comments: https://bitbucket.org/simias/wolf/src/873cad46f3c8281e7d9429... the magic happens in wolf_horizontal_intersect. Note that I use fixed point which obfuscates the arithmetic a bit (I was targeting an embedded platform without hard float at the time).
That being said if you're interested in writing something like that I recommend that you use any basic tutorial on raycasting and try implementing it "from scratch" yourself, it's pretty simple and quite fun IMO. IIRC I used this one: http://lodev.org/cgtutor/raycasting.html
The text in the README describes the technique very well. The JavaScript code is a little bit more complicated to read because most of the source contains the usual organizational tasks around the algorithm (initialization, keyboard input, ...). But this is also true for the provided C-code of this article.
I am writing a rendering engine that I try to keep minimal and clean while having some cool computer graphics stuff in it. Although it is in modern C++. :-)
It uses a deferred rendering pipeline and currently I am trying to get light volumes to work.
The const can easily dropped later in production from both entities, potentionally creating two return values.
This is analougous to good use of privacy in C++ classes.
hero = spin(hero, key);
Becomes:
hero.spin(key);
Where external objects dictate the private state of the hero object.
Pointer aliasing is another thing. Inlining is easier with fewer pointers. A fun C++ experiment is to count your instruction and data cache points with valgrind's cachegrind with pod types both passed by value and by reference (&).
You might be surprised. For small structs, copying is very fast and may give you a result with better locality. Passing around structs by pointer means more pointer dereferencing, which has its own overhead and can cause cache misses.
You are correct. On a much larger project with a similar software engine, if I change all structs I pass by value to pass by reference my instruction count shoots up by 40% according to cachegrind (flto + ofast).
Amazing how powerful pass by value can be when it comes to pod structs.
This is pretty neat. I love how it fits (sanely) in a single file. I've been meaning to get into SDL for a while now, so this will be an interesting read.
Given that the original game predated those sorts of systems hitting consumer desktops by years, it shouldn't be surprising at all. This is what early 3d games & software rendering did before accelerators came along, and as CPUs got faster in that interim there were plenty of fully polygonal software rendered engines as well.
I wonder how many polygons make up a level of Wolfenstein in total - i.e. if you just asked a modern GPU to render every single wall in a level and relied on it to do the z-order sorting, culling, etc., what frame rate would you get?
I'd guess that if you were to triangulate an average map it might have probably been only in the region of 1-10k triangles.
Modern graphics cards on the other hand can easily push a few million triangles every frame with decent framerates. At 10k triangles * 10 or 20 maps you could load all maps from the game simultaneously and still easily get decent fps.
If you're on macOS, just slap a quick `brew install sdl2` before the instructions and it's golden - assuming you run https://brew.sh/