An ode to the father of the electric age.
View
Windows 7 :
===========
git clone https://github.com/TTimo/doom3.gpl.git
MacOS X :
=========
git clone https://github.com/badsector/Doom3-for-MacOSX-
base folder containing the Doom 3 assets. Since I did not want to waste time extracting them from the Doom 3 CDs and updating them: I downloaded the Steam version. It seems id Software team did the same since the Visual Studio project released still contains "+set fs_basepath C:\Program Files (x86)\Steam\steamapps\common\doom 3" in the debug settings!neo subfolder.| Projects | Builds | Observations | |
| Windows | MacO SX | ||
| Game | gamex86.dll | gamex86.so | Doom3 gameplay |
| Game-d3xp | gamex86.dll | gamex86.so | Doom3 eXPension (Ressurection) gameplay |
| MayaImport | MayaImport.dll | - | Part of the assets creation toolchain: Loaded at runtime in order to open Maya files and import monsters, camera path and maps. |
| Doom3 | Doom3.exe | Doom3.app | Doom 3 Engine |
| TypeInfo | TypeInfo.exe | - | In-house RTTI helper: Generates GameTypeInfo.h: A map of all the Doom3 class types with each member size. This allow memory debugging via TypeInfo class. |
| CurlLib | CurlLib.lib | - | HTTP client used to download files (Staticaly linked against gamex86.dll and doom3.exe). |
| idLib | idLib.lib | idLib.a | id Software library. Includes parser,lexer,dictionary ... (Staticaly linked against gamex86.dll and doom3.exe). |
idlib.a and gamex86.dll but the core of the engine was still closed source.libc is extensively used.Filesystem) are in the Doom3.exe project. This is a problem since gamex86.dll needs to load assets as well. Those subsystems are dynamically loaded by gamex86.dll from doom3.exe (this is what the arrow materializes in the drawing). If we use a PE explorer on the DLL we can see that gamex86.dll export one method: GetGameAPI:LoadLibrary.GetGameAPI in the dll using win32's GetProcAddress.GetGameAPI. gameExport_t * GetGameAPI_t( gameImport_t *import );
At the end of the "handshake", Doom3.exe has a pointer to a idGame object and Game.dll has a pointer to a gameImport_t object containing additional references to all missing subsystems such as idFileSystem. typedef struct {
int version; // API version
idSys * sys; // non-portable system services
idCommon * common; // common
idCmdSystem * cmdSystem // console command system
idCVarSystem * cvarSystem; // console variable system
idFileSystem * fileSystem; // file system
idNetworkSystem * networkSystem; // network system
idRenderSystem * renderSystem; // render system
idSoundSystem * soundSystem; // sound system
idRenderModelManager * renderModelManager; // render model manager
idUserInterfaceManager * uiManager; // user interface manager
idDeclManager * declManager; // declaration manager
idAASFileManager * AASFileManager; // AAS file manager
idCollisionModelManager * collisionModelManager; // collision model manager
} gameImport_t;
Doom 3's view on Game/Modd objects:typedef struct
{
int version; // API version
idGame * game; // interface to run the game
idGameEdit * gameEdit; // interface for in-game editing
} gameExport_t;
Notes : A great resource to understand better each subsystems is the Doom3 SDK documentation page: It seems to have been written by someone with deep understanding of the code in 2004 (so probably a member of the development team).cloc: ./cloc-1.56.pl neo
2180 text files.
2002 unique files.
626 files ignored.
http://cloc.sourceforge.net v 1.56 T=19.0 s (77.9 files/s, 47576.6 lines/s)
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C++ 517 87078 113107 366433
C/C++ Header 617 29833 27176 111105
C 171 11408 15566 53540
Bourne Shell 29 5399 6516 39966
make 43 1196 874 9121
m4 10 1079 232 9025
HTML 55 391 76 4142
Objective C++ 6 709 656 2606
Perl 10 523 411 2380
yacc 1 95 97 912
Python 10 108 182 895
Objective C 1 145 20 768
DOS Batch 5 0 0 61
Teamcenter def 4 3 0 51
Lisp 1 5 20 25
awk 1 2 1 17
-------------------------------------------------------------------------------
SUM: 1481 137974 164934 601047
-------------------------------------------------------------------------------
| #Lines of code | Doom | idTech1 | idTech2 | idTech3 | idTech4 |
| Engine | 39079 | 143855 | 135788 | 239398 | 601032 |
| Tools | 341 | 11155 | 28140 | 128417 | - |
| Total | 39420 | 155010 | 163928 | 367815 | 601032 |
lcc codebase (the C compiler used to generate QVM bytecode) .idMath::InvSqrt and spacial localization optimizations are here but most of the code just tries to use the tools when they are available (GPU Shaders, OpenGL VBO, SIMD, Altivec, SMP, L2 Optimizations (R_AddModelSurfaces per model processing)...).const placement). idCommonLocal commonLocal; // OS Specialized object
idCommon * common = &commonLocal; // Interface pointer (since Init is OS dependent it is an abstract method
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{
Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 ); //Min = 201,326,592 Max = 1,073,741,824
Sys_CreateConsole();
// Since the engine is multi-threaded mutexes are initialized here: One mutex per "critical" (concurrent execution) section of code.
for (int i = 0; i < MAX_CRITICAL_SECTIONS; i++ ) {
InitializeCriticalSection( &win32.criticalSections[i] );
}
common->Init( 0, NULL, lpCmdLine ); // Assess how much VRAM is available (not done via OpenGL but OS call)
Sys_StartAsyncThread(){ // The next look runs is a separate thread.
while ( 1 ){
usleep( 16666 ); // Run at 60Hz
common->Async(); // Do the job
Sys_TriggerEvent( TRIGGER_EVENT_ONE ); // Unlock other thread waiting for inputs
pthread_testcancel(); // Check if we have been cancelled by the main thread (on shutdown).
}
}
Sys_ShowConsole
while( 1 ){
Win_Frame(); // Show or hide the console
common->Frame(){
session->Frame() // Game logic
{
for (int i = 0 ; i < gameTicsToRun ; i++ )
RunGameTic(){
game->RunFrame( &cmd ); // From this point execution jumps in the GameX86.dll address space.
for( ent = activeEntities.Next(); ent != NULL; ent = ent->activeNode.Next() )
ent->GetPhysics()->UpdateTime( time ); // let entities think
}
}
session->UpdateScreen( false ); // normal, in-sequence screen update
{
renderSystem->BeginFrame
idGame::Draw // Renderer front-end. Doesn't actually communicate with the GPU !!
renderSystem->EndFrame
R_IssueRenderCommands // Renderer back-end. Issue GPU optimized commands to the GPU.
}
}
}
}
Sys_StartAsyncThread which indicate that Doom3 is multi-threaded. The goal of this thread is to handle the time-critical functions that the engine don't want limited to the frame rate: idCommonLocal commonLocal; // Implementation
idCommon * common = &commonLocal; // Interface manipulated in the code
This way the compiler can determine statically the method to call: No vtable lookup. It is actually a double win since the pointer does not have to be dereferenced at runtime either since its address is known at compile time.IN_frame().dmap is a complete departure from the traditional bsp builder. I reviewed it to the deep down on a dedicated page.Today morning, the current project I was working on went live successfully. Yep, it may not sound that big to anyone else. Buts its big for me. This is the first project I have lead from offshore. And one where I have lost considerable amount of sleep and peace of mind. Its been five years since I started my career, and this is one of those milestones I have been striving for since day one. And although I have been part of many smooth releases and go-lives in the past, I never thought that this release would be so taxing on mind and soul.
But all said and done, its surely something I am happy and proud about. ![]()