doom3 source code review: renderer

ruralrompΛογισμικό & κατασκευή λογ/κού

2 Δεκ 2013 (πριν από 3 χρόνια και 11 μήνες)

141 εμφανίσεις

1

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7











DOOM3
源码剖析

http://fabiensanglard.net/doom3/index.php

2

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7






DOOM3 SOURCE CODE RE
VIEW: INTRODUCTION (
PART 1 OF 6)

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

4

F
ROM NOTES TO ARTICLE
S
...

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

4

B
ACKGROUND

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙

4

F
IRST CONTACT

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙

4

A
RCHITECTURE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙

6

T
HE
C
ODE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙

8

U
NROLLING THE LOOP

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

10

R
ENDERER

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙

11

P
ROFILING

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙

12

S
CRIPTING AND
V
IRTUAL
M
ACHINE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙

12

I
NTERVIEWS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙

12

R
ECOMMENDED READINGS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

13

O
NE MORE THING

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

13

DOOM3 SOURCE CODE RE
VIEW: DMAP (PART 2 O
F 6)

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙

16

T
HE EDITOR

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙

16

C
ODE OVERVIEW
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙


17

0.

L
OADING THE LEVEL GEO
METRY

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙

18

1.

M
AKE
S
TRUCTURAL
B
SP
F
ACE
L
IST
&

F
ACE
BSP

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙

19

2.

M
AKE
T
REE
P
ORTALS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

20

3.

F
ILTER
B
RUSHES
I
NTO
T
REE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙

22

4.

F
LOOD
E
NTITIES
&

F
ILL
O
UTSIDE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙

22

5.

C
LIP
S
IDES
B
Y
T
REE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

23

6.

F
LOOD
A
REAS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙


23

7.

P
UT
P
RIMITIVES
I
N
A
REAS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

24

8.

P
RE
LIGHT

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙

24

9.

F
IX
G
LOBAL
T
JUNCTIONS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

25

10.

W
RITE OUTPUT

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

25

H
ISTORY

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙

25

R
ECOMMENDED READINGS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

26

DOOM3 SOURCE CODE RE
VIEW: RENDERER (PAR
T 3 OF 6)

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

29

A
RCHITECTURE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙

33

F
RONTEND
/B
ACKEND
/GPU

COLLABORATION

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

34

D
OOM
3

R
ENDERER
F
RONTEND

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙

35

D
OOM
3

R
ENDERER
B
ACKEND

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙

39

I
NTERACTIVE SURFACES

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

45

S
O MUCH MORE
....

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

46

R
ECOMMENDED READINGS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

47

DOOM3 SOURCE CODE RE
VIEW: PROFILING (PA
RT 4 OF 6)

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

48

O
VERVIEW

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙

48

M
AIN
T
HREAD

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙

48

G
AME
L
OGIC

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙

48

R
ENDERER

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙

49

R
ENDERER
:

F
RONTEND

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

49

R
ENDERER
:

B
ACKEND

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

50

F
LAT STATS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙

51

DOOM3 SOURCE CODE RE
VIEW: SCRIPTING VM (
PART 5 OF 6)

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

52

3

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

A
RCHITECTURE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙

52

C
OMPILER

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙

53

I
NTERPRETER

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙

53

R
ECOMMENDED READINGS

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

54

DOOM3 SOURCE CODE RE
VIEW: INTERVIEW
S (PART 6 OF 6)

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

55

1996
-
2007:

A
LL PLANS AND INTERVI
EWS FROM
J
OHN
C
ARMACK

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

55

2012

Q&A

WITH
J
OHN
C
ARMACK

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙

55

2004

(O
CTOBER
):

I
NTERVIEW FOR
"T
HE MAKING OF
D
OOM
3"

BOOK
.

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙

56

2004

Q
UAKECON KEYNOTE

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙

64

J
OHN
C
ARMACK
:

2003

AT ID
S
OFTWARE STUDIO

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙

64

2001

F
IRST VIDEO AT
M
AC
W
ORD

∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙
∙∙∙∙∙∙

64


4

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


DOOM3 SOURCE CODE REVIEW: INTRODUCTION

(PART 1 OF 6)

JUNE 8, 2012

On November 23
, 2011 id Software maintained the tradition and released the source code of their previous engine.
This time is was the turn of

idTech4

which powered Prey, Quake 4 and of course Doom 3. Within hours the
GitHub
repository was forked more than 400 times and people started to look at the game internal mechanisms/port the engine
on other platforms. I also jumped on it and promptly completed the

Mac

OS X Intel version

which John
Carmack
kindly advertised
.


In terms of clarity and comments this is the best code release from id Software after

Doom iPhone

codebase (which is
more recent and hence better commented). I highly recommend everybody to read, build and experiment with it.


Here are

my notes

rega
rding what I understood. As usual I have cleaned them up: I hope it will save someone a few
hours and I also hope it will motivate some of us to read more code and become better programmers.


Part 1: O
verview

Part 2: Dmap


Part 3: Renderer

Part 4: Profiling


Part 5: Scripting

Part 6: Interviews

(including Q&A with John Carmack)



From notes to articles...

I have noticed that I am using more and more drawing and

less and less text in order to explain codebase. So far I
have used
gliffy

to draw but this tool has some frustrating limitations (such as lack of alpha channel). I am thinking of
authoring a tool specialized in drawing

for 3D engines using SVG and Javascript. I wonder if something like this
already exist ? Anyway, back to the code...


Background

Getting our hands on the source code of such a ground breaking engine is exciting. Upon release in 2004 Doom III set
new visua
l and audio standards for real
-
time engines, the most notable being "Unified Lighting and Shadows". For the
first time the technology was allowing artists to express themselves on an hollywood scale. Even 8 years later the first
encounter with the HellKnig
ht in Delta
-
Labs
-
4 still looks insanely great:


First contact

The source code is now distributed via Github which is a good thing since the FTP server from id Software was
almost always down or overloaded.

5

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


The

original release

from TTimo compiles well with Visual Studio
2010 Professional. Unfortunately Visual Studio 2010 "Express" lacks
MFC and hence cannot be used. This was disappointing upon release
but some people

have since removed the dependencies
.



Windows 7 :

===========


git clone
https://github.com/TTimo/doom3.gpl.git







For code reading and exploring I prefer to use XCode 4.0 o
n Mac
OS X: The search speed from SpotLight, the variables highlights
and the "Command
-
Click" to reach a definition make the experience
superior to Visual Studio. The XCode project was broken upon
release but it was
easy to fix with a few steps

and there is now a
Github repository by "bad sector" which works well on Mac OS X
Lion.




MacOS X :

=========


git clone
https://github.com/bads
ector/Doom3
-
for
-
MacOSX
-






Notes

:

It seems "variable hightlights" and "Control
-
Click" are also available on Visual Studio 2010 after installing
the
Visual Studio 2010 Productivity Power Tools
. I cannot understand why this is no
t part of the vanilla install.


Both codebases are now in the best state possible :

One click away from an executable !



Download the code.



Hit F8 / Commma
nd
-
B.



Run !


Trivia

:

In order to run the game you will need the

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 d
id 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!



Trivia

:

The engine was developed with Visual Studio .NET (
source
). But the code does not feature a single line of
C# and the version released requires Visual Studio 2010 Pro
fessional in order to compile.


Trivia

:

Id Software team seems to be fan of the Matrix franchise: Quake III working tit
le was "Trinity" and Doom
III working title was "Neo". This explains why you will find all of the source code in the

neo

subfolder.


6

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

Architecture

The solution is divided in projects that reflect the overall architecture of the engine:


Projects

Builds

Obse
rvations


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 fi
les 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 TypeI
nfo 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.ex
e).


Like every engine since idTech2 we find one closed source binary (doom.exe) and one open source
dynamic library
(gamex86.dll).:



7

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

Most of the codebase has been accessible since October 2004 via the

Doom3 SDK
: O
nly the Doom3 executable
source code was missing. Modders were able to build

idlib.a

and

gamex86.dll

but the core of the e
ngine was still
closed source.


Note

:

The engine does not use the Standard C++ Library: All containers (map,linked list...) are re
-
im
plemented
but

libc

is extensively used.



Note

:

In the Game module each class extends idClass. This allows the engine to perform in
-
house RTTI and also ins
tantiate classes by
classname.


Trivia

:

If you look at the drawing you will see that a few essentia
l frameworks (such as

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
:




Things are working exactly the way

Quake2 loaded the renderer and the game ddls
: Exchangin
g objects pointers:



When Doom3.exe starts up it:



Loads the DLL in its process memory space via

LoadLibrary.



Get the address of

GetGameAPI

in the dll using win32's

GetProcAddress.



Call

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
8

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

a

gameImport_t

object containing additional references to all missing subsystems such as

idFileSystem
.


Gamex86's view on Doom 3 executable objects:


ty
pedef 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; // netw
ork 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 * collisionModelManag
er; // 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 * gameEd
it; // 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).


The Code

Before digging, some stats from

cloc:




./cloc
-
1.56.pl neo




2180 text files.



2002 unique files.



626 files ignored.




http://cloc.so
urceforge.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



O
bjective C++
6 709 656 2606

9

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



Perl 10 5
23 411 2380



yacc 1 95 97 912



Python 10 108 182 895



Objective C

1

145

20 768



DOS Batch 5 0 0 61



T
eamcenter def
4 3 0

51



Lisp 1 5

20

25



awk 1 2 1

17


-------------------------------------------------------------------------------



SUM: 1481 137974 164934


601047



-------------------------------------------------------------------------------


The number of line of code is not usually a good metric for anything but here it can be very helpful in order to assess
the effort to comprehend the engine.
601,047 lines of code makes the engine twice as "difficult" to understand
compared to Quake III. A few stats with regards to the history of id Software engines # lines of code:


#Lines of code

Doom

idTech1

idTech2

idTech3

idTech4

Engine

39079

143855

13578
8

239398

601032

Tools

341

11155

28140

128417

-

Total

39420

155010

163928

367815

601032




Note

:

The huge increase in idTech3 for the tools comes from

lcc

codebase (the C compiler u
sed to generate QVM
bytecode) .


Note

:

No tools are accounted for Doom
3 since they are inte
grated to the engine codebase.


From a high level here are a few fun facts:



For the first time in id Software history the code is C++ instead of C. John Carmack

elab
orated on this

during our
Q&A.



Abstraction and polymorphism are used a lot across the code. But a nice trick avoids the vtable performance hit
on some objects.



All assets are stored in human readable text form. No more binaries. The code is making extensi
ve usage of
10

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

lexer/parser. John Carmack

elaborated on this

during our Q&A.



Templates are used in low level utility classes (mainly idLib) but are never seen in the upper levels so they
won't
make your eyes bleed the way Google's V8 source code does.



In terms of code commenting it is the second best codebase from id software, the only one better is

Doom iPhone
,
probably because
it is more recent than Doom3. 30% comments is still outstanding and find it rare to find a
project that well commented! In some part of the code (see dmap page) there are actually more comments than
statements.



OOP encapsulation makes the the code clean an
d easy to read.



The days of low level assembly optimization are gone. A few tricks such as

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, SI
MD, Altivec, SMP, L2 Optimizations (R_AddModelSurfaces

per model
processing)...).

It is also interesting to take a look at

idTech4 The Coding Standard

defined by John Carm
ack (I particularly
appreciated the comments about

const

placement).


Unrolling the loop

Here is the main loop unrolled with the most important parts of the engine:



// OS Specialized object

idCommonLocal

commonLocal;


// Interface pointer (since Init is
OS dependent it is an abstract method

idCommon *

common = &commonLocal;


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )

{


//

Min = 201,326,592 Max = 1,073,741,824

Sys_SetPhysicalWorkMemory( 192 << 20,
1024 << 20 );

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++ ) {

InitializeCriticalS
ection( &win32.criticalSections[i] );

}



// Assess how much VRAM is available (not done via OpenGL but OS call)

common
-
>Init( 0, NULL, lpCmdLine );




// The next look runs is a separate thread.

Sys_StartAsyncThread(){

while ( 1 ){


// Run at 60Hz

usle
ep( 16666 );

// Do the job

common
-
>Async();

// Unlock other thread waiting for inputs

Sys_TriggerEvent( TRIGGER_EVENT_ONE );

// Check if we have been cancelled by the main thread (on shutdown).

pthread_testcancel();

}

}



Sys_ShowConsole



while( 1 ){


//

Show or hide the console

Win_Frame();

common
-
>Frame(){


// Game logic

11

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

session
-
>Frame()

{

for (int i = 0 ; i < gameTicsToRun ; i++ )

RunGameTic(){

// From this point execution jumps in the GameX86.dll address space.

game
-
>RunFrame( &cmd );


for( ent = a
ctiveEntities.Next(); ent != NULL;

ent = ent
-
>activeNode.Next() )


// let entities think

ent
-
>Get
Physics()
-
>UpdateTime( time );

}

}




// normal, in
-
sequence screen update

session
-
>UpdateScreen( false );

{

renderSystem
-
>BeginFrame


// Renderer
front
-
end. Doesn't actually communicate with the GPU !!

idGame::Draw


renderSystem
-
>EndFrame


// Renderer back
-
end. Issue GPU optimized commands to the GPU.

R_IssueRenderCommands

}

}

}

}


For more details here is the

fully unrolled loop

that I used as a map while reading the code.

It is a standard main loop for an id Software engine. Except for

Sys_StartAsyncThread

which indicate that Doom3 is
multi
-
threaded. The goal of this thread is to ha
ndle the time
-
critical functions that the engine don't want limited to the
frame rate:



Sound mixing.



User input generation.


Trivia

:

idTech4 high level objects are all abstract classes with virtual methods. This would normally involves a
performance hit s
ince each virtual method address would have to be looked up in a vtable before calling it at runtime.
But there is a "trick" to avoid that. All object are instantiated statically on the heap as follow:



idCommonLocal commonLocal; /
/ Implementation


idCommon * common = &commonLocal; // Pointer for gamex86.dll


Since an object allocated on the heap has a known type the compiler can optimize away the vtable lookup. The
interface pointer is used during the handshake so

doom3.exe

can exchange objects reference with

gamex86.dll.



Trivia

:

Having read most engines from id Software I find it noticeable that some method name have NEVER
changed since doom1 engine: The method responsible for pumping mouse and joystick inputs i
s still
called:

IN_frame().


Renderer

Two important parts:



Since Doom3 uses a portal system, the preprocessing tool

dmap

is a complete departure from the traditional bsp
builder. I reviewed it to the deep down

on a dedicated page
.


12

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7





The runtime renderer has a very interesting architecture since it is broken in two parts with a frontend and
backend:

More on the dedicated page
.





Profiling

I
used Xcode's

Instruments

to check where the CPU cycle were going. The results and analysis are

here
.


Scripting and Virtual Machine

In every idTech product the VM and the scripting language totally changed from the previous version...and they did it
again:
Details are here
.


Interviews

While reading the code, several novelties puzzled me so I wrote to John Carmack and he was nice enough to reply
with in
-
depth explanations about:



C++.



Renderer broken in two pieces.

13

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



Text
-
based assets.



Interp
reted bytecode.

I also compiled all videos and press interviews about idTech4. It is all in the

interviews page
.


Recommended readings

As usual a few books that you may enjoy if you enjoy

the cod
e:




One more thing

Summer is coming a
nd it w
as not always easy to focus...


14

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

15

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



...but overall it was a blast to read most of it. Since idTech5 source code will not be released anytime soon (if ever)
this leaves me with idTech3 (Quake III) not yet reviewed. Maybe I will write something about
it if enough people are
interested.

16

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


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 i
s 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 (C
onstructive 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 indmap).


A designer may have a g
ood 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 it
s 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.


17

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

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




Fi
llOutside
(
e

);



}
else

{




common
-
>
Printf

(
"**********************
\
n"

);




common
-
>
Warning
(
"******* leaked *******"

);




common
-
>
Printf

(
"**********************
\
n"

);




LeakFile
(
e
-
>
tree

);




// bail out here. If someone really wants to




// pr
ocess 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

);



// opt
imizing is a superset of fixing tjunctions


if

( !
dmapGlobals
.
noOptimize

) {



OptimizeEntity
(
e

);


}
else

if

( !
dmapGlobals
.
noTJunc

) {



FixEntityTjunctions
(
e

);


}

18

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



// 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, playe
r 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

{

brushDe
f3

}

}

.

.

.

// 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.


19

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

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 selec
t 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 fac
es marked as "portal" (they have
materialtextures/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.


20

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


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 s
ubspaces 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).
T
hen for each nodes int the BSP: split each portal in the node, add the splitting plane as portal and
recurse.



21

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7




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
p
ortals 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:



22

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

3. FilterBrushesIntoTree



This step works like a game of Shape Sorter with the BSP
being the board and the brushes being the shapes. Ea
ch
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 s
pawning 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
.


23

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7




We now have a level where each subspace is either r
eachable 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 id
entified 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:

24

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7




At the end of the process each no
n 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 enti
ty 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:



25

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

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 1
84 ) (
-
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 ) (
-
101
0 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 TJun
ction 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:



For each area a set of surface faces grouped by material.



The BSP Tree with areaID for leafs.



Inter
-
area
-
portals winding.



Shadow Volumes.


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 t
emporary portal system:



qbsp.exe

read a

.map

and generated a

.prt

file that contained connectivity information between
leafs in the BSP: Portals (exactly like Step2. MakeTreePortals).



vis.exe

used the

.prt

as input. For each leaf:



fillFlood into connected
leaf using portals.



Before flooding into a leaf: Test for visibility by clipping the next portal with the two previous
portal anti view frustrum (many people claimed that visibility was done by casting thousands
of rays but this is a myth that many still b
elieve nowadays).

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.


26

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



Since a l
eaf 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 consi
dered 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 po
rtal 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
algo
rithm 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.


Recommended readings

The great article by Sean Barret:

The 3D Software Rendering Technology o
f 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, g
ross/fine culling, general observer and visible supersets.


27

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7






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":



28

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


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



29

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


DOOM3 SOURCE CODE REVIEW: RENDERER

(PART 3 OF 6)

idTech4 renderer features three key innovations:



"Unified Lighting and Shadows": The level faces and the entities fac
es go through the same
pipeline and shaders.



"Visible Surface Determination": A portal system allows VSD to be performed at runtime: No
more PVS.



"Multi
-
pass Rendering".


By far the most important is that idTech4 is a multi
-
pass renderer. The contribution
of each light in the
view is accumulated in the GPU framebuffer via additive blending. Doom 3 takes full advantage of the
fact that color framebuffer registers saturate instead of wrapping around.


CPU register (wrap around) :


========================
====



1111 1111


+ 0000 0100


---------


= 0000 0011




GPU register ( saturate) :


==========================



1111 1111


+ 0000 0100


---------

= 1111 1111


I build a custom

level

to illustrate additive blending. The following screenshot shows three lights in a
room resulting in three passes with the result of each pass accumulated in the framebuffer. Notice the
white illumination at the cente
r of the screen where all lights blend together .

30

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



I modified the engine in order to isolate each light pass, they can be viewed using the left and right
arrows:


31

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



更多图片


I modified the engine further in order to see the framebuffer state AFTER each light pass. Use left and
right arrow to move in time.


32

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



更多图片


Trivia

:

It is possible to take the result of each light pass, blend them manually with photoshop (Linear
D
odge to mimic OpenGL additive blending) and reach the

exact same visual result
.



Additive blending combined to support of shadows and bumpmapping resulted in an engine that can
still produce very nice result even by 2012 standards:

33

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



Architecture

The renderer is not monolithic like previous idTech engines but rather broken down in two parts called
Frontend and Backend:



Frontend:

1.

Analyze world database and determine what contribu
tes to the view.

2.

Store the result in an Intermediate Representation (def_view_t) and upload/reuse cache
geometry in the GPU's VBO.

3.

Issue a RC_DRAW_VIEW command.



Backend:

1.

The RC_DRAW_VIEW wakes up the backend.

2.

Use the Intermediate Representation as input an
d issue commands to the GPU using the
VBOs.

34

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


The architecture of the renderer draws a striking similarity with

LCC the retargetable compiler

that was
used to generate the Quake3 Virtual Machine byt
ecode:





I initially thought the renderer design as influenced by LCC design but the renderer is built in two parts
because it was
meant to be multi
-
thread

on SMP systems. The fr
ont
-
end would run on one core and the
back
-
end on an other core. Unfortunately due to instability on certain drivers the extra thread had to be
disabled and both ends run on the same thread.


Genesis trivia :

Archelology can be done with code as well: If y
ou look closely at the unrolled code
renderer (
frontend
,
backend
) you can clearly see that the
engine switches from C++ to C (from objects
to static methods):


This is due to the genesis of the code. idTech4 renderer was written by John Carmack using Quake3
engine (C codebase) before he was proficient in C++. The renderer was later integrated to the

idtech4
C++ codebase.


How much Quake is there in Doom3 ? Hard to tell but it is funny to see that the main method in the
Mac OS X version is:


-

(void)quakeMain;


Frontend/Backend/GPU collaboration

Here is a drawing that illustrate the collaboration betw
een the frontend, the backend and the GPU:

35

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



1.

The Frontend analyzes the world state and issues two things:



An

intermediate representation

containing a list of each light contributing to the view. Each
light contains a list of the entity surfaces interactin
g with it.



Each light
-
entity interaction that is going to be used for this frame is also cached in
a

interaction table. Data is usually uploaded to a GPU VBO.

2.

The Backend takes the

intermediate representation

as input. It goes through each lights in the li
st
and makes OpenGL draw calls for each entity that interact with the light. The draw command
obviously reference the VBO and textures.

3.

The GPU receives the OpenGL commands and render to the screen.


Doom3 Renderer Frontend

The frontend performs the hard p
art: Visible Surface Determination (VSD). The goal is to find every
light/entity combination affecting the view. Those combinations are called

interactions
. Once each
interaction have been found the frontend makes sure everything needed by the backend is u
ploaded to
the GPU Ram (it keeps track of everything via an "interaction table"). The last step is to generate
an

Intermediate representation

that will be read by the backend so it can generate OpenGL
Commands.


In the code this is how it looks:


-

idCommo
n
::
Frame


-

idSession
::
UpdateScreen


-

idSession
::
Draw


-

idGame
::
Draw


-

idPlayerView
::
RenderPlayerView


-

idPlayerView
::
SingleView


-

idRenderWorld
::
RenderScene


-

build

params


//This is the

frontend

36

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


-

::
R_RenderView
(
params
)


{


R_SetViewMatrix


R_SetupViewFrustum


R_SetupProjection



//Most of the beef is here.


static_cast
<
idRen
derWorldLocal

*>(
parms
-
>
renderWorld
)
-
>
FindViewLightsAndEntities
()


{


//Walk the BSP and find the current Area


PointInArea


//

Recursively pass portals to find lights and entities i
nteracting with the

//
view.


FlowViewThroughPortals

}



//Improve Z
-
buffer accuracy by moving far plan as close as the farthest entity.


R_ConstrainViewFrustum


// Find entities that a
re not in a visible area but still casting a shadow

//
(usually enemies)


R_AddLightSurfaces


// Instantiate animated models (for monsters)


R_AddModelSurfaces


R_RemoveUnecessaryViewLigh
ts


// A simple C qsort call. C++ sort would have been faster thanks to inlining.


R_SortDrawSurfs


R_GenerateSubViews


R_AddDrawViewCmd



}


Note

:

The switch from C to C++

is obvious here.


It

is alwasy easier to understand with a drawing so here is a level: Thanks for the designer's visplanes
the engine sees four areas:



Upon loading the

.proc

the engine also loaded the

.map

containing all the lights and moving entities
37

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

definitions. For each light the engine has built a list of each area impacted:





Light 1 :

=========



-

A r e a 0



-

A r e a 1


L i g h t 2 :

= = = = = = = = =



-

A r e a 1


-

A r e a 2


-

A r e a 3



A t r u n t i me w e n o w h a v e a p l a y e r p o s i t i o n a n d mo n s t e r s c a
s t i n g s h a d o w s. F o r s c e n e c o r r e c t n e s s, a l l
mo n s t e r s a n d s h a d o w mu s t b e f o u n d.




H e r e i s t h e p r o c e s s:

1.

F i n d i n w h i c h a r e a t h e p l a y e r i s b y w a l k i n g t h e B S P t r e e i n

P o i n t I n A r e a.

2.

F l o w V i e w T h r o u g h P o r t a l s

: S t a r t i n g f r o m t h e c u r r e n t a r e a f l o o d f i l l i n t o o t h e r v i s i
b l e a r e a u s i n g
p o r t a l s y s t e m. R e s h a p e t h e v i e w f r u s t r u m e a c h t i me a p o r t a l i s p a s s e d: T h i s i s b e a u t i f u l l y e x p l a i n e d
i n t h e

R e a l t i me r e n d e r i n g

b o o k b i b l e:

38

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



. Now we have a list of every lights contributing

to the screen and

most

entities which are stored in
the Interaction table:




Interaction table (Light/Entity) :


==================================



Light 1
-

Area 0


Light 1
-

Area 1


Light 1
-

Monster 1



Light 2
-

Ar
ea 1


Light 2
-

Monster 1


The interaction table is still incomplete: The interaction Light2
-
Monster2 is missing, the shadow
cast by Monster2 would be missing.


3.

R_AddLightSurfaces

will find the entity not in the view but casting shadow by going th
rough each
light's area list.


Interaction table (Light/Entity) :


==================================



Light 1
-

Area 0


Light 1
-

Area 1


Light 1
-

Monster 1



Light 2
-

Area 1


Light 2
-

Monster 1


Light 2
-

Monster 2


39

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

4.

R_AddModelSurfaces

: All interaction have been found, it is now time to upload the vertices and
indices to the GPU's VBO if they are not there already. Animated monster geometry is instantiated
here as well (model AND shadow volume)

5.

All "intel
ligent" work has been done. Issue a

RC_DRAW_VIEW

command
via

R_AddDrawViewCmd

that will trigger the backend to render to the screen.


Doom3 Renderer Backend

The backend is in charge of rendering the Intermediate Representation while accounting for the
limi
tations of the GPU: Doom3 supported five GPU rendering path:



R10 (GeForce256)



R20 (GeForce3)



R200 (Radeon 8500)



ARB (OpenGL 1.X)



ARB2 (OpenGL 2.0)

As of 2012 only ARB2 is relevant to modern GPUs: Not only standards provide portability

they also
increase lo
ngevity.


Depending on the card capability idtech4 enabled bump
-
mapping (
A tutorial

about using a hellknight I
wrote a few years ago) and specular
-
mapping but all of them try the hardest to

save

as much fillrate as
possible

with:




OpenGL Scissor test (specific to each light, generated by the frontend



Filling the Z
-
buffer as first step.


The backend unrolled code is as follow:


idRenderSystemLocal
::
EndFrame


R_IssueRenderCommands


RB_ExecuteBackE
ndCommands


RB_DrawView


RB_ShowOverdraw


RB_STD_DrawView


{


RB_BeginDrawingView

// clear the z buffer, set the projection matrix, etc


RB_DetermineLightScale



RB_STD_FillDepthBuffer

// fill the depth buffer and clear color

buffer to black.



// Go through each light and draw a pass, accumulating result in the framebuffer


_DrawInteractions



{


5
GPU

specific

path



switch

(
renderer
)



{


R10

(
GeForce256
)


R20

(
geForce3
)



R200

(
Radeon

8500)


ARB

(
OpenGL

1.X)


ARB2

(
OpenGL

2.0)



}



// disable stencil shadow test



qglStencilFunc
(
GL_ALWAYS
, 128, 255 );




RB_STD_LightScale

40

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7




//draw any non
-
light dependent shading passes (screen,neon, etc..
.)



int

processed

=
RB_STD_DrawShaderPasses
(
drawSurfs
,
numDrawSurfs

)




// fob and blend lights



RB_STD_FogAllLights
();




// now draw any post
-
processing effects using _currentRender



if

(
processed

<
numDrawSurfs

)




RB_STD_DrawShaderPasses
(
drawSurfs
+
processed
,
numDrawSurfs
-
processed

);


}


In order to follow the backend steps, I took a famous screen from Doom3 level And I froze the engine
at every steps in the rendition :




Since Doom3 uses bumpmapping and specular map
ping on top of the diffuse texture, to render a
surface can take up to 3 textures lookup. Since a pixel can potentially be impacted by 5
-
7 lights it is not
crazy to assume 21 textures lookup per pixels..not even accounting for overdraw. The first step of t
he
backend is to reach 0 overdraw: Disable every shaders, write only to the depth buffer and render all
geometry:



41

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



The depth buffer is now filled. From now on depth write is disabled and depth test is enabled.


Render first to the z
-
buffer may seem cou
nter
-
productive at first but it is actually extremely valuable to
save fillrate:



Prevent from running expensive shaders on non
-
visible surfaces.



Prevent from rendering non visible shadows to the stencil buffer.



Since surfaces are rendered in no particular
order (back to front or front to back) there would be a
lot of overdraw. This step totally remove overdraw.

Note that the color buffer is cleared to black: Doom3 world is naturally pitch black since there is no
"ambient" light: In order to be visible a sur
face/polygon must interact. with a light. This explains why
Doom3 was so dark !



After this the engine is going to perform 11 passes (one for each light). I broke down the rendering
process . The next slideshow shows each individual light pass: you can mo
ve in time with the left and
right arrow.


42

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



更多图片


Now the details of what happens in the GPU framebuffer:


43

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



Stencil buffer and Scissors test:


Before each light pass, if a shadow is cast by the light then the stencil test has to be enable. I won't
elab
orate on the depth
-
fail/depth pass controversy and the infamous move of Creative Labs. The source
code released features the depth pass algorithm which is slower since it requires building better shadow
volume. Some people have managed to put the depth fai
l algorithm back in the source but be aware that
this is only legal in E
urope !


In order to save fillrate the frontend generate a screen space rectangle to be used as scissor test by
OpenGL. This avoid running shader on pixels where the surface would have

been pitch black anyway
d
ue to distance from the light.


The stencil buffer just before light pass 8. Any non
-
black area will be lit while the other will prevent
writing to the framebuffer: The mask principle sis clearly visible

44

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



The stencil buffer just

before light pass 7. The scissor set to save fillrate is clearly visible.


45

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



Interactive surfaces

The last step in rendition is

RB_STD_DrawShaderPasses: It render all surfaces that don't need light.
Among them are the screen and the amazing interactive G
UI surfaces that is one of the part of the
engine John Carmack was the most proud off. I don't think this part of the engine ever got the respect it
deserve. Back in 2004 the introduction cinematic used to be a video that would play fullscreen. After
the v
ideo played the level would load and the engine would kick in... but not in Doom III:



……


Steps :



Level load.



Cinematic starts playing.



At 5mn5s the camera moves away.



The video we just saw was a SCREEN IN THE GAME ENGINE !

I remember when I saw this fo
r the first time I thought it was a trick. I thought the video player would
cut and the designer had a texture on the screen and a camera position that would match the last frame
of the video. I was wrong: idTech4 can actually play videos in a GUI interact
ive surface elements. For
this it reused RoQ: The technology that Graeme Devine brought with
him when he joined id Software.


46

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

Trivia

:

The RoQ used for the intro was impressive for 2005 and it was an audacious move to have it in
a screen within the game:



I
t is 30 frames per seconds.



Each frame is 512x512: Quite a high resolution at the time



Each frame is generated in

idCinematicLocal::ImageForTime

on the CPU and uploaded on the fly
to the GPU as an OpenGL texture.


But the interactive surfaces can do so muc
h more than that thanks to scripting and its ability to call
native methods. Some people got

really interested

and managed to have

Doom 1 run
in it

!




Trivia

:The Interactive Surface technology was also reused in order to design all the menus in Doom3
(settings, main screen etc,....).


So much more....

This page is only the tip of the iceberg and it is possible to go

so much deeper
.


47

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

Recommended readings



If you are reading this and you don't own a copy of

Realtime rendering

you
are depriving yourself of priceless information.

48

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


DOO
M3 SOURCE CODE REVIEW: PROFILING

(PART 4 OF 6)

XCode comes with a great tool for profiling:

Instruments
. I used it in sampling mode during a playing
session (removing the game loading and level GPU pre
-
caching altogether):


Overview

The high level loop shows the three
threads running in the process:



Main thread where gamelogic
and rendition occur.



Auxiliary

thread were inputs
are collected and sound effects
are mixed.



Music thread (consuming 8%
of resources), created by
CoreAudio and
calling

idAudioHardwareOSX

at regular intervals (note:
sound effects are done with
OpenAL but do not run in their
own thread).



Main Thread


The Doom 3 MainThead
runs...QuakeMain! Amusingly the team
that ported Quake 3 to Mac OS X must
have reused some old code. Inside the time
repartition is as follow:



65% dedicated to graphic rendition
(UpdateScreen).



25% dedicated to gamel
ogic: This is
surprisingly high for an id Software
game.



Game Logic

The gamelogic occurs in gamex86.dll space (or game.dylib on Mac OS X):


The game logic account for 25% of the Main Thread time which is unusually high. Two reasons:



A: The virtual machi
ne is run and allows entities to think. All of the bytecode is interpreted and the
scripting language seems to have been overused.

49

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7



The Physic engine is more complex (LCP solvers) and hence more demanding than previous
games. It is run on each object and in
clude ragdoll and interactions solving.



Renderer

As previously described the renderer is made of two parts:



Frontend (idSessionLocal::Draw) accounting for 43.9% of the rendition process. Note that

Draw

is
a pretty poor name since the frontend does not p
erform a single draw call to OpenGL !



Backend (idRenderSessionLocale:EndFrame) accounting for 55.9% of the rendition process.




The load distribution is pretty much even and it is not that surprising since:



The frontend performs a lot of calculation with

regard to Visual Surface Determination.



The frontend also performs model animation and shadow silhouette finding.



The frontend upload vertices to the GPU.



The backend spends a lot of time setting up parameters for the shaders and communicating with
the GP
U (i.e: submitting triangles indices or per vertex normal matrix for bumpmapping
in

glDrawElements).


Renderer: Frontend

Renderer FontEnd:



No surprise here, most of the time (91%) is spent uploading data to the GPU in VBOs
(R_AddModelSurfaces). A little
bit of time (4%) is visible when going through areas, trying to find all
interactions (R_AddLightSurfaces). A minimal amount (2.9%) is spent in Visual Surface
50

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

Determination: Traversing the BSP and running the portal system.




Renderer: Backend

Renderer B
ackEnd:


The backend obviously triggers a buffer swap (GLimp_SwapBuffers) and spend some time
synchronizing (10%) with the screen since the game was running in double buffering environment. 5%
is the cost of avoiding totally overdraw with a first pass aimi
ng to populate the Z
-
Buffer first
(RB_STS_FillDepthBuffer).





51

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7

Flat stats



If you feel like loading the Instruments trace and exploring yourself:

Here it the profile file
.

52

zhouguopeng
整理
http://www.cppblog.com/zgpxgame/

2012/7/7


DOO
M3 SOURCE CODE REVIEW: SCRIPTING VM

(PART 5 OF 6)

From idTech1 to idTech3 the only thing that

completely

changed every time was the scripting system:



idTech1: QuakeC running in a Virtual Machine.



idTech2: C compiled to an x86 shared library (no virtual Mac
hine).



idTech3: C compiled to bytecode with LCC, running in QVM (Quake Virtual Machine). On x86
the bytecode was converted to native instructions at loadtime.

idTech4 is no exception, once again everything is different:



The scripting is done via an Object
Oriented language similar to C++.



The language is fairly limited (no typedef, five basic types).



It is

always

interpreted via a virtual machine: There is no JIT conversion to native instruction like
in idTech3 (John Carmack

elaborated on this

during our Q&A).

A good introduction is to read the

Doom3 Scripting SDK notes
.


Architecture

Here is the big picture:


Compilation

:

At loadtime

the

idCompiler

is fed one predetermined.script