Files
itgmania212121/stepmania/src/RageDisplay_OGL.cpp
T

1931 lines
52 KiB
C++
Raw Normal View History

2003-05-22 05:28:37 +00:00
#include "global.h"
/*
-----------------------------------------------------------------------------
Class: RageDisplay
Desc: See header.
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
Chris Danford
Glenn Maynard
-----------------------------------------------------------------------------
*/
/*
* This header pulls in GL headers and defines things that require them.
* This only needs to be included if you actually use these; most of the
* time, RageDisplay.h is sufficient.
*/
#include "SDL_utils.h"
/* ours is more up-to-date */
#define NO_SDL_GLEXT
#define __glext_h_ /* try harder to stop glext.h from being forced on us by someone else */
#include "SDL_opengl.h"
#undef __glext_h_
#include "glext.h"
2003-12-21 02:06:40 +00:00
#include "RageFile.h"
#include "PrefsManager.h" // XXX
2003-12-21 02:06:40 +00:00
2003-05-22 05:28:37 +00:00
/* Windows's broken gl.h defines GL_EXT_paletted_texture incompletely: */
#ifndef GL_TEXTURE_INDEX_SIZE_EXT
#define GL_TEXTURE_INDEX_SIZE_EXT 0x80ED
#endif
#include <set>
2003-08-03 05:40:40 +00:00
#include <sstream>
2003-05-22 05:28:37 +00:00
/* Not in glext.h: */
typedef bool (APIENTRY * PWSWAPINTERVALEXTPROC) (int interval);
/* Extension functions we use. Put these in a namespace instead of in oglspecs_t,
* so they can be called like regular functions. */
namespace GLExt {
extern PWSWAPINTERVALEXTPROC wglSwapIntervalEXT;
extern PFNGLCOLORTABLEPROC glColorTableEXT;
extern PFNGLCOLORTABLEPARAMETERIVPROC glGetColorTableParameterivEXT;
extern PFNGLACTIVETEXTUREARBPROC glActiveTextureARB;
extern PFNGLGENBUFFERSARBPROC glGenBuffersARB;
extern PFNGLBINDBUFFERARBPROC glBindBufferARB;
extern PFNGLBUFFERDATAARBPROC glBufferDataARB;
extern PFNGLBUFFERSUBDATAARBPROC glBufferSubDataARB;
extern PFNGLDELETEBUFFERSARBPROC glDeleteBuffersARB;
extern PFNGLDRAWRANGEELEMENTSPROC glDrawRangeElements;
2003-05-22 05:28:37 +00:00
};
2003-09-22 11:14:31 +00:00
#if defined(DARWIN)
#include "archutils/Darwin/Vsync.h"
#endif
2003-05-22 05:28:37 +00:00
#include "RageDisplay.h"
#include "RageDisplay_OGL.h"
2003-05-22 05:28:37 +00:00
#include "RageUtil.h"
#include "RageLog.h"
#include "RageTimer.h"
#include "RageException.h"
#include "RageTexture.h"
#include "RageTextureManager.h"
#include "RageMath.h"
#include "RageTypes.h"
#include "GameConstantsAndTypes.h"
#include "StepMania.h"
#include "RageUtil.h"
#include "SDL_endian.h"
2003-05-22 05:28:37 +00:00
#include "arch/arch.h"
#include "arch/LowLevelWindow/LowLevelWindow.h"
2003-06-30 06:23:42 +00:00
#ifdef WIN32
#pragma comment(lib, "opengl32.lib")
#pragma comment(lib, "glu32.lib")
2003-06-30 06:23:42 +00:00
#endif
2003-05-22 05:28:37 +00:00
//
// Globals
//
PWSWAPINTERVALEXTPROC GLExt::wglSwapIntervalEXT = NULL;
PFNGLCOLORTABLEPROC GLExt::glColorTableEXT = NULL;
PFNGLCOLORTABLEPARAMETERIVPROC GLExt::glGetColorTableParameterivEXT = NULL;
PFNGLACTIVETEXTUREARBPROC GLExt::glActiveTextureARB = NULL;
PFNGLGENBUFFERSARBPROC GLExt::glGenBuffersARB = NULL;
PFNGLBINDBUFFERARBPROC GLExt::glBindBufferARB = NULL;
PFNGLBUFFERDATAARBPROC GLExt::glBufferDataARB = NULL;
PFNGLBUFFERSUBDATAARBPROC GLExt::glBufferSubDataARB = NULL;
PFNGLDELETEBUFFERSARBPROC GLExt::glDeleteBuffersARB = NULL;
PFNGLDRAWRANGEELEMENTSPROC GLExt::glDrawRangeElements = NULL;
2003-10-09 06:33:11 +00:00
static bool g_bEXT_texture_env_combine = true;
static bool g_bGL_EXT_bgra = true;
2003-10-09 06:33:11 +00:00
static bool g_bReversePackedPixelsWorks = true;
static bool g_b4BitPalettesWork = true;
2003-05-22 05:28:37 +00:00
/* OpenGL system information that generally doesn't change at runtime. */
/* Range and granularity of points and lines: */
float g_line_range[2];
float g_line_granularity;
float g_point_range[2];
float g_point_granularity;
/* OpenGL version * 10: */
int g_glVersion;
/* GLU version * 10: */
int g_gluVersion;
2003-05-22 05:28:37 +00:00
/* Available extensions: */
set<string> g_glExts;
/* We don't actually use normals (we don't tunr on lighting), there's just
* no GL_T2F_C4F_V3F. */
const GLenum RageSpriteVertexFormat = GL_T2F_C4F_N3F_V3F;
2003-05-22 05:28:37 +00:00
LowLevelWindow *wind;
static void InvalidateAllGeometry();
2003-05-30 20:16:30 +00:00
2003-10-03 21:38:38 +00:00
static RageDisplay::PixelFormatDesc PIXEL_FORMAT_DESC[RageDisplay::NUM_PIX_FORMATS] = {
2003-05-30 20:16:30 +00:00
{
/* R8G8B8A8 */
2003-05-30 20:16:30 +00:00
32,
2003-06-28 01:45:44 +00:00
{ 0xFF000000,
2003-05-30 20:16:30 +00:00
0x00FF0000,
2003-06-28 01:45:44 +00:00
0x0000FF00,
0x000000FF }
2003-05-30 20:16:30 +00:00
}, {
2003-06-28 01:45:44 +00:00
/* R4G4B4A4 */
2003-05-30 20:16:30 +00:00
16,
{ 0xF000,
0x0F00,
0x00F0,
0x000F },
}, {
2003-06-28 01:45:44 +00:00
/* R5G5B5A1 */
2003-05-30 20:16:30 +00:00
16,
{ 0xF800,
0x07C0,
0x003E,
0x0001 },
}, {
2003-06-28 01:45:44 +00:00
/* R5G5B5 */
2003-05-30 20:16:30 +00:00
16,
{ 0xF800,
0x07C0,
0x003E,
0x0000 },
}, {
2003-06-28 01:45:44 +00:00
/* R8G8B8 */
2003-05-30 20:16:30 +00:00
24,
2003-06-28 01:45:44 +00:00
{ 0xFF0000,
2003-05-30 20:16:30 +00:00
0x00FF00,
2003-06-28 01:45:44 +00:00
0x0000FF,
2003-05-30 20:16:30 +00:00
0x000000 }
2003-06-30 03:13:53 +00:00
}, {
/* Paletted */
8,
{ 0,0,0,0 } /* N/A */
}, {
/* B8G8R8A8 */
24,
{ 0x0000FF,
0x00FF00,
0xFF0000,
0x000000 }
2003-05-30 20:16:30 +00:00
}, {
2003-06-30 03:13:53 +00:00
/* A1B5G5R5 */
16,
{ 0x7C00,
0x03E0,
0x001F,
0x8000 },
2003-05-30 20:16:30 +00:00
}
};
2003-07-03 02:42:44 +00:00
static map<GLenum, CString> g_Strings;
static void InitStringMap()
{
static bool Initialized = false;
if(Initialized) return;
Initialized = true;
#define X(a) g_Strings[a] = #a;
X(GL_RGBA8); X(GL_RGBA4); X(GL_RGB5_A1); X(GL_RGB5); X(GL_RGBA); X(GL_RGB);
X(GL_BGR); X(GL_BGRA);
2003-10-09 06:33:11 +00:00
X(GL_COLOR_INDEX8_EXT); X(GL_COLOR_INDEX4_EXT); X(GL_COLOR_INDEX);
2003-07-03 02:42:44 +00:00
X(GL_UNSIGNED_BYTE); X(GL_UNSIGNED_SHORT_4_4_4_4); X(GL_UNSIGNED_SHORT_5_5_5_1);
X(GL_UNSIGNED_SHORT_1_5_5_5_REV);
2003-07-07 22:56:02 +00:00
X(GL_INVALID_ENUM); X(GL_INVALID_VALUE); X(GL_INVALID_OPERATION);
X(GL_STACK_OVERFLOW); X(GL_STACK_UNDERFLOW); X(GL_OUT_OF_MEMORY);
2003-07-03 02:42:44 +00:00
}
static CString GLToString( GLenum e )
{
if( g_Strings.find(e) != g_Strings.end() )
return g_Strings[e];
2003-12-24 20:32:20 +00:00
return ssprintf( "%i", int(e) );
2003-07-03 02:42:44 +00:00
}
/* GL_PIXFMT_INFO is used for both texture formats and surface formats. For example,
* it's fine to ask for a FMT_RGB5 texture, but to supply a surface matching
* FMT_RGB8. OpenGL will simply discard the extra bits.
*
* It's possible for a format to be supported as a texture format but not as a
* surface format. For example, if packed pixels aren't supported, we can still
* use GL_RGB5_A1, but we'll have to convert to a supported surface pixel format
* first. It's not ideal, since we'll convert to RGBA8 and OGL will convert back,
* but it works fine.
*/
2003-05-30 20:16:30 +00:00
struct GLPixFmtInfo_t {
GLenum internalfmt; /* target format */
GLenum format; /* target format */
GLenum type; /* data format */
2003-10-03 21:38:38 +00:00
} GL_PIXFMT_INFO[RageDisplay::NUM_PIX_FORMATS] = {
2003-05-30 20:16:30 +00:00
{
/* R8G8B8A8 */
2003-05-30 20:16:30 +00:00
GL_RGBA8,
GL_RGBA,
GL_UNSIGNED_BYTE,
}, {
/* B4G4R4A4 */
GL_RGBA4,
GL_RGBA,
GL_UNSIGNED_SHORT_4_4_4_4,
}, {
/* B5G5R5A1 */
GL_RGB5_A1,
GL_RGBA,
GL_UNSIGNED_SHORT_5_5_5_1,
}, {
/* B5G5R5 */
GL_RGB5,
GL_RGBA,
GL_UNSIGNED_SHORT_5_5_5_1,
}, {
/* B8G8R8 */
GL_RGB8,
GL_RGB,
GL_UNSIGNED_BYTE,
2003-06-30 03:13:53 +00:00
}, {
/* Paletted */
GL_COLOR_INDEX8_EXT,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
}, {
/* B8G8R8 */
GL_RGB8,
GL_BGR,
GL_UNSIGNED_BYTE,
2003-05-30 20:16:30 +00:00
}, {
2003-06-30 03:13:53 +00:00
/* A1R5G5B5 (matches D3DFMT_A1R5G5B5) */
GL_RGB5_A1,
GL_BGRA,
GL_UNSIGNED_SHORT_1_5_5_5_REV,
2003-05-30 20:16:30 +00:00
}
};
2003-06-28 01:45:44 +00:00
static void FixLilEndian()
{
2003-06-28 01:45:44 +00:00
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
static bool Initialized = false;
if( Initialized )
return;
Initialized = true;
2003-10-03 21:38:38 +00:00
for( int i = 0; i < RageDisplay::NUM_PIX_FORMATS; ++i )
{
2003-10-03 21:38:38 +00:00
RageDisplay::PixelFormatDesc &pf = PIXEL_FORMAT_DESC[i];
2003-06-28 01:45:44 +00:00
/* OpenGL and SDL handle byte formats differently; we need
* to flip non-paletted masks to make them line up. */
if( GL_PIXFMT_INFO[i].type != GL_UNSIGNED_BYTE || pf.bpp == 8 )
continue;
for( int mask = 0; mask < 4; ++mask)
2003-06-11 01:22:14 +00:00
{
int m = pf.masks[mask];
switch( pf.bpp )
{
2003-06-28 01:45:44 +00:00
case 24: m = mySDL_Swap24(m); break;
2003-06-11 01:22:14 +00:00
case 32: m = SDL_Swap32(m); break;
2003-06-28 01:45:44 +00:00
default: ASSERT(0);
2003-06-11 01:22:14 +00:00
}
pf.masks[mask] = m;
}
}
#endif
}
2003-05-30 20:16:30 +00:00
2003-05-22 05:28:37 +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]);
}
2003-06-12 19:40:44 +00:00
static void FlushGLErrors()
{
/* Making an OpenGL call doesn't also flush the error state; if we happen
* to have an error from a previous call, then the assert below will fail.
* Flush it. */
while( glGetError() != GL_NO_ERROR )
;
}
#define AssertNoGLError() \
{ \
GLenum error = glGetError(); \
ASSERT_M( error == GL_NO_ERROR, GLToString(error) ) \
}
2004-04-16 05:31:08 +00:00
static void TurnOffHardwareVBO()
{
if( GLExt::glBindBufferARB )
{
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, 0 );
GLExt::glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, 0 );
}
}
2003-11-13 18:02:00 +00:00
#if defined(__unix__) && !defined(unix)
#define unix
#endif
#if defined(unix)
#define Font X11___Font
#define Screen X11___Screen
#include "GL/glx.h"
#undef Font
#undef Screen
#endif
#if defined(unix)
static Display *g_X11Display = NULL;
#endif
2003-11-13 18:02:00 +00:00
static void LogGLXDebugInformation()
{
#if defined(unix)
ASSERT( g_X11Display );
2003-11-13 18:02:00 +00:00
const int scr = DefaultScreen( g_X11Display );
2003-11-13 18:02:00 +00:00
LOG->Info( "Display: %s", DisplayString(g_X11Display) );
2003-11-13 18:02:00 +00:00
LOG->Info( "Screen: %i", scr );
LOG->Info( "Direct rendering: %s", glXIsDirect( g_X11Display, glXGetCurrentContext() )? "yes":"no" );
2004-03-06 02:47:31 +00:00
int XServerVersion = XVendorRelease( g_X11Display ); /* eg. 40201001 */
int major = XServerVersion / 10000000; XServerVersion %= 10000000;
int minor = XServerVersion / 100000; XServerVersion %= 100000;
int revision = XServerVersion / 1000; XServerVersion %= 1000;
int patch = XServerVersion;
LOG->Info( "X server vendor: %s, %i.%i.%i.%i", XServerVendor( g_X11Display ), major, minor, revision, patch );
LOG->Info( "Server GLX vendor: %s", glXQueryServerString( g_X11Display, scr, GLX_VENDOR ) );
LOG->Info( "Server GLX version: %s", glXQueryServerString( g_X11Display, scr, GLX_VERSION ) );
LOG->Info( "Client GLX vendor: %s", glXGetClientString( g_X11Display, GLX_VENDOR ) );
LOG->Info( "Client GLX version: %s", glXGetClientString( g_X11Display, GLX_VERSION ) );
2003-11-13 18:02:00 +00:00
#endif
}
RageDisplay_OGL::RageDisplay_OGL( VideoModeParams p, bool bAllowUnacceleratedRenderer )
2003-05-22 05:28:37 +00:00
{
2003-05-27 00:48:40 +00:00
LOG->Trace( "RageDisplay_OGL::RageDisplay_OGL()" );
LOG->MapLog("renderer", "Current renderer: OpenGL");
2003-05-22 05:28:37 +00:00
2003-06-28 01:45:44 +00:00
FixLilEndian();
2003-07-03 02:42:44 +00:00
InitStringMap();
2003-05-22 05:28:37 +00:00
wind = MakeLowLevelWindow();
try {
SetVideoMode( p );
} catch(...) {
/* SetVideoMode can throw. */
delete wind;
throw;
}
2003-05-22 05:28:37 +00:00
// Log driver details
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));
LOG->Info("OGL Max texture size: %i", GetMaxTextureSize() );
LOG->Info("GLU Version: %s", gluGetString(GLU_VERSION));
2003-11-13 18:02:00 +00:00
LogGLXDebugInformation();
2003-05-22 05:28:37 +00:00
if( IsSoftwareRenderer() )
{
if( !bAllowUnacceleratedRenderer )
{
delete wind;
RageException::ThrowNonfatal(
"Your system is reporting that OpenGL hardware acceleration is not available. "
"Please obtain an updated driver from your video card manufacturer.\n\n" );
}
2003-05-22 05:28:37 +00:00
LOG->Warn("This is a software renderer!");
}
2003-05-22 05:28:37 +00:00
#if defined(_WINDOWS)
/* GLDirect is a Direct3D wrapper for OpenGL. It's rather buggy; and if in
* any case GLDirect can successfully render us, we should be able to do so
* too using Direct3D directly. (If we can't, it's a bug that we can work
* around--if GLDirect can do it, so can we!) */
if( !strncmp( (const char *) glGetString(GL_RENDERER), "GLDirect", 8 ) )
{
delete wind;
RageException::ThrowNonfatal( "GLDirect was detected. GLDirect is not compatible with StepMania, and should be disabled.\n" );
}
#endif
#if defined(unix)
if( !glXIsDirect( g_X11Display, glXGetCurrentContext() ) )
{
if( !bAllowUnacceleratedRenderer )
{
delete wind;
RageException::ThrowNonfatal(
"Your system is reporting that direct rendering is not available. "
"Please obtain an updated driver from your video card manufacturer." );
}
LOG->Warn("Direct rendering is not enabled!");
}
#endif
2003-05-22 05:28:37 +00:00
/* Log this, so if people complain that the radar looks bad on their
* system we can compare them: */
glGetFloatv(GL_LINE_WIDTH_RANGE, g_line_range);
glGetFloatv(GL_LINE_WIDTH_GRANULARITY, &g_line_granularity);
LOG->Info("Line width range: %.3f-%.3f +%.3f", g_line_range[0], g_line_range[1], g_line_granularity);
glGetFloatv(GL_POINT_SIZE_RANGE, g_point_range);
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &g_point_granularity);
LOG->Info("Point size range: %.3f-%.3f +%.3f", g_point_range[0], g_point_range[1], g_point_granularity);
}
#if defined(unix) && defined(HAVE_LIBXTST)
#include <X11/extensions/XTest.h>
#endif
void RageDisplay_OGL::Update(float fDeltaTime)
2003-05-22 05:28:37 +00:00
{
wind->Update(fDeltaTime);
2004-01-03 07:55:20 +00:00
2004-02-17 07:17:54 +00:00
if( PREFSMAN->m_bDisableScreenSaver )
{
/* Disable the screensaver. */
#if defined(unix) && defined(HAVE_LIBXTST)
2004-02-17 07:17:54 +00:00
ASSERT( g_X11Display );
/* This causes flicker. */
// XForceScreenSaver( g_X11Display, ScreenSaverReset );
/*
* Instead, send a null relative mouse motion, to trick X into thinking there has been
* user activity.
*
* This also handles XScreenSaver; XForceScreenSaver only handles the internal X11
* screen blanker.
*
* This will delay the X blanker, DPMS and XScreenSaver from activating, and will
* disable the blanker and XScreenSaver if they're already active (unless XSS is
* locked). For some reason, it doesn't un-blank DPMS if it's already active.
*/
XLockDisplay( g_X11Display );
int event_base, error_base, major, minor;
if( XTestQueryExtension( g_X11Display, &event_base, &error_base, &major, &minor ) )
{
XTestFakeRelativeMotionEvent( g_X11Display, 0, 0, 0 );
XSync( g_X11Display, False );
}
XUnlockDisplay( g_X11Display );
2004-01-03 07:55:20 +00:00
#endif
}
2003-05-22 05:28:37 +00:00
}
bool RageDisplay_OGL::IsSoftwareRenderer()
2003-05-22 05:28:37 +00:00
{
#if defined(WIN32)
2003-05-22 05:28:37 +00:00
return
( strcmp((const char*)glGetString(GL_VENDOR),"Microsoft Corporation")==0 ) &&
( strcmp((const char*)glGetString(GL_RENDERER),"GDI Generic")==0 );
#else
return false;
#endif
2003-05-22 05:28:37 +00:00
}
RageDisplay_OGL::~RageDisplay_OGL()
2003-05-22 05:28:37 +00:00
{
delete wind;
}
bool HasExtension(CString ext)
{
return g_glExts.find(ext) != g_glExts.end();
}
2003-10-09 06:33:11 +00:00
static void CheckPalettedTextures( bool LowColor )
2003-05-30 20:16:30 +00:00
{
2004-03-13 06:42:07 +00:00
CString error;
2004-03-13 07:38:46 +00:00
do
2004-03-13 06:42:07 +00:00
{
2004-03-13 07:38:46 +00:00
if( GLExt::glColorTableEXT == NULL )
{
error = "glColorTableEXT missing";
break;
}
2004-03-13 06:42:07 +00:00
2004-03-13 07:38:46 +00:00
if( GLExt::glGetColorTableParameterivEXT == NULL )
{
error = "glGetColorTableParameterivEXT missing";
break;
}
2003-05-30 20:16:30 +00:00
2004-03-13 07:38:46 +00:00
/* Check to see if paletted textures really work. */
GLenum glTexFormat = GL_PIXFMT_INFO[RageDisplay::FMT_PAL].internalfmt;
GLenum glImageFormat = GL_PIXFMT_INFO[RageDisplay::FMT_PAL].format;
GLenum glImageType = GL_PIXFMT_INFO[RageDisplay::FMT_PAL].type;
2003-05-30 20:16:30 +00:00
2004-03-13 07:38:46 +00:00
int bits = 8;
2003-10-09 06:33:11 +00:00
2004-03-13 07:38:46 +00:00
if( LowColor )
{
glTexFormat = GL_COLOR_INDEX4_EXT;
bits = 4;
}
2003-10-09 06:33:11 +00:00
2004-03-13 07:38:46 +00:00
FlushGLErrors();
2003-11-13 18:04:31 +00:00
#define GL_CHECK_ERROR(f) \
2004-03-13 07:38:46 +00:00
{ \
GLenum glError = glGetError(); \
if( glError != GL_NO_ERROR ) { \
error = ssprintf(f " failed (%s)", GLToString(glError).c_str() ); \
break; \
} \
}
2003-11-13 18:04:31 +00:00
2004-03-13 07:38:46 +00:00
glTexImage2D(GL_PROXY_TEXTURE_2D,
2003-05-30 20:16:30 +00:00
0, glTexFormat,
16, 16, 0,
glImageFormat, glImageType, NULL);
2004-03-13 07:38:46 +00:00
GL_CHECK_ERROR( "glTexImage2D" );
2003-05-30 20:16:30 +00:00
2003-10-09 06:33:11 +00:00
GLuint ifmt = 0;
glGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INTERNAL_FORMAT), (GLint *)&ifmt );
2004-04-03 21:33:10 +00:00
GL_CHECK_ERROR( "glGetTexLevelParameteriv(GL_TEXTURE_INTERNAL_FORMAT)" );
2003-10-09 06:33:11 +00:00
if( ifmt != glTexFormat )
{
error = ssprintf( "Expected format %s, got %s instead",
2004-03-13 07:38:46 +00:00
GLToString(glTexFormat).c_str(),
GLToString(ifmt).c_str() );
break;
2003-10-09 06:33:11 +00:00
}
2004-03-13 07:38:46 +00:00
GLubyte palette[256*4];
memset(palette, 0, sizeof(palette));
GLExt::glColorTableEXT(GL_PROXY_TEXTURE_2D, GL_RGBA8, 1 << bits, GL_RGBA, GL_UNSIGNED_BYTE, palette);
GL_CHECK_ERROR( "glColorTableEXT" );
2003-05-30 20:16:30 +00:00
GLint size = 0;
glGetTexLevelParameteriv(GL_PROXY_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INDEX_SIZE_EXT), &size);
2004-03-13 06:42:07 +00:00
GL_CHECK_ERROR( "glGetTexLevelParameteriv(GL_TEXTURE_INDEX_SIZE_EXT)" );
2003-10-09 06:33:11 +00:00
if( bits > size || size > 8 )
{
2003-12-24 20:32:20 +00:00
error = ssprintf("Expected %i-bit palette, got a %i-bit one instead", bits, int(size));
2004-03-13 07:38:46 +00:00
break;
}
2003-11-13 18:04:31 +00:00
GLint RealWidth = 0;
2003-11-14 00:07:47 +00:00
GLExt::glGetColorTableParameterivEXT(GL_PROXY_TEXTURE_2D, GL_COLOR_TABLE_WIDTH, &RealWidth);
GL_CHECK_ERROR( "glGetColorTableParameterivEXT(GL_COLOR_TABLE_WIDTH)" );
2003-11-13 18:04:31 +00:00
if( RealWidth != 1 << bits )
{
2003-12-24 20:32:20 +00:00
error = ssprintf("GL_COLOR_TABLE_WIDTH returned %i instead of %i", int(RealWidth), 1 << bits );
2004-03-13 07:38:46 +00:00
break;
2003-11-13 18:04:31 +00:00
}
2004-03-13 07:38:46 +00:00
2003-11-13 18:04:31 +00:00
GLint RealFormat = 0;
GLExt::glGetColorTableParameterivEXT(GL_PROXY_TEXTURE_2D, GL_COLOR_TABLE_FORMAT, &RealFormat);
GL_CHECK_ERROR( "glGetColorTableParameterivEXT(GL_COLOR_TABLE_FORMAT)" );
if( RealFormat != GL_RGBA8 )
{
error = ssprintf("GL_COLOR_TABLE_FORMAT returned %s instead of GL_RGBA8", GLToString(RealFormat).c_str() );
2004-03-13 07:38:46 +00:00
break;
2003-11-13 18:04:31 +00:00
}
2004-03-13 07:38:46 +00:00
} while(0);
#undef GL_CHECK_ERROR
2003-05-30 20:16:30 +00:00
2004-03-13 07:38:46 +00:00
if( error == "" )
return;
2003-05-30 20:16:30 +00:00
2003-10-09 06:33:11 +00:00
if( LowColor )
{
/* Disable 4-bit palettes, but allow 8-bit ones. */
g_b4BitPalettesWork = false;
LOG->Info("4-bit paletted textures disabled: %s.", error.c_str());
} else {
/* If 8-bit palettes don't work, disable them entirely--don't trust 4-bit
* palettes if it can't even get 8-bit ones right. */
GLExt::glColorTableEXT = NULL;
GLExt::glGetColorTableParameterivEXT = NULL;
LOG->Info("Paletted textures disabled: %s.", error.c_str());
}
2003-05-30 20:16:30 +00:00
}
static void CheckReversePackedPixels()
{
/* Try to create a texture. */
FlushGLErrors();
glTexImage2D(GL_PROXY_TEXTURE_2D,
0, GL_RGBA,
16, 16, 0,
2003-07-09 04:18:01 +00:00
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, NULL);
const GLenum glError = glGetError();
if( glError == GL_NO_ERROR )
g_bReversePackedPixelsWorks = true;
else
{
g_bReversePackedPixelsWorks = false;
LOG->Info("GL_UNSIGNED_SHORT_1_5_5_5_REV failed (%s), disabled",
GLToString(glError).c_str() );
}
}
2003-05-22 05:28:37 +00:00
void SetupExtensions()
{
2003-06-30 06:23:42 +00:00
const float fGLVersion = (float) atof( (const char *) glGetString(GL_VERSION) );
2003-05-22 05:28:37 +00:00
g_glVersion = int(roundf(fGLVersion * 10));
GetGLExtensions(g_glExts);
const float fGLUVersion = (float) atof( (const char *) gluGetString(GLU_VERSION) );
g_gluVersion = int(roundf(fGLUVersion * 10));
2003-05-22 05:28:37 +00:00
/* Find extension functions and reset broken flags */
2003-09-22 11:14:31 +00:00
#if !defined(DARWIN)
2003-05-22 05:28:37 +00:00
GLExt::wglSwapIntervalEXT = (PWSWAPINTERVALEXTPROC) wind->GetProcAddress("wglSwapIntervalEXT");
2003-09-22 11:14:31 +00:00
#else
GLExt::wglSwapIntervalEXT = wglSwapIntervalEXT;
#endif
2003-05-22 05:28:37 +00:00
GLExt::glColorTableEXT = (PFNGLCOLORTABLEPROC) wind->GetProcAddress("glColorTableEXT");
GLExt::glGetColorTableParameterivEXT = (PFNGLCOLORTABLEPARAMETERIVPROC) wind->GetProcAddress("glGetColorTableParameterivEXT");
GLExt::glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wind->GetProcAddress("glActiveTextureARB");
GLExt::glGenBuffersARB = (PFNGLGENBUFFERSARBPROC) wind->GetProcAddress("glGenBuffersARB");
GLExt::glBindBufferARB = (PFNGLBINDBUFFERARBPROC) wind->GetProcAddress("glBindBufferARB");
GLExt::glBufferDataARB = (PFNGLBUFFERDATAARBPROC) wind->GetProcAddress("glBufferDataARB");
GLExt::glBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) wind->GetProcAddress("glBufferSubDataARB");
GLExt::glDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) wind->GetProcAddress("glDeleteBuffersARB");
GLExt::glDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) wind->GetProcAddress("glDrawRangeElements");
2003-05-22 05:28:37 +00:00
g_bEXT_texture_env_combine = HasExtension("GL_EXT_texture_env_combine");
g_bGL_EXT_bgra = HasExtension("GL_EXT_bgra");
2003-10-09 06:33:11 +00:00
CheckPalettedTextures( false );
2004-04-03 21:33:10 +00:00
if( g_b4BitPalettesWork ) // don't bother if the last one failed
CheckPalettedTextures( true );
CheckReversePackedPixels();
2003-05-22 05:28:37 +00:00
// Checks for known bad drivers
CString sRenderer = (const char*)glGetString(GL_RENDERER);
}
void 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 )
continue;
// if(i == Actual) str += "*** ";
2003-05-22 05:28:37 +00:00
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.c_str());
} else
LOG->Info("%s", str.c_str());
}
#endif
}
void RageDisplay_OGL::ResolutionChanged()
2003-05-22 05:28:37 +00:00
{
2003-06-05 19:29:27 +00:00
SetViewport(0,0);
2003-05-22 05:28:37 +00:00
/* Clear any junk that's in the framebuffer. */
BeginFrame();
EndFrame();
2003-05-22 05:28:37 +00:00
}
2003-06-05 19:29:27 +00:00
// Return true if mode change was successful.
// bNewDeviceOut is set true if a new device was created and textures
// need to be reloaded.
CString RageDisplay_OGL::TryVideoMode( VideoModeParams p, bool &bNewDeviceOut )
2003-05-22 05:28:37 +00:00
{
// LOG->Trace( "RageDisplay_OGL::SetVideoMode( %d, %d, %d, %d, %d, %d )", windowed, width, height, bpp, rate, vsync );
CString err;
err = wind->TryVideoMode( p, bNewDeviceOut );
if( err != "" )
return err; // failed to set video mode
2003-05-22 05:28:37 +00:00
2003-06-05 19:29:27 +00:00
if( bNewDeviceOut )
2003-05-22 05:28:37 +00:00
{
/* We have a new OpenGL context, so we have to tell our textures that
* their OpenGL texture number is invalid. */
if(TEXTUREMAN)
TEXTUREMAN->InvalidateTextures();
/* Recreate all vertex buffers. */
InvalidateAllGeometry();
2003-05-22 05:28:37 +00:00
}
#if defined(unix)
{
SDL_SysWMinfo info;
SDL_VERSION(&info.version);
g_X11Display = NULL;
if ( SDL_GetWMInfo(&info) < 0 )
LOG->Warn("SDL_GetWMInfo failed: %s", SDL_GetError());
else
g_X11Display = info.info.x11.display;
}
#endif
2003-05-22 05:28:37 +00:00
this->SetDefaultRenderStates();
DumpOpenGLDebugInfo();
/* 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( GLExt::wglSwapIntervalEXT )
2003-06-05 19:29:27 +00:00
GLExt::wglSwapIntervalEXT(p.vsync);
2003-05-22 05:28:37 +00:00
ResolutionChanged();
return ""; // successfully set mode
2003-05-22 05:28:37 +00:00
}
void RageDisplay_OGL::SetViewport(int shift_left, int shift_down)
2003-05-22 05:28:37 +00:00
{
/* left and down are on a 0..SCREEN_WIDTH, 0..SCREEN_HEIGHT scale.
* Scale them to the actual viewport range. */
2003-06-05 19:29:27 +00:00
shift_left = int( shift_left * float(wind->GetVideoModeParams().width) / SCREEN_WIDTH );
shift_down = int( shift_down * float(wind->GetVideoModeParams().height) / SCREEN_HEIGHT );
2003-05-22 05:28:37 +00:00
2003-06-05 19:29:27 +00:00
glViewport(shift_left, -shift_down, wind->GetVideoModeParams().width, wind->GetVideoModeParams().height);
2003-05-22 05:28:37 +00:00
}
int RageDisplay_OGL::GetMaxTextureSize() const
2003-05-22 05:28:37 +00:00
{
GLint size;
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &size);
return size;
}
void RageDisplay_OGL::BeginFrame()
2003-05-22 05:28:37 +00:00
{
glClearColor( 0,0,0,1 );
2003-11-18 20:33:18 +00:00
SetZWrite( true );
2003-05-22 05:28:37 +00:00
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}
void RageDisplay_OGL::EndFrame()
2003-05-22 05:28:37 +00:00
{
wind->SwapBuffers();
ProcessStatsOnFlip();
}
2003-10-09 06:33:11 +00:00
bool RageDisplay_OGL::Supports4BitPalettes()
{
return g_b4BitPalettesWork;
}
2004-02-12 06:49:13 +00:00
SDL_Surface* RageDisplay_OGL::CreateScreenshot()
2003-05-22 05:28:37 +00:00
{
2003-06-05 19:29:27 +00:00
int width = wind->GetVideoModeParams().width;
int height = wind->GetVideoModeParams().height;
2003-07-09 06:16:19 +00:00
const PixelFormatDesc &desc = PIXEL_FORMAT_DESC[FMT_RGB8];
SDL_Surface *image = SDL_CreateRGBSurfaceSane(
2003-06-05 19:29:27 +00:00
SDL_SWSURFACE, width, height,
2003-07-09 06:16:19 +00:00
desc.bpp, desc.masks[0], desc.masks[1], desc.masks[2], desc.masks[3] );
SDL_Surface *temp = SDL_CreateRGBSurfaceSane(
2003-06-05 19:29:27 +00:00
SDL_SWSURFACE, width, height,
2003-07-09 06:16:19 +00:00
desc.bpp, desc.masks[0], desc.masks[1], desc.masks[2], desc.masks[3] );
2003-05-22 05:28:37 +00:00
2003-06-05 19:29:27 +00:00
glReadPixels(0, 0, wind->GetVideoModeParams().width, wind->GetVideoModeParams().height, GL_RGB,
2003-05-22 05:28:37 +00:00
GL_UNSIGNED_BYTE, image->pixels);
// flip vertically
int pitch = image->pitch;
2003-06-05 19:29:27 +00:00
for( int y=0; y<wind->GetVideoModeParams().height; y++ )
2003-05-22 05:28:37 +00:00
memcpy(
(char *)temp->pixels + pitch * y,
2003-06-05 19:29:27 +00:00
(char *)image->pixels + pitch * (height-1-y),
3*width );
2003-12-12 10:05:58 +00:00
SDL_FreeSurface( image );
2003-05-22 05:28:37 +00:00
2004-02-12 06:49:13 +00:00
return temp;
2003-05-22 05:28:37 +00:00
}
2003-06-05 19:29:27 +00:00
RageDisplay::VideoModeParams RageDisplay_OGL::GetVideoModeParams() const { return wind->GetVideoModeParams(); }
2003-05-22 05:28:37 +00:00
static void SetupVertices( const RageSpriteVertex v[], int iNumVerts )
2003-05-22 05:28:37 +00:00
{
static float *Vertex, *Texture, *Normal;
static GLubyte *Color;
2003-05-22 05:28:37 +00:00
static int Size = 0;
if(iNumVerts > Size)
{
Size = iNumVerts;
delete [] Vertex;
delete [] Color;
delete [] Texture;
delete [] Normal;
2003-05-22 05:28:37 +00:00
Vertex = new float[Size*3];
Color = new GLubyte[Size*4];
2003-05-22 05:28:37 +00:00
Texture = new float[Size*2];
Normal = new float[Size*3];
2003-05-22 05:28:37 +00:00
}
for(unsigned i = 0; i < unsigned(iNumVerts); ++i)
{
Vertex[i*3+0] = v[i].p[0];
Vertex[i*3+1] = v[i].p[1];
Vertex[i*3+2] = v[i].p[2];
Color[i*4+0] = v[i].c.r;
Color[i*4+1] = v[i].c.g;
Color[i*4+2] = v[i].c.b;
Color[i*4+3] = v[i].c.a;
2003-05-22 05:28:37 +00:00
Texture[i*2+0] = v[i].t[0];
Texture[i*2+1] = v[i].t[1];
2003-05-27 05:26:53 +00:00
Normal[i*3+0] = v[i].n[0];
Normal[i*3+1] = v[i].n[1];
2003-05-27 08:39:36 +00:00
Normal[i*3+2] = v[i].n[2];
2003-05-22 05:28:37 +00:00
}
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, Vertex);
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(4, GL_UNSIGNED_BYTE, 0, Color);
2003-05-22 05:28:37 +00:00
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, Texture);
glEnableClientState(GL_NORMAL_ARRAY);
2003-05-27 08:39:35 +00:00
glNormalPointer(GL_FLOAT, 0, Normal);
}
2003-05-22 05:28:37 +00:00
void RageDisplay_OGL::SendCurrentMatrices()
{
glMatrixMode( GL_PROJECTION );
glLoadMatrixf( (const float*)GetProjectionTop() );
RageMatrix modelView;
RageMatrixMultiply( &modelView, GetCentering(), GetViewTop() );
RageMatrixMultiply( &modelView, &modelView, GetWorldTop() );
glMatrixMode( GL_MODELVIEW );
glLoadMatrixf( (const float*)&modelView );
}
class RageCompiledGeometrySWOGL : public RageCompiledGeometry
{
public:
void Allocate( const vector<msMesh> &vMeshes )
{
m_vPosition.resize( GetTotalVertices() );
m_vTexture.resize( GetTotalVertices() );
m_vNormal.resize( GetTotalVertices() );
m_vTriangles.resize( GetTotalTriangles() );
}
void Change( const vector<msMesh> &vMeshes )
{
for( unsigned i=0; i<vMeshes.size(); i++ )
{
const MeshInfo& meshInfo = m_vMeshInfo[i];
const msMesh& mesh = vMeshes[i];
const vector<RageModelVertex> &Vertices = mesh.Vertices;
const vector<msTriangle> &Triangles = mesh.Triangles;
{
for( unsigned j=0; j<Vertices.size(); j++ )
{
m_vPosition[meshInfo.iVertexStart+j] = Vertices[j].p;
m_vTexture[meshInfo.iVertexStart+j] = Vertices[j].t;
m_vNormal[meshInfo.iVertexStart+j] = Vertices[j].n;
}
}
{
for( unsigned j=0; j<Triangles.size(); j++ )
for( unsigned k=0; k<3; k++ )
{
int iVertexIndexInVBO = meshInfo.iVertexStart + Triangles[j].nVertexIndices[k];
m_vTriangles[meshInfo.iTriangleStart+j].nVertexIndices[k] = (word) iVertexIndexInVBO;
}
}
}
}
void Draw( int iMeshIndex ) const
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
const MeshInfo& meshInfo = m_vMeshInfo[iMeshIndex];
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &m_vPosition[0]);
glDisableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2, GL_FLOAT, 0, &m_vTexture[0]);
glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, &m_vNormal[0]);
glDrawElements(
GL_TRIANGLES,
meshInfo.iTriangleCount*3,
GL_UNSIGNED_SHORT,
&m_vTriangles[0]+meshInfo.iTriangleStart );
}
protected:
vector<RageVector3> m_vPosition;
vector<RageVector2> m_vTexture;
vector<RageVector3> m_vNormal;
vector<msTriangle> m_vTriangles;
};
class RageCompiledGeometryHWOGL : public RageCompiledGeometrySWOGL
{
protected:
// vertex buffer object names
GLuint m_nPositions;
GLuint m_nTextureCoords;
GLuint m_nNormals;
GLuint m_nTriangles;
void AllocateBuffers();
void UploadData();
public:
RageCompiledGeometryHWOGL();
~RageCompiledGeometryHWOGL();
/* This is called when our OpenGL context is invalidated. */
void Invalidate();
void Allocate( const vector<msMesh> &vMeshes );
void Change( const vector<msMesh> &vMeshes );
void Draw( int iMeshIndex ) const;
};
static set<RageCompiledGeometryHWOGL *> g_GeometryList;
static void InvalidateAllGeometry()
{
set<RageCompiledGeometryHWOGL *>::iterator it;
for( it = g_GeometryList.begin(); it != g_GeometryList.end(); ++it )
(*it)->Invalidate();
}
RageCompiledGeometryHWOGL::RageCompiledGeometryHWOGL()
{
g_GeometryList.insert( this );
m_nPositions = m_nTextureCoords = m_nNormals = m_nTriangles = 0;
AllocateBuffers();
}
RageCompiledGeometryHWOGL::~RageCompiledGeometryHWOGL()
{
g_GeometryList.erase( this );
FlushGLErrors();
GLExt::glDeleteBuffersARB( 1, &m_nPositions );
AssertNoGLError();
GLExt::glDeleteBuffersARB( 1, &m_nTextureCoords );
AssertNoGLError();
GLExt::glDeleteBuffersARB( 1, &m_nNormals );
AssertNoGLError();
GLExt::glDeleteBuffersARB( 1, &m_nTriangles );
AssertNoGLError();
}
void RageCompiledGeometryHWOGL::AllocateBuffers()
{
FlushGLErrors();
if( !m_nPositions )
{
GLExt::glGenBuffersARB( 1, &m_nPositions );
AssertNoGLError();
}
if( !m_nTextureCoords )
{
GLExt::glGenBuffersARB( 1, &m_nTextureCoords );
AssertNoGLError();
}
if( !m_nNormals )
{
GLExt::glGenBuffersARB( 1, &m_nNormals );
AssertNoGLError();
}
if( !m_nTriangles )
{
GLExt::glGenBuffersARB( 1, &m_nTriangles );
AssertNoGLError();
}
}
void RageCompiledGeometryHWOGL::UploadData()
{
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nPositions );
GLExt::glBufferDataARB(
GL_ARRAY_BUFFER_ARB,
GetTotalVertices()*sizeof(RageVector3),
&m_vPosition[0],
GL_STATIC_DRAW_ARB );
2004-04-16 05:31:08 +00:00
// AssertNoGLError();
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureCoords );
GLExt::glBufferDataARB(
GL_ARRAY_BUFFER_ARB,
GetTotalVertices()*sizeof(RageVector2),
&m_vTexture[0],
GL_STATIC_DRAW_ARB );
2004-04-16 05:31:08 +00:00
// AssertNoGLError();
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nNormals );
GLExt::glBufferDataARB(
GL_ARRAY_BUFFER_ARB,
GetTotalVertices()*sizeof(RageVector3),
&m_vNormal[0],
GL_STATIC_DRAW_ARB );
2004-04-16 05:31:08 +00:00
// AssertNoGLError();
GLExt::glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nTriangles );
GLExt::glBufferDataARB(
GL_ELEMENT_ARRAY_BUFFER_ARB,
GetTotalTriangles()*sizeof(msTriangle),
&m_vTriangles[0],
GL_STATIC_DRAW_ARB );
2004-04-16 05:31:08 +00:00
// AssertNoGLError();
}
void RageCompiledGeometryHWOGL::Invalidate()
{
/* Our vertex buffers no longer exist. Reallocate and reupload. */
m_nPositions = m_nTextureCoords = m_nNormals = m_nTriangles = 0;
AllocateBuffers();
UploadData();
}
void RageCompiledGeometryHWOGL::Allocate( const vector<msMesh> &vMeshes )
{
RageCompiledGeometrySWOGL::Allocate( vMeshes );
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nPositions );
GLExt::glBufferDataARB(
GL_ARRAY_BUFFER_ARB,
GetTotalVertices()*sizeof(RageVector3),
NULL,
GL_STATIC_DRAW_ARB );
AssertNoGLError();
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureCoords );
GLExt::glBufferDataARB(
GL_ARRAY_BUFFER_ARB,
GetTotalVertices()*sizeof(RageVector2),
NULL,
GL_STATIC_DRAW_ARB );
AssertNoGLError();
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nNormals );
GLExt::glBufferDataARB(
GL_ARRAY_BUFFER_ARB,
GetTotalVertices()*sizeof(RageVector3),
NULL,
GL_STATIC_DRAW_ARB );
AssertNoGLError();
GLExt::glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nTriangles );
GLExt::glBufferDataARB(
GL_ELEMENT_ARRAY_BUFFER_ARB,
GetTotalTriangles()*sizeof(msTriangle),
NULL,
GL_STATIC_DRAW_ARB );
AssertNoGLError();
}
void RageCompiledGeometryHWOGL::Change( const vector<msMesh> &vMeshes )
{
RageCompiledGeometrySWOGL::Change( vMeshes );
UploadData();
}
void RageCompiledGeometryHWOGL::Draw( int iMeshIndex ) const
{
FlushGLErrors();
const MeshInfo& meshInfo = m_vMeshInfo[iMeshIndex];
if( !meshInfo.iVertexCount || !meshInfo.iTriangleCount )
return;
glEnableClientState(GL_VERTEX_ARRAY);
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nPositions );
AssertNoGLError();
glVertexPointer(3, GL_FLOAT, 0, NULL );
AssertNoGLError();
glDisableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureCoords );
AssertNoGLError();
glTexCoordPointer(2, GL_FLOAT, 0, NULL);
AssertNoGLError();
// TRICKY: Don't bind and send normals if lighting is disabled. This
// will save some effort transforming these values.
GLboolean bLighting;
glGetBooleanv( GL_LIGHTING, &bLighting );
2004-04-17 18:11:03 +00:00
GLboolean bTextureGenS;
2004-04-17 20:07:39 +00:00
glGetBooleanv( GL_TEXTURE_GEN_S, &bTextureGenS );
2004-04-17 18:11:03 +00:00
GLboolean bTextureGenT;
2004-04-17 20:07:39 +00:00
glGetBooleanv( GL_TEXTURE_GEN_T, &bTextureGenT );
2004-04-17 18:11:03 +00:00
if( bLighting || bTextureGenS || bTextureGenT )
{
glEnableClientState(GL_NORMAL_ARRAY);
GLExt::glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nNormals );
AssertNoGLError();
glNormalPointer(GL_FLOAT, 0, NULL);
AssertNoGLError();
}
else
{
glDisableClientState(GL_NORMAL_ARRAY);
}
GLExt::glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nTriangles );
AssertNoGLError();
#define BUFFER_OFFSET(o) ((char*)(o))
ASSERT( GLExt::glDrawRangeElements);
GLExt::glDrawRangeElements(
GL_TRIANGLES,
meshInfo.iVertexStart, // minimum array index contained in indices
meshInfo.iVertexStart+meshInfo.iVertexCount-1,
// maximum array index contained in indices
meshInfo.iTriangleCount*3, // number of elements to be rendered
GL_UNSIGNED_SHORT,
BUFFER_OFFSET(meshInfo.iTriangleStart*sizeof(msTriangle)) );
AssertNoGLError();
}
RageCompiledGeometry* RageDisplay_OGL::CreateCompiledGeometry()
{
if( GLExt::glGenBuffersARB )
return new RageCompiledGeometryHWOGL;
else
return new RageCompiledGeometrySWOGL;
}
void RageDisplay_OGL::DeleteCompiledGeometry( RageCompiledGeometry* p )
{
delete p;
}
void RageDisplay_OGL::DrawQuadsInternal( const RageSpriteVertex v[], int iNumVerts )
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
SendCurrentMatrices();
SetupVertices( v, iNumVerts );
2003-05-22 05:28:37 +00:00
glDrawArrays( GL_QUADS, 0, iNumVerts );
}
void RageDisplay_OGL::DrawQuadStripInternal( const RageSpriteVertex v[], int iNumVerts )
2004-04-13 07:28:25 +00:00
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
2004-04-13 07:28:25 +00:00
SendCurrentMatrices();
SetupVertices( v, iNumVerts );
glDrawArrays( GL_QUAD_STRIP, 0, iNumVerts );
}
void RageDisplay_OGL::DrawFanInternal( const RageSpriteVertex v[], int iNumVerts )
2003-05-22 05:28:37 +00:00
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
2003-05-22 05:28:37 +00:00
glMatrixMode( GL_PROJECTION );
SendCurrentMatrices();
SetupVertices( v, iNumVerts );
2003-05-22 05:28:37 +00:00
glDrawArrays( GL_TRIANGLE_FAN, 0, iNumVerts );
}
void RageDisplay_OGL::DrawStripInternal( const RageSpriteVertex v[], int iNumVerts )
2003-05-22 05:28:37 +00:00
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
SendCurrentMatrices();
SetupVertices( v, iNumVerts );
2003-05-22 05:28:37 +00:00
glDrawArrays( GL_TRIANGLE_STRIP, 0, iNumVerts );
}
void RageDisplay_OGL::DrawTrianglesInternal( const RageSpriteVertex v[], int iNumVerts )
2003-05-22 05:28:37 +00:00
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
SendCurrentMatrices();
SetupVertices( v, iNumVerts );
2003-05-22 05:28:37 +00:00
glDrawArrays( GL_TRIANGLES, 0, iNumVerts );
}
void RageDisplay_OGL::DrawCompiledGeometryInternal( const RageCompiledGeometry *p, int iMeshIndex )
2003-05-22 05:28:37 +00:00
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
SendCurrentMatrices();
p->Draw( iMeshIndex );
2003-05-22 05:28:37 +00:00
}
void RageDisplay_OGL::DrawLineStripInternal( const RageSpriteVertex v[], int iNumVerts, float LineWidth )
2003-05-22 05:28:37 +00:00
{
2004-04-16 05:31:08 +00:00
TurnOffHardwareVBO();
2003-12-20 21:29:55 +00:00
if( !GetVideoModeParams().bSmoothLines )
{
2004-04-16 05:06:31 +00:00
/* Fall back on the generic polygon-based line strip. */
RageDisplay::DrawLineStripInternal(v, iNumVerts, LineWidth );
return;
}
SendCurrentMatrices();
2003-05-22 05:28:37 +00:00
/* 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. */
glEnable(GL_LINE_SMOOTH);
/* 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. */
2003-06-05 19:29:27 +00:00
float WidthVal = float(wind->GetVideoModeParams().width) / SCREEN_WIDTH;
float HeightVal = float(wind->GetVideoModeParams().height) / SCREEN_HEIGHT;
LineWidth *= (WidthVal + HeightVal) / 2;
/* Clamp the width to the hardware max for both lines and points (whichever
* is more restrictive). */
LineWidth = clamp(LineWidth, g_line_range[0], g_line_range[1]);
LineWidth = clamp(LineWidth, g_point_range[0], g_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: */
SetupVertices( v, iNumVerts );
glDrawArrays( GL_LINE_STRIP, 0, iNumVerts );
glDisable(GL_LINE_SMOOTH);
/* 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);
/* 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
* 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. */
RageMatrix mat;
glGetFloatv( GL_MODELVIEW_MATRIX, (float*)mat );
if(mat.m[0][0] < 1e-5 && mat.m[1][1] < 1e-5)
return;
glEnable(GL_POINT_SMOOTH);
SetupVertices( v, iNumVerts );
glDrawArrays( GL_POINTS, 0, iNumVerts );
glDisable(GL_POINT_SMOOTH);
2003-05-22 05:28:37 +00:00
}
void RageDisplay_OGL::ClearAllTextures()
2003-05-22 05:28:37 +00:00
{
for( int i=0; i<MAX_TEXTURE_UNITS; i++ )
SetTexture( i, NULL );
// HACK: Reset the active texture to 0.
// TODO: Change all texture functions to take a stage number.
if( GLExt::glActiveTextureARB )
GLExt::glActiveTextureARB(GL_TEXTURE0_ARB);
}
void RageDisplay_OGL::SetTexture( int iTextureUnitIndex, RageTexture* pTexture )
{
if( GLExt::glActiveTextureARB == NULL )
{
// multitexture isn't supported. Ignore all textures except for 0.
if( iTextureUnitIndex != 0 )
return;
}
else
{
switch( iTextureUnitIndex )
{
case 0:
GLExt::glActiveTextureARB(GL_TEXTURE0_ARB);
break;
case 1:
GLExt::glActiveTextureARB(GL_TEXTURE1_ARB);
break;
default:
ASSERT(0);
}
}
if( pTexture )
{
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, pTexture->GetTexHandle() );
}
else
{
glDisable( GL_TEXTURE_2D );
}
2003-05-22 05:28:37 +00:00
}
void RageDisplay_OGL::SetTextureModeModulate()
2003-05-22 05:28:37 +00:00
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
void RageDisplay_OGL::SetTextureModeGlow(GlowMode m)
2003-05-22 05:28:37 +00:00
{
if(m == GLOW_WHITEN && !g_bEXT_texture_env_combine)
m = GLOW_BRIGHTEN; /* we can't do GLOW_WHITEN */
switch(m)
{
case GLOW_BRIGHTEN:
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
return;
case GLOW_WHITEN:
/* Source color is the diffuse color only: */
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_COMBINE_RGB_EXT), GL_REPLACE);
glTexEnvi(GL_TEXTURE_ENV, GLenum(GL_SOURCE0_RGB_EXT), GL_PRIMARY_COLOR_EXT);
/* Source alpha is texture alpha * diffuse alpha: */
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);
return;
}
}
void RageDisplay_OGL::SetTextureModeAdd()
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);
}
void RageDisplay_OGL::SetTextureFiltering( bool b )
2003-05-22 05:28:37 +00:00
{
}
void RageDisplay_OGL::SetBlendMode( BlendMode mode )
2003-05-22 05:28:37 +00:00
{
glEnable(GL_BLEND);
glDepthRange( 0.05, 1.0 );
2003-05-22 05:28:37 +00:00
switch( mode )
{
case BLEND_NORMAL:
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
break;
case BLEND_ADD:
2003-10-22 01:47:40 +00:00
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
2003-05-22 05:28:37 +00:00
break;
case BLEND_NO_EFFECT:
2003-11-18 20:33:18 +00:00
/* XXX: Would it be faster and have the same effect to say glDisable(GL_COLOR_WRITEMASK)? */
2003-05-22 05:28:37 +00:00
glBlendFunc( GL_ZERO, GL_ONE );
/* This is almost exclusively used to draw masks to the Z-buffer. Make sure
* masks always win the depth test when drawn at the same position. */
2004-04-25 18:52:27 +00:00
glDepthRange( 0.0, 0.95 ); // this bias is bigger than it needs to be to handle the coplanar case
2003-05-22 05:28:37 +00:00
break;
default:
ASSERT(0);
}
}
2003-11-18 20:33:18 +00:00
bool RageDisplay_OGL::IsZWriteEnabled() const
2003-05-22 05:28:37 +00:00
{
bool a;
2003-11-18 20:33:18 +00:00
glGetBooleanv( GL_DEPTH_WRITEMASK, (unsigned char*)&a );
2003-05-22 05:28:37 +00:00
return a;
}
2003-11-18 20:33:18 +00:00
bool RageDisplay_OGL::IsZTestEnabled() const
2003-05-22 05:28:37 +00:00
{
2003-11-18 20:33:18 +00:00
GLenum a;
2003-12-10 16:42:02 +00:00
glGetIntegerv( GL_DEPTH_FUNC, (GLint*)&a );
2003-11-18 20:33:18 +00:00
return a != GL_ALWAYS;
2003-05-22 05:28:37 +00:00
}
2003-11-18 20:33:18 +00:00
void RageDisplay_OGL::ClearZBuffer()
2003-05-22 05:28:37 +00:00
{
2003-11-18 20:33:18 +00:00
bool write = IsZWriteEnabled();
SetZWrite( true );
2003-05-22 05:28:37 +00:00
glClear( GL_DEPTH_BUFFER_BIT );
2003-11-18 20:33:18 +00:00
SetZWrite( write );
}
void RageDisplay_OGL::SetZWrite( bool b )
{
glDepthMask( b );
}
void RageDisplay_OGL::SetZTest( bool b )
{
glEnable( GL_DEPTH_TEST );
if( b )
glDepthFunc( GL_LEQUAL );
else
glDepthFunc( GL_ALWAYS );
2003-05-22 05:28:37 +00:00
}
void RageDisplay_OGL::SetTextureWrapping( bool b )
2003-05-22 05:28:37 +00:00
{
2003-09-06 05:32:42 +00:00
GLenum mode = b ? GL_REPEAT : GL_CLAMP_TO_EDGE;
2003-05-22 05:28:37 +00:00
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode );
}
void RageDisplay_OGL::SetMaterial(
2004-01-06 21:25:46 +00:00
const RageColor &emissive,
const RageColor &ambient,
const RageColor &diffuse,
const RageColor &specular,
2003-05-22 05:28:37 +00:00
float shininess
)
{
// TRICKY: If lighting is off, then setting the material
// will have no effect. Even if lighting is off, we still
// want Models to have basic color and transparency.
// We can do this fake lighting by setting the vertex color.
GLboolean bLighting;
glGetBooleanv( GL_LIGHTING, &bLighting );
if( bLighting )
{
glMaterialfv( GL_FRONT, GL_EMISSION, emissive );
glMaterialfv( GL_FRONT, GL_AMBIENT, ambient );
glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse );
glMaterialfv( GL_FRONT, GL_SPECULAR, specular );
glMaterialf( GL_FRONT, GL_SHININESS, shininess );
}
else
{
glColor4fv( emissive + ambient + diffuse );
}
2003-05-22 05:28:37 +00:00
}
void RageDisplay_OGL::SetLighting( bool b )
2003-05-22 05:28:37 +00:00
{
if( b ) glEnable( GL_LIGHTING );
else glDisable( GL_LIGHTING );
}
void RageDisplay_OGL::SetLightOff( int index )
2003-05-22 05:28:37 +00:00
{
glDisable( GL_LIGHT0+index );
}
void RageDisplay_OGL::SetLightDirectional(
2003-05-22 05:28:37 +00:00
int index,
2004-01-06 21:25:46 +00:00
const RageColor &ambient,
const RageColor &diffuse,
const RageColor &specular,
const RageVector3 &dir )
2003-05-22 05:28:37 +00:00
{
// Light coordinates are transformed by the modelview matrix, but
// we are being passed in world-space coords.
glPushMatrix();
glLoadIdentity();
glEnable( GL_LIGHT0+index );
2003-06-27 02:11:13 +00:00
glLightfv(GL_LIGHT0+index, GL_AMBIENT, ambient);
glLightfv(GL_LIGHT0+index, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0+index, GL_SPECULAR, specular);
2003-05-22 05:28:37 +00:00
float position[4] = {dir.x, dir.y, dir.z, 0};
2003-06-27 02:11:13 +00:00
glLightfv(GL_LIGHT0+index, GL_POSITION, position);
2003-05-22 05:28:37 +00:00
glPopMatrix();
}
2004-02-04 11:05:33 +00:00
void RageDisplay_OGL::SetCullMode( CullMode mode )
2003-05-22 05:28:37 +00:00
{
2004-02-04 11:05:33 +00:00
switch( mode )
{
case CULL_BACK:
2003-05-22 05:28:37 +00:00
glEnable( GL_CULL_FACE );
2004-02-04 11:05:33 +00:00
glCullFace( GL_BACK );
break;
case CULL_FRONT:
glEnable( GL_CULL_FACE );
glCullFace( GL_FRONT );
break;
case CULL_NONE:
2003-05-22 05:28:37 +00:00
glDisable( GL_CULL_FACE );
2004-02-04 11:05:33 +00:00
break;
default:
ASSERT(0);
}
2003-05-22 05:28:37 +00:00
}
2003-10-03 21:38:38 +00:00
const RageDisplay::PixelFormatDesc *RageDisplay_OGL::GetPixelFormatDesc(PixelFormat pf) const
{
ASSERT( pf < NUM_PIX_FORMATS );
return &PIXEL_FORMAT_DESC[pf];
}
void RageDisplay_OGL::DeleteTexture( unsigned uTexHandle )
2003-05-22 05:28:37 +00:00
{
unsigned int uTexID = uTexHandle;
2003-06-12 19:40:44 +00:00
FlushGLErrors();
2003-05-22 05:28:37 +00:00
glDeleteTextures(1,reinterpret_cast<GLuint*>(&uTexID));
AssertNoGLError();
2003-05-22 05:28:37 +00:00
}
2003-10-03 21:38:38 +00:00
RageDisplay::PixelFormat RageDisplay_OGL::GetImgPixelFormat( SDL_Surface* &img, bool &FreeImg, int width, int height )
{
PixelFormat pixfmt = FindPixelFormat( img->format->BitsPerPixel, img->format->Rmask, img->format->Gmask, img->format->Bmask, img->format->Amask );
if( pixfmt == NUM_PIX_FORMATS || !SupportsSurfaceFormat(pixfmt) )
{
/* The source isn't in a supported, known pixel format. We need to convert
* it ourself. Just convert it to RGBA8, and let OpenGL convert it back
* down to whatever the actual pixel format is. This is a very slow code
* path, which should almost never be used. */
pixfmt = FMT_RGBA8;
ASSERT( SupportsSurfaceFormat(pixfmt) );
const PixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
SDL_SetAlpha( img, 0, SDL_ALPHA_OPAQUE );
SDL_Rect area;
area.x = area.y = 0;
area.w = short(width);
area.h = short(height);
SDL_Surface *imgconv = SDL_CreateRGBSurfaceSane(SDL_SWSURFACE, width, height,
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3]);
SDL_BlitSurface(img, &area, imgconv, &area);
img = imgconv;
FreeImg = true;
}
else
FreeImg = false;
return pixfmt;
}
unsigned RageDisplay_OGL::CreateTexture(
2003-05-22 05:28:37 +00:00
PixelFormat pixfmt,
SDL_Surface* img,
bool bGenerateMipMaps )
2003-05-22 05:28:37 +00:00
{
2003-08-29 03:03:29 +00:00
ASSERT( pixfmt < NUM_PIX_FORMATS );
2004-01-24 19:35:53 +00:00
ASSERT( img->w == power_of_two(img->w) && img->h == power_of_two(img->h) );
2003-08-29 03:03:29 +00:00
/* Find the pixel format of the image we've been given. */
bool FreeImg;
PixelFormat imgpixfmt = GetImgPixelFormat( img, FreeImg, img->w, img->h );
2003-05-22 05:28:37 +00:00
GLenum glTexFormat = GL_PIXFMT_INFO[pixfmt].internalfmt;
GLenum glImageFormat = GL_PIXFMT_INFO[imgpixfmt].format;
GLenum glImageType = GL_PIXFMT_INFO[imgpixfmt].type;
2003-05-22 05:28:37 +00:00
2003-10-09 06:33:11 +00:00
/* If we support 4-bit palettes, and this image fits in 4 bits, then change
* internalformat to GL_COLOR_INDEX4_EXT. We'll still upload it as 256 colors,
* but OpenGL can discard the extra bits. */
if( (img->unused1 & FOUR_BIT_PALETTE) && Supports4BitPalettes() )
{
LOG->Trace("did 4 bit");
glTexFormat = GL_COLOR_INDEX4_EXT;
}
2004-01-24 19:35:53 +00:00
// HACK: OpenGL 1.2 types aren't available in GLU 1.3. Don't call GLU for mip
// mapping if we're using an OGL 1.2 type and don't have >= GLU 1.3.
// http://pyopengl.sourceforge.net/documentation/manual/gluBuild2DMipmaps.3G.html
2004-01-24 22:46:06 +00:00
if( bGenerateMipMaps && g_gluVersion < 13 )
2004-01-24 19:35:53 +00:00
{
switch( pixfmt )
{
// OpenGL 1.1 types
case FMT_RGBA8:
case FMT_RGB8:
case FMT_PAL:
case FMT_BGR8:
break;
// OpenGL 1.2 types
default:
LOG->Trace( "Can't generate mipmaps for type %s because GLU version %.1f is too old.", GLToString(glImageType).c_str(), g_gluVersion/10.f );
bGenerateMipMaps = false;
break;
}
}
// allocate OpenGL texture resource
unsigned int uTexHandle;
glGenTextures(1, reinterpret_cast<GLuint*>(&uTexHandle));
ASSERT(uTexHandle);
glBindTexture( GL_TEXTURE_2D, uTexHandle );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, bGenerateMipMaps ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
SetTextureWrapping( false );
glPixelStorei(GL_UNPACK_ROW_LENGTH, img->pitch / img->format->BytesPerPixel);
2004-01-24 22:18:12 +00:00
if(pixfmt == FMT_PAL)
{
/* The image is paletted. Let's try to set up a paletted texture. */
GLubyte palette[256*4];
memset(palette, 0, sizeof(palette));
int p = 0;
/* Copy the palette to the simple, unpacked data OGL expects. If
* we're color keyed, change it over as we go. */
for(int i = 0; i < img->format->palette->ncolors; ++i)
{
palette[p++] = img->format->palette->colors[i].r;
palette[p++] = img->format->palette->colors[i].g;
palette[p++] = img->format->palette->colors[i].b;
if(img->flags & SDL_SRCCOLORKEY && i == int(img->format->colorkey))
palette[p++] = 0;
else
palette[p++] = img->format->palette->colors[i].unused;
}
/* Set the palette. */
GLExt::glColorTableEXT(GL_TEXTURE_2D, GL_RGBA8, 256, GL_RGBA, GL_UNSIGNED_BYTE, palette);
GLint RealFormat = 0;
GLExt::glGetColorTableParameterivEXT(GL_TEXTURE_2D, GL_COLOR_TABLE_FORMAT, &RealFormat);
ASSERT( RealFormat == GL_RGBA8); /* This is a case I don't expect to happen. */
}
2003-06-12 19:40:44 +00:00
FlushGLErrors();
if( bGenerateMipMaps )
{
GLenum error = gluBuild2DMipmaps(
GL_TEXTURE_2D, glTexFormat,
img->w, img->h,
glImageFormat, glImageType, img->pixels );
if( error != 0 )
{
ostringstream s;
s << "gluBuild2DMipmaps(format " << GLToString(glTexFormat) <<
", w " << img->w << ", h " << img->h <<
", format " << GLToString(glImageFormat) <<
", type " << GLToString(glImageType) <<
"): " << gluErrorString(error);
LOG->Trace( s.str().c_str() );
ASSERT(0);
}
}
else
{
glTexImage2D(
GL_TEXTURE_2D, 0, glTexFormat,
2003-05-22 05:28:37 +00:00
img->w, img->h, 0,
glImageFormat, glImageType, img->pixels);
GLenum error = glGetError();
if( error != GL_NO_ERROR )
{
ostringstream s;
s << "glTexImage2D(format " << GLToString(glTexFormat) <<
", w " << img->w << ", h " << img->h <<
", format " << GLToString(glImageFormat) <<
", type " << GLToString(glImageType) <<
"): " << GLToString(error);
LOG->Trace( s.str().c_str() );
ASSERT(0);
}
}
2003-05-22 05:28:37 +00:00
2003-06-28 02:19:31 +00:00
/* Sanity check: */
2003-05-22 05:28:37 +00:00
if( pixfmt == FMT_PAL )
{
GLint size = 0;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INDEX_SIZE_EXT), &size);
if(size != 8)
2003-05-30 20:16:30 +00:00
RageException::Throw("Thought paletted textures worked, but they don't.");
2003-05-22 05:28:37 +00:00
}
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glFlush();
if( FreeImg )
SDL_FreeSurface( img );
2003-05-22 05:28:37 +00:00
return uTexHandle;
}
void RageDisplay_OGL::UpdateTexture(
2003-05-22 05:28:37 +00:00
unsigned uTexHandle,
SDL_Surface* img,
2003-05-22 05:28:37 +00:00
int xoffset, int yoffset, int width, int height )
{
glBindTexture( GL_TEXTURE_2D, uTexHandle );
bool FreeImg;
PixelFormat pixfmt = GetImgPixelFormat( img, FreeImg, width, height );
glPixelStorei(GL_UNPACK_ROW_LENGTH, img->pitch / img->format->BytesPerPixel);
2003-05-22 05:28:37 +00:00
// GLenum glTexFormat = GL_PIXFMT_INFO[pixfmt].internalfmt;
GLenum glImageFormat = GL_PIXFMT_INFO[pixfmt].format;
GLenum glImageType = GL_PIXFMT_INFO[pixfmt].type;
glTexSubImage2D(GL_TEXTURE_2D, 0,
xoffset, yoffset,
width, height,
glImageFormat, glImageType, img->pixels);
/* Must unset PixelStore when we're done! */
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glFlush();
if( FreeImg )
SDL_FreeSurface( img );
2003-05-22 05:28:37 +00:00
}
2003-06-22 01:53:04 +00:00
CString RageDisplay_OGL::GetTextureDiagnostics( unsigned id ) const
{
return "";
}
void RageDisplay_OGL::SetAlphaTest( bool b )
2003-05-22 05:28:37 +00:00
{
glAlphaFunc( GL_GREATER, 0.01f );
if( b )
glEnable( GL_ALPHA_TEST );
else
glDisable( GL_ALPHA_TEST );
}
RageMatrix RageDisplay_OGL::GetOrthoMatrix( float l, float r, float b, float t, float zn, float zf )
{
RageMatrix m(
2/(r-l), 0, 0, 0,
0, 2/(t-b), 0, 0,
0, 0, -2/(zf-zn), 0,
-(r+l)/(r-l), -(t+b)/(t-b), -(zf+zn)/(zf-zn), 1 );
return m;
2003-06-22 01:20:58 +00:00
}
2003-09-21 18:07:29 +00:00
/*
* Although we pair texture formats (eg. GL_RGB8) and surface formats
* (pairs of eg. GL_RGB8,GL_UNSIGNED_SHORT_5_5_5_1), it's possible for
* a format to be supported for a texture format but not a surface
* format. This is abstracted, so you don't need to know about this
* as a user calling CreateTexture.
*
* One case of this is if packed pixels aren't supported. We can still
* use 16-bit color modes, but we have to send it in 32-bit. Almost
* everything supports packed pixels.
*
* Another case of this is incomplete packed pixels support. Some implementations
* neglect GL_UNSIGNED_SHORT_*_REV.
*/
bool RageDisplay_OGL::SupportsSurfaceFormat( PixelFormat pixfmt )
{
switch( GL_PIXFMT_INFO[pixfmt].type )
{
case GL_UNSIGNED_SHORT_1_5_5_5_REV:
return g_bGL_EXT_bgra && g_bReversePackedPixelsWorks;
default:
return true;
}
}
bool RageDisplay_OGL::SupportsTextureFormat( PixelFormat pixfmt, bool realtime )
{
/* If we support a pixfmt for texture formats but not for surface formats, then
* we'll have to convert the texture to a supported surface format before uploading.
* This is too slow for dynamic textures. */
if( realtime && !SupportsSurfaceFormat( pixfmt ) )
return false;
switch( GL_PIXFMT_INFO[pixfmt].format )
{
case GL_COLOR_INDEX:
return GLExt::glColorTableEXT && GLExt::glGetColorTableParameterivEXT;
case GL_BGR:
case GL_BGRA:
return g_bGL_EXT_bgra;
default:
return true;
}
}
2004-01-06 19:54:13 +00:00
void RageDisplay_OGL::SetSphereEnironmentMapping( bool b )
{
if( b )
{
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
}
else
{
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
}
}