Files
itgmania212121/stepmania/src/RageDisplay.cpp
T

867 lines
25 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"
2002-12-30 00:48:31 +00:00
#include "RageDisplayInternal.h"
2002-05-20 08:59:37 +00:00
#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"
2003-01-24 23:57:02 +00:00
#include "StepMania.h"
2003-01-26 03:51:59 +00:00
/* XXX: remove this once we add oglspecs_t::DisableAALines */
#include "PrefsManager.h"
2002-06-14 22:25:22 +00:00
2003-01-25 03:57:14 +00:00
#include <math.h>
2002-11-12 00:51:27 +00:00
RageDisplay* DISPLAY = NULL;
2002-05-20 08:59:37 +00:00
////////////
// Globals
////////////
SDL_Surface *g_screen = NULL; // this class is a singleton, so there can be only one
int g_flags = 0; /* SDL video flags */
GLenum g_vertMode = GL_TRIANGLES;
RageTimer g_LastCheckTimer;
int g_iNumVerts;
2003-01-11 05:12:17 +00:00
int g_iFPS, g_iVPF, g_iCFPS;
2002-11-15 08:01:34 +00:00
int g_PerspectiveMode = 0;
2002-11-16 21:21:43 +00:00
int g_CurrentHeight, g_CurrentWidth, g_CurrentBPP;
2003-01-08 22:30:32 +00:00
int g_ModelMatrixCnt=0;
2002-11-11 20:43:30 +00:00
int RageDisplay::GetFPS() const { return g_iFPS; }
int RageDisplay::GetVPF() const { return g_iVPF; }
2003-01-11 05:12:17 +00:00
int RageDisplay::GetCumFPS() const { return g_iCFPS; }
2002-11-11 20:43:30 +00:00
static int g_iFramesRenderedSinceLastCheck,
2003-01-11 05:12:17 +00:00
g_iFramesRenderedSinceLastReset,
g_iVertsRenderedSinceLastCheck,
g_iNumChecksSinceLastReset;
2003-01-08 23:16:28 +00:00
2002-12-30 04:04:18 +00:00
PWSWAPINTERVALEXTPROC GLExt::wglSwapIntervalEXT;
2003-01-22 22:41:05 +00:00
PFNGLCOLORTABLEPROC GLExt::glColorTableEXT;
PFNGLCOLORTABLEPARAMETERIVPROC GLExt::glGetColorTableParameterivEXT;
2002-11-12 00:51:27 +00:00
2003-01-11 05:12:17 +00:00
/* We don't actually use normals (we don't tunr on lighting), there's just
* no GL_T2F_C4F_V3F. */
const GLenum RageVertexFormat = GL_T2F_C4F_N3F_V3F;
2003-01-09 04:35:20 +00:00
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]);
}
2002-11-12 09:29:46 +00:00
RageDisplay::RageDisplay( bool windowed, int width, int height, int bpp, int rate, bool vsync )
{
2003-01-14 21:58:01 +00:00
LOG->Trace( "RageDisplay::RageDisplay()" );
2003-01-24 23:57:02 +00:00
/* Give hooks a chance to do some debug init before we try to fire up OpenGL. */
HOOKS->PreDisplayInit();
2002-12-30 00:48:31 +00:00
m_oglspecs = new oglspecs_t;
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();
2003-01-19 21:05:03 +00:00
// Log driver details
2003-01-19 21:11:35 +00:00
LOG->Info("OGL Vendor: %s", glGetString(GL_VENDOR));
LOG->Info("OGL Renderer: %s", glGetString(GL_RENDERER));
LOG->Info("OGL Version: %s", glGetString(GL_VERSION));
LOG->Info("OGL Extensions: %s", glGetString(GL_EXTENSIONS));
if( IsSoftwareRenderer() )
LOG->Warn("This is a software renderer!");
2003-01-19 21:05:03 +00:00
/* Log this, so if people complain that the radar looks bad on their
* system we can compare them: */
2002-12-30 00:48:31 +00:00
glGetFloatv(GL_LINE_WIDTH_RANGE, m_oglspecs->line_range);
glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &m_oglspecs->line_granularity);
2003-01-25 00:19:21 +00:00
LOG->Info("Line width range: %f-%f +%f", m_oglspecs->line_range[0], m_oglspecs->line_range[1], m_oglspecs->line_granularity);
2002-12-30 00:48:31 +00:00
glGetFloatv(GL_POINT_SIZE_RANGE, m_oglspecs->point_range);
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &m_oglspecs->point_granularity);
2003-01-25 00:19:21 +00:00
LOG->Info("Point size range: %f-%f +%f", m_oglspecs->point_range[0], m_oglspecs->point_range[1], m_oglspecs->point_granularity);
}
bool RageDisplay::IsSoftwareRenderer()
{
return
( stricmp((const char*)glGetString(GL_VENDOR),"Microsoft Corporation")==0 ) &&
( stricmp((const char*)glGetString(GL_RENDERER),"GDI Generic")==0 );
}
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 );
2002-12-17 05:05:41 +00:00
/* Use <= for depth testing. This lets us set all components of an actor to the
* same depth. */
glDepthFunc(GL_LEQUAL);
2002-11-18 20:55:23 +00:00
/* Line antialiasing is fast on most hardware, and saying "don't care"
* should turn it off if it isn't. */
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);
glHint(GL_POINT_SMOOTH_HINT, GL_DONT_CARE);
/* Initialize the default ortho projection. */
glMatrixMode( GL_PROJECTION );
glLoadIdentity();
glOrtho(0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, SCREEN_NEAR, SCREEN_FAR );
glMatrixMode( GL_MODELVIEW );
2002-11-12 09:29:46 +00:00
}
2002-11-12 20:39:08 +00:00
2002-05-20 08:59:37 +00:00
RageDisplay::~RageDisplay()
{
SDL_QuitSubSystem(SDL_INIT_VIDEO);
2002-12-30 00:48:31 +00:00
delete m_oglspecs;
}
bool RageDisplay::HasExtension(CString ext) const
{
return m_oglspecs->glExts.find(ext) != m_oglspecs->glExts.end();
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) );
2002-12-30 00:48:31 +00:00
m_oglspecs->glVersion = int(roundf(fGLVersion * 10));
GetGLExtensions(m_oglspecs->glExts);
/* Check for extensions: */
m_oglspecs->EXT_texture_env_combine = HasExtension("GL_EXT_texture_env_combine");
m_oglspecs->WGL_EXT_swap_control = HasExtension("WGL_EXT_swap_control");
2003-01-22 22:41:05 +00:00
m_oglspecs->EXT_paletted_texture = HasExtension("GL_EXT_paletted_texture");
2002-12-30 00:48:31 +00:00
/* Find extension functions. */
wglSwapIntervalEXT = (PWSWAPINTERVALEXTPROC) SDL_GL_GetProcAddress("wglSwapIntervalEXT");
2003-01-22 22:41:05 +00:00
glColorTableEXT = (PFNGLCOLORTABLEPROC) SDL_GL_GetProcAddress("glColorTableEXT");
glGetColorTableParameterivEXT = (PFNGLCOLORTABLEPARAMETERIVPROC) SDL_GL_GetProcAddress("glGetColorTableParameterivEXT");
2002-12-30 00:48:31 +00:00
/* Make sure we have all components for detected extensions. */
if(m_oglspecs->WGL_EXT_swap_control)
{
if(!wglSwapIntervalEXT)
{
LOG->Warn("wglSwapIntervalEXT but wglSwapIntervalEXT() not found");
m_oglspecs->WGL_EXT_swap_control=false;
}
}
2003-01-22 22:41:05 +00:00
if(m_oglspecs->EXT_paletted_texture)
{
if(!glColorTableEXT || !glGetColorTableParameterivEXT)
{
LOG->Warn("GL_EXT_paletted_texture but glColorTableEXT or glGetColorTableParameterivEXT not found");
m_oglspecs->EXT_paletted_texture = false;
}
2003-01-22 22:41:05 +00:00
}
2002-11-12 20:39:08 +00:00
}
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
2003-01-20 21:54:58 +00:00
ASSERT( bpp == 16 || bpp == 32 );
switch( bpp )
2002-05-20 08:59:37 +00:00
{
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 32:
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);
2003-02-14 23:07:34 +00:00
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 02:37:15 +00:00
else
SDL_SM_SetRefreshRate(rate);
2002-11-12 09:29:46 +00:00
#endif
2003-01-20 21:54:58 +00:00
bool NewOpenGLContext = false;
#ifdef SDL_HAS_CHANGEVIDEOMODE
if(g_screen)
{
/* We can change the video mode without nuking the GL context. */
NewOpenGLContext = !!SDL_SM_ChangeVideoMode_OpenGL(&g_screen, width, height, bpp, g_flags);
ASSERT(g_screen);
}
else
2002-11-12 09:29:46 +00:00
#endif
2003-01-20 21:54:58 +00:00
{
g_screen = SDL_SetVideoMode(width, height, bpp, g_flags);
if(!g_screen)
RageException::Throw("SDL_SetVideoMode failed: %s", SDL_GetError());
2002-05-20 08:59:37 +00:00
2003-01-20 21:54:58 +00:00
NewOpenGLContext = true;
}
if(NewOpenGLContext)
{
LOG->Trace("New OpenGL context");
2002-11-16 21:21:43 +00:00
2003-01-20 21:54:58 +00:00
/* We have a new OpenGL context, so we have to tell our textures that
* their OpenGL texture number is invalid. */
2002-11-16 21:21:43 +00:00
if(TEXTUREMAN)
TEXTUREMAN->InvalidateTextures();
2003-01-20 21:54:58 +00:00
SetupOpenGL();
2002-11-11 21:05:02 +00:00
2002-11-12 09:29:46 +00:00
SDL_WM_SetCaption("StepMania", "StepMania");
}
2002-05-20 08:59:37 +00:00
2003-01-24 00:19:56 +00:00
DumpOpenGLDebugInfo();
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?) */
2002-12-30 00:48:31 +00:00
if(m_oglspecs->WGL_EXT_swap_control) {
2002-11-12 20:39:08 +00:00
wglSwapIntervalEXT(vsync);
}
2002-11-12 09:29:46 +00:00
g_CurrentWidth = g_screen->w;
g_CurrentHeight = g_screen->h;
2002-11-16 21:21:43 +00:00
g_CurrentBPP = bpp;
2003-01-20 21:54:58 +00:00
{
/* Find out what we really got. */
int r,g,b,a, colorbits, depth, stencil;
SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &r);
SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &g);
SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &b);
SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &a);
SDL_GL_GetAttribute(SDL_GL_BUFFER_SIZE, &colorbits);
SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
SDL_GL_GetAttribute(SDL_GL_STENCIL_SIZE, &stencil);
LOG->Info("Got %i bpp (%i%i%i%i), %i depth, %i stencil",
colorbits, r, g, b, a, depth, stencil);
}
SetViewport(0,0);
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
2003-01-20 21:54:58 +00:00
return NewOpenGLContext;
2002-05-20 08:59:37 +00:00
}
2003-01-24 00:19:56 +00:00
void RageDisplay::DumpOpenGLDebugInfo()
{
#if defined(WIN32)
/* Dump Windows pixel format data. */
int Actual = GetPixelFormat(wglGetCurrentDC());
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(pfd));
pfd.nSize=sizeof(pfd);
pfd.nVersion=1;
int pfcnt = DescribePixelFormat(GetDC(g_hWndMain),1,sizeof(pfd),&pfd);
for (int i=1; i <= pfcnt; i++)
{
memset(&pfd, 0, sizeof(pfd));
pfd.nSize=sizeof(pfd);
pfd.nVersion=1;
DescribePixelFormat(GetDC(g_hWndMain),i,sizeof(pfd),&pfd);
bool skip = false;
bool rgba = (pfd.iPixelType==PFD_TYPE_RGBA);
bool mcd = ((pfd.dwFlags & PFD_GENERIC_FORMAT) && (pfd.dwFlags & PFD_GENERIC_ACCELERATED));
bool soft = ((pfd.dwFlags & PFD_GENERIC_FORMAT) && !(pfd.dwFlags & PFD_GENERIC_ACCELERATED));
bool icd = !(pfd.dwFlags & PFD_GENERIC_FORMAT) && !(pfd.dwFlags & PFD_GENERIC_ACCELERATED);
bool opengl = !!(pfd.dwFlags & PFD_SUPPORT_OPENGL);
bool window = !!(pfd.dwFlags & PFD_DRAW_TO_WINDOW);
bool dbuff = !!(pfd.dwFlags & PFD_DOUBLEBUFFER);
if(!rgba || soft || !opengl || !window || !dbuff)
skip = true;
/* Skip the above, unless it happens to be the one we chose. */
if(skip && i != Actual)
continue;
CString str = ssprintf("Mode %i: ", i);
if(i == Actual) str += "*** ";
if(skip) str += "(BOGUS) ";
if(soft) str += "software ";
if(icd) str += "ICD ";
if(mcd) str += "MCD ";
if(!rgba) str += "indexed ";
if(!opengl) str += "!OPENGL ";
if(!window) str += "!window ";
if(!dbuff) str += "!dbuff ";
str += ssprintf("%i (%i%i%i) ", pfd.cColorBits, pfd.cRedBits, pfd.cGreenBits, pfd.cBlueBits);
if(pfd.cAlphaBits) str += ssprintf("%i alpha ", pfd.cAlphaBits);
if(pfd.cDepthBits) str += ssprintf("%i depth ", pfd.cDepthBits);
if(pfd.cStencilBits) str += ssprintf("%i stencil ", pfd.cStencilBits);
if(pfd.cAccumBits) str += ssprintf("%i accum ", pfd.cAccumBits);
if(i == Actual && skip)
{
/* We chose a bogus format. */
LOG->Warn("%s", str.GetString());
} else
2003-01-24 01:35:53 +00:00
LOG->Info("%s", str.GetString());
2003-01-24 00:19:56 +00:00
}
#endif
}
2002-12-28 21:37:18 +00:00
void RageDisplay::ResolutionChanged(int width, int height)
{
g_CurrentWidth = width;
g_CurrentHeight = height;
SetViewport(0,0);
/* Clear any junk that's in the framebuffer. */
Clear();
Flip();
}
void RageDisplay::SetViewport(int shift_left, int shift_down)
{
/* left and down are on a 0..SCREEN_WIDTH, 0..SCREEN_HEIGHT scale.
* Scale them to the actual viewport range. */
shift_left = int( shift_left * float(g_CurrentWidth) / SCREEN_WIDTH );
shift_down = int( shift_down * float(g_CurrentWidth) / SCREEN_WIDTH );
glViewport(shift_left, -shift_down, g_CurrentWidth, g_CurrentHeight);
}
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
{
SDL_GL_SwapBuffers();
g_iFramesRenderedSinceLastCheck++;
2003-01-11 05:12:17 +00:00
g_iFramesRenderedSinceLastReset++;
if( g_LastCheckTimer.PeekDeltaTime() >= 1.0f ) // update stats every 1 sec.
{
g_LastCheckTimer.GetDeltaTime();
2003-01-11 05:12:17 +00:00
g_iNumChecksSinceLastReset++;
g_iFPS = g_iFramesRenderedSinceLastCheck;
2003-01-11 05:12:17 +00:00
g_iCFPS = g_iFramesRenderedSinceLastReset / g_iNumChecksSinceLastReset;
2002-11-11 20:43:30 +00:00
g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFPS;
2003-01-08 23:16:28 +00:00
g_iFramesRenderedSinceLastCheck = g_iVertsRenderedSinceLastCheck = 0;
2003-01-11 05:12:17 +00:00
LOG->Trace( "FPS: %d, CFPS %d, VPF: %d", g_iFPS, g_iCFPS, g_iVPF );
}
2002-05-20 08:59:37 +00:00
}
2003-02-10 22:08:10 +00:00
float RageDisplay::PredictedSecondsUntilNextFlip() const
{
float fps = max( 30.f, GetFPS() );
return 1.f/fps;
}
2002-12-21 07:54:24 +00:00
void RageDisplay::ResetStats()
{
2003-01-08 23:16:28 +00:00
g_iFPS = g_iVPF = 0;
2003-01-11 05:12:17 +00:00
g_iFramesRenderedSinceLastCheck = g_iFramesRenderedSinceLastReset = 0;
g_iNumChecksSinceLastReset = 0;
g_iVertsRenderedSinceLastCheck = 0;
2002-12-21 07:54:24 +00:00
g_LastCheckTimer.GetDeltaTime();
}
2003-01-22 22:41:05 +00:00
void RageDisplay::DisablePalettedTexture()
{
m_oglspecs->EXT_paletted_texture = false;
}
void RageDisplay::SaveScreenshot( CString sPath )
{
ASSERT( sPath.Right(3).CompareNoCase("bmp") == 0 ); // we can only save bitmaps
SDL_Surface *image = SDL_CreateRGBSurface(
SDL_SWSURFACE, g_CurrentWidth, g_CurrentHeight,
24, 0x0000FF, 0x00FF00, 0xFF0000, 0x000000);
SDL_Surface *temp = SDL_CreateRGBSurface(
SDL_SWSURFACE, g_CurrentWidth, g_CurrentHeight,
24, 0x0000FF, 0x00FF00, 0xFF0000, 0x000000);
glReadPixels(0, 0, g_CurrentWidth, g_CurrentHeight, GL_RGB,
GL_UNSIGNED_BYTE, image->pixels);
// flip vertically
for( int y=0; y<g_CurrentHeight; y++ )
memcpy(
(char *)temp->pixels + 3 * g_CurrentWidth * y,
(char *)image->pixels + 3 * g_CurrentWidth * (g_CurrentHeight-1-y),
3*g_CurrentWidth );
SDL_SaveBMP( temp, sPath );
2003-02-01 01:11:00 +00:00
SDL_FreeSurface( image );
SDL_FreeSurface( temp );
}
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
}
2003-01-08 23:16:28 +00:00
void RageDisplay::DrawQuads( const RageVertex v[], int iNumVerts )
{
ASSERT( (iNumVerts%4) == 0 );
2003-01-08 23:16:28 +00:00
glInterleavedArrays( RageVertexFormat, sizeof(RageVertex), v );
glDrawArrays( GL_QUADS, 0, iNumVerts );
2003-01-08 23:16:28 +00:00
g_iVertsRenderedSinceLastCheck += iNumVerts;
}
void RageDisplay::DrawFan( const RageVertex v[], int iNumVerts )
{
ASSERT( iNumVerts >= 3 );
2003-01-08 23:16:28 +00:00
glInterleavedArrays( RageVertexFormat, sizeof(RageVertex), v );
glDrawArrays( GL_TRIANGLE_FAN, 0, iNumVerts );
g_iVertsRenderedSinceLastCheck += iNumVerts;
}
2003-01-08 23:16:28 +00:00
void RageDisplay::DrawStrip( const RageVertex v[], int iNumVerts )
{
ASSERT( iNumVerts >= 3 );
2003-01-08 23:16:28 +00:00
glInterleavedArrays( RageVertexFormat, sizeof(RageVertex), v );
glDrawArrays( GL_TRIANGLE_STRIP, 0, iNumVerts );
g_iVertsRenderedSinceLastCheck += iNumVerts;
}
2003-01-25 03:57:14 +00:00
/* Draw a line as a quad. GL_LINES with antialiasing off can draw odd
* ends at odd angles--they're forced to axis-alignment regardless of the
* angle of the line. */
void RageDisplay::DrawPolyLine(const RageVertex &p1, const RageVertex &p2, float LineWidth )
{
/* soh cah toa strikes strikes again! */
float opp = p2.p.x - p1.p.x;
float adj = p2.p.y - p1.p.y;
float hyp = powf(opp*opp + adj*adj, 0.5f);
float lsin = opp/hyp;
float lcos = adj/hyp;
RageVertex p[4];
p[0] = p[1] = p1;
p[2] = p[3] = p2;
float ydist = lsin * LineWidth/2;
float xdist = lcos * LineWidth/2;
p[0].p.x += xdist;
p[0].p.y -= ydist;
p[1].p.x -= xdist;
p[1].p.y += ydist;
p[2].p.x -= xdist;
p[2].p.y += ydist;
p[3].p.x += xdist;
p[3].p.y -= ydist;
DrawQuad(p);
}
/* Draw a line loop with rounded corners using polys. This is used on
* cards that have strange allergic reactions to antialiased points and
* lines. */
void RageDisplay::DrawLoop_Polys( const RageVertex v[], int iNumVerts, float LineWidth )
{
ASSERT( iNumVerts >= 3 );
2003-02-14 07:04:19 +00:00
int i;
for(i = 0; i < iNumVerts; ++i)
2003-01-25 03:57:14 +00:00
DrawPolyLine(v[i], v[(i+1)%iNumVerts], LineWidth);
/* Join the lines with circles so we get rounded corners. */
GLUquadricObj *q = gluNewQuadric();
for(i = 0; i < iNumVerts; ++i)
2003-01-25 03:57:14 +00:00
{
glPushMatrix();
glColor4fv(v[i].c);
glTexCoord3fv(v[i].t);
glTranslatef(v[i].p.x, v[i].p.y, v[i].p.z);
gluDisk(q, 0, LineWidth/2, 32, 32);
glPopMatrix();
}
gluDeleteQuadric(q);
}
/* Draw a nice AA'd line loop. One problem with this is that point and line
* sizes don't always precisely match, which doesn't look quite right.
* It's worth it for the AA, though. */
void RageDisplay::DrawLoop_LinesAndPoints( const RageVertex v[], int iNumVerts, float LineWidth )
{
ASSERT( iNumVerts >= 3 );
glEnable(GL_LINE_SMOOTH);
2002-11-18 20:55:23 +00:00
2002-11-14 09:03:19 +00:00
/* Our line width is wrt the regular internal SCREEN_WIDTHxSCREEN_HEIGHT screen,
* but these width functions actually want raster sizes (that is, actual pixels).
* Scale the line width and point size by the average ratio of the scale. */
float WidthVal = float(g_CurrentWidth) / SCREEN_WIDTH;
float HeightVal = float(g_CurrentHeight) / SCREEN_HEIGHT;
LineWidth *= (WidthVal + HeightVal) / 2;
/* Clamp the width to the hardware max for both lines and points (whichever
* is more restrictive). */
2002-12-30 00:48:31 +00:00
LineWidth = clamp(LineWidth, m_oglspecs->line_range[0], m_oglspecs->line_range[1]);
LineWidth = clamp(LineWidth, m_oglspecs->point_range[0], m_oglspecs->point_range[1]);
/* Hmm. The granularity of lines and points might be different; for example,
* if lines are .5 and points are .25, we might want to snap the width to the
* nearest .5, so the hardware doesn't snap them to different sizes. Does it
* matter? */
glLineWidth(LineWidth);
/* Draw the line loop: */
2003-01-08 23:16:28 +00:00
glInterleavedArrays( RageVertexFormat, sizeof(RageVertex), v );
glDrawArrays( GL_LINE_LOOP, 0, iNumVerts );
2002-11-18 20:55:23 +00:00
glDisable(GL_LINE_SMOOTH);
2002-11-14 09:03:19 +00:00
/* Round off the corners. This isn't perfect; the point is sometimes a little
* larger than the line, causing a small bump on the edge. Not sure how to fix
* that. */
glPointSize(LineWidth);
2002-11-18 20:55:23 +00:00
/* Hack: if the points will all be the same, we don't want to draw
* any points at all, since there's nothing to connect. That'll happen
* if both scale factors in the matrix are ~0. (Actually, I think
* it's true if two of the three scale factors are ~0, but we don't
2003-01-25 03:57:14 +00:00
* use this for anything 3d at the moment anyway ...) This is needed
* because points aren't scaled like regular polys--a zero-size point
* will still be drawn. */
2003-01-08 22:30:32 +00:00
RageMatrix mat;
glGetFloatv( GL_MODELVIEW_MATRIX, (float*)mat );
2002-11-18 20:55:23 +00:00
if(mat.m[0][0] < 1e-5 && mat.m[1][1] < 1e-5)
return;
glEnable(GL_POINT_SMOOTH);
2003-01-08 23:16:28 +00:00
glInterleavedArrays( RageVertexFormat, sizeof(RageVertex), v );
glDrawArrays( GL_POINTS, 0, iNumVerts );
glDisable(GL_POINT_SMOOTH);
}
2003-01-25 03:57:14 +00:00
void RageDisplay::DrawLoop( const RageVertex v[], int iNumVerts, float LineWidth )
{
2003-01-26 03:51:59 +00:00
/* XXX: -1, 0, 1 */
if(PREFSMAN->m_iPolygonRadar == 1)
DrawLoop_Polys(v, iNumVerts, LineWidth);
else
DrawLoop_LinesAndPoints(v, iNumVerts, LineWidth);
2003-01-25 03:57:14 +00:00
}
void RageDisplay::PushMatrix()
{
2003-01-08 22:30:32 +00:00
glPushMatrix();
ASSERT(++g_ModelMatrixCnt<20);
}
void RageDisplay::PopMatrix()
{
2003-01-08 22:30:32 +00:00
glPopMatrix();
ASSERT(g_ModelMatrixCnt-->0);
}
2002-11-15 08:01:34 +00:00
/* Switch from orthogonal to perspective view.
*
* Tricky: we want to maintain all of the zooms, rotations and translations
* that have been applied already. They're in our internal screen space (that
* is, 640x480 ortho). We can't simply leave them where they are, since they'll
* be applied before the perspective transform, which means they'll be in the
* wrong coordinate space.
*
* Handle translations (the right column of the transform matrix) to the viewport.
* Move rotations and scaling (0,0 through 1,1) to after the perspective transform.
* Actually, those might be able to stay where they are, I'm not sure; it's translations
* that are annoying. So, XXX: see if rots and scales can be left on the modelview
* matrix instead of messing with the projection matrix.
*
* When finished, the current position will be the "viewpoint" (at 0,0). negative
* Z goes into the screen, positive X and Y is right and down.
*/
void RageDisplay::EnterPerspective(float fov, bool preserve_loc)
{
g_PerspectiveMode++;
if(g_PerspectiveMode > 1) {
/* havn't yet worked out the details of this */
LOG->Trace("EnterPerspective called when already in perspective mode");
g_PerspectiveMode++;
return;
}
/* Save the old matrices. */
DISPLAY->PushMatrix();
glMatrixMode( GL_PROJECTION );
2002-11-15 08:22:53 +00:00
glPushMatrix();
2002-11-15 08:01:34 +00:00
glLoadIdentity();
float aspect = SCREEN_WIDTH/(float)SCREEN_HEIGHT;
gluPerspective(fov, aspect, 1.000f, 1000.0f);
/* Flip the Y coordinate, so positive numbers go down. */
glScalef(1, -1, 1);
2003-01-09 04:35:20 +00:00
if(!preserve_loc)
{
glMatrixMode( GL_MODELVIEW );
return;
}
RageMatrix matTop;
glGetFloatv( GL_MODELVIEW_MATRIX, (float*)matTop );
{
/* Pull out the 2d translation. */
float x = matTop.m[3][0], y = matTop.m[3][1];
/* These values are where the viewpoint should be. By default, it's in the
* center of the screen, and these values are from the top-left, so subtract
* the difference. */
x -= SCREEN_WIDTH/2;
y -= SCREEN_HEIGHT/2;
DISPLAY->SetViewport(int(x), int(y));
}
/* Pull out the 2d rotations and scales. */
{
RageMatrix mat;
RageMatrixIdentity(&mat);
mat.m[0][0] = matTop.m[0][0];
mat.m[0][1] = matTop.m[0][1];
mat.m[1][0] = matTop.m[1][0];
mat.m[1][1] = matTop.m[1][1];
glMultMatrixf((float *) mat);
2002-11-15 08:01:34 +00:00
}
2003-01-09 04:35:20 +00:00
/* We can't cope with perspective matrices or things that touch Z. (We shouldn't
* have touched those while in 2d, anyway.) */
ASSERT(matTop.m[0][2] == 0.f && matTop.m[0][3] == 0.f && matTop.m[1][2] == 0.f &&
matTop.m[1][3] == 0.f && matTop.m[2][0] == 0.f && matTop.m[2][1] == 0.f &&
matTop.m[2][2] == 1.f && matTop.m[3][2] == 0.f && matTop.m[3][3] == 1.f);
/* Reset the matrix back to identity. */
2002-11-15 08:01:34 +00:00
glMatrixMode( GL_MODELVIEW );
2003-01-09 04:35:20 +00:00
glLoadIdentity();
2002-11-15 08:01:34 +00:00
}
void RageDisplay::ExitPerspective()
{
g_PerspectiveMode--;
if(g_PerspectiveMode) return;
/* Restore the old matrices. */
2002-11-15 08:22:53 +00:00
glMatrixMode( GL_PROJECTION );
glPopMatrix();
glMatrixMode( GL_MODELVIEW );
2002-11-15 08:01:34 +00:00
DISPLAY->PopMatrix();
/* Restore the viewport. */
DISPLAY->SetViewport(0, 0);
}
/* gluLookAt. The result is post-multiplied to the matrix (M = L * M) instead of
* pre-multiplied. */
void RageDisplay::LookAt(const RageVector3 &Eye, const RageVector3 &At, const RageVector3 &Up)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
gluLookAt(Eye.x, Eye.y, Eye.z, At.x, At.y, At.z, Up.x, Up.y, Up.z);
RageMatrix view;
glGetFloatv( GL_MODELVIEW_MATRIX, (float*)view ); /* cheesy :) */
glPopMatrix();
2003-01-08 22:30:32 +00:00
smPostMultMatrixf(view);
2002-11-15 08:01:34 +00:00
}
2002-08-29 21:30:16 +00:00
void RageDisplay::Translate( float x, float y, float z )
{
2003-01-08 22:30:32 +00:00
glTranslatef(x, y, z);
}
2003-01-08 22:30:32 +00:00
2002-08-29 21:30:16 +00:00
void RageDisplay::TranslateLocal( float x, float y, float z )
{
RageMatrix matTemp;
RageMatrixTranslation( &matTemp, x, y, z );
2003-01-08 22:30:32 +00:00
smPostMultMatrixf(matTemp);
}
2002-08-29 21:30:16 +00:00
void RageDisplay::Scale( float x, float y, float z )
{
2003-01-08 22:30:32 +00:00
glScalef(x, y, z);
}
2002-08-29 21:30:16 +00:00
void RageDisplay::RotateX( float r )
{
2003-01-08 22:30:32 +00:00
glRotatef(r* 180/PI, 1, 0, 0);
}
2002-08-29 21:30:16 +00:00
void RageDisplay::RotateY( float r )
{
2003-01-08 22:30:32 +00:00
glRotatef(r* 180/PI, 0, 1, 0);
}
2002-08-29 21:30:16 +00:00
void RageDisplay::RotateZ( float r )
{
2003-01-08 22:30:32 +00:00
glRotatef(r* 180/PI, 0, 0, 1);
}
2003-01-08 22:30:32 +00:00
void RageDisplay::smPostMultMatrixf( const RageMatrix &f )
{
RageMatrix m;
glGetFloatv( GL_MODELVIEW_MATRIX, (float*)m );
RageMatrixMultiply( &m, &f, &m );
glLoadMatrixf((const float*) m);
}
void RageDisplay::SetTexture( RageTexture* pTexture )
{
2003-01-08 23:16:28 +00:00
glBindTexture( GL_TEXTURE_2D, pTexture? pTexture->GetGLTextureID() : 0 );
}
void RageDisplay::SetTextureModeModulate()
{
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)
{
2003-02-14 23:13:22 +00:00
glBlendFunc( GLenum(src), GLenum(dst) );
}
2002-11-12 00:51:27 +00:00
void RageDisplay::SetTextureModeGlow()
{
2002-12-30 00:48:31 +00:00
if(!m_oglspecs->EXT_texture_env_combine) {
2002-11-12 00:51:27 +00:00
SetBlendMode( GL_SRC_ALPHA, GL_ONE );
return;
}
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);
2003-02-14 23:13:22 +00:00
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_COMBINE_RGB_EXT), GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_SOURCE0_RGB_EXT), GL_PRIMARY_COLOR_EXT);
2002-11-12 00:51:27 +00:00
/* Source alpha is texture alpha * diffuse alpha: */
2003-02-14 23:13:22 +00:00
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_COMBINE_ALPHA_EXT), GL_MODULATE);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_OPERAND0_ALPHA_EXT), GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_SOURCE0_ALPHA_EXT), GL_PRIMARY_COLOR_EXT);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_OPERAND1_ALPHA_EXT), GL_SRC_ALPHA);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_SOURCE1_ALPHA_EXT), GL_TEXTURE);
2002-11-12 00:51:27 +00:00
}
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()
{
glEnable( GL_DEPTH_TEST );
}
void RageDisplay::DisableZBuffer()
{
glDisable( GL_DEPTH_TEST );
2002-10-18 19:01:32 +00:00
}
void RageDisplay::EnableTextureWrapping()
{
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()
{
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
}