3
*************************************************************************
5
ArmageTron -- Just another Tron Lightcycle Game in 3D.
6
Copyright (C) 2000 Manuel Moos (manuel@moosnet.de)
8
**************************************************************************
10
This program is free software; you can redistribute it and/or
11
modify it under the terms of the GNU General Public License
12
as published by the Free Software Foundation; either version 2
13
of the License, or (at your option) any later version.
15
This program is distributed in the hope that it will be useful,
16
but WITHOUT ANY WARRANTY; without even the implied warranty of
17
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
GNU General Public License for more details.
20
You should have received a copy of the GNU General Public License
21
along with this program; if not, write to the Free Software
22
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
***************************************************************************
35
#include "tInitExit.h"
36
#include "tDirectories.h"
43
#include "tCommandLine.h"
44
#include "tConfiguration.h"
45
#include "tRecorder.h"
48
#include "SDL_thread.h"
49
#include "SDL_mutex.h"
52
#define SCREENSHOT_PNG_BITDEPTH 8
53
#define SCREENSHOT_BYTES_PER_PIXEL 3
61
//#error "need SDL 1.1"
66
// nothing to be done.
69
//#elif defined(HAVE_FXMESA)
73
static fxMesaContext ctx=NULL;
82
static HGLRC hRC=NULL;
84
#elif defined(unix) || defined(__unix__)
94
#include <SDL_syswm.h>
96
// graphics initialisation and cleanup:
97
bool rSysDep::InitGL(){
99
SDL_VERSION(&system.version);
100
if (!SDL_GetWMInfo(&system)){
101
std::cerr << "Video information not available!\n";
106
con << "SDL version: " << (int)system.version.major
107
<< "." << (int)system.version.minor << "." << (int)system.version.patch << '\n';
113
int x=fxQueryHardware();
115
std::cerr << "No 3Dfx hardware available.\n" << x << '\n';
119
GLint attribs[]={FXMESA_DOUBLEBUFFER,FXMESA_DEPTH_SIZE,16,FXMESA_NONE};
120
ctx=fxMesaCreateBestContext(0,sr_screenWidth,sr_screenHeight,attribs);
123
std::cerr << "Could not create FX rendering context!\n";
127
fxMesaMakeCurrent(ctx);
131
// windows GL initialisation stolen from
132
// http://www.geocities.com/SiliconValley/Code/1219/opengl32.html
135
HWND hWnd=system.window;
137
PIXELFORMATDESCRIPTOR pfd;
140
// get the device context (DC)
142
if (!hDC) return false;
144
// set the pixel format for the DC
145
ZeroMemory( &pfd, sizeof( pfd ) );
146
pfd.nSize = sizeof( pfd );
148
pfd.dwFlags = PFD_DRAW_TO_WINDOW |
149
PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
150
pfd.iPixelType = PFD_TYPE_RGBA;
151
pfd.cColorBits = currentScreensetting.colorDepth ? 24 : 16;
153
pfd.iLayerType = PFD_MAIN_PLANE;
154
iFormat = ChoosePixelFormat( hDC, &pfd );
155
SetPixelFormat( hDC, iFormat, &pfd );
157
// create and enable the render context (RC)
158
hRC = wglCreateContext( hDC );
159
if (!hRC || !wglMakeCurrent( hDC, hRC ))
163
#elif defined(unix) || defined(__unix__)
164
if (system.subsystem!=SDL_SYSWM_X11){
165
std::cerr << "System is not X11!\n";
166
std::cerr << (int)system.subsystem << "!=" << (int)SDL_SYSWM_X11 <<'\n';
172
dpy=system.info.x11.display;
173
win=system.info.x11.window;
175
int errorbase,tEventbase;
176
if (glXQueryExtension(dpy,&errorbase,&tEventbase) == False){
177
std::cerr << "OpenGL through GLX not supported.\n";
181
int configuration[]={GLX_DOUBLEBUFFER,GLX_RGBA,GLX_DEPTH_SIZE ,12, GLX_RED_SIZE,1,
182
GLX_BLUE_SIZE,1,GLX_GREEN_SIZE,1,None};
184
XVisualInfo *vi=glXChooseVisual(dpy,DefaultScreen(dpy),configuration);
187
std::cerr << "Could not initialize Visual.\n";
191
cx=glXCreateContext(dpy,vi,
195
std::cerr << "Could not initialize GL context.\n";
199
if (!glXMakeCurrent(dpy,win,cx)){
210
void rSysDep::ExitGL(){
211
SDL_SysWMinfo system;
212
SDL_GetWMInfo(&system);
218
fxMesaDestroyContext(ctx);
225
HWND hWnd=system.window;
227
// windows GL cleanup stolen from
228
// http://www.geocities.com/SiliconValley/Code/1219/opengl32.html
231
wglMakeCurrent( NULL, NULL );
232
wglDeleteContext( hRC );
233
ReleaseDC( hWnd, hDC );
238
#elif defined(unix) || defined(__unix__)
241
// glXReleaseBuffersMESA( dpy, win );
242
glXMakeCurrent(dpy,None,NULL);
243
glXDestroyContext(dpy, cx );
250
bool sr_screenshotIsPlanned=false;
252
static bool png_screenshot=true;
253
static tConfItem<bool> pns("PNG_SCREENSHOT",png_screenshot);
256
static void SDL_SavePNG(SDL_Surface *image, tString filename){
263
if (!(fp = fopen(filename, "wb"))) {
264
fprintf(stderr, "can't open file for writing\n");
268
if (!(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))) {
272
if (!(info_ptr = png_create_info_struct(png_ptr))) {
273
png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
277
png_init_io(png_ptr, fp);
279
png_set_IHDR(png_ptr, info_ptr, sr_screenWidth, sr_screenHeight,
280
SCREENSHOT_PNG_BITDEPTH, PNG_COLOR_TYPE_RGB,
281
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
282
PNG_FILTER_TYPE_DEFAULT);
283
png_write_info(png_ptr, info_ptr);
286
if(!(row_ptrs = (png_byte**) malloc(sr_screenHeight * sizeof(png_byte*)))) {
287
png_destroy_write_struct(&png_ptr, &info_ptr);
291
for(i = 0; i < sr_screenHeight; i++) {
292
row_ptrs[i] = (png_byte *)image->pixels + (sr_screenHeight - i - 1)
293
* SCREENSHOT_BYTES_PER_PIXEL * sr_screenWidth;
296
png_write_image(png_ptr, row_ptrs);
297
png_write_end(png_ptr, info_ptr);
298
png_destroy_write_struct(&png_ptr, &info_ptr);
305
static void make_screenshot(){
314
image = SDL_CreateRGBSurface(SDL_SWSURFACE, sr_screenWidth, sr_screenHeight,
315
24, 0x0000FF, 0x00FF00, 0xFF0000 ,0);
316
temp = SDL_CreateRGBSurface(SDL_SWSURFACE, sr_screenWidth, sr_screenHeight,
317
24, 0x0000FF, 0x00FF00, 0xFF0000, 0);
319
// make upside down screenshot
320
glReadPixels(0,0,sr_screenWidth, sr_screenHeight, GL_RGB,
321
GL_UNSIGNED_BYTE, image->pixels);
324
for (idx = 0; idx < sr_screenHeight; idx++)
326
memcpy(reinterpret_cast<char *>(temp->pixels) + 3 * sr_screenWidth * idx,
327
reinterpret_cast<char *>(image->pixels)+ 3
328
* sr_screenWidth*(sr_screenHeight - idx-1),
332
// save screenshot in unused slot
337
tString fileName("screenshot_");
344
// test if file exists
346
if ( tDirectories::Screenshot().Open( s, fileName ) )
348
// yes! try next number
355
SDL_SavePNG(image, tDirectories::Screenshot().GetWritePath( fileName ));
357
SDL_SaveBMP(temp, tDirectories::Screenshot().GetWritePath( fileName ) );
362
SDL_FreeSurface(image);
363
SDL_FreeSurface(temp);
367
class PerformanceCounter
370
PerformanceCounter(): count_(0){ tRealSysTimeFloat(); }
371
unsigned int Count(){ return count_++; }
372
~PerformanceCounter()
374
double time = tRealSysTimeFloat();
376
s << count_ << " frames in " << time << " seconds: " << count_ / time << " fps.\n";
378
MessageBox (NULL, s.str().c_str() , "Performance", MB_OK);
380
std::cout << s.str();
387
static double s_nextFastForwardFrameRecorded=0; // the next frame to render in recorded time
388
static double s_nextFastForwardFrameReal=0; // the next frame to render in real time
391
// settings for fast forward mode
392
static REAL sr_FF_Maxstep=1; // maximum step between rendered frames
393
static tSettingItem<REAL> c_ff( "FAST_FORWARD_MAXSTEP",
396
static REAL sr_FF_MaxstepReal=.05; // maximum step in real time between rendered frames
397
static tSettingItem<REAL> c_ffre( "FAST_FORWARD_MAXSTEP_REAL",
400
static REAL sr_FF_MaxstepRel=1; // maximum step between rendered frames relative to end of FF mode
401
static tSettingItem<REAL> c_ffr( "FAST_FORWARD_MAXSTEP_REL",
405
static double s_fastForwardTo=0;
406
static bool s_fastForward =false;
407
static bool s_benchmark =false;
409
class rFastForwardCommandLineAnalyzer: public tCommandLineAnalyzer
412
virtual bool DoAnalyze( tCommandLineParser & parser )
416
if ( parser.GetOption( forward, "--fastforward" ) )
418
// set fast forward mode
419
s_fastForward = true;
422
std::stringstream str(static_cast< char const * >( forward ) );
423
str >> s_fastForwardTo;
428
if ( parser.GetSwitch( "--benchmark" ) )
430
// set benchmark mode
438
virtual void DoHelp( std::ostream & s )
440
s << "--fastforward <time> : lets time run very fast until the given time is reached\n";
441
s << "--benchmark : renders frames as they were recorded\n";
445
static rFastForwardCommandLineAnalyzer analyzer;
447
// #define MILLION 1000000
450
static double lastFrame = -1;
451
static void sr_DelayFrame( int targetFPS )
453
// calculate microseconds per frame
454
int uSecsPerFrame = MILLION/(targetFPS + 10);
456
// calculate microseconds spent rendering
457
double thisFrame = tRealSysTimeFloat();
459
int uSecsPassed = static_cast<int>( MILLION * ( thisFrame - lastFrame ) );
461
// con << uSecsPassed << "\n";
464
int uSecsToWait = uSecsPerFrame - uSecsPassed;
465
if ( uSecsToWait > 0 )
466
tDelay( uSecsToWait );
468
// call glFinish to wait for GPU
473
rSysDep::rSwapMode rSysDep::swapMode_ = rSysDep::rSwap_glFlush;
474
//rSysDep::rSwapMode rSysDep::swapMode_ = rSysDep::rSwap_60Hz;
478
// for setting breakpoints in optimized mode, too
479
static void breakpoint(){}
481
static bool sr_netSyncThreadGoOn = true;
482
static rSysDep::rNetIdler * sr_netIdler = NULL;
483
int sr_NetSyncThread(void *lockVoid)
485
SDL_mutex *lock = (SDL_mutex *)lockVoid;
489
while ( sr_netSyncThreadGoOn )
492
// wait for network data
493
bool toDo = sr_netIdler->Wait();
498
// disable rendering (during auto-scrolling of console, for example)
499
bool glout = sr_glOut;
502
// new network data arrived, handle it
505
// enable rendering again
515
static SDL_Thread * sr_netSyncThread = NULL;
516
static SDL_mutex * sr_netLock = NULL;
517
void rSysDep::StartNetSyncThread( rNetIdler * idler )
523
// can't use thrading trouble while recording
524
if ( tRecorder::IsRunning() )
527
if ( sr_netSyncThread )
532
sr_netLock = SDL_CreateMutex();
535
sr_netSyncThread = SDL_CreateThread( sr_NetSyncThread, sr_netLock );
536
if ( !sr_netSyncThread )
539
// lock mutex, the thread should only do work while the main thread is waiting for the refresh
540
SDL_mutexP( sr_netLock );
543
void rSysDep::StopNetSyncThread()
545
// stop and delete thread
546
if ( sr_netSyncThread )
548
SDL_mutexV( sr_netLock );
549
sr_netSyncThreadGoOn = false;
550
SDL_WaitThread( sr_netSyncThread, NULL );
551
sr_netSyncThread = NULL;
558
SDL_DestroyMutex( sr_netLock );
563
void rSysDep::SwapGL(){
566
static PerformanceCounter counter;
570
double time = tSysTimeFloat();
571
double realTime = tRealSysTimeFloat();
573
bool next_glOut = sr_glOut;
575
// adapt playback speed to recorded speed
576
if ( !s_benchmark && !s_fastForward && tRecorder::IsPlayingBack() )
578
static double timeOffset=0;
579
static double lastRendered=0;
581
// calculate how much we're behind the rendering schedule
582
double behind = - time + realTime + timeOffset;
583
// std::cout << behind << " " << sr_glOut << "\n";
585
// large delays can only be caused by breakpoints or map downloads; ignore them
586
if ( behind > .5 || realTime > lastRendered + .2 )
588
timeOffset -= behind;
593
// we're a bit behind, skip the next frame
600
lastRendered=realTime;
601
// we're ahead, pause a bit
603
timeOffset -= behind;
604
else if ( behind < -.1 )
606
int delay = int( -( behind + .1 ) * 1000000 );
607
// std::cout << behind << ":" << delay << "\n";
608
tDelayForce( delay );
613
// we're not behind any more. Reactivate rendering.
619
lastRendered=realTime;
624
// display next frame in fast foward mode
625
if ( s_fastForward && ( time > s_nextFastForwardFrameRecorded || realTime > s_nextFastForwardFrameReal ) || next_glOut )
631
// in playback or recording mode, always execute frame tasks, they may be improtant for consistency
632
if ( tRecorder::IsRunning() )
633
rPerFrameTask::DoPerFrameTasks();
639
rPerFrameTask::DoPerFrameTasks();
641
// unlock the mutex while waiting for the swap operation to finish
642
SDL_mutexV( sr_netLock );
657
#if defined(SDL_OPENGL)
658
if (lastSuccess.useSDL)
659
SDL_GL_SwapBuffers();
660
//#elif defined(HAVE_FXMESA)
661
//fxMesaSwapBuffers();
665
if (!lastSuccess.useSDL){
668
#elif defined(unix) || defined(__unix__)
669
glXSwapBuffers(dpy,win);
674
if (sr_screenshotIsPlanned){
676
sr_screenshotIsPlanned=false;
681
SDL_mutexP( sr_netLock );
684
// disable output in fast forward mode
685
if ( s_fastForward && tRecorder::IsPlayingBack() )
687
if ( time < s_fastForwardTo )
689
// next displayed frame should be ten percent closer to the target, but at most 10 seconds
690
s_nextFastForwardFrameRecorded = ( s_fastForwardTo - time ) * sr_FF_MaxstepRel;
691
if ( s_nextFastForwardFrameRecorded > sr_FF_Maxstep )
692
s_nextFastForwardFrameRecorded = sr_FF_Maxstep ;
693
s_nextFastForwardFrameRecorded += time;
694
s_nextFastForwardFrameReal = realTime + sr_FF_MaxstepReal ;
700
std::cout << "End of fast forward mode.\n";
702
s_fastForward = false;
707
if ( !s_fastForward )
713
// store frame time for next frame
714
// lastFrame = tRealSysTimeFloat();
716
sr_glOut = next_glOut;
721
static SDL_mutex *mut;
723
static void stuff_init(){
724
mut=SDL_CreateMutex();
727
static tInitExit stuff_ie(&stuff_init);
731
//std::cerr << "locking...";
737
//std::cerr << " locked!\n";
741
//std::cerr << "unlocking...";
747
//std::cerr << " unlocked!\n";
751
void rSysDep::ClearGL(){
755
if (sr_screenshotIsPlanned){
757
sr_screenshotIsPlanned=false;
761
glClearColor(0.0,0.0,0.0,1.0);
762
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);