原文地址 http://fabiensanglard.net/doom3/
(翻译的 有很多 错误!!!!有的我现在能改的尽量改一些,但是就事实来说,作者很多地方有些地方写的可以!!!! 比如原作者 博客文章中讲述doom3法线贴图,凹凸贴图bump等等部分,图文都有,包括一些领悟,配图,doom3前向光照,变换,光线的变化等等.....
嗯,有些地方适中!(有的领悟就...........大同小异,理解源代码多一些,好处之一就是想修改的时候,能够自己改一改代码..............)
现在看也有不足,比如ui其实也是要调用opengl等渲染的,渲染2d的比如各种文字,按钮, 游戏中很多作者似乎只重视3d的显示很多,并没有写2d的很多渲染,包括其他的很多,现在看 原作者只是写了一部分,而且只是doom3引擎部分的一部分而已,而不全面,且没有那么深入.........我从作者文章中得到的很多领悟 有限 ..........
本质上还是要翻阅源代码!!!嗯,要看源代码,源代码内容最多!id tech 系列引擎 并不是虚幻引擎! 也不是cryengine!等等,各有千秋!有它自身的特点,比如关卡编辑器brush可以编辑,地图编辑器内部就可以,这个功能其实就比epic的很多要早!但是有的很多工具其他比如的可能epic要占一定优势,各有千秋,并不一致!
有些图片加载不上来,我删除了,有需要看英文原文就是了,有的也未必全部.....
本质上要看源代码,源代码 其实可以把很多注释 翻译成汉语,看看,我自己有时间的话,翻译一下,感觉比以前领悟了一些............前进了一些 ....................)
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
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-
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 | ||
game | gamex86.dll | gamex86.so | Doom3游戏 |
d3xp | gamex86.dll | gamex86.so | Doom3 eXPension(Ressurection)游戏 |
MayaImport | MayaImport.dll | - | 资产创建工具链的一部分:在运行时加载, 以打开Maya文件并导入怪物,摄像头路径和地图。 |
doom3 | Doom3.exe | Doom3.app | Doom 3引擎 |
TypeInfo | TypeInfo.exe | - | 内部RTTI帮助器:生成 具有每个成员大小的所有Doom3类类型的映射。 这允许通过TypeInfo类进行内存调试。 |
CurlLib | CurlLib.lib | - | HTTP客户端用于下载文件 (Staticaly 链接到 gamex86.dll 和doom3.exe)。 |
idlib | 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);
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;
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;//碰撞模型管理器
} gameImport_t;
game / Modd对象中的Doom 3的视图:
typedef结构
{
int 版本; // API版本
idGame *game; //界面运行游戏
idGameEdit * gameEdit; //界面进行游戏内编辑
} gameExport_t;
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
-------------------------------------------------- -----------------------------
./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。
}
}
}
}
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()
。
渲染
两个重要部分:
- 由于Doom3 使用 portal (门户)系统,所以预处理工具
dmap
完全脱离了传统的bsp构建器。 - 我在一个专门的页面上深入审查了它。
- 运行时渲染器具有非常有趣的体系结构,因为它在前端和后端分为两部分:更多的专用页面。
?
剖析
我使用Xcode的仪器来检查CPU周期在哪里。结果和分析在这里。
脚本和虚拟机
在每个idTech产品中,VM和 脚本语言从以前的版本 都 完全改变了,
他们再次做到了: 细节在这里。
面试
在阅读代码时,几个新奇使我感到困惑,所以我写信给约翰·卡马克,他很高兴回复深入的解释:
- C ++。
- 渲染器分为两块。
- 基于文本的资产。
- 解释的字节码
我还编辑了关于idTech4的所有视频和新闻采访。这些都在采访页面。
推荐读物
像往常一样你可以享受的书,如果你喜欢的代码:
还有一件事
夏天来了,并不总是很容易集中
...但 总的 来说,这是一个大部分 的 阅读。
由于idTech5 源代码 将不会 很快 被发布(如果有的话),
这让我与idTech3(Quake III)尚未被审查。
也许如果有足够的人有兴趣,我会写一些关于它的内容。