Monday, 13th March 2006
A few years back I had a go with DirectX 8, helped along by the excellent DirectX4VB website and tutorials.
The best thing I cranked out was a simple DOOM-ish 3D engine, minus sprites (just walls and floors).
(All images are click-for-big).
I fancied another go, this time not in VB6 but in C# and not with DirectX 8 but Managed DirectX 9.
I had an old copy of the SDK so got the obligatory spinning coloured untextured triangle going, then hunted for a proper project to learn with.
Keeping with the theme of last time's venture, I decided to go back to DOOM, but not DOOM-ish - I'd load the levels and graphics from an original DOOM WAD file. There's something oddly satisfying about pressing F5 and watch a familiar 3D world spring into view
I'd written a very simple VB.NET WAD resource viewer, so rewrote the various graphics functions in C# and dug out a hex editor to work out what goes on inside DOOM.
Each level has a similar bunch of data lumps ("lump" is the name of a single resource within the WAD); THINGS that define the location of objects inside the map, NODES for the BSP tree, VERTEXES for the vertices that make up a level and so on.
One problem with DOOM is that there is no "vectorised" floor. The walls are easy enough; by studying the LINEDEF/SIDEDEF/SECTOR lumps you can work out the VERTEXES you need to use to build up a single wall. Floors are filled in after the walls are drawn; to be able to draw a floor on 3D hardware I'd need to split down the sectors into triangles manually. For the moment I shall concentrate on walls.
A quick bit of coding later; drawing by splitting each wall into a small vertex buffer (two triangles) then drawing (one end of the wall is white, the other grey) gives me this:
Something's working... but something isn't, as well. Moving around looks odd, and no wonder; no Z-buffering!
Switching on Z-buffering and colouring each wall based on the light level of the sector gives me these rather better results:
One thing that I've never been aware of when it comes to 3D graphics are the best practices for how I send data to the graphics card. For this, I've grouped every single wall by texture. I've loaded all the textures I'll need for the walls into an array, then built up a vertex buffer for each texture's walls. When I render, I cycle through each texture, setting it as active then drawing that texture's vertex buffer. It appears to be fast enough, but then again DOOM with it's ~2000 poly levels isn't demanding much!
Texture mapping adds a lot of detail to the world... so:
The textures appear a bit odd as they're being simply stretched to fill the entire wall they're put on rather than tile neatly as DOOM intended. That's an easy enough fix:
There are still some texturing glitches, most noticeably on walls where the lower texture (the texture that spans the gap between the floors of two sectors of different heights) where they are marked as "unpegged":
I can't quite work out how the texturing is meant to go there... so I've left it for the moment, and had a bash at floors.
It appears that a combination of subsectors (SSECTORS) and segments (SEGS) can be used to represent convex polygons that make up sectors. I added a bit of code to the level loader that rips out the subsector and segment information - it produced these images:
This doesn't look too promising; if you look at the central part (where the green acid pool is -- this is E1M1) you can see there's a rather large area with no segments in it at all. I added a simple bit of code that broke up the segments into fans; here's the result:
The hole is very noticable in that image. Quite a few of the segments are made up of single lines (just two points), which leads me to believe that I'm thinking that they're something they're not.
For the moment DOOM has to go without floors.