June 8, 2012

Doom3 Source Code Review: Dmap (Part 2 of 6) >>

Like every id Software engine the maps generated by the design team were heavily preprocessed by a tool in order to increase performances at runtime.

For idTech4 the tool is named dmap and its goal is to read a soup of polyhedron from a .map file, identify areas connected by inter-area-portals and save those in a .proc file.

The goal is to power the runtime portal system of doom3.exe. There is an amazing paper from 1992 by Seth Teller: "Visibility Computations in Densely Occluded Polyhedral environment" : It describes well how idTech4 works with many explanatory drawings.

The editor

Designers produce level maps via CSG (Constructive Solid Geometry): They use polyhedrons that usually have 6 faces and place them on the map.
Those blocks are called brushes and the following drawing shows 8 brushes used (I use the same map to explain each step in dmap).

A designer may have a good idea of what is "inside" (on the left) but dmap receives a brushes soup where nothing is inside and nothing is outside (on the right).

Designer view Dmap view as brushes are read from the .map file.


A brush is not defined via faces but by its planes. It may seem very inefficient to give planes instead of faces but it is very helpful later when checking if two faces are on the same plane. There is no inside or outside since the planes are not oriented "consistently". The planes orientation can point outside or inside the volume indifferently.

Code overview

Dmap source code is very well commented, just look at the amount of green: There is more comments than code !

                                                                      
                                                                      
     bool ProcessModel( uEntity_t *e, bool floodFill ) {
     
     bspface_t        *faces;
     
     // build a bsp tree using all of the sides
     // of all of the structural brushes
     faces = MakeStructuralBspFaceList ( e->primitives );
     e->tree = FaceBSP( faces );
     
     // create portals at every leaf intersection
     // to allow flood filling
     MakeTreePortals( e->tree );
     
     // classify the leafs as opaque or areaportal
     FilterBrushesIntoTree( e );
     
     // see if the bsp is completely enclosed
     if ( floodFill && !dmapGlobals.noFlood ) {
       if ( FloodEntities( e->tree ) ) {
         // set the outside leafs to opaque
         FillOutside( e );
       } else {
         common->Printf ( "**********************\n" );
         common->Warning( "******* leaked *******" );
         common->Printf ( "**********************\n" );
         LeakFile( e->tree );
         // bail out here.  If someone really wants to
         // process a map that leaks, they should use
         // -noFlood
         return false;
       }
     }
     
     // get minimum convex hulls for each visible side
     // this must be done before creating area portals,
     // because the visible hull is used as the portal
     ClipSidesByTree( e );
     
     // determine areas before clipping tris into the
     // tree, so tris will never cross area boundaries
     FloodAreas( e );
     
     // we now have a BSP tree with solid and non-solid leafs marked with areas
     // all primitives will now be clipped into this, throwing away
     // fragments in the solid areas
     PutPrimitivesInAreas( e );
     
     // now build shadow volumes for the lights and split
     // the optimize lists by the light beam trees
     // so there won't be unneeded overdraw in the static
     // case
     Prelight( e );
     
     // optimizing is a superset of fixing tjunctions
     if ( !dmapGlobals.noOptimize ) {
        OptimizeEntity( e );
     } else  if ( !dmapGlobals.noTJunc ) {
        FixEntityTjunctions( e );
     }
     
     // now fix t junctions across areas
     FixGlobalTjunctions( e );
     
     return true;
     }
                                                                      
                                                                      

0. Loading the level geometry

A .map file is a list of entities. The level is the first entity in the file and has a "worldspawn" class. An entity contains a list of primitives that are almost always brushes. The remaining entities are lights, monsters, player spawning location, weapons etc ...


   Version 2
                            
    // entity 0
    {
        "classname" "worldspawn"
        // primitive 0
        {
            brushDef3
            {
                ( 0 0 -1 -272 ) ( ( 0.0078125 0 -8.5 ) ( 0 0.03125 -16 ) ) "textures/base_wall/stelabwafer1" 0 0 0
                ( 0 0 1 -56   ) ( ( 0.0078125 0 -8.5 ) ( 0 0.03125 16  ) ) "textures/base_wall/stelabwafer1" 0 0 0
                ( 0 -1 0 -3776) ( ( 0.0078125 0 4    ) ( 0 0.03125 0   ) ) "textures/base_wall/stelabwafer1" 0 0 0
                ( -1 0 0 192  ) ( ( 0.0078125 0 8.5  ) ( 0 0.03125 0   ) ) "textures/base_wall/stelabwafer1" 0 0 0
                ( 0 1 0 3712  ) ( ( 0.006944  0 4.7  ) ( 0 0.034   1.90) ) "textures/base_wall/stelabwafer1" 0 0 0
                ( 1 0 0 -560  ) ( ( 0.0078125 0 -4   ) ( 0 0.03125 0   ) ) "textures/base_wall/stelabwafer1" 0 0 0
            }
        }
        // primitive 1
        {
           brushDef3
        }
        // primitive 2
        {
            brushDef3
        }        
    }    
    .
    .
    .
   // entity 37
   {
        "classname" "light"
        "name" "light_51585"
        "origin" "48 1972 -52"
        "texture" "lights/round_sin"
        "_color" "0.55 0.06 0.01"
        "light_radius" "32 32 32"
        "light_center" "1 3 -1"
   }
                        
                            

Each brush is described as a set of planes. The sides of a brush are called faces (also called windings) and each is obtained by clipping a plane with every other planes in the brush.

Note : During the loading phase a really neat and fast "Plane Hashing System" is used: idPlaneSet built on top of a idHashIndex it is really worth taking a look at it.

1. MakeStructuralBspFaceList & FaceBSP

The first step is to slice the map via Binary Space Partition. Every single non transparent faces in the map wil be used as splitting plane.

The heuristic to select a splitter is:

1 : If the map is more than 5000 units: Slice using an Axis Aligned Plane in the middle of the space. In the following drawing a 6000x6000 space is sliced three times.

2 : When there is no more parts bigger than 5000 units: Use the faces marked as "portal" (they have material textures/editor/visportal). In the following drawing the portal brushes are in blue.

3 : Finally use the remaining faces. Select the face that is collinear to the most other planes AND split the less faces ; Also try to favor axial splitters. The splitting planes are marked in red.


The process stops when no more faces are available: The BSP tree leaf all represent a convex subspace:



2. MakeTreePortals

The map is now divided into convex subspaces but those subspaces have no awareness of each other. The goal of this step is to connect each leaf to its neighbors by creating portals automatically. The idea is to start with six portals boundering the map: The connect "outside" to "inside" (the root of the BSP). Then for each nodes int the BSP: split each portal in the node, add the splitting plane as portal and recurse.





The original six portals are going to be split and propagated all the way down to the leafs. This is not as trivial as it seems since each time a node is split: Every portals it is connected to must also be split.

In the drawing on the left one portal is connecting two BSP sibling nodes. Upon following the left child its splitting plane cut the portal in two. We can see that the other node portals must also be updated so they don't connect to a sibling anymore but to its "nephews".



At the end of the process the six original portals have been split in hundreds of portals and new portals have been created on the splitting planes: Each leaf in the BSP has gained awareness of its neighbors via a linked list of portals connecting it to leafs sharing an edge:

3. FilterBrushesIntoTree

This step works like a game of Shape Sorter with the BSP being the board and the brushes being the shapes. Each brush is sent down the BSP in order to discover which leafs are opaque.

This work because of a well defined heuristic: If a brush is crossing a splitting plane a little but not more than EPSILON then it is not split. Instead it is sent all together on the plane side where all the other elements of the brush are.

Now "inside" and "outside" are starting to be visible.



A leaf hit by a brush is considered opaque (solid) and is hence marked accordingly.

4. FloodEntities & FillOutside



Using a player spawning entity a floodfill algorithm is triggered from each leaf. It mark leafs reachable by entities.


The final step FillOutside go through each leaf and if it is not reachable mark it as opaque.


We now have a level where each subspace is either reachable or opaque: Navigation via leaf portals can now be consistent by checking if the target leaf is opaque or not.



5. ClipSidesByTree

It is now time to discard useless parts of the brushes: Each original brush side is now sent down the BSP. If a side is within an opaque space then it is discarded. Otherwise it is added to the side's visibleHull list.

This result in a "skin" of the level, only visible parts are kept.



From this point only side's visibleHull are considered for the remaining operations.



6. FloodAreas

Now dmap group leafs together with area IDs: For each leaf a floodfilling algorithm is triggered. It tries to flow everywhere using portals associated to the leaf.

This is where the designer work is tremendously important: Areas can be identified only if visportals (the portal brushes mentioned in Step 1) were manually placed on the map. Without them dmap will identify only one area and the entire map will be sent to the GPU each frame.

The Floodfilling recursive algorithm is stopped only by areaportals and opaque nodes. In the following drawing an automatically generated portal (in red) will allow flood but a designer placed visportal (in blue, also called areaportal ) will stop it, making two areas:



At the end of the process each non opaque leaf belongs to an area and the inter-area-portals (in blue) have been identified.



7. PutPrimitivesInAreas

This step combines the areas identified in Step 6 and the visibleHull calculated in Step 5 in an other "Shape Sorter" game: This time the board is the areas and the shapes are the visibleHull.

An array of areas is allocated and each visibleHull of each brush is sent down the BSP: Surfaces are added to the area array at index areaIDs.


Note : Pretty clever, this step will also optimize entity spawning. If some entities are marked "func_static" they are instantiated now and associated to an area. This is a way to "melt" boxes, barrels,chairs into an area (also have its shadow volume pre-generated).



8. Prelight

For each static light dmap pre-calculate the shadow volumes geometry. Those volumes are later saved in the .proc as it. The only trick is that shadow volumes are saved with a name "_prelight_light" concatenated to the light ID so the engine can match the light from the .map file and the shadow volume from the .proc file:

 


      shadowModel { /* name = */ "_prelight_light_2900"

      /* numVerts = */ 24 /* noCaps = */ 72 /* noFrontCaps = */ 84 /* numIndexes = */ 96 /* planeBits = */ 5
   
     ( -1008 976 183.125 ) ( -1008 976 183.125 ) ( -1013.34375 976 184 ) ( -1013.34375 976 184 ) ( -1010 978 184 ) 
     ( -1008 976 184 ) ( -1013.34375 976 168 ) ( -1013.34375 976 168 ) ( -1008 976 168.875 ) ( -1008 976 168.875 ) 
     ( -1010 978 168 ) ( -1008 976 167.3043518066 ) ( -1008 976 183.125 ) ( -1008 976 183.125 ) ( -1010 978 184 ) 
     ( -1008 976 184 ) ( -1008 981.34375 184 ) ( -1008 981.34375 184 ) ( -1008 981.34375 168 ) ( -1008 981.34375 168 ) 
     ( -1010 978 168 ) ( -1008 976 167.3043518066 ) ( -1008 976 168.875 ) ( -1008 976 168.875 ) 
  
     4 0 1 4 1 5 2 4 3 4 5 3 0 2 1 2 3 1 
     8 10 11 8 11 9 6 8 7 8 9 7 10 6 7 10 7 11 
     14 13 12 14 15 13 16 12 13 16 13 17 14 16 15 16 17 15 
     22 21 20 22 23 21 22 18 19 22 19 23 18 20 21 18 21 19 
     1 3 5 7 9 11 13 15 17 19 21 23 4 2 0 10 8 6 
     16 14 12 22 20 18 
     }

  



9. FixGlobalTjunctions

Fixing TJunction is usually important in order to avoid visual artefacts but this is even more important in idTech4: The geometry is also used to generated the shadow while writing to the stencil buffer. T-Junctions are twice as annoying.



10. Write output

In the end all this preprocessing is saved to a .proc file:

History

Many code segments from dmap feature similarities with code found in the preprocessing tools of Quake (qbsp.exe), Quake 2 (q2bsp.exe) or Quake (q3bsp.exe). That's because the Potentially visible Set was generated via a temporary portal system:

A drawing is always better: Let's say qbsp.exe identified 6 leafs connected by portals and now vis.exe is running to generate the PVS. This process will be executed for each leaf but this example focus exclusively on leaf 1.



Since a leaf is always visible by itself the initial PVS for leaf 1 is as follow:


Leaf ID 1 2 3 4 5 6
Bit vector (PVS for leaf 1) 1 ? ? ? ? ?


The floodFilling algorithm starts: The rule is that as long as we don't have two portals in the path, the leaf is considered visible from the starting point. This means we reach leaf3 with the following PVS:


Leaf ID 1 2 3 4 5 6
Bit vector (PVS for leaf 1) 1 1 1 ? ? ?






Once in the leaf3 we can actually start checking for visibility:

By taking two points form the portal n-2 and the portal n-1 we can generate clipping planes and test if the next portals are potentially visible.

In the drawing the can see that portals leading to leaf 4 and 6 will fail the tests while portal toward 5 will succeed. The floorFilling algorithm will then recurse to leaf6. In the end the PVS for leaf 1 will be:


Leaf ID 1 2 3 4 5 6
Bit vector (PVS for leaf 1) 1 1 1 0 1 0



In idTech4 the PVS is not generated, instead the portal data is conserved. The visibility of each area is computed at runtime by projecting the portal windings into screen space and clipping them against each others.

Trivia : Michael Abrash explained this entire process in 10 seconds and a sharpie in this excellent video from GDC Vault:

.



Recommended readings

The great article by Sean Barret: The 3D Software Rendering Technology of 1998's Thief: The Dark Project mentions Seth Teller's 1992 thesis work in three parts; "Visibility Computations for Global Illumination Algorithms, " : A lot can be read about visibility precomputation, virtual light sources, portals, portal sequence, gross/fine culling, general observer and visible supersets.



Michael Abarash Graphic Programming Black Book: The chapter 60 is pure gold when it comes to explain how to split a segment with a plane.



The proof to the spliting segment formula is in "Computer Graphics: Principles and Practice":



More about T-Junction fixing in "Mathematics for 3D Game Programming and Computer Praphics":




Add a comment



Name Homepage
E-mail
(Will not appear online)
Comment



Comments (75)


#1 - Kel Solaar - 06/09/2012 - 03:31
Awesome and very interesting review! Definitely worth a read!
#2 - Nicolas - 06/09/2012 - 04:29
Hi Fabien,

Very nice article, thanks for sharing your work, I hope you will write a review of id tech 3.
#3 - Rich Jones - 06/09/2012 - 05:07
Excellent! I'm so glad you're doing this for Doom3 - hopefully your insights will help the Doom3 Mod/TC community thrive!
#4 - stef - 06/09/2012 - 05:25
Great article as usually... thanks !!
#5 - Paul - 06/09/2012 - 06:38
I'd to love to read Q3 source review - this game inspired me to start hacking my own little games.
#6 - Hannes - 06/09/2012 - 07:03
Thanks for the analysis and write up.

I cloned repo and tried to find unit and integration tests, but couldn't. Did I miss something?

It's quite amazing that such a big project could be maintained and developed with reasonable velocity without tests.
#7 - Job - 06/09/2012 - 08:21
Nice deconstruction as always!

I found the way Doom 3 did the interfaces on the screens very impressive and immersive - maybe even more than the lighting effects. Can you elaborate a bit on how they did that?)
#8 - Sha - 06/09/2012 - 08:40
Very interesting read!

Of the many forks you mentioned. Can you name some that are actively improving the code and adding features?
#9 - frostyNinja - 06/09/2012 - 09:10
Interesting compilation. Thought I should let you know about a typo:
"...Carmack and we was nice..."
I believe you meant to say "he was nice"

Great article, nonetheless!
#10 - daniel - 06/09/2012 - 11:10
Hi Fabian,
Great read... again!
It is really astonishing that there is one render-pass per light and still run so fluid on 2004 graphics hardware.

Definitely looking forward to a review of idTech3. Besides from the write-up and nice graphics it might be a an "easy" job for you compared to anyone else. With the knowledge of Quake 1 and 2 this will also make it possible to set everything into perspective nicely.

Cheers,
Daniel
#11 - Tom Campbell - 06/09/2012 - 12:24
Love these. Of course I'd be delighted if you did an idTech3 review.
#12 - Shawn - 06/09/2012 - 12:34
A wonderful article!

I've enjoyed your past code reviews so much I've gone back and read them more than once; I'm certain I will read this at least a few times!

I sincerely appreciate the time you took to write this. I hope that you have the time to review iDTech3 as well, especially since it will provide an opportunity to compare and contrast how the codebase has progressed since then.
#13 - Brian Cardarella - 06/09/2012 - 13:13
Awesome, amazingly well done!
#14 - J - 06/09/2012 - 14:30
Vancouver? Cool! Nice views. Thanks for the article.
#15 - Amir Masoud Abdol - 06/09/2012 - 15:31
Such an interesting thing to do :) Thanks for the notes.
#16 - Indloon - 06/09/2012 - 15:50
Please,review the Id Tech 3 engine :)

You ill get a cookie for that ^^
#17 - Los Puerco Lobos - 06/09/2012 - 16:26
I felt the love so hard
#18 - Neil - 06/09/2012 - 16:55
Great article!

Can you explain how the use of statically instantiated instances of system level objects and the use of a pointer to their abstract base classes avoids the usual vtable lookup overhead?
#19 - Will Lam - 06/09/2012 - 18:35
Nice write up! Didn't know you're leaving in Toronto - awesome!
#20 - Gaffer - 06/09/2012 - 19:02
Fantastic work. Thanks!
#21 - Venser - 06/09/2012 - 19:39
Quake 3 please
#22 - Kaiyi Li - 06/10/2012 - 01:38
Fabien, I spotted this on hacker news. You effort in this article is incredible and impressive, very clear and easy to understand. If you remember, we've worked together briefly in 2009, and I am happy to see that you're doing well in Toronto. Hope all the best.
#23 - Albert1 - 06/10/2012 - 07:27
Good work, Fabien!
However, id's Trinity is named after the Dallas Trinity River, as Carmack explained in this interview http://www.firingsquad.com/features/carmack/
#24 - Ralph Schaafsma - 06/10/2012 - 08:09
Very interesting read. I'd like to see a ID-T3 code review from you as well.
#25 - Nikita - 06/10/2012 - 08:12
That's so awesome! I've been waiting for id Tech 4 review for ages!
You've got some very interesting stuff here :)
#26 - Emmanuel Astier - 06/10/2012 - 08:26
Hi,

Great job for this very interesting article.

One question, you said :
"Abstraction and polymorphism are used a lot across the code. But a nice trick avoids the vtable performance hit on some objects."

Can you tell us a little more about this 'nice trick' ?

Thanks !
#27 - Harley - 06/10/2012 - 15:16
+1 for idTech3 review
#28 - Volker - 06/10/2012 - 15:43
Really great article. In-depth explanations like this is what a lot of open source projects would need to attract more developers.

A Quake3 review would also be very nice (or even ioquake3?).
#29 - cristaloleg - 06/11/2012 - 02:25
Great work! Thank's.
#30 - Keith Kaisershot - 06/11/2012 - 03:17
From your notes in renderer.txt:

// the following is Mr.E's code
// Note from FAB WHO is Mr.E ?

FWIW I'm going to hazard a guess and say that Mr.E is "Mr. Elusive" a.k.a. Jon Paul van Waveren. =)
#31 - Carsten Holtkamp - 06/11/2012 - 09:20
Haven't read this article yet. Just a quick overview, but it looks good. I am quite interested in static source code analysis, especially with nice helping graphics. Just wanted to add as a note, there was a overview of the id3tech source already started:
http://element61.blogspot.de/2005/08/looking-at-quake-3-source-part-1.html
http://element61.blogspot.de/2005/08/looking-at-quake-3-source-part-2.html
http://element61.blogspot.de/2005/09/looking-at-quake-3-source-part-3.html

A lot of work was done on ioquake, and there is a nice improve code aswell (normal maps, png textures and so on):
http://www.moddb.com/mods/etxreal
#32 - Yiming Qian - 06/11/2012 - 10:07
LOL,in the second last picture, I can see the background was Ryerson University
#33 - Jean-Francois Moy - 06/11/2012 - 11:24
Great post, thank you for your in-depth analysis.
#34 - Vikram - 06/11/2012 - 11:34
You made me cry.. Thanks for this
#35 - Viktor Liehr - 06/11/2012 - 12:09
Thanks Fabian for your Review, it was a great read! I would be really interested in if you complete your review of idTech3.
#36 - Jonathan - 06/11/2012 - 16:49
Thanks for this! I found it very interesting to read! I happen to have the Doom 3 cds laying around so I installed it and played it a little (brings back memories). I was able to compile the Doom 3 source code successfully but when I transferred it to where the original Doom 3 exec is(and where the Base dir is) I got the following error. It seems to find the pak files okay but then exits out.

DOOM 1.3.1.1304 win-x86 Jun 11 2012 15:14:43
4081 MHz AMD CPU with MMX & SSE & SSE2 & SSE3 & HTT
15840 MB System Memory
0 MB Video Memory
Winsock Initialized
Found interface: {53C6CF71-C4AA-4430-9C4C-DAF112BEA668} Intel(R) 82583V Gigabit Network Connection - 192.168.1.130/255.255.255.0
Found interface: {2B7C12A1-7F58-4FAE-B3EA-1A35703E9055} VirtualBox Host-Only Ethernet Adapter - 192.168.56.1/255.255.255.0
Found interface: {0D903444-3D1B-4028-850D-2058C798DBBF} VMware Virtual Ethernet Adapter for VMnet1 - 192.168.88.1/255.255.255.0
Found interface: {4EB006CA-2A77-4C41-9349-05E7EDCEE56B} VMware Virtual Ethernet Adapter for VMnet8 - 192.168.109.1/255.255.255.0
Sys_InitNetworking: adding loopback interface
doom using MMX & SSE & SSE2 & SSE3 for SIMD processing
enabled Flush-To-Zero mode
enabled Denormals-Are-Zero mode
------ Initializing File System ------
Loaded pk4 C:\Program Files (x86)\DOOM 3\base\game00.pk4 with checksum 0xf07eb555
Loaded pk4 C:\Program Files (x86)\DOOM 3\base\pak000.pk4 with checksum 0x28d208f1
Loaded pk4 C:\Program Files (x86)\DOOM 3\base\pak001.pk4 with checksum 0x40244be0
Loaded pk4 C:\Program Files (x86)\DOOM 3\base\pak002.pk4 with checksum 0xc51ecdcd
Loaded pk4 C:\Program Files (x86)\DOOM 3\base\pak003.pk4 with checksum 0xcd79d028
Loaded pk4 C:\Program Files (x86)\DOOM 3\base\pak004.pk4 with checksum 0x765e4f8b
Current search path:
C:\Program Files (x86)\DOOM 3/base
C:\Program Files (x86)\DOOM 3\base\pak004.pk4 (5137 files)
C:\Program Files (x86)\DOOM 3\base\pak003.pk4 (4676 files)
C:\Program Files (x86)\DOOM 3\base\pak002.pk4 (6120 files)
C:\Program Files (x86)\DOOM 3\base\pak001.pk4 (8972 files)
C:\Program Files (x86)\DOOM 3\base\pak000.pk4 (2698 files)
C:\Program Files (x86)\DOOM 3\base\game00.pk4 (2 files)
game DLL: 0x0 in pak: 0x0
Addon pk4s:
file system initialized.
--------------------------------------
----- Initializing Decls -----
------------------------------
------- Initializing renderSystem --------
using ARB renderSystem
renderSystem initialized.
--------------------------------------
4966 strings read from strings/english.lang
Couldn't open journal files
execing editor.cfg
execing default.cfg
execing DoomConfig.cfg
"\\" isn't a valid key
couldn't exec autoexec.cfg
4966 strings read from strings/english.lang
----- Initializing Sound System ------
sound system initialized.
--------------------------------------
found DLL in pak file: C:\Program Files (x86)\DOOM 3\base\game00.pk4/gamex86.dll
copy gamex86.dll to C:\Program Files (x86)\DOOM 3\base\gamex86.dll
idRenderSystem::Shutdown()
Shutting down OpenGL subsystem
...shutting down QGL
wrong game DLL API version
#37 - Jonathan - 06/11/2012 - 20:07
Ok..got this fixed. Updated to the latest patch (1.3.1) and now it works now with the compiled code!
#38 - Pavel Shevaev - 06/12/2012 - 00:01
Fabien, many thanks. It's an amazing amount of priceless information. id should hire you, seriously :D
#39 - Dominik - 06/12/2012 - 03:56
Thanks a lot Fabien for your great work here! This is so damn interesting. Keep it on! :)
#40 - abitofcode - 06/12/2012 - 04:15
Great write up!
#41 - vanhelgen - 06/12/2012 - 06:21
Great article, as for the tool support: I recommend using a vector drawing program (e.g. Inkscape) the svg markup can be embedded into the html document and you can even style the svg elements with css. If you prefer something more geeky you could use some charting library with an svg renderer like Raphael.js to implement a tool by yourself.
Hope this helps, have fun!
#42 - Joe Garret - 06/12/2012 - 06:56
nice what you are doing. Anything in plan for android?
#43 - Kirill - 06/12/2012 - 12:04
Man, thats great work !!! Thank you very much!
#44 - Nick - 06/12/2012 - 18:38
Thank you!!! I appreciate it to nth degree! Keep being awesome!
#45 - Gustavo De Micheli - 06/13/2012 - 10:09
Great reading!! Its awesome how the article explains the insides of the engine.
I think that it would be super cool that you do the review on idTech3 (for the sake of completitude? he).
#46 - Edouard - 06/14/2012 - 05:16
Amazing, as usual !
And yes, a Quake 3 code review would be awesome ;)
Keep going, you rock.
#47 - bijup - 06/14/2012 - 08:38
for those who want to compile iodoom3 source in visual c++ express editions, please follow this guide http://icculus.org/~riot/doom3_vcpp_express.txt
#48 - bijup - 06/14/2012 - 08:40
yes please, could u review idtech3 as well? tyvm for this
#49 - Lelala - 06/14/2012 - 10:50
Fabien, WOW! Thanks for that great article.
Nice to see someone actually took down some deep steps into Johns system.
Just inspirating :-)
#50 - Den - 06/24/2012 - 22:10
Wow cool. thanks man. very thanks for review.
#51 - slaj - 06/25/2012 - 19:34
really awesome article!
#52 - Tigrou - 07/06/2012 - 08:01
Hi Fabien, very nice review as usual. I already figured out a lot of things a long time ago while looking at code by myself but some things remained a mystery and your review cleared them out.

There is still one thing i don't not understand : network update code (idAsyncNetwork::RunFrame()) seems to be called only once in main loop.

Thats means someone with a pretty slow graphic card (eg : only capable of 10 fps) will only send packets to server at same rate.
Other players would see him with "laggy movement" while it could be a lot better.

Why idAsyncNetwork::RunFrame() could not be placed in a separate thread (like input and sound mixing) and thus packets would be send to server independently from updating the world and rendering ?

IMAO mostly user commands are send to server, so there is no need to have world updated (using game->runframe()) before sending this information.
#53 - Fabien Sanglard - 07/06/2012 - 08:09
@tigrou:

1. Shaggy movement are avoided via client position prediction.
2. There is little value in sending commands at 60Hz is the player cannot even see the result of its actions.
#54 - Prashant - 07/07/2012 - 00:59
the link to id tech 4 coding standard work:
ftp://ftp.idsoftware.com/idstuff/doom3/source/CodeStyleConventions.doc

is there any other place this can be downloaded from?
#55 - Fabien Sanglard - 07/07/2012 - 15:28
@Prashant: I uploaded a mirror in pdf version:

http://fd.fabiensanglard.net/doom3/CodeStyleConventions.pdf
#56 - Viktor - 08/17/2012 - 11:09
Another little GEM of a review.

Big Thx
#57 - Caveau - 09/20/2012 - 10:59
Hi, This is a very great article. Nice deconstruction as always.
Thank you for sharing with us !
#58 - M. Sponholz - 10/06/2012 - 22:32
You inspire me good sir!!
#59 - Pesarak - 10/15/2012 - 00:53
I don't get this part "The engine was developed with Visual Studio .NET (source). But the code does not feature a single line of C#" ... hearing this from -you- is really strange since you seem to be an experienced professional ! it gives me the impression that you think VS can/should only be used when C# is needed, am I right?!

anyways... like it or not, good or bad, when your main target platform is MS Windows, your best option is Visual Studio !
#60 - Pesarak - 10/15/2012 - 01:56
I'm going through this review and your notes and I have to say THESE ARE GREAT ! thanks for posting them
#61 - fabien sanglard - 10/16/2012 - 19:10
@Pesarak

Of course you can use C and C++ with VS.NET.

But when the .NET framework and associated Visual Studio were released, there were strongly associated with C#, VB and J++ (see CLI Infrastructure). Developers were strongly encouraged to use things that would have tied the codebase to Windows but the id software dev team did no use any of those Microsoft only features and I found it amusing.
#62 - Pesarak - 10/17/2012 - 11:44
@Fabien

Hi there, thanks for the reply :)

I do agree with your statement, Microsoft naturally intended VS to be used mainly for Windows-only purposes, but as I said in my first comment, in general, if your main target platform is MS Windows (or Xbox) or even if it is not, but you're developing on an MS Windows machine (personally I've been doing iOS and Android development on VS2010), your best option in terms of both the IDE and the C/C++ compiler is Visual Studio. In particular Visual Studio's profiler, performance analyzer, code analysis, and even debugger -at least on MS Windows- is truly unmatched.

I'm not saying it is the best option that could exist, of course people with another mindset than Microsoft's could come up with something much better, but the truth is that at least on the MS Windows front this hasn't happened yet. VS is the best option that does exist at the moment until someone or some company comes up with something better.

So I still don't get your point but that doesn't really matter :)

I'm truly amazed by this review and your notes, believe it or not I have made this page as one of my many browser's home pages ! again, thanks for posting your notes and this review. I really needed it and it has helped me a lot.
#63 - john Smith - 10/17/2012 - 13:26
Great work Fabien, thanks!
#64 - Judd - 10/26/2012 - 15:42
Holy cow! my friend sent me this link knowing that I was such a huge Doom and Quake nut. Such memories.. ahh.. the LAN parties.

Thanks for digging into the source code to write this. I wish I took up programming in my earlier years to understand the more technical aspects of this post :)
#65 - FipS - 12/21/2012 - 11:32
Good reading! Thanks for your effort.
#66 - Alistair - 01/15/2013 - 16:04
I'm not sure I would have done anything with that view from the window! Great review, very helpful. Thanks.
#67 - Latorie - 01/17/2013 - 21:45
Great game, great article - thanks!
#68 - nitrofurano - 01/18/2013 - 15:25
i wonder why Linux and others are not being cited in this article? :S
#69 - André Taiar - 01/25/2013 - 13:13
Wow... That's a cool article!

Congratulations!
#70 - Eli - 01/29/2013 - 07:17
Well done Fabien! Another masterpiece article.
#71 - Arman - 02/17/2013 - 13:37
Awesome job, this helps immensely the programmers, to learn about a huge project's architecture. The notes you made are priceless! Can't thank you enough, keep it up :]
#72 - Robert - 02/21/2013 - 21:22
Does anybody know why the Game is a DLL and the Engine is an EXE file? It looks counter-intuitive as we normal see it the other way around. Is it for modding? Or does it have some inner design decision that I can't understand?
#73 - Jay McGavren - 02/27/2013 - 23:11
Here's the game footage from my first couple experiments (bouncy barrels, enemies attack each other): http://www.youtube.com/watch?v=fjs8FHtrxf0
#74 - stephen - 03/12/2013 - 00:03
Thanks for your work! I plan on starting my study of this well documented site!
#75 - Xiaoming Nan - 05/16/2013 - 05:47
Awesome! you are from Ryerson U? Great! I'm at Ryerson, too. Just start studying cloud gaming. Maybe we can find a time to chat.

 

@2012