Files
itgmania212121/stepmania/src/RageDisplay.cpp
T

541 lines
13 KiB
C++
Raw Normal View History

2002-05-20 08:59:37 +00:00
#include "stdafx.h"
/*
-----------------------------------------------------------------------------
Class: RageDisplay
Desc: See header.
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
Chris Danford
-----------------------------------------------------------------------------
*/
#include "RageDisplay.h"
#include "RageUtil.h"
#include "RageLog.h"
2002-07-27 19:29:51 +00:00
#include "RageTimer.h"
#include "RageException.h"
#include "RageTexture.h"
2002-11-12 09:29:46 +00:00
#include "RageTextureManager.h"
#include "RageMath.h"
#include "RageTypes.h"
#include "GameConstantsAndTypes.h"
2002-06-14 22:25:22 +00:00
2002-11-12 00:51:27 +00:00
#include <set>
2002-05-20 08:59:37 +00:00
2002-11-12 00:51:27 +00:00
#include "SDL.h"
/* ours is more up-to-date */
#define NO_SDL_GLEXT
#include "SDL_opengl.h"
2002-05-20 08:59:37 +00:00
2002-11-12 00:51:27 +00:00
#include "glext.h"
PFNGLBLENDFUNCSEPARATEPROC glBlendFuncSeparate;
RageDisplay* DISPLAY = NULL;
2002-05-20 08:59:37 +00:00
////////////
// Globals
////////////
const int MAX_NUM_VERTICIES = 1000;
SDL_Surface *g_screen = NULL; // this class is a singleton, so there can be only one
vector<RageMatrix> g_matModelStack; // model matrix stack
RageMatrix g_matView; // view matrix
RageMatrix& GetTopModelMatrix() { return g_matModelStack.back(); }
int g_flags = 0; /* SDL video flags */
GLenum g_vertMode = GL_TRIANGLES;
RageVertex g_vertQueue[MAX_NUM_VERTICIES];
RageTimer g_LastCheckTimer;
int g_iNumVerts;
float g_fLastCheckTime;
2002-11-11 20:43:30 +00:00
int g_iFPS, g_iVPF, g_iDPF;
2002-11-11 21:05:02 +00:00
int g_glVersion;
2002-05-20 08:59:37 +00:00
2002-11-11 20:43:30 +00:00
int RageDisplay::GetFPS() const { return g_iFPS; }
int RageDisplay::GetVPF() const { return g_iVPF; }
int RageDisplay::GetDPF() const { return g_iDPF; }
2002-11-13 01:30:01 +00:00
DEVMODE SMGfx;
2002-11-11 20:43:30 +00:00
static int g_iFramesRenderedSinceLastCheck,
g_iVertsRenderedSinceLastCheck,
g_iDrawsSinceLastCheck;
2002-11-12 00:51:27 +00:00
set<string> g_glExts;
static bool g_GL_EXT_texture_env_combine;
2002-11-12 19:29:45 +00:00
typedef BOOL (APIENTRY * PWSWAPINTERVALEXTPROC) (int interval);
PWSWAPINTERVALEXTPROC wglSwapIntervalEXT;
2002-11-12 00:51:27 +00:00
void GetGLExtensions(set<string> &ext)
{
const char *buf = (const char *)glGetString(GL_EXTENSIONS);
vector<CString> lst;
split(buf, " ", lst);
for(unsigned i = 0; i < lst.size(); ++i)
ext.insert(lst[i]);
LOG->Trace("OpenGL extensions: %s", buf);
}
2002-11-12 09:29:46 +00:00
RageDisplay::RageDisplay( bool windowed, int width, int height, int bpp, int rate, bool vsync )
{
// LOG->Trace( "RageDisplay::RageDisplay()" );
SDL_InitSubSystem(SDL_INIT_VIDEO);
2002-11-12 09:29:46 +00:00
SetVideoMode( windowed, width, height, bpp, rate, vsync );
2002-11-12 00:51:27 +00:00
2002-11-12 09:29:46 +00:00
SetupOpenGL();
}
2002-11-12 09:29:46 +00:00
void RageDisplay::SetupOpenGL()
{
/*
* Set up OpenGL for 2D rendering.
*/
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
/*
* Set state variables
*/
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
2002-11-12 20:39:08 +00:00
2002-05-20 08:59:37 +00:00
RageDisplay::~RageDisplay()
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
2002-08-29 22:30:06 +00:00
}
2002-11-12 20:39:08 +00:00
void RageDisplay::SetupExtensions()
{
double fGLVersion = atof( (const char *) glGetString(GL_VERSION) );
g_glVersion = int(roundf(fGLVersion * 10));
LOG->Trace( "OpenGL version %.1f", g_glVersion / 10.);
GetGLExtensions(g_glExts);
if(g_glExts.find("GL_EXT_texture_env_combine") != g_glExts.end())
g_GL_EXT_texture_env_combine = true;
glBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) SDL_GL_GetProcAddress ("glBlendFuncSeparate");
/* Windows vsync: */
if(g_glExts.find("WGL_EXT_swap_control") != g_glExts.end()) {
wglSwapIntervalEXT = (PWSWAPINTERVALEXTPROC) SDL_GL_GetProcAddress("wglSwapIntervalEXT");
if(wglSwapIntervalEXT)
LOG->Trace("Got wglSwapIntervalEXT");
}
}
2002-11-12 09:29:46 +00:00
/* Set the video mode. In some cases, changing the video mode will reset
* the rendering context; returns true if we need to reload textures. */
bool RageDisplay::SetVideoMode( bool windowed, int width, int height, int bpp, int rate, bool vsync )
2002-08-29 22:30:06 +00:00
{
// LOG->Trace( "RageDisplay::SetVideoMode( %d, %d, %d, %d, %d, %d )", windowed, width, height, bpp, rate, vsync );
2002-08-29 22:30:06 +00:00
2002-11-12 09:29:46 +00:00
g_flags = 0;
2002-11-12 02:43:36 +00:00
if( !windowed )
g_flags |= SDL_FULLSCREEN;
g_flags |= SDL_DOUBLEBUF;
g_flags |= SDL_RESIZABLE;
g_flags |= SDL_OPENGL;
2002-08-29 22:30:06 +00:00
SDL_ShowCursor( ~g_flags & SDL_FULLSCREEN );
2002-08-29 22:30:06 +00:00
switch( bpp )
2002-05-20 08:59:37 +00:00
{
case 15:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
break;
case 16:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
break;
case 24:
case 32:
default:
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
2002-05-20 08:59:37 +00:00
}
2002-11-12 09:29:46 +00:00
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, TRUE);
2002-05-20 08:59:37 +00:00
2002-11-12 09:29:46 +00:00
#ifdef SDL_HAS_REFRESH_RATE
if(rate == REFRESH_DEFAULT)
SDL_SM_SetRefreshRate(0);
2002-11-13 01:30:01 +00:00
/*else
// Change Windows Refresh Rate:
// This is a modification of a code snippet I found
// It changes the windows desktop refresh rate if
// it's on a windows platform.
// -=Lance Gilbert=-
#if defined(WIN32)
::ZeroMemory(&SMGfx, sizeof(SMGfx));
SMGfx.dmDisplayFrequency = rate;
SMGfx.dmFields = DM_DISPLAYFREQUENCY;
SMGfx.dmSize = sizeof(SMGfx);
ChangeDisplaySettings(&SMGfx, 0);
#endif*/
2002-11-12 09:29:46 +00:00
#endif
bool need_reload = false;
2002-11-12 09:56:57 +00:00
#ifndef SDL_HAS_CHANGEVIDEOMODE
2002-11-12 09:29:46 +00:00
/* We can't change the video mode without nuking the GL context. If we have
* a screen, NULL it out, and signal the caller to reload all textures. */
if(g_screen) {
g_screen = NULL;
need_reload = true;
TEXTUREMAN->InvalidateTextures();
}
#endif
2002-05-20 08:59:37 +00:00
2002-11-12 09:29:46 +00:00
if(!g_screen) {
g_screen = SDL_SetVideoMode(width, height, bpp, g_flags);
if(!g_screen)
throw RageException("Failed to open screen!");
2002-11-11 21:05:02 +00:00
2002-11-12 09:29:46 +00:00
SDL_WM_SetCaption("StepMania", "StepMania");
}
2002-11-12 09:56:57 +00:00
#ifdef SDL_HAS_CHANGEVIDEOMODE
2002-11-12 09:29:46 +00:00
else
{
SDL_SM_ChangeVideoMode_OpenGL(g_flags, g_screen, width, height);
}
#endif
2002-05-20 08:59:37 +00:00
2002-11-12 20:39:08 +00:00
/* Now that we've initialized, we can search for extensions (some of which
* we may need to set up the video mode). */
SetupExtensions();
/* Set vsync the Windows way, if we can. (What other extensions are there
* to do this, for other archs?) */
if(wglSwapIntervalEXT) {
wglSwapIntervalEXT(vsync);
}
2002-11-12 09:29:46 +00:00
if(need_reload) {
/* OpenGL state was lost; set up again. */
SetupOpenGL();
}
glViewport(0, 0, g_screen->w, g_screen->h);
2002-05-20 08:59:37 +00:00
2002-11-12 09:29:46 +00:00
/* Clear any junk that's in the framebuffer. */
Clear();
Flip();
2002-05-20 08:59:37 +00:00
2002-11-12 09:29:46 +00:00
return need_reload;
2002-05-20 08:59:37 +00:00
}
int RageDisplay::GetMaxTextureSize() const
2002-05-20 08:59:37 +00:00
{
GLint size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
return size;
2002-05-20 08:59:37 +00:00
}
void RageDisplay::Clear()
2002-05-20 08:59:37 +00:00
{
glClearColor( 0,0,0,1 );
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
2002-05-20 08:59:37 +00:00
}
void RageDisplay::Flip()
2002-05-20 08:59:37 +00:00
{
FlushQueue();
SDL_GL_SwapBuffers();
g_iFramesRenderedSinceLastCheck++;
if( g_LastCheckTimer.PeekDeltaTime() >= 1.0f ) // update stats every 1 sec.
{
g_LastCheckTimer.GetDeltaTime();
g_iFPS = g_iFramesRenderedSinceLastCheck;
g_iFramesRenderedSinceLastCheck = 0;
2002-11-11 20:43:30 +00:00
g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFPS;
g_iVertsRenderedSinceLastCheck = 0;
g_iDPF = g_iDrawsSinceLastCheck / g_iFPS;
g_iDrawsSinceLastCheck = 0;
2002-11-12 20:39:08 +00:00
LOG->Trace( "FPS: %d, VPF: %d, DPF: %d", g_iFPS, g_iVPF, g_iDPF );
}
2002-05-20 08:59:37 +00:00
}
bool RageDisplay::IsWindowed() const
2002-05-20 08:59:37 +00:00
{
return true; // FIXME
2002-05-20 08:59:37 +00:00
}
void RageDisplay::DrawQuad( const RageVertex v[4] ) // upper-left, upper-right, lower-left, lower-right
2002-05-20 08:59:37 +00:00
{
DrawQuads( v, 4 );
2002-05-20 08:59:37 +00:00
}
void RageDisplay::DrawQuads( const RageVertex v[], int iNumVerts )
{
if( g_vertMode != GL_QUADS )
FlushQueue();
ASSERT( (iNumVerts%4) == 0 );
g_vertMode = GL_QUADS;
AddVerts( v, iNumVerts );
}
void RageDisplay::DrawFan( const RageVertex v[], int iNumVerts )
{
if( g_vertMode != GL_TRIANGLE_FAN )
FlushQueue();
ASSERT( iNumVerts >= 3 );
g_vertMode = GL_TRIANGLE_FAN;
AddVerts( v, iNumVerts );
FlushQueue();
}
void RageDisplay::DrawStrip( const RageVertex v[], int iNumVerts )
{
if( g_vertMode != GL_TRIANGLE_STRIP )
FlushQueue();
ASSERT( iNumVerts >= 3 );
g_vertMode = GL_TRIANGLE_STRIP;
AddVerts( v, iNumVerts );
FlushQueue();
}
void RageDisplay::AddVerts( const RageVertex v[], int iNumVerts )
{
for( int i=0; i<iNumVerts; i++ )
{
// Don't overflow the queue
if( g_iNumVerts+1 > MAX_NUM_VERTICIES )
FlushQueue();
// transform the verticies as we copy
RageVec3TransformCoord( &g_vertQueue[g_iNumVerts].p, &v[i].p, &GetTopModelMatrix() );
g_vertQueue[g_iNumVerts].c = v[i].c;
g_vertQueue[g_iNumVerts].t = v[i].t;
g_iNumVerts++;
}
}
void RageDisplay::FlushQueue()
{
if( g_iNumVerts == 0 )
return;
2002-08-19 20:02:30 +00:00
glInterleavedArrays( GL_T2F_C4UB_V3F, sizeof(RageVertex), g_vertQueue );
glDrawArrays( g_vertMode, 0, g_iNumVerts );
2002-08-19 20:02:30 +00:00
g_iVertsRenderedSinceLastCheck += g_iNumVerts;
g_iNumVerts = 0;
g_iDrawsSinceLastCheck++;
}
void RageDisplay::SetViewTransform( const RageMatrix* pMatrix )
{
FlushQueue();
// OpenGL doesn't have a separate view matrix. We need to save it and muliply in later
g_matView = *pMatrix;
}
void RageDisplay::SetProjectionTransform( const RageMatrix* pMatrix )
{
FlushQueue();
glMatrixMode( GL_MODELVIEW );
glLoadMatrixf( (float*)pMatrix );
}
void RageDisplay::GetViewTransform( RageMatrix* pMatrixOut )
{
*pMatrixOut = g_matView;
}
void RageDisplay::GetProjectionTransform( RageMatrix* pMatrixOut )
{
glGetFloatv( GL_PROJECTION_MATRIX, (float*)pMatrixOut );
}
void RageDisplay::ResetMatrixStack()
{
RageMatrix ident;
RageMatrixIdentity( &ident );
g_matModelStack.clear();
g_matModelStack.push_back(ident);
}
void RageDisplay::PushMatrix()
{
g_matModelStack.push_back( GetTopModelMatrix() );
ASSERT(g_matModelStack.size()<20); // check for infinite loop
}
void RageDisplay::PopMatrix()
{
g_matModelStack.erase( g_matModelStack.end()-1, g_matModelStack.end() );
ASSERT(g_matModelStack.size()>=1); // popped a matrix we didn't push
}
2002-08-29 21:30:16 +00:00
void RageDisplay::Translate( float x, float y, float z )
{
RageMatrix matTemp;
RageMatrixTranslation( &matTemp, x, y, z );
RageMatrix& matTop = GetTopModelMatrix();
RageMatrixMultiply( &matTop, &matTemp, &matTop );
}
2002-08-29 21:30:16 +00:00
void RageDisplay::TranslateLocal( float x, float y, float z )
{
RageMatrix matTemp;
RageMatrixTranslation( &matTemp, x, y, z );
RageMatrix& matTop = GetTopModelMatrix();
RageMatrixMultiply( &matTop, &matTop, &matTemp );
}
2002-08-29 21:30:16 +00:00
void RageDisplay::Scale( float x, float y, float z )
{
RageMatrix matTemp;
RageMatrixScaling( &matTemp, x, y, z );
RageMatrix& matTop = GetTopModelMatrix();
RageMatrixMultiply( &matTop, &matTemp, &matTop );
}
2002-08-29 21:30:16 +00:00
void RageDisplay::RotateX( float r )
{
RageMatrix matTemp;
RageMatrixRotationX( &matTemp, r );
RageMatrix& matTop = GetTopModelMatrix();
RageMatrixMultiply( &matTop, &matTemp, &matTop );
}
2002-08-29 21:30:16 +00:00
void RageDisplay::RotateY( float r )
{
RageMatrix matTemp;
RageMatrixRotationY( &matTemp, r );
RageMatrix& matTop = GetTopModelMatrix();
RageMatrixMultiply( &matTop, &matTemp, &matTop );
}
2002-08-29 21:30:16 +00:00
void RageDisplay::RotateZ( float r )
{
RageMatrix matTemp;
RageMatrixRotationZ( &matTemp, r );
RageMatrix& matTop = GetTopModelMatrix();
RageMatrixMultiply( &matTop, &matTemp, &matTop );
}
void RageDisplay::SetTexture( RageTexture* pTexture )
{
static int iLastTexID = 0;
int iNewTexID = pTexture ? pTexture->GetGLTextureID() : 0;
if( iLastTexID != iNewTexID )
FlushQueue();
iLastTexID = iNewTexID;
glBindTexture( GL_TEXTURE_2D, iNewTexID );
}
void RageDisplay::SetTextureModeModulate()
{
int a;
glGetTexEnviv( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &a );
if( a != GL_MODULATE )
FlushQueue();
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
2002-11-12 00:51:27 +00:00
/* Set the blend mode for both texture and alpha. This is all that's
* available pre-OpenGL 1.4. */
void RageDisplay::SetBlendMode(int src, int dst)
{
int a, b;
glGetIntegerv( GL_BLEND_SRC, &a );
glGetIntegerv( GL_BLEND_DST, &b );
2002-11-12 00:51:27 +00:00
if( a!=src || b!=dst )
FlushQueue();
2002-11-12 00:51:27 +00:00
glBlendFunc( src, dst );
}
2002-11-12 00:51:27 +00:00
void RageDisplay::SetTextureModeGlow()
{
2002-11-12 00:51:27 +00:00
if(!g_GL_EXT_texture_env_combine) {
SetBlendMode( GL_SRC_ALPHA, GL_ONE );
return;
}
2002-11-12 00:51:27 +00:00
FlushQueue();
2002-11-12 00:51:27 +00:00
/* Source color is the diffuse color only: */
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_PRIMARY_COLOR_EXT);
/* Source alpha is texture alpha * diffuse alpha: */
glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA_EXT, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PRIMARY_COLOR_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA_EXT, GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA_EXT, GL_TEXTURE);
}
void RageDisplay::SetBlendModeNormal()
{
SetBlendMode( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
}
void RageDisplay::SetBlendModeAdd()
{
2002-11-12 00:51:27 +00:00
SetBlendMode( GL_ONE, GL_ONE );
}
2002-11-12 01:25:03 +00:00
bool RageDisplay::ZBufferEnabled() const
{
bool a;
glGetBooleanv( GL_DEPTH_TEST, (unsigned char*)&a );
2002-11-12 01:25:03 +00:00
return a;
}
2002-11-12 01:25:03 +00:00
void RageDisplay::EnableZBuffer()
{
if( !ZBufferEnabled() )
FlushQueue();
glEnable( GL_DEPTH_TEST );
}
void RageDisplay::DisableZBuffer()
{
2002-11-12 01:25:03 +00:00
if( ZBufferEnabled() )
FlushQueue();
glDisable( GL_DEPTH_TEST );
2002-10-18 19:01:32 +00:00
}
void RageDisplay::EnableTextureWrapping()
{
int a, b;
glGetTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &a );
glGetTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &b );
2002-10-18 19:01:32 +00:00
if( a!=GL_REPEAT || b!=GL_REPEAT )
2002-10-18 19:01:32 +00:00
FlushQueue();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
2002-10-18 19:01:32 +00:00
}
void RageDisplay::DisableTextureWrapping()
{
int a, b;
glGetTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, &a );
glGetTexParameteriv( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, &b );
2002-10-18 19:01:32 +00:00
if( a!=GL_CLAMP || b!=GL_CLAMP )
2002-10-18 19:01:32 +00:00
FlushQueue();
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
2002-10-18 19:01:32 +00:00
}