Linguica Posted January 14, 2010 iPhone developer Fabian Sanglard, before diving into the iPhone version of Doom, spent some time studying the original 1993-vintage Doom we all know and/or love in order to learn its many secrets. He's now posted a detailed article about the original software Doom renderer full of references to linedefs and segs and ssectors and all those things I understand just as vaguely now as I did back in the day. Most interestingly, the page includes some Quicktime videos showing the process of drawing a single Doom frame, slowed down considerably of course. 0 Share this post Link to post
magicsofa Posted January 15, 2010 It's way over my level of understanding but still cool to read about. It's always funny to see the inner workings of stuff like 3d rendering, where the common person doesn't realize how much calculation and time-saving is going on. 0 Share this post Link to post
myk Posted January 15, 2010 Pretty cool. This might serve as a reference for some things in the Doom Wiki. 0 Share this post Link to post
Macro11_1 Posted January 15, 2010 http://www.amitbhawani.com/blog/Images/W/Website-Bandwidth-Exceeded.jpg ops, didn't realize news posts don't allow images? :( 0 Share this post Link to post
_bruce_ Posted January 15, 2010 Love this stuff... Though Limit exceeded... at the moment... 0 Share this post Link to post
Reisal Posted January 15, 2010 I was gonna say the same...damn..I'm interested in that article too! 0 Share this post Link to post
Sporku Posted January 15, 2010 Really interesting stuff. I've always wondered what it'd look like if you did a step-by-step of the rendering process. 0 Share this post Link to post
_bruce_ Posted January 15, 2010 Link works now - props to Fabien Sanglard for his amazing work... 0 Share this post Link to post
david_a Posted January 15, 2010 What did he actually do? Slow down Chocolate Doom somehow? 0 Share this post Link to post
Xtroose Posted January 15, 2010 This is probably the best Doom engine technical article I've ever read. He did a great job explaining all the important functions and data structures. I was actually able to understand most of it. I'd really like to know how he did the slow motion rendering. I'd love to try the same thing with Eternity. It would be nice to see how portals are rendered in my test map. I'm especially curious about how the engine draws textures and sprites in the infinite hallway. 0 Share this post Link to post
Quasar Posted January 15, 2010 Xtroose said:This is probably the best Doom engine technical article I've ever read. He did a great job explaining all the important functions and data structures. I was actually able to understand most of it. I'd really like to know how he did the slow motion rendering. I'd love to try the same thing with Eternity. It would be nice to see how portals are rendered in my test map. I'm especially curious about how the engine draws textures and sprites in the infinite hallway. (This is to the best of my understanding - SoM is the real expert, so he may have to correct me on some fine points) Basically portal windows are accumulated during the drawing of the main scene and are then drawn afterward (not strictly recursively, as you might assume). Any portals accrued during THAT process are then subsequently drawn, etc. So just picture that exact entire process happening over again within smaller portions of the screen, and that's what you would see. Eternity will not currently render the same portal during the same frame more than 8 times, so the level of recursive descent is inherently limited. This is necessary to prevent runaway rendering into empty portal windows :) 0 Share this post Link to post
Xtroose Posted January 15, 2010 So that's how it works. I was sure there had to be a limit in the number of consecutive renders. Looks like a lot is going on behind the scenes, so I find it just amazing how fast the renderer is. SoM really did a great job. Thank you for taking the time to explain this to me. 0 Share this post Link to post
DaniJ Posted January 16, 2010 That is a really nice article. There are couple of very minor errors (e.g., the worst case draw rate for a given pixel can be much higher than three if you consider psprites and the various HUD displays) but nothing earth shattering. 0 Share this post Link to post
SoM Posted January 18, 2010 DaniJ said:That is a really nice article. There are couple of very minor errors (e.g., the worst case draw rate for a given pixel can be much higher than three if you consider psprites and the various HUD displays) but nothing earth shattering. I think the point of that was more to illustrate overdraw than to say that any pixels can only be written to 3 times in a frame. Although, it is worded awkwardly... 0 Share this post Link to post
fabinou Posted January 18, 2010 I'm glad some people enjoyed my article, I write this stuff mainly as memo for myself, but it is obviously a huge bonus when people like it. The way it's done is actually not rocket science, here is the process if you want to do it on Eternity: The approach is to limit the number of pixel drawn each frames, save the frame as a bitmap and generate a movie out of it. I used two variables: max_num_pixels=0 and remaining_pixels_for_frame. At the beginning on a frame in D_Display: remaining_pixels_for_frame = max_num_pixels; max_num_pixels++; All draw operations are nicely grouped in r_draw.c, depending on the rendition quality (high or low), the engine will use either: R_DrawColumn () R_DrawFuzzColumn () R_DrawTranslatedColumn () R_DrawSpan () or R_DrawColumnLow () R_DrawFuzzColumnLow () R_DrawTranslatedColumnLow () R_DrawSpanLow () For each method, test: if (remaining_pixels_for_frame >0 ) { remaining_pixels_for_frame-- draw } Two issues: - Doom never cleared the framebuffer because the entire screen was updated each frame, you need to do this otherwise you still have the menu or whatever was there before you started: In D_Display:memset(screens[0], 0, SCREENWIDTH * SCREENHEIGHT); Each frame, the screen is saved as BMP: In I_FinishUpdate:memset(screenName,0,256); sprintf(screenName,"./screens/draw%d.bmp",numFrames); SDL_SaveBMP(screen,screenName); I then used Quicktime to open an image sequence and generate an mp4. For the iPhone version, it's a little bit more complicated because it's working with GL_TRIANGLES and GL_TRIANGLE_STRIP but the approach is the same: limit the number of triangles, read the framebuffer via a glReadPixels, write the frame. In the end you get something like this: http://fd.fabiensanglard.net/doomIphone/doomiPhonePreview.mov Walls and Flats are drawn not in distance order but in textureID order to limit the number of openGL state change (something that you probably saw in Quake engine as well). The video also illustrate the way the tessellation worked via the blue floor (you can see the triangles drawn as a fan, left to right) If I find enough interesting stuff about iPhone version, I'll publish something. EDIT: I don't believe Eternity is SDL based but you can as well generated a TGA, here is a code snippet from my dEngine: int num_screenshot=0; void dEngine_WriteScreenshot(char* directory) { char* data; int i;//,j; FILE* pScreenshot; char fullPath[256]; char num[5]; char tga_header[18]; char* pixel; char tmpChannel; int renderHeight = 480; int renderWidth = 320 ; sprintf(num, "%04d", num_screenshot++); //itoa(,num,3); memset(fullPath, 256, sizeof(char)); strcat(fullPath,directory); strcat(fullPath,num); strcat(fullPath,".tga"); data = calloc(renderHeight*renderWidth, 4); glReadPixels(0,0,renderWidth,renderHeight,GL_RGBA, GL_UNSIGNED_BYTE,data); pScreenshot = fopen(fullPath, "wb"); memset(tga_header, 0, 18); tga_header[2] = 2; tga_header[12] = (renderWidth & 0x00FF); tga_header[13] = (renderWidth & 0xFF00) / 256; tga_header[14] = (renderHeight & 0x00FF) ; tga_header[15] =(renderHeight & 0xFF00) / 256; tga_header[16] = 32 ; fwrite(&tga_header, 18, sizeof(char), pScreenshot); //RGBA > BGRA pixel = data; for(i=0 ; i < renderWidth * renderHeight ; i++) { tmpChannel = pixel[0]; pixel[0] = pixel[2]; pixel[2] = tmpChannel; pixel += 4; } fwrite(data, renderWidth * renderHeight, 4 * sizeof(char), pScreenshot); fclose(pScreenshot); free(data); } It's no great code but it gets the job done. 0 Share this post Link to post
Quasar Posted January 18, 2010 Cool! Eternity has code to write TGA files already, you'd just need to modify it slightly from the screenshot engine :) Also if you enable the old BOOM option for showing HOM, the screen will already be cleared at the start of each frame (though it might be necessary to change the HOM floodfill color from that obnoxious red to something like black). 0 Share this post Link to post
hobomaster22 Posted January 19, 2010 fabinou said:Lot's of awesome stuff This is an awesome article. I've always wanted to understand more in depth how the Doom rendering worked. I've glanced at the code but the old procedural code hurts my brain. I'll have to try this stuff out when I have some free time. Thanks! 0 Share this post Link to post
Wobbo Posted February 12, 2010 Great Article, answers a lot of questions I've had for over 10 years. 0 Share this post Link to post