原文地址 http://fabiensanglard.net/doom3/
DOOM3源代碼評測:簡介(第1部分,共6部分)>>
2011年11月23日,id軟件保持傳統,併發布了其以前引擎的源代碼。這一次是輪到了idTech4,使用它,創造出Prey,quake4,當然是Doom
3.在幾個小時之內,GitHub上的源代碼被下載了400多次,人們開始看遊戲內部機制/在其他平臺上引導引擎。我也跳上它,並及時完成了Mac OS X的Intel版本
約翰·卡馬克。
在清晰度和評論方面,這是 Doom iPhone代碼庫(這是更新的,因此更好的評論)的id軟件最好的開源代碼。我強烈建議大家閱讀,編譯和實驗。
下面是我的筆記就我的理解。像往常一樣,我已經清理了它:我希望它會節省一個人幾個小時,我也希望它會激勵我們中的一些人閱讀更多的代碼,併成爲更好的程序員。
第1部分:概述
第2部分:Dmap
第3部分:渲染器
第4部分:剖析
第5部分:腳本
第6部分:訪談(包括與約翰·卡馬克的問答)
從筆記到文章...
我注意到我正在使用越來越多的繪圖和越來越少的文本來解釋代碼庫。到目前爲止,我已經使用gliffy繪製,但這個工具有一些令人沮喪的限制(如缺乏alpha通道)。我正在考慮編寫一個專門用於使用SVG和Javascript繪製3D引擎的工具。我想知道這樣的事情是否已經存在?無論如何,回到代碼...
背景
掌握這樣一個突破性引擎的源代碼是令人興奮的。Doom III於2004年發佈,爲實時引擎設定了新的視覺和音頻標準,最爲顯着的是“統一照明與陰影”。這項技術首次允許藝術家以好萊塢的規模表達自己。即使8年後,在Delta-Labs-4中與HellKnight的第一次遭遇仍然看起來非常棒:
第一次聯繫
源代碼現在通過Github進行分發,這是一件好事,因爲來自id Software的FTP服務器幾乎總是關閉或重載。
來自TTimo的原始版本與Visual
Studio 2010 Professional編譯良好。不幸的是Visual Studio 2010“Express”缺少MFC,因此無法使用。這是令人失望的釋放,但有些人已經刪除了依賴。
Windows 7的 : =========== git clone https://github.com/TTimo/doom3.gpl.git
對於代碼閱讀和探索,我更喜歡在Mac OS X上使用XCode 4.0:SpotLight的搜索速度,變量亮點和“命令點擊”達到定義使得體驗優於Visual Studio。XCode項目在發佈時被破壞,但是很容易解決幾個步驟,現在有一個Github存儲庫由“壞扇區”,在Mac
OS X Lion上運行良好。
MacOS X: ========= git克隆https://github.com/badsector/Doom3-for-MacOSX-
注意:安裝
Visual Studio 2010生產力電動工具後,Visual Studio 2010中也可以看到“變量hightlights”和“Control-Click”。我不明白爲什麼這不是香草安裝的一部分。
兩個代碼庫現在都處於最佳狀態:一次點擊可執行文件!
- 下載代碼
- 擊中F8 / Commmand-B。
- 跑 !
瑣事:爲了運行遊戲,您將需要base
包含Doom
3遊戲的文件夾。因爲我不想浪費時間從Doom 3 CD中提取它們並更新它們:我下載了Steam版本。似乎id軟件團隊做的一樣,因爲Visual Studio項目發佈仍然包含"+set
fs_basepath C:\Program Files (x86)\Steam\steamapps\common\doom 3"
在調試設置!
瑣事:引擎是用Visual Studio .NET開發的。但代碼不具有單行的C#,發佈的版本需要Visual
Studio 2010 Professional才能編譯。
瑣事: Id軟件團隊似乎是Matrix電影系列(黑客帝國)的粉絲:Quake
III的工作題目是“Trinity”,Doom
III的工作題目是“Neo”。(都出自黑客帝國電影中)
建築
該解決方案分爲反映引擎整體架構的項目:
項目 | 構建 | 意見 | |
視窗 | MacO SX | ||
遊戲 | gamex86.dll | gamex86.so | Doom3遊戲 |
遊戲d3xp | gamex86.dll | gamex86.so | Doom3 eXPension(Ressurection)遊戲 |
MayaImport | MayaImport.dll | - | 資產創建工具鏈的一部分:在運行時加載,以打開Maya文件並導入怪物,攝像頭路徑和地圖。 |
毀滅戰士 | Doom3.exe | Doom3.app | Doom 3引擎 |
所屬類別 | TypeInfo.exe | - |
內部RTTI幫助器:生成GameTypeInfo.h :具有每個成員大小的所有Doom3類類型的映射。這允許通過TypeInfo類進行內存調試。 |
CurlLib | CurlLib.lib | - | HTTP客戶端用於下載文件(Staticaly鏈接到gamex86.dll和doom3.exe)。 |
伊德利卜 | idLib.lib | idLib.a | id軟件庫。包括解析器,詞法分析器,字典...(Staticaly鏈接到gamex86.dll和doom3.exe)。 |
像idTech2的每個引擎一樣,我們找到一個封閉的源代碼二進制(doom.exe)和一個開放源代碼的動態庫(gamex86.dll):
自2004年10月以來,大多數代碼庫已經通過Doom3 SDK訪問:只有Doom3可執行源代碼失蹤。模式能夠構建idlib.a
,gamex86.dll
但發動機的核心仍然是封閉源。
注意:引擎不使用標準C ++庫:所有容器(映射,鏈接列表...)都被重新實現,但libc
被廣泛使用。
注意:在遊戲模塊中,每個類都擴展了idClass。這允許引擎執行內部RTTI,並通過類名實例化類。
瑣事:如果你看圖紙,你會看到幾個基本框架(如Filesystem
)在Doom3.exe項目中。這是一個問題,因爲gamex86.dll也需要加載資源。這些子系統由doom3.exe中的gamex86.dll動態加載(這是圖中箭頭實現的)。如果我們用PE
Explorer中的DLL我們可以看到,gamex86.dll導出一個方法:GetGameAPI
:
一切正常完全相同的方式Quake2中加載的渲染和遊戲的DDL:交換對象的指針:
當Doom3.exe啓動它:exe(這是圖中箭頭實現的)。如果我們用PE Explorer中的DLL我們可以看到,gamex86.dll導出一個方法: :一切正常完全相同的方式Quake2中加載的渲染和遊戲的DDL:交換對象的指針:當Doom3.exe啓動它:exe(這是圖中箭頭實現的)。如果我們用PE
Explorer中的DLL我們可以看到,gamex86.dll導出一個方法: :一切正常完全相同的方式Quake2中加載的渲染和遊戲的DDL:交換對象的指針:當Doom3.exe啓動它:
- 通過DLL加載其進程內存空間
LoadLibrary
。 GetGameAPI
使用win32來獲取dll中的地址GetProcAddress
。- 打電話
GetGameAPI
。
gameExport_t * GetGameAPI_t(gameImport_t * import);
在“握手”結束時,Doom3.exe具有指向idGame
對象的指針,Game.dll具有指向gameImport_t
包含對所有缺少子系統的其他引用的對象的指針,例如idFileSystem
。
Gamex86對Doom 3可執行對象的看法:
typedef struct { int 版本; // API版本 idSys * sys; //非便攜式系統服務 idCommon * common; // common idCmdSystem * cmdSystem // console command system idCVarSystem * cvarSystem; //控制檯變量系統 idFileSystem * fileSystem; //文件系統 idNetworkSystem * networkSystem; //網絡系統 idRenderSystem * renderSystem; // render system idSoundSystem * soundSystem; // sound system idRenderModelManager * renderModelManager; //渲染模型管理器 idUserInterfaceManager * uiManager; //用戶界面管理器 idDeclManager * declManager; //聲明管理器 idAASFileManager * AASFileManager; // AAS文件管理器 idCollisionModelManager * collisionModelManager; //碰撞模型經理 //渲染模型管理器 idUserInterfaceManager * uiManager; //用戶界面管理器 idDeclManager * declManager; //聲明管理器 idAASFileManager * AASFileManager; // AAS文件管理器 idCollisionModelManager * collisionModelManager; //碰撞模型經理 //渲染模型管理器 idUserInterfaceManager * uiManager; //用戶界面管理器 idDeclManager * declManager; //聲明管理器 idAASFileManager * AASFileManager; // AAS文件管理器 idCollisionModelManager * collisionModelManager; //碰撞模型經理 // AAS文件管理器 idCollisionModelManager * collisionModelManager; //碰撞模型經理 // AAS文件管理器 idCollisionModelManager * collisionModelManager; //碰撞模型經理 } gameImport_t;
遊戲/ Modd對象中的Doom 3的視圖:
typedef結構 { int 版本; // API版本 idGame *game; //界面運行遊戲 idGameEdit * gameEdit; //界面進行遊戲內編輯 } gameExport_t;
注意:瞭解更好的每個子系統的一個很好的資源是Doom3 SDK文檔頁面(該頁面現在需要翻牆人機驗證後打開):它似乎是在2004年深入瞭解代碼的人寫的(所以可能是開發團隊的成員)。
代碼
在挖掘之前,一些統計數據來自cloc
:
./cloc-1.56.pl新 2180個文本文件。 2002獨特文件。 626個文件被忽略。 http://cloc.sourceforge.net v 1.56 T = 19.0 s(77.9 files / s,47576.6 lines / s) -------------------------------------------------- ----------------------------- 語言文件空白評論代碼 -------------------------------------------------- ----------------------------- C ++ 517 87078 113107 366433 C / C ++標頭617 29833 27176 111105 C 171 11408 15566 53540 Bourne Shell 29 5399 6516 39966 使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 目標C 1 145 20 768 DOS批次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 -------------------------------------------------- -----------------------------
代碼行的數量通常不是一個很好的指標,但是在這裏,爲了評估引擎的理解力度可能非常有幫助。與Quake III相比,601,047行代碼使引擎兩倍“難”。關於id的歷史的幾個統計軟件引擎#代碼行:
#代碼線 | 厄運 | idTech1 | idTech2 | idTech3 | idTech4 |
發動機 | 39079 | 143855 | 135788 | 239398 | 601032 |
工具 | 341 | 11155 | 28140 | 128417 | - |
總 | 39420 | 155010 | 163928 | 367815 | 601032 |
注意:對於工具來說,idTech3的巨大增長來自lcc
代碼庫(用於生成QVM字節碼的C編譯器)。
注意:由於Doom3被集成到引擎代碼庫中,所以沒有任何工具被歸結爲Doom3。
從高層來看,這裏有幾個有趣的事實:
- 在第一次在軟件歷史上,代碼是C ++而不是C.約翰·卡馬克在我們的問答中闡述了這一點。
- 抽象和多態在代碼中使用很多。但是一個好的技巧避免了一些對象的vtable性能。
- 所有資產都以人類可讀的文本形式存儲。沒有更多的二進制該代碼正在廣泛使用詞法分析器/解析器。約翰·卡馬克(John Carmack)在我們的問答中闡述了這一點。
- 模板用於低級實用程序類(主要是idLib),但從來沒有在上層看到,所以他們不會讓你的眼睛流血的方式谷歌的V8源代碼。
- 在代碼評論方面,它是來自id軟件的第二好的代碼庫,唯一一個更好的是Doom iPhone,可能是因爲它比Doom3更新。30%的評論仍然很出色,很少找到一個很好的評論的項目!在代碼的某些部分(參見dmap頁面)實際上比語句更多的註釋。
- OOP封裝使代碼清潔,易於閱讀。
- 低級裝配優化的時代已經過去了。這裏有一些技巧
idMath::InvSqrt
和空間本地化優化,但大多數代碼只是嘗試在可用時使用這些工具(GPU着色器,OpenGL VBO,SIMD,Altivec,SMP,L2優化(R_AddModelSurfaces
每個模型處理)...) 。
查看由John Carmack定義的idTech4編碼標準(鏡像)也很有意思(我特別讚賞關於const
佈局的評論)。
展開循環
這是主要循環展開引擎最重要的部分:
idCommonLocal commonLocal; // OS專用對象 idCommon * common =&commonLocal; //接口指針(因爲Init是依賴於操作系統的,它是一種抽象的方法 int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { Sys_SetPhysicalWorkMemory(192 << 20,1024 << 20); //最小= 201,326,592最大= 1,073,741,824 Sys_CreateConsole(); //由於引擎是多線程的互斥體在這裏初始化:每個“關鍵”(併發執行)代碼的一個互斥體。 for(int i = 0; i <MAX_CRITICAL_SECTIONS; i ++){ InitializeCriticalSection(&win32.criticalSections [i]); } common-> Init(0,NULL,lpCmdLine); //評估VRAM有多少(不是通過OpenGL完成但OS調用) Sys_StartAsyncThread(){ //下一個查找運行是一個單獨的線程。 而(1){ usleep(16666); //運行在60Hz common-> Async(); //做工作 Sys_TriggerEvent(TRIGGER_EVENT_ONE); //解鎖其他線程等待輸入 pthread_testcancel(); //檢查主線程是否被取消(在關機時)。 } } Sys_ShowConsole 而(1){ Win_Frame(); //顯示或隱藏控制檯 共>框架(){ session-> Frame() //遊戲邏輯 { for(int i = 0; i <gameTicsToRun; i ++) RunGameTic(){ game-> RunFrame(&cmd); //從這一點起,執行跳轉到GameX86.dll的地址空間。 for(ent = activeEntities.Next(); ent!= NULL; ent = ent-> activeNode.Next()) ent-> GetPhysics() - > UpdateTime(time); //讓實體思考 } } session-> UpdateScreen(false); //正常的,按序屏幕更新 { renderSystem-> BeginFrame idGame :: Draw // Renderer前端。實際上並沒有與GPU溝通! renderSystem-> EndFrame R_IssueRenderCommands //渲染器後端。將GPU優化的命令發佈到GPU。 } } } }
有關詳細信息,請參閱閱讀代碼時作爲地圖使用的完全展開循環。
它是id軟件引擎的標準主循環。除了Sys_StartAsyncThread
表示Doom3是多線程的。此線程的目標是處理引擎不希望限制幀速率的時間關鍵功能:
- 聲音混合
- 用戶輸入生成。
瑣事: idTech4高級對象都是具有虛擬方法的抽象類。這通常會涉及性能問題,因爲每個虛擬方法地址在運行時調用之前都必須在vtable中查找。但是有一個“伎倆”來避免這種情況。所有對象都靜態實例化:
idCommonLocal commonLocal; //實現 idCommon * common =&commonLocal; //指針for gamex86.dll
由於在數據段中靜態分配的對象具有已知類型,編譯器可以commonLocal
在調用方法時優化遠程執行vtable查找。接口指針在握手期間使用,因此doom3.exe
可以交換對象引用,gamex86.dll
但在這種情況下,vtable的成本未被優化。
瑣事:從id軟件中讀取大多數引擎,我發現一些方法名稱自從doom1引擎以來就沒有改變:負責抽取鼠標和操縱桿輸入的方法仍然被稱爲:IN_frame()
。
渲染
兩個重要部分:
剖析
我使用Xcode的儀器來檢查CPU週期在哪裏。結果和分析在這裏。
腳本和虛擬機
在每個idTech產品中,VM和腳本語言從以前的版本都完全改變了,他們再次做到了: 細節在這裏。
面試
在閱讀代碼時,幾個新奇使我感到困惑,所以我寫信給約翰·卡馬克,他很高興回覆深入的解釋:
- C ++。
- 渲染器分爲兩塊。
- 基於文本的資產。
- 解釋的字節碼
我還編輯了關於idTech4的所有視頻和新聞採訪。這些都在採訪頁面。
推薦讀數
還有一件事
夏天來了,並不總是很容易集中
...但總的來說,這是一個大部分的閱讀。由於idTech5源代碼將不會很快被髮布(如果有的話),這讓我與idTech3(Quake III)尚未被審查。也許如果有足夠的人有興趣,我會寫一些關於它的內容。