DOOM-Style Renderer
Saturday, December 4th, 2010I’m gonna start this blog off with something I did about a year ago. It’s a DOOM-style “3D” renderer in Flash! It uses textures from the original Wolfenstein… ah, memories.
Still in “early tech demo” stage, but I might make it into an actual game at some point.
My first attempt was to use a “Wolfenstein” raycasting rendering system that draws vertical wall strips, however that turned out to be too expensive and slow in Flash. After a little digging I came across the Flash 10 drawTriangles method which was pretty quick and worked well.
So here’s a quick rundown of how the “DOOM” style rendering works ( I put that in quotes, because it’s not quite the same as DOOM due to the drawTriangles rendering method ):
First things first, I create a 2D top-down representation of the level. This is simply a collection of 2D vectors that form a few convex sectors. There’s an editor mode in the SWF where you can draw and save levels using a 2D grid, but it’s VERY programmery, so it’s currently not accessible. Each sector has it’s own floor and ceiling height. In the demo level there are 3 sectors ( you can think of them as rooms ). The big room you start out in, the long hallway and the small room at the other end of the hallway. You can adjust the floor and ceiling heights in a room by standing in it and using G / SHIFT-G and the UP / DOWN arrow keys. This logic could be used to create elevators and other tricks like rising and falling water.
When the game is running, each 2D wall endpoint is transformed into the player’s view space and some basic culling is done to figure out exactly what walls are possibly visible. You can see this if you press ‘M’ to view the debug map and look at the green colored walls. Note, that at this point, it’s totally possible to have a wall that’s within the player’s view cone, but obscured by a closer wall. The next step takes care of that issue. You’ll also notice that there are “walls” where the sectors meet. These are normal walls, but are handled specially by the renderer to only draw the bottom or top “steps” if they’re visible. The rest of wall isn’t rendered obviously. These are more complicated, but just require a little more handling in the renderer to draw them properly.
Once the walls are determined, they’re then projected to screen space and the top and bottom of each wall end point is calculated. This data is then used to clip each wall against the others so we don’t have any overlap. After all the math is done, we should have a perfectly clipped set of wall segments that span from the left side of the screen to the right without any overlap. This clipping and culling is what makes the rendering so fast since there won’t be any pixels that are touched more than once. This is only possible given that the map is really just a collection of a few 2D vectors, making the clipping and projecting extremely simple compared to a true 3D scene.
Floor and ceiling sections are rendered by simply extending the tops and bottoms of each wall section straight up / down to the extents of the screen. There’s a lot more to it than that, but that’s basic concept.
A few more calculations are done to figure out the texture UVs for each wall, ceiling and floor section and then everything’s rendered using drawTriangles. The beauty of all of this is that once all of the math is done ( and it’s not a huge amount ), the rendering is just a few textured triangles that don’t overlap. It’s extremely fast!
“Lighting” is faked by rendering each triangle in the scene twice. Once for the texture and another time for a black, shaded triangle that gets darker the further it is away from the player. This obviously slows the renderer down as it’s now having to touch each pixel twice, but the effect is worth it. Press “F” to see what it looks like without it.
Sprites ( the barrels ) are simply points inside the 2D map. For each point, it’s again transformed into player view space and clipped against walls and other sprites. The sides are extended out from the point using the current perpendicular view vector in order to create a billboard that’s always facing the player. Once everything’s been clipped, they too are rendered using two drawTriangles calls and another two for shading using a single shade value depending on the distance from the player.
As far as input goes, the biggest challenge was getting the mouse to feel as good as possible given that Flash doesn’t have the ability to lock the mouse within the Flash region. Without this functionality, there’s no way to implement true FPS mouse aim controls since the player can’t continue to move the mouse in the same direction as it will just leave the Flash area. However, after a ton of tweaking and tests, I think what’s in there now is actually pretty good. It definitely takes a little bit to get used to, but after that it feels ok. The logic behind it is very simple: there is a square in the center of the screen ( you can see it by pressing ‘I’ ). When the mouse is inside that region, the player’s view is adjusted slightly but is static. As soon as the mouse leaves that region, the player’s view will start turning towards the mouse. The closer to the edge of the Flash region, the faster the player’s view will move. That’s it! Obviously it’d be nice to eventually have some options so that each player can tweak the input speed and what not to their liking.
So, that’s a very high level overview of how everything works. If you want more detail, checkout the research sites below… it’s a dump of all my “DOOM-Style Renderer Research” bookmarks. Or send me an e-mail.
Enjoy!
[swfobj src="http://www.regularkid.com/wf/WF.swf" width="600" height="337"]
Controls
( Click to activate mouse controls )
MOUSE - Look
SPACE - Jump
C - Crouch
F - Toggle shading ( fog )
M - Toggle debug mini-map
I – Toggle debug mouse input look region
G + UP/DOWN ARROW KEYS – Adjust ceiling height for current sector
SHIFT-G + UP/DOWN ARROW KEYS – Adjust floor height for current sector
SHIFT-F + UP/DOWN ARROW KEYS – Adjust fog distance
Research Sites:
Best tutorial on the web for understanding Wolfenstein rendering
drawTriangles tutorial
Another drawTriangles tutorial but includes info on perspective texture calculations
Article on Flash bitmap data and vector optimizations
Another Wolfenstein rendering how-to
DOOM / Hexen / Heretic Flash port using original source and Alchemy
Collection of some great old-school graphics tutorials including a section on raycasting
I also downloaded the original Wolfenstein and DOOM source code files, but can’t seem to find the link to them. If you can find them, they’re obviously a GREAT reference. Plus, it’s just plain old cool to dig through John Carmack’s code!
There are 11 comments in this article: