Reimagining Some of the Engine ...
Moderator: InsideQC Admins
5 posts
• Page 1 of 1
Reimagining Some of the Engine ...
Got kind of tired of making modifications to the Quake engine, mostly because getting to do anything "fun" can be too time consuming get cool results. You have to re-write everything and it kind of limits experimentation speed ...
Every operating system has its own file structure and Vista/Win 7 and beyond don't like writing to the application path. And it doesn't make sense to write intentional user stuff in the same place as potential engine temp files (i.e. I download a map or a install a mod, that isn't the same as my config ... I can re-download Travail, but I can't re-download a demo I made).
And taking the Host operations, which really don't vary much and giving it a home ... this never belonged in sys_win.c to begin with ...
And taking all the operating system specific crap, isolating it in one place ...
Throw everything that can possibly be system neutral, but global, into its own "haystack".
This function in Quake is a hopeless mess. Some MHness in this. Rather insistent on sizing at any time. Note how the bordersize is known ...
More stuff to some degree shaped by looking at MH's code ... note that I'm not doing host.windowArea = some RECT but host.windowArea.width = some RECT.width, etc.
This is because I'm not going to use a Windows RECT to store the client area or the Window area. Those are not really Windows-specific pieces of information, so I use an fRECT where f = Fake but the same and I can use that data structure on anything. I also avoid qboolean data type because it is {false, true} and that is a C++ datatype. So I use {False, True}, which is annoying because I prefer "true and false" ... but "True and False" don't conflict with C++ and I do want to potentially be able to utilize C++ code if the situation fits, and if I use "true and false" ... a mess begins being unable to share structs.
The beginnings of an "automatic shutdown" system ... the code works as-is. To hell with Sys_Error and Host_Error. There shouldn't be 2 of them. There should be Host_Error and it should just shutdown whatever is actually initialized.
What I really want to do is rapidly put this all together and then start doing geometry, collision, lighting, particle and prediction/networking experiments. It is too hard to do most of that with Quake because the number of changes required grows quickly out of control.
End blah ...
Every operating system has its own file structure and Vista/Win 7 and beyond don't like writing to the application path. And it doesn't make sense to write intentional user stuff in the same place as potential engine temp files (i.e. I download a map or a install a mod, that isn't the same as my config ... I can re-download Travail, but I can't re-download a demo I made).
- Code: Select all
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
// These are the subfolders used by the engine on Windows
#define ENGINE_WIN32_CACHES_SUBPATH "caches"
#define ENGINE_WIN32_DATA_SUBPATH "Gamedata"
char binaryPath[MAX_OSPATH_IS_1024];
// Stage 1: Finalize versioning names
Host_SetIdentity (System_OS_Version());
// Stage 2: Finalize path names
StringLCopy (binaryPath, System_GetBinaryFullPath());
String_Edit_Remove_FileName (binaryPath);
Host_SetDirectories (
/* Binary path */ System_GetBinaryFullPath(),
/* Input data path */ va ("%s/%s", binaryPath, ENGINE_WIN32_DATA_SUBPATH),
/* Write docs path */ va ("%s/%s", System_GetAppDataPath (), host.appName),
/* Cache write dir */ va ("%s/%s/%s", System_GetAppDataPath (), host.appName, ENGINE_WIN32_CACHES_SUBPATH)
);
// Stage 3: Wire it up
// Fill in any Windows specific stuff here
w32_plat.hInstance = hInstance;
w32_plat.registeredClassName = host.appVersionString;
w32_plat.hIcon = LoadIcon (w32_plat.hInstance, MAKEINTRESOURCE (IDI_ICON1)); // ID1_ICON1 is defined in resource.h
Host_Init (); // Keep separate in case we need access to memory, messages, earlier.
Host_Main_Loop (); // Main loop
Host_Shutdown (); // Shut everything down
return (0); // Success
}
And taking the Host operations, which really don't vary much and giving it a home ... this never belonged in sys_win.c to begin with ...
- Code: Select all
void Host_Main_Loop (void)
{
unsigned previous_time_milliseconds = System_Milliseconds (); // Note ... this will initialize time, which does not count as a subsystem at least at this time
// Main program loop
while (!host.terminate)
{
Platform_Run_Loop_Events (); // Let system handle messages, decide to sleep, whatever
host.time_milliseconds = System_Milliseconds ();
host.timeslice = host.time_milliseconds - previous_time_milliseconds;
previous_time_milliseconds = host.time_milliseconds;
Host_Frame (host.timeslice);
}
}
And taking all the operating system specific crap, isolating it in one place ...
- Code: Select all
typedef struct
{
// Required to construct a window
HINSTANCE hInstance;
HICON hIcon; // Window icon
const char *registeredClassName; // Set to the application version string
// Window extra details ...
HWND mainwindow; // HWND for our window
HDC maindc; // device context for our window
HGLRC mainRC; // rendering context
} platform_t;
Throw everything that can possibly be system neutral, but global, into its own "haystack".
- Code: Select all
typedef struct
{
const char *operatingSysText; // Text description of operating system
const char *appName; // Application name
const char *appVersion; // The version string
const char *appVersionString; // Application version name
// All these directories are full operating system path ...
const char *binaryFileName; // (Not fullpath)
const char *binaryDir; // Path to the binary (yeah, it may not be an ".exe")
const char *bundleDir; // Input. <Program Files/whatever> Data that the application needs. (pak files)
const char *documentsDir; // Output. <My Documents Something>User created files (save games, configs, etc.)
const char *cacheDir; // Temp files, downloaded maps and such. <App Data something> Non-user created content directory
// Display environment ... on Windows this is the desktop for the primary monitor
int deviceWidth;
int deviceHeight;
int deviceBitsPerPixel;
int deviceRefreshRate;
float deviceScale;
// Display state info ...
fbool cl_isFullScreen; // Are we displaying fullscreen (therefore we probably changed the display settings
int cl_width; // Client width
int cl_height; // Client height
int cl_bitsPerPixel; // Client bits per pixel
int cl_refreshRate; // Refresh rate in Hz
char cl_describeVideo[64]; // Description
fRECT windowArea;
fRECT clientArea;
int borderAreaWidth;
int borderAreaHeight;
int mouse_center_x; // Center of window to keep mouse centered
int mouse_center_y; // Center of window to keep mouse centered
// Messaging state ...
fbool isAltTabCapable; // If we have no video window, we can't ALT-TAB
fbool isMinimized;
fbool isActiveApp;
fbool isVideoSuspended;
fbool isSoundSuspended;
// Generate start
fbool startup_complete;
// Timing
fbool terminate; // If set to true a normal "Quit" has been issued. Finishes frame. Exits ..
unsigned time_milliseconds;
unsigned timeslice;
} host_t;
This function in Quake is a hopeless mess. Some MHness in this. Rather insistent on sizing at any time. Note how the bordersize is known ...
- Code: Select all
LONG WINAPI MainWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
// MSDSN: If an application processes this message, it should return zero to continue
// check for input messages
if (Win32_ReadInputMessages (hWnd, uMsg, wParam, lParam)) return 0;
switch (uMsg)
{
case WM_ACTIVATE: // WM_ACTIVATE: Sent to both the window being activated and the window being deactivated.
Platform_Focus_Change (LOWORD(wParam) != WA_INACTIVE, (fbool)HIWORD(wParam));
return 0;
case WM_KILLFOCUS: // WM_KILLFOCUS: Sent to a window immediately before it loses the keyboard focus.
if (host.cl_isFullScreen)
ShowWindow (w32_plat.mainwindow, SW_SHOWMINNOACTIVE); // Minmize if fullscreen
return 0;
case WM_CLOSE: // WM_CLOSE: Signal that a window/application should terminate.
Host_Normal_Quit ();
return 0;
case WM_CREATE: // WM_CREATE: After window created, but before is visible
case WM_DESTROY: // WM_DESTROY: Sent when a window immediately after removed from screen
case WM_SYSCHAR: // WM_SYSCHAR: keep Alt-Space from happening and any ALT + letter combination
return 0;
case WM_SIZE: // WM_SIZE: Sent to a window after its size has changed.
Platform_Window_Update_Info ();
Host_Render_Frame ();
return 0;
case WM_MOVE: // WM_MOVE: Sent after a window has been moved.
Platform_Window_Update_Info ();
return 0;
case WM_GETMINMAXINFO: // WM_GETMINMAXINFO: Sent before size change; can be used to override default mins/maxs
if (host.isAltTabCapable) // If we aren't ALT-TAB capable, we cannot resize.
{
MINMAXINFO *mmi = (MINMAXINFO *) lParam;
mmi->ptMinTrackSize.x = 320 + host.borderAreaWidth;
mmi->ptMinTrackSize.y = 200 + host.borderAreaHeight;
}
return 0;
}
// pass all unhandled messages to DefWindowProc
return DefWindowProc (hWnd, uMsg, wParam, lParam);
}
More stuff to some degree shaped by looking at MH's code ... note that I'm not doing host.windowArea = some RECT but host.windowArea.width = some RECT.width, etc.
This is because I'm not going to use a Windows RECT to store the client area or the Window area. Those are not really Windows-specific pieces of information, so I use an fRECT where f = Fake but the same and I can use that data structure on anything. I also avoid qboolean data type because it is {false, true} and that is a C++ datatype. So I use {False, True}, which is annoying because I prefer "true and false" ... but "True and False" don't conflict with C++ and I do want to potentially be able to utilize C++ code if the situation fits, and if I use "true and false" ... a mess begins being unable to share structs.
- Code: Select all
// Refresh rects and mouse center
// Called at window resize/move and window construction.
void Platform_Window_Update_Info (void)
{
WINDOWINFO windowinfo;
RECT twindowArea;
windowinfo.cbSize = sizeof (WINDOWINFO);
GetWindowRect (w32_plat.mainwindow, &twindowArea);
GetWindowInfo (w32_plat.mainwindow, &windowinfo);
host.windowArea.top = twindowArea.top;
host.windowArea.bottom = twindowArea.bottom;
host.windowArea.left = twindowArea.left;
host.windowArea.right = twindowArea.right;
host.clientArea.top = windowinfo.rcClient.top;
host.clientArea.bottom = windowinfo.rcClient.bottom;
host.clientArea.left = windowinfo.rcClient.left;
host.clientArea.right = windowinfo.rcClient.right;
host.mouse_center_x = host.clientArea.left + (RECT_WIDTH(host.clientArea) >> 1);
host.mouse_center_y = host.clientArea.top + (RECT_HEIGHT(host.clientArea) >> 1);
host.borderAreaWidth = RECT_WIDTH(host.windowArea) - RECT_WIDTH(host.clientArea);
host.borderAreaHeight = RECT_HEIGHT(host.windowArea) - RECT_HEIGHT(host.clientArea);
// Fill in the description
host.cl_width = windowinfo.rcClient.right - windowinfo.rcClient.left;
host.cl_height = windowinfo.rcClient.bottom - windowinfo.rcClient.top;
SNPrintf (host.cl_describeVideo, sizeof(host.cl_describeVideo), "%i x %i %i bpp rate = %i Hz (%s)", host.cl_width, host.cl_height, host.cl_bitsPerPixel, host.cl_refreshRate, host.cl_isFullScreen ? "fullscreen": "windowed");
}
The beginnings of an "automatic shutdown" system ... the code works as-is. To hell with Sys_Error and Host_Error. There shouldn't be 2 of them. There should be Host_Error and it should just shutdown whatever is actually initialized.
- Code: Select all
typedef fbool (*func_begin) (void);
typedef void (*func_end) (void);
typedef struct
{
func_begin Startup;
func_end Shutdown;
fbool initialized;
} subsystem_t;
subsystem_t subsystems [] =
{
{ Platform_Init, Platform_Shutdown, False }, // Gets desktop size
{ Platform_Window_Create, NULL, False }, // Sets up video
{ Renderer_OpenGL_Init, Renderer_OpenGL_Shutdown, False }, // Initializes OpenGL
};
int num_sub_systems = sizeof (subsystems) / sizeof(subsystems[0]);
// Initialize sub-systems
void Host_Init (void)
{
int i;
// Initialize each sub system
for (i = 0; i < num_sub_systems; i ++)
{
subsystems[i].initialized = subsystems[i].Startup ();
// if (subsystems[i].initialized)
// Printf something
}
host.startup_complete = True;
}
// Shutdown sub-systems
void Host_Shutdown (void)
{
// Last in, first out ...
int i, j;
for (j = 0; j < num_sub_systems; j ++)
{
i = (num_sub_systems - j) - 1; // ie 10 - 0 - 1 = starts at 9
if (!subsystems[i].initialized || subsystems[i].Shutdown == NULL)
continue;
subsystems[i].Shutdown ();
// Printf something
}
}
What I really want to do is rapidly put this all together and then start doing geometry, collision, lighting, particle and prediction/networking experiments. It is too hard to do most of that with Quake because the number of changes required grows quickly out of control.
End blah ...
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Reimagining Some of the Engine ...
Making WinQuake a bit more "Windows 95" like could be fun.
- Resizable display context (on GL this no doubt would be evil for reinitializing, but software should be fine and stretchable to anything above 320x200)
- A menu bar with options, video modes (ala Unreal and Jazz Jackrabbit 2)
- a custom title bar (ala Jazz Jackrbabit 2 and some other games)
- Resizable display context (on GL this no doubt would be evil for reinitializing, but software should be fine and stretchable to anything above 320x200)
- A menu bar with options, video modes (ala Unreal and Jazz Jackrabbit 2)
- a custom title bar (ala Jazz Jackrbabit 2 and some other games)
i should not be here
- leileilol
- Posts: 2783
- Joined: Fri Oct 15, 2004 3:23 am
Re: Reimagining Some of the Engine ...
Me personally, I'm trying to imagine a world where maintaining multi-platform projects -- in particular Quake (although I'm kinda focused on the GL side) isn't such a son of a bitch [trying to keep in mind what Spike said about Q3 system agnostic input queuing].
But yeah, I'd kill for one of your above in software rendering. Fruitz of Dojo Quake actually does that on a Mac (running 320x200 software mode in a larger sized window like 640 x 400 or 960 x 600, etc.)
/Wishes the software renderer code wasn't such obtuse spaghetti as software rendering is completely multiplatform neutral.
But yeah, I'd kill for one of your above in software rendering. Fruitz of Dojo Quake actually does that on a Mac (running 320x200 software mode in a larger sized window like 640 x 400 or 960 x 600, etc.)
/Wishes the software renderer code wasn't such obtuse spaghetti as software rendering is completely multiplatform neutral.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
Re: Reimagining Some of the Engine ...
Once you start multi-platform support, this is a never ending task * 3.
- r00k
- Posts: 1110
- Joined: Sat Nov 13, 2004 10:39 pm
Re: Reimagining Some of the Engine ...
r00k wrote:Once you start multi-platform support, this is a never ending task * 3.
There is some truth in that.
The night is young. How else can I annoy the world before sunsrise?
Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
-

Baker - Posts: 3666
- Joined: Tue Mar 14, 2006 5:15 am
5 posts
• Page 1 of 1
Who is online
Users browsing this forum: No registered users and 1 guest