Hacker News new | past | comments | ask | show | jobs | submit login
A Tiny Wolfenstein-Like Software Engine (github.com/glouw)
267 points by glouwbug on March 7, 2018 | hide | past | favorite | 44 comments

FWIW, this a nice miniature C project to noodle with Makefiles and some basic C if you're new to it.

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/

> early wolfenstein and doom days.

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.

A quick google search found this page, which explains it much better than I can: http://doom.wikia.com/wiki/Doom_rendering_engine

The Doom community left Wikia to host their own, independent, non-profit wiki, please always try to use and link that one instead: https://doomwiki.org/wiki/Doom_rendering_engine

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.

[1] http://fabiensanglard.net/doomIphone/doomClassicRenderer.php

He's actually working on a companion book covering the Doom / id Tech 1 engine now, too.

Correct. The doom adage was in reference to ceiling and floor casting support; wolfenstein painted the ceiling and floors a solid color.

Related --- doing something very similar in 128 bytes of binary: https://news.ycombinator.com/item?id=7960358

There should be Awesome List with small simple implementations of any kind, like this.

In principle, such a list exists. It is called Obfuscated C Code Contest


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 forgot the fluid simulator in 4kB.

Great learning project. Having it <300 lines whiles till being clear and easy to navigate is fine work.

Can anyone recommend something similar but with more code comments?

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).

It looks like this while running: https://svkt.org/~simias/misc/wolf-floor.png

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

Please add Readme with few words about project.


A text editor by the guy that made redis, in less than 1000 lines and a lot of comments.

There’s also a tutorial to build kilo at https://viewsourcecode.org/snaptoken/

Tiny Renderer or how OpenGL works: software rendering in 500 lines of code


Very similar technique (raycasting), but for different type of data.


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.


Thats a very interesting name for your project

Not really better documented, but still great gem to read: https://github.com/fogleman/Craft

Biased because it's my own, but here's one in Javascript and React: https://github.com/ahuth/raycast.

Interesting that it passes/returns structs by value quite a lot which is not usual in C (in my experience.) I assume there's a reason for this? e.g.

static Hero spin(Hero hero, const uint8_t* key)


I do so to enforce a single return value from functions.


    static void damage(const Hero* hero, const Sprite* sprite);
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);

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 (&).

I think it's just for simplicity, since all that copying can't be faster than passing in a reference to it.

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.

Performance of modern CPUs is very nonobvious.

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.

Very well written and easy to read C.

Neat that they did it without a graphics API

That's what "software engine" in the title is supposed to describe :)

SDL2 is the API

True, I guess I meant a rasterization library. It looked like they were mostly using SDL2 for interfacing with the OS and blitting to the screen.

(and to be more specific I really meant without opengl|directx|vulcan etc)

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.

Interesting how meaning changes. Back when Wolfenstein was made, "without a graphics API" just meant

    mov ax, 13h
    int 10h

... and even there you were using a graphics api, provided by your video card's bios and compatible across various manufacturers.

Not really - you'd be mostly just writing into the video buffer.

int10h is the API (BIOS)... that just causes the mode switch. What he really meant (direct RAM access):

   void putpixel_13h(int x, int y, int color)
      _farpokeb(_dos_ds, 0xA0000+y*320+x, color);

Yeah, but nobody does that.

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.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact