I have started to create a new class , GFXRenderer .
Based upon gfxlib.h functions , GFXRenderer will be the centralized interface for rendering
stuff .
I'm not happy with the actual design/architecture of the gameEngine ( even if it is working
good , and i feel a lot of respect for that ! ) because some stuffs are linked each other
where they shouldn't ( in my point of view ) .
Example of bad design is Universe class that deal with rendering ( why ? ) :
/**
* Class Universe Deals with universal constants. It is a global,
* accessed from anywhere as _Universe-> Universe may be queried for
* Relationships, the current star system rendering is taking place in
* etc. It acts as a wrapper to the active Star System.
* Additionally it handles beginning and ending the main loop.
* And starting and ending graphics. (incl the task of wiping temp lights)
* Deprecated: loaded dynamic gldrv module
*/
The answer is because of the main loop ! ( single thread for gamelogic,physic,inputs,audio,render )
I wonder if it's possible in a single threaded app to separate the Gamelogic and the rendering system . ( a loop for the game logic-input-audio- an other for rendering ops.)
Look now what this mean today :
Code: Select all
void GameUniverse::Loop( void main_loop() )
{
GFXLoop( main_loop );
}
The render stage is looped inside the gameLogic .
It is also initialised in the gameLogic :
Code: Select all
void GameUniverse::Init( int argc, char **argv, const char *galaxy )
{
current_cockpit = 0;
//Select drivers
#if defined (__APPLE__)
//get the current working directory so when glut trashes it we can restore.
char pwd[MAXPATHLEN];
getcwd( pwd, MAXPATHLEN );
#endif
GFXInit( argc, argv );
#if defined (__APPLE__)
//Restore it
chdir( pwd );
#endif
StartGFX();
InitInput();
hud_camera = Camera();
...
...
AND GFX is only Opengl stuff :
Code: Select all
void GFXInit( int argc, char **argv )
{
char vsname[12] = "Vega Strike";
char vsicon[9] = "vega.ico";
winsys_init (&argc,argv,&vsname[0],&vsicon[0]);
/* Ingore key-repeat messages */
winsys_enable_key_repeat( false );
glViewport( 0, 0, g_game.x_resolution, g_game.y_resolution );
float clearcol[4];
gl_options.wireframe = game_options.use_wireframe;
gl_options.max_texture_dimension = game_options.max_texture_dimension;
gl_options.max_movie_dimension = game_options.max_movie_dimension;
bool textsupported = (vsExtensionSupported( "GL_ARB_texture_non_power_of_two" ) || vsExtensionSupported( "GL_ARB_texture_rectangle" ) || vsExtensionSupported( "GL_NV_texture_rectangle" )) ? "true" : "false" ;
gl_options.rect_textures = game_options.rect_textures ? true : textsupported;
if (gl_options.rect_textures) {
VSFileSystem::vs_dprintf(3, "RECT textures supported\n");
// Fetch max rect textue dimension
GLint max_rect_dimension = 65535;
glGetIntegerv(
GL_MAX_RECTANGLE_TEXTURE_SIZE_ARB,
&max_rect_dimension);
gl_options.max_rect_dimension = max_rect_dimension;
VSFileSystem::vs_dprintf(3, "RECT max texture dimension: %d\n", max_rect_dimension);
}
bool vidsupported = ( gl_options.rect_textures || ( vsExtensionSupported( "GL_ARB_texture_non_power_of_two" ) && vsVendorMatch("nvidia")));
gl_options.pot_video_textures = game_options.pot_video_textures ? true : vidsupported;
if (!gl_options.pot_video_textures && gl_options.rect_textures) {
// Enforce max rect texture for movies, which use them
if (gl_options.max_movie_dimension > gl_options.max_rect_dimension)
gl_options.max_movie_dimension = gl_options.max_rect_dimension;
}
if (gl_options.pot_video_textures)
VSFileSystem::vs_dprintf(1, "Forcing POT video textures\n");
else
VSFileSystem::vs_dprintf(3, "Using NPOT video textures\n");
// Removing gl_options soon
gl_options.smooth_shade = game_options.SmoothShade;
gl_options.mipmap = game_options.mipmapdetail;
gl_options.compression = game_options.texture_compression;
gl_options.Multitexture = game_options.reflection;
gl_options.smooth_lines = game_options.smooth_lines;
gl_options.smooth_points = game_options.smooth_points;
gl_options.display_lists = game_options.displaylists;
gl_options.s3tc = game_options.s3tc;
gl_options.ext_clamp_to_edge = game_options.ext_clamp_to_edge;
gl_options.ext_clamp_to_border = game_options.ext_clamp_to_border;
vs_config->getColor( "space_background", clearcol );
glClearColor( clearcol[0], clearcol[1], clearcol[2], clearcol[3] );
winsys_set_reshape_func( Reshape );
initfov();
glShadeModel( GL_SMOOTH );
glEnable( GL_CULL_FACE );
glCullFace( GL_BACK );
glShadeModel( GL_SMOOTH );
glEnable( GL_DEPTH_TEST );
glDepthFunc( GL_LESS );
if (gl_options.wireframe)
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
if (gl_options.smooth_shade == 0)
glShadeModel( GL_FLAT );
glEnable( GL_ALPHA_TEST );
glAlphaFunc( GL_GREATER, 0.0 );
init_opengl_extensions();
GFXInitTextureManager();
if (gl_options.Multitexture)
GFXActiveTexture( 0 );
glEnable( GL_TEXTURE_2D ); //use two-dimensional texturing
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
#if defined (IRIX)
glEnable( GL_SHARED_TEXTURE_PALETTE_EXT );
#endif
if ( vsExtensionSupported( "GL_EXT_color_table" ) || vsExtensionSupported( "GL_EXT_shared_texture_palette" ) ) {
gl_options.PaletteExt = 1;
VSFileSystem::vs_dprintf(3, "OpenGL::EXTColorTable supported\n");
} else {
gl_options.PaletteExt = 0;
VSFileSystem::vs_dprintf( 2, "OpenGL::EXTColorTable unsupported\n" );
}
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
if (gl_options.Multitexture) {
for (int i = 1; i < 4; ++i) {
GFXActiveTexture( i );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
GFXTextureEnv( i, GFXADDTEXTURE );
glPixelStorei( GL_UNPACK_SKIP_ROWS, 0 );
glPixelStorei( GL_UNPACK_SKIP_PIXELS, 0 );
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glPixelStorei( GL_UNPACK_SWAP_BYTES, 0 );
// Choose cubemap or spheremap coordinates.
if (i == 1) {
#ifdef NV_CUBE_MAP
GFXToggleTexture( true, 1, CUBEMAP );
GFXTextureCoordGenMode( 1, CUBE_MAP_GEN, NULL, NULL );
#else
const float tempo[4] = {1, 0, 0, 0};
GFXToggleTexture( true, 1, TEXTURE2D );
GFXTextureCoordGenMode( 1, SPHERE_MAP_GEN, tempo, tempo );
#endif
}
}
}
GFXActiveTexture( 0 );
glClearDepth( 1 );
glEnable( GL_BLEND );
glDisable( GL_ALPHA_TEST );
GFXBlendMode( ONE, ZERO );
glColor3f( 0, 0, 0 );
glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glMatrixMode( GL_TEXTURE );
glLoadIdentity(); //set all matricies to identity
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glEnable( GL_LIGHTING );
glDisable( GL_NORMALIZE );
glDisable( GL_LINE_SMOOTH );
glDisable( GL_POINT_SMOOTH );
int con;
GFXCreateLightContext( con );
//glutSetCursor(GLUT_CURSOR_NONE);
/* Avoid scrambled screen on startup - Twice, for triple buffering */
if ( game_options.ClearOnStartup ) {
glClear( GL_COLOR_BUFFER_BIT );
winsys_swap_buffers();
glClear( GL_COLOR_BUFFER_BIT );
winsys_swap_buffers();
}
glEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
winsys_show_cursor( false );
}
I have to deal as consequence to read/write operations to disk inside GFXRenderer
because of this implementation in Universe !
That is bad , and i will try to find an other way to do that . ( checking relationships etc..)
The renderer should be an independent module that you first setup with viewports and camera . ( and perhaps is responsible also for window creation ? ) .
All other operations should be dedicated to specialized class , used in Universe for example .
The actual render process :
step 1
definition of main-loop :
Code: Select all
void GameUniverse::Loop( void main_loop() )
{
GFXLoop( main_loop );
}
step 2 ( frame level )
Code: Select all
void main_loop()
{
//Evaluate number of loops per second each XX loops
if (loop_count == 500) {
last_check = cur_check;
cur_check = getNewTime();
if (last_check != 1) {
//Time to update test
avg_loop = ( (nb_checks-1)*avg_loop+( loop_count/(cur_check-last_check) ) )/(nb_checks);
nb_checks = nb_checks+1;
}
loop_count = -1;
}
loop_count++;
//Execute DJ script
Music::MuzakCycle();
_Universe->StartDraw();// frame level
if (myterrain)
myterrain->AdjustTerrain( _Universe->activeStarSystem() );
if (Network != NULL)
for (size_t jj = 0; jj < _Universe->numPlayers(); jj++)
Network[jj].checkMsg( NULL );
#ifndef NO_GFX
VSFileSystem::vs_dprintf(3, "Drawn %d vertices in %d batches\n",
gl_vertices_this_frame,
gl_batches_this_frame);
gl_vertices_this_frame = 0;
gl_batches_this_frame = 0;
#endif
//Commit audio scene status to renderer
if (g_game.sound_enabled)
Audio::SceneManager::getSingleton()->commit();
}
The actual renderloop is that part of code :
Code: Select all
void GameUniverse::StartDraw()
{
#ifndef WIN32
RESETTIME();
#endif
GFXBeginScene();
size_t i;
StarSystem *lastStarSystem = NULL;
for (i = 0; i < cockpit.size(); ++i) {
SetActiveCockpit( i );
float x, y, w, h;
CalculateCoords( i, cockpit.size(), x, y, w, h );
AccessCamera()->SetSubwindow( x, y, w, h );
if (cockpit.size() > 1 && AccessCockpit( i )->activeStarSystem != lastStarSystem) {
active_star_system[0]->SwapOut();
lastStarSystem = AccessCockpit()->activeStarSystem;
active_star_system[0] = lastStarSystem;
lastStarSystem->SwapIn();
}
AccessCockpit()->SelectProperCamera();
if (cockpit.size() > 0)
AccessCamera()->UpdateGFX();
if ( !RefreshGUI() && !UniverseUtil::isSplashScreenShowing() )
activeStarSystem()->Draw();
AccessCamera()->SetSubwindow( 0, 0, 1, 1 );
}
UpdateTime();
UpdateTimeCompressionSounds();
_Universe->SetActiveCockpit( ( (int) ( rand01()*cockpit.size() ) )%cockpit.size() );
for (i = 0; i < star_system.size() && i < game_options.NumRunningSystems; ++i)
star_system[i]->Update( (i == 0) ? 1 : game_options.InactiveSystemTime/i, true );
StarSystem::ProcessPendingJumps();
for (i = 0; i < cockpit.size(); ++i) {
SetActiveCockpit( i );
pushActiveStarSystem( AccessCockpit( i )->activeStarSystem );
ProcessInput( i ); //input neesd to be taken care of;
popActiveStarSystem();
}
if (screenshotkey) {
KBData b;
Screenshot( b, PRESS );
screenshotkey = false;
}
GFXEndScene();
//so we don't starve the audio thread
micro_sleep( getmicrosleep() );
//remove systems not recently visited?
static int sorttime = 0;
if (game_options.garbagecollectfrequency != 0) {
//don't want to delete something when there is something pending to jump therexo
if ( PendingJumpsEmpty() ) {
if ( (++sorttime)%game_options.garbagecollectfrequency == 1 ) {
SortStarSystems( star_system, active_star_system.back() );
if (star_system.size() > game_options.numoldsystems && game_options.deleteoldsystems) {
if ( std::find( active_star_system.begin(), active_star_system.end(),
star_system.back() ) == active_star_system.end() ) {
delete star_system.back();
star_system.pop_back();
} else {
VSFileSystem::vs_fprintf( stderr, "error with active star system list\n" );
}
}
}
}
}
}
Some reading about game engine patern and rendering :
http://gameprogrammingpatterns.com/game-loop.html
http://www.koonsolo.com/news/dewitters-gameloop/
Run, run as fast as you can
We’ve already seen the simplest possible game loop:
while (true)
{
processInput();
update();
render();
}
Today , it's like
while (true)
{
processInput();
Universe.update(render());
}
The render stage is inside the Universe update .
Well , i go back to the code ...
( the 0.5.2 vc9 solution in trunk/ is still not working .
The compilation and link are ok but the exe generated crash at first loading time .
Severe , i must kill the process , no error file write ...
The precompiled exe in the package is working
Perhaps a bad configuration in the project ?)