Doom3引擎初始化分析

     首先,进入的是Doom3的WinMain函数。(其中"..."为省略其中代码)
下面展示的是WinMain函数的主要结构


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {
...
Sys_SetPhysicalWorkMemory( 192 << 20, 1024 << 20 );
Sys_GetCurrentMemoryStatus( exeLaunchMemoryStats );
...
// done before Com/Sys_Init since we need this for error output
//创建控制台,用于输出失败错误
Sys_CreateConsole();
...
Sys_Milliseconds();
...
//初始化引擎各各系统
common->Init( 0, NULL, lpCmdLine );
...
//创建异步线程???
Sys_StartAsyncThread();
...


    //进入游戏主循环
while( 1 ) {


Win_Frame();
...
//是否容许工具显示。(其中包括编辑器、材质编辑器等的显示...)
#ifdef ID_ALLOW_TOOLS
...
#endif
//游戏“帧”
common->Frame();
}
return 0;
}


下面看一下doom3的初始化过程。其中WinMain函数中的"common->init(...)"就是用来初始化引擎系统的函数
下面看看common到底是什么。


在"Common.h"文件中,我们看到common是一个idCommon*指针类型全局变量。


以下是idCommon声明,我们看到idCommon是一个接口类。
class idCommon {
public:
virtual ~idCommon( void ) {}


// Initialize everything.
// if the OS allows, pass argc/argv directly (without executable name)
// otherwise pass the command line in a single string (without executable name)
virtual void Init( int argc, const char **argv, const char *cmdline ) = 0;


...
};
extern idCommon * common;
该 *common的初始化,放在了Common.cpp里。




idCommonLocal commonLocal; //实例化了idCommonLocal
idCommon * common = &commonLocal; //让全局指针指向commonLocal




在"Common.cpp"先实现了idCommon接口类。既idCommonLocal继承自idCommon
并实现了idCommon的接口。


好下面回到common的初始化例程。


void idCommonLocal::Init( int argc, const char **argv, const char *cmdline ) {
try {
// set interface pointers used by idLib
idLib::sys = sys;
idLib::common = common;
idLib::cvarSystem = cvarSystem;
idLib::fileSystem = fileSystem;


// 初始化idLib,idLib是一些基础库,如数学库,SIMD,字符串之类
idLib::Init();

...

// 初始化控制台,命令系统
cmdSystem->Init();


// 初始化全局变量系统
cvarSystem->Init();


...

// 注册全部静态变量
idCVar::RegisterStaticVars();

...

// 初始化键盘输入和绑定
idKeyInput::Init();


// 初始化控制台
console->Init();


// 获取操作系统类型,内存大小,cpu类型之类基础系统信息
Sys_Init();


// 初始化网络
Sys_InitNetworking();


...

// 初始化SIMD指令集
InitSIMD();


// 初始化命令
InitCommands();


#ifdef ID_WRITE_VERSION
config_compressor = idCompressor::AllocArithmetic();
#endif
// 初始化游戏
InitGame();
...
}catch( idException & ) {
Sys_Error( "Error during initialization" );
}
}


在idCommon::init()中最主要的初始化步骤在InitGame()。下面跟入InitGame().


void idCommonLocal::InitGame( void ) {
// 初始化文件系统
fileSystem->Init();


// 初始化声明系统
declManager->Init();


// 检查是否是工具模式,若是则强制关闭全屏模式
CheckToolMode();
...


// 初始化renderSystem数据结构,但当前并没有开启Opengl
renderSystem->Init();


// 初始化字符串字典系统
InitLanguageDict();


...

// 读取字体等...
console->LoadGraphics();


// 事件循环系统初始化
eventLoop->Init();

...


// 开启声音系统
soundSystem->Init();


...


// init async network
idAsyncNetwork::Init();
...
if ( idAsyncNetwork::serverDedicated.GetInteger() == 1 ) {
idAsyncNetwork::server.InitPort();
cvarSystem->SetCVarBool( "s_noSound", true );
} else {

...
//初始化渲染系统
InitRenderSystem();
}
...



// 初始化ui管理器
uiManager->Init();


...


// load the game dll
LoadGameDLL();

...


// 初始化session
session->Init();


...
}


其中对窗体与显示配置的初始化,放在了InitRenderSystem() 函数内。


void idCommonLocal::InitRenderSystem( void ) {
if ( com_skipRenderer.GetBool() ) {
return;
}


renderSystem->InitOpenGL();
PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04343" ) );
}


在该函数的内部调用了 renderSystem->InitOpenGL()函数。其中renderSystem为指向idRenderSystem的全局指针
而初始化放在了。RenderSystem.cpp内部。idRenderSystem为接口类,其中在RenderSystem.cpp中idRenderSystemLocal
继承并实现了idRenderSystem。并让renderSystem指针指向实例idRenderSystemLocal tr;


下面跟入idRenderSystemLocal::InitOpenGL()函数


void idRenderSystemLocal::InitOpenGL( void ) {
// if OpenGL isn't started, start it now
if ( !glConfig.isInitialized ) {
int err;


R_InitOpenGL();


globalImages->ReloadAllImages();


err = qglGetError();
if ( err != GL_NO_ERROR ) {
common->Printf( "glGetError() = 0x%x\n", err );
}
}
}


其中主要的工作是调用 R_InitOpenGL 。


R_InitOpenGL(void)的主要职责是,初始化一个可用的用于渲染的OpenGL子系统。
这是通过调用系统特定的GLimp_Init来实现的,GLimp_Init将初始化一个可用的OGL
子系统,它将设置所有必须的OpenGL状态,包括图片、顶点渲染器、先是列表。




void R_InitOpenGL( void ) {
GLint temp;
glimpParms_t parms;
int i;


...

//尝试初始化当前系统OpenGL子系统两次,第一次正常初始化,
//若初始化不成功则进入安全模式。
for ( i = 0 ; i < 2 ; i++ ) {
// set the parameters we are trying
R_GetModeInfo( &glConfig.vidWidth, &glConfig.vidHeight, r_mode.GetInteger() );


parms.width = glConfig.vidWidth;
parms.height = glConfig.vidHeight;
parms.fullScreen = r_fullscreen.GetBool();
parms.displayHz = r_displayRefresh.GetInteger();
parms.multiSamples = r_multiSamples.GetInteger();
parms.stereo = false;

//在这里调用了对应平台的GLimp_Init(...)
if ( GLimp_Init( parms ) ) {
// it worked
break;
}


if ( i == 1 ) {
common->FatalError( "Unable to initialize OpenGL" );
}


// if we failed, set everything back to "safe mode"
// and try again
r_mode.SetInteger( 3 );
r_fullscreen.SetInteger( 1 );
r_displayRefresh.SetInteger( 0 );
r_multiSamples.SetInteger( 0 );
}


// input and sound systems need to be tied to the new window
Sys_InitInput();
soundSystem->InitHW();


// get our config strings
glConfig.vendor_string = (const char *)qglGetString(GL_VENDOR);
glConfig.renderer_string = (const char *)qglGetString(GL_RENDERER);
glConfig.version_string = (const char *)qglGetString(GL_VERSION);
glConfig.extensions_string = (const char *)qglGetString(GL_EXTENSIONS);


// OpenGL driver constants
qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp );
glConfig.maxTextureSize = temp;


// stubbed or broken drivers may have reported 0...
if ( glConfig.maxTextureSize <= 0 ) {
glConfig.maxTextureSize = 256;
}


glConfig.isInitialized = true;


...
}


R_InitOpenGL()最重要的地方就是调用 GLimp_Init(...),其中该GLimp_Init函数的实现是放在neo\sys\... 下的各各平台
的子文件夹下。其中这里用的是win32文件夹下的实现版本,若更换平台只需包含不同平台的同名文件即可。


下面列出的代码是Win32实现版本里的GLimp_Init(glimpParms_t parms )实现。该函数在win_glimp.cpp文件里。


bool GLimp_Init( glimpParms_t parms ) {
const char *driverName;
HDC hDC;


common->Printf( "Initializing OpenGL subsystem\n" );


// check our desktop attributes
hDC = GetDC( GetDesktopWindow() );
win32.desktopBitsPixel = GetDeviceCaps( hDC, BITSPIXEL );
win32.desktopWidth = GetDeviceCaps( hDC, HORZRES );
win32.desktopHeight = GetDeviceCaps( hDC, VERTRES );
ReleaseDC( GetDesktopWindow(), hDC );


// we can't run in a window unless it is 32 bpp
if ( win32.desktopBitsPixel < 32 && !parms.fullScreen ) {
common->Printf("^3Windowed mode requires 32 bit desktop depth^0\n");
return false;
}




//保存当前硬件的gamma值,以便他能够在退出游戏时恢复
GLimp_SaveGamma();


//创建窗口类
GLW_CreateWindowClasses();


//获取OpenGL的全部函数指针
driverName = r_glDriver.GetString()[0] ? r_glDriver.GetString() : "opengl32";
if ( !QGL_Init( driverName ) ) {
common->Printf( "^3GLimp_Init() could not load r_glDriver \"%s\"^0\n", driverName );
return false;
}


// getting the wgl extensions involves creating a fake window to get a context,
// which is pretty disgusting, and seems to mess with the AGP VAR allocation
GLW_GetWGLExtensionsWithFakeWindow();


// try to change to fullscreen
if ( parms.fullScreen ) {
//设置为全屏,通过改变显示器设置
//为了便于调试可以将GLW_SetFullScreen里的调用ChangeDisplaySettings处都
//禁用掉,以保持当前编辑模式下的像素显示模式。
if ( !GLW_SetFullScreen( parms ) ) {
GLimp_Shutdown();
return false;
}
}


//创建主窗体,并选择最佳pixel 格式,并且初始化渲染上下文RC
//其中GLW_CreateWindow(parms)函数会调用 GLW_InitDriver来选择
//最佳像素模式,并生成hdc与hrc并使其成为当前渲染环境。
if ( !GLW_CreateWindow( parms ) ) {
GLimp_Shutdown();
return false;
}


// wglSwapinterval, etc
GLW_CheckWGLExtensions( win32.hDC );


// check logging
GLimp_EnableLogging( ( r_logFile.GetInteger() != 0 ) );


return true;
}


Doom3引擎中用一个类型为Win32Vars_t 的全局变量 win32 来维护windows特有的一些资源例如
HWND,HDC,HGLRC,桌面分辨率之类。
下面列出Win32Vars_t结构源码:


typedef struct {
HWND hWnd;
HINSTANCE hInstance;
bool activeApp; // changed with WM_ACTIVATE messages
bool mouseReleased; // when the game has the console down or is doing a long operation
bool movingWindow; // inhibit mouse grab when dragging the window
bool mouseGrabbed; // current state of grab and hide
OSVERSIONINFOEX osversion;


cpuid_t cpuid;


// when we get a windows message, we store the time off so keyboard processing
// can know the exact time of an event (not really needed now that we use async direct input)
int sysMsgTime;


bool windowClassRegistered;


WNDPROC wndproc;


HDC hDC; // handle to device context
HGLRC hGLRC; // handle to GL rendering context
PIXELFORMATDESCRIPTOR pfd;
int pixelformat;


HINSTANCE hinstOpenGL; // HINSTANCE for the OpenGL library


int desktopBitsPixel;
int desktopWidth, desktopHeight;


bool cdsFullscreen;


FILE *log_fp;


unsigned short oldHardwareGamma[3][256];
// desktop gamma is saved here for restoration at exit


static idCVar sys_arch;
static idCVar sys_cpustring;
static idCVar in_mouse;
static idCVar win_allowAltTab;
static idCVar win_notaskkeys;
static idCVar win_username;
static idCVar win_xpos; // archived X coordinate of window position
static idCVar win_ypos; // archived Y coordinate of window position
static idCVar win_outputDebugString;
static idCVar win_outputEditString;
static idCVar win_viewlog;
static idCVar win_timerUpdate;
static idCVar win_allowMultipleInstances;


CRITICAL_SECTION criticalSections[MAX_CRITICAL_SECTIONS];
HANDLE backgroundDownloadSemaphore;


HINSTANCE hInstDI; // direct input


LPDIRECTINPUT8 g_pdi;
LPDIRECTINPUTDEVICE8 g_pMouse;
LPDIRECTINPUTDEVICE8 g_pKeyboard;


HANDLE renderCommandsEvent;
HANDLE renderCompletedEvent;
HANDLE renderActiveEvent;
HANDLE renderThreadHandle;
unsigned long renderThreadId;
void (*glimpRenderThread)( void );
void *smpData;
int wglErrors;
// SMP acceleration vars


} Win32Vars_t;


下面来分析 GLW_CreateWindowClasses 函数。
以下是该函数的源码。


static void GLW_CreateWindowClasses( void ) {
WNDCLASS wc;


//
// register the window class if necessary
//
if ( win32.windowClassRegistered ) {
return;
}


memset( &wc, 0, sizeof( wc ) );


wc.style         = 0;
wc.lpfnWndProc   = (WNDPROC) MainWndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = win32.hInstance;
wc.hIcon         = LoadIcon( win32.hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
wc.hbrBackground = (struct HBRUSH__ *)COLOR_GRAYTEXT;
wc.lpszMenuName  = 0;
wc.lpszClassName = WIN32_WINDOW_CLASS_NAME;


if ( !RegisterClass( &wc ) ) {
common->FatalError( "GLW_CreateWindow: could not register window class" );
}
common->Printf( "...registered window class\n" );


// now register the fake window class that is only used
// to get wgl extensions
wc.style         = 0;
wc.lpfnWndProc   = (WNDPROC) FakeWndProc;
wc.cbClsExtra    = 0;
wc.cbWndExtra    = 0;
wc.hInstance     = win32.hInstance;
wc.hIcon         = LoadIcon( win32.hInstance, MAKEINTRESOURCE(IDI_ICON1));
wc.hCursor       = LoadCursor (NULL,IDC_ARROW);
wc.hbrBackground = (struct HBRUSH__ *)COLOR_GRAYTEXT;
wc.lpszMenuName  = 0;
wc.lpszClassName = WIN32_FAKE_WINDOW_CLASS_NAME;


if ( !RegisterClass( &wc ) ) {
common->FatalError( "GLW_CreateWindow: could not register window class" );
}
common->Printf( "...registered fake window class\n" );


win32.windowClassRegistered = true;
}


引擎创建了两个窗口类,一个是主窗口的窗口类,一个是Fake的窗口类(假造窗口类),
该Fake窗口类是用来创建一个假窗口来获取显示参数用的。其中主窗口类里的消息响应函数
为 MainWndProc。其中MainWndProc用来处理一些windows窗体的一般消息,并更新win32全局
对象,或将接到的消息打包为引擎内部命令还有就是将消息放入引擎内部的事件队列中。
MainWndProc源码请参见“win_wndproc.cpp”的 
LONG WINAPI MainWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) 函数实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值