2864 lines
80 KiB
C++
2864 lines
80 KiB
C++
#include "global.h"
|
|
|
|
#include "RageDisplay_OGL.h"
|
|
#include "RageDisplay_OGL_Helpers.h"
|
|
using namespace RageDisplay_Legacy_Helpers;
|
|
|
|
#include "RageFile.h"
|
|
#include "RageSurface.h"
|
|
#include "RageSurfaceUtils.h"
|
|
#include "RageUtil.h"
|
|
#include "RageLog.h"
|
|
#include "RageTextureManager.h"
|
|
#include "RageMath.h"
|
|
#include "RageTypes.h"
|
|
#include "RageUtil.h"
|
|
#include "EnumHelper.h"
|
|
#include "DisplaySpec.h"
|
|
#include "LocalizedString.h"
|
|
|
|
#include "arch/LowLevelWindow/LowLevelWindow.h"
|
|
|
|
#include <cmath>
|
|
#include <cstddef>
|
|
#include <set>
|
|
|
|
#if defined(WINDOWS)
|
|
#include <GL/wglew.h>
|
|
#endif
|
|
|
|
#if defined(_MSC_VER)
|
|
#pragma comment(lib, "opengl32.lib")
|
|
#pragma comment(lib, "glu32.lib")
|
|
#endif
|
|
|
|
#ifdef NO_GL_FLUSH
|
|
#define glFlush()
|
|
#endif
|
|
|
|
//
|
|
// Globals
|
|
//
|
|
|
|
static bool g_bReversePackedPixelsWorks = true;
|
|
static bool g_bColorIndexTableWorks = true;
|
|
|
|
/* OpenGL system information that generally doesn't change at runtime. */
|
|
|
|
/* Range and granularity of points and lines: */
|
|
static float g_line_range[2];
|
|
static float g_point_range[2];
|
|
|
|
/* OpenGL version * 10: */
|
|
static int g_glVersion;
|
|
|
|
/* GLU version * 10: */
|
|
static int g_gluVersion;
|
|
|
|
static int g_iMaxTextureUnits = 0;
|
|
|
|
/* We don't actually use normals (we don't turn on lighting), there's just
|
|
* no GL_T2F_C4F_V3F. */
|
|
static const GLenum RageSpriteVertexFormat = GL_T2F_C4F_N3F_V3F;
|
|
|
|
/* If we support texture matrix scaling, a handle to the vertex program: */
|
|
static GLhandleARB g_bTextureMatrixShader = 0;
|
|
|
|
static std::map<uintptr_t, RenderTarget *> g_mapRenderTargets;
|
|
static RenderTarget *g_pCurrentRenderTarget = nullptr;
|
|
|
|
static LowLevelWindow *g_pWind;
|
|
|
|
static bool g_bInvertY = false;
|
|
|
|
static void InvalidateObjects();
|
|
|
|
static RageDisplay::RagePixelFormatDesc PIXEL_FORMAT_DESC[NUM_RagePixelFormat] = {
|
|
{
|
|
/* R8G8B8A8 */
|
|
32,
|
|
{ 0xFF000000,
|
|
0x00FF0000,
|
|
0x0000FF00,
|
|
0x000000FF }
|
|
}, {
|
|
/* B8G8R8A8 */
|
|
32,
|
|
{ 0x0000FF00,
|
|
0x00FF0000,
|
|
0xFF000000,
|
|
0x000000FF }
|
|
}, {
|
|
/* R4G4B4A4 */
|
|
16,
|
|
{ 0xF000,
|
|
0x0F00,
|
|
0x00F0,
|
|
0x000F },
|
|
}, {
|
|
/* R5G5B5A1 */
|
|
16,
|
|
{ 0xF800,
|
|
0x07C0,
|
|
0x003E,
|
|
0x0001 },
|
|
}, {
|
|
/* R5G5B5X1 */
|
|
16,
|
|
{ 0xF800,
|
|
0x07C0,
|
|
0x003E,
|
|
0x0000 },
|
|
}, {
|
|
/* R8G8B8 */
|
|
24,
|
|
{ 0xFF0000,
|
|
0x00FF00,
|
|
0x0000FF,
|
|
0x000000 }
|
|
}, {
|
|
/* Paletted */
|
|
8,
|
|
{ 0,0,0,0 } /* N/A */
|
|
}, {
|
|
/* B8G8R8 */
|
|
24,
|
|
{ 0x0000FF,
|
|
0x00FF00,
|
|
0xFF0000,
|
|
0x000000 }
|
|
}, {
|
|
/* A1R5G5B5 */
|
|
16,
|
|
{ 0x7C00,
|
|
0x03E0,
|
|
0x001F,
|
|
0x8000 },
|
|
}, {
|
|
/* X1R5G5B5 */
|
|
16,
|
|
{ 0x7C00,
|
|
0x03E0,
|
|
0x001F,
|
|
0x0000 },
|
|
}
|
|
};
|
|
|
|
/* g_GLPixFmtInfo is used for both texture formats and surface formats. For example,
|
|
* it's fine to ask for a RagePixelFormat_RGB5 texture, but to supply a surface matching
|
|
* RagePixelFormat_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.
|
|
*/
|
|
struct GLPixFmtInfo_t {
|
|
GLenum internalfmt; /* target format */
|
|
GLenum format; /* target format */
|
|
GLenum type; /* data format */
|
|
} const g_GLPixFmtInfo[NUM_RagePixelFormat] = {
|
|
{
|
|
/* R8G8B8A8 */
|
|
GL_RGBA8,
|
|
GL_RGBA,
|
|
GL_UNSIGNED_BYTE,
|
|
}, {
|
|
/* R8G8B8A8 */
|
|
GL_RGBA8,
|
|
GL_BGRA,
|
|
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,
|
|
}, {
|
|
/* Paletted */
|
|
GL_COLOR_INDEX8_EXT,
|
|
GL_COLOR_INDEX,
|
|
GL_UNSIGNED_BYTE,
|
|
}, {
|
|
/* B8G8R8 */
|
|
GL_RGB8,
|
|
GL_BGR,
|
|
GL_UNSIGNED_BYTE,
|
|
}, {
|
|
/* A1R5G5B5 (matches D3DFMT_A1R5G5B5) */
|
|
GL_RGB5_A1,
|
|
GL_BGRA,
|
|
GL_UNSIGNED_SHORT_1_5_5_5_REV,
|
|
}, {
|
|
/* X1R5G5B5 */
|
|
GL_RGB5,
|
|
GL_BGRA,
|
|
GL_UNSIGNED_SHORT_1_5_5_5_REV,
|
|
}
|
|
};
|
|
|
|
|
|
static void FixLittleEndian()
|
|
{
|
|
if constexpr (!Endian::little) {
|
|
return;
|
|
}
|
|
|
|
static bool bInitialized = false;
|
|
if (bInitialized)
|
|
return;
|
|
bInitialized = true;
|
|
|
|
for( int i = 0; i < NUM_RagePixelFormat; ++i )
|
|
{
|
|
RageDisplay::RagePixelFormatDesc &pf = PIXEL_FORMAT_DESC[i];
|
|
|
|
/* OpenGL and RageSurface handle byte formats differently; we need
|
|
* to flip non-paletted masks to make them line up. */
|
|
if (g_GLPixFmtInfo[i].type != GL_UNSIGNED_BYTE || pf.bpp == 8)
|
|
continue;
|
|
|
|
for( int mask = 0; mask < 4; ++mask)
|
|
{
|
|
int m = pf.masks[mask];
|
|
switch( pf.bpp )
|
|
{
|
|
case 24: m = Swap24(m); break;
|
|
case 32: m = Swap32(m); break;
|
|
default:
|
|
FAIL_M(ssprintf("Unsupported BPP value: %i", pf.bpp));
|
|
}
|
|
pf.masks[mask] = m;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void TurnOffHardwareVBO()
|
|
{
|
|
if (GLEW_ARB_vertex_buffer_object)
|
|
{
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
|
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
|
|
}
|
|
}
|
|
|
|
RageDisplay_Legacy::RageDisplay_Legacy()
|
|
{
|
|
LOG->Trace( "RageDisplay_Legacy::RageDisplay_Legacy()" );
|
|
LOG->MapLog("renderer", "Current renderer: OpenGL");
|
|
|
|
FixLittleEndian();
|
|
RageDisplay_Legacy_Helpers::Init();
|
|
|
|
g_pWind = nullptr;
|
|
g_bTextureMatrixShader = 0;
|
|
offscreenRenderTarget = nullptr;
|
|
}
|
|
|
|
RString GetInfoLog( GLhandleARB h )
|
|
{
|
|
GLint iLength;
|
|
glGetObjectParameterivARB( h, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iLength );
|
|
if (!iLength)
|
|
return RString();
|
|
|
|
GLcharARB *pInfoLog = new GLcharARB[iLength];
|
|
glGetInfoLogARB( h, iLength, &iLength, pInfoLog );
|
|
RString sRet = pInfoLog;
|
|
delete [] pInfoLog;
|
|
TrimRight( sRet );
|
|
return sRet;
|
|
}
|
|
|
|
GLhandleARB CompileShader( GLenum ShaderType, RString sFile, std::vector<RString> asDefines )
|
|
{
|
|
/* XXX: This would not be necessary if it wasn't for the special case for Cel. */
|
|
if (ShaderType == GL_FRAGMENT_SHADER_ARB && !glewIsSupported("GL_VERSION_2_0"))
|
|
{
|
|
LOG->Warn("Fragment shaders not supported by driver. Some effects will not be available.");
|
|
return 0;
|
|
}
|
|
|
|
RString sBuffer;
|
|
{
|
|
RageFile file;
|
|
if (!file.Open(sFile))
|
|
{
|
|
LOG->Warn( "Error compiling shader %s: %s", sFile.c_str(), file.GetError().c_str() );
|
|
return 0;
|
|
}
|
|
|
|
if (file.Read(sBuffer, file.GetFileSize()) == -1)
|
|
{
|
|
LOG->Warn( "Error compiling shader %s: %s", sFile.c_str(), file.GetError().c_str() );
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
LOG->Trace( "Compiling shader %s", sFile.c_str() );
|
|
GLhandleARB hShader = glCreateShaderObjectARB( ShaderType );
|
|
std::vector<const GLcharARB *> apData;
|
|
std::vector<GLint> aiLength;
|
|
for (RString &s : asDefines)
|
|
{
|
|
s = ssprintf( "#define %s\n", s.c_str() );
|
|
apData.push_back( s.data() );
|
|
aiLength.push_back( s.size() );
|
|
}
|
|
apData.push_back( "#line 1\n" );
|
|
aiLength.push_back( 8 );
|
|
|
|
apData.push_back( sBuffer.data() );
|
|
aiLength.push_back( sBuffer.size() );
|
|
glShaderSourceARB( hShader, apData.size(), &apData[0], &aiLength[0] );
|
|
|
|
glCompileShaderARB( hShader );
|
|
|
|
RString sInfo = GetInfoLog( hShader );
|
|
|
|
GLint bCompileStatus = GL_FALSE;
|
|
glGetObjectParameterivARB( hShader, GL_OBJECT_COMPILE_STATUS_ARB, &bCompileStatus );
|
|
if (!bCompileStatus)
|
|
{
|
|
LOG->Warn( "Error compiling shader %s:\n%s", sFile.c_str(), sInfo.c_str() );
|
|
glDeleteObjectARB( hShader );
|
|
return 0;
|
|
}
|
|
|
|
if (!sInfo.empty())
|
|
LOG->Trace( "Messages compiling shader %s:\n%s", sFile.c_str(), sInfo.c_str() );
|
|
|
|
return hShader;
|
|
}
|
|
|
|
GLhandleARB LoadShader( GLenum ShaderType, RString sFile, std::vector<RString> asDefines )
|
|
{
|
|
/* Vertex shaders are supported by more hardware than fragment shaders.
|
|
* If this causes any trouble I will have to up the requirement for both
|
|
* of them to at least GL 2.0. Regardless we need basic GLSL support.
|
|
* -Colby */
|
|
if (!glewIsSupported("GL_ARB_shading_language_100 GL_ARB_shader_objects") ||
|
|
(ShaderType == GL_FRAGMENT_SHADER_ARB && !glewIsSupported("GL_VERSION_2_0")) ||
|
|
(ShaderType == GL_VERTEX_SHADER_ARB && !glewIsSupported("GL_ARB_vertex_shader")))
|
|
{
|
|
LOG->Warn("%s shaders not supported by driver. Some effects will not be available.",
|
|
(ShaderType == GL_FRAGMENT_SHADER_ARB) ? "Fragment" : "Vertex");
|
|
return 0;
|
|
}
|
|
|
|
// XXX: dumb, but I don't feel like refactoring ragedisplay for this. -Colby
|
|
GLhandleARB secondaryShader = 0;
|
|
if (sFile == "Data/Shaders/GLSL/Cel.vert")
|
|
secondaryShader = CompileShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Cel.frag", asDefines);
|
|
else if (sFile == "Data/Shaders/GLSL/Shell.vert")
|
|
secondaryShader = CompileShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Shell.frag", asDefines);
|
|
else if (sFile == "Data/Shaders/GLSL/Distance field.vert")
|
|
secondaryShader = CompileShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Distance field.frag", asDefines);
|
|
|
|
GLhandleARB hShader = CompileShader( ShaderType, sFile, asDefines );
|
|
if (hShader == 0)
|
|
return 0;
|
|
|
|
GLhandleARB hProgram = glCreateProgramObjectARB();
|
|
glAttachObjectARB( hProgram, hShader );
|
|
|
|
if (secondaryShader)
|
|
{
|
|
glAttachObjectARB( hProgram, secondaryShader );
|
|
glDeleteObjectARB( secondaryShader );
|
|
}
|
|
glDeleteObjectARB( hShader );
|
|
|
|
// Link the program.
|
|
glLinkProgramARB( hProgram );
|
|
GLint bLinkStatus = false;
|
|
glGetObjectParameterivARB( hProgram, GL_OBJECT_LINK_STATUS_ARB, &bLinkStatus );
|
|
|
|
if (!bLinkStatus)
|
|
{
|
|
LOG->Warn( "Error linking shader %s: %s", sFile.c_str(), GetInfoLog(hProgram).c_str() );
|
|
glDeleteObjectARB( hProgram );
|
|
return 0;
|
|
}
|
|
return hProgram;
|
|
}
|
|
|
|
static int g_iAttribTextureMatrixScale;
|
|
|
|
static GLhandleARB g_bUnpremultiplyShader = 0;
|
|
static GLhandleARB g_bColorBurnShader = 0;
|
|
static GLhandleARB g_bColorDodgeShader = 0;
|
|
static GLhandleARB g_bVividLightShader = 0;
|
|
static GLhandleARB g_hHardMixShader = 0;
|
|
static GLhandleARB g_hOverlayShader = 0;
|
|
static GLhandleARB g_hScreenShader = 0;
|
|
static GLhandleARB g_hYUYV422Shader = 0;
|
|
static GLhandleARB g_gShellShader = 0;
|
|
static GLhandleARB g_gCelShader = 0;
|
|
static GLhandleARB g_gDistanceFieldShader = 0;
|
|
|
|
void InitShaders()
|
|
{
|
|
// xxx: replace this with a ShaderManager or something that reads in
|
|
// the shaders and determines shader type by file extension. -aj
|
|
// argh shaders in stepmania are painful -colby
|
|
std::vector<RString> asDefines;
|
|
|
|
// used for scrolling textures (I think)
|
|
g_bTextureMatrixShader = LoadShader( GL_VERTEX_SHADER_ARB, "Data/Shaders/GLSL/Texture matrix scaling.vert", asDefines );
|
|
|
|
// these two are for dancing characters and are both actually shader pairs
|
|
g_gShellShader = LoadShader( GL_VERTEX_SHADER_ARB, "Data/Shaders/GLSL/Shell.vert", asDefines );
|
|
g_gCelShader = LoadShader( GL_VERTEX_SHADER_ARB, "Data/Shaders/GLSL/Cel.vert", asDefines );
|
|
g_gDistanceFieldShader = LoadShader( GL_VERTEX_SHADER_ARB, "Data/Shaders/GLSL/Distance field.vert", asDefines );
|
|
|
|
// effects
|
|
g_bUnpremultiplyShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Unpremultiply.frag", asDefines );
|
|
g_bColorBurnShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Color burn.frag", asDefines );
|
|
g_bColorDodgeShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Color dodge.frag", asDefines );
|
|
g_bVividLightShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Vivid light.frag", asDefines );
|
|
g_hHardMixShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Hard mix.frag", asDefines );
|
|
g_hOverlayShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Overlay.frag", asDefines );
|
|
g_hScreenShader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/Screen.frag", asDefines );
|
|
g_hYUYV422Shader = LoadShader( GL_FRAGMENT_SHADER_ARB, "Data/Shaders/GLSL/YUYV422.frag", asDefines );
|
|
|
|
// Bind attributes.
|
|
if (g_bTextureMatrixShader)
|
|
{
|
|
FlushGLErrors();
|
|
g_iAttribTextureMatrixScale = glGetAttribLocationARB( g_bTextureMatrixShader, "TextureMatrixScale" );
|
|
if (g_iAttribTextureMatrixScale == -1)
|
|
{
|
|
LOG->Trace( "Scaling shader link failed: couldn't bind attribute \"TextureMatrixScale\"" );
|
|
glDeleteObjectARB( g_bTextureMatrixShader );
|
|
g_bTextureMatrixShader = 0;
|
|
}
|
|
else
|
|
{
|
|
AssertNoGLError();
|
|
|
|
/* Older Catalyst drivers seem to throw GL_INVALID_OPERATION here. */
|
|
glVertexAttrib2fARB( g_iAttribTextureMatrixScale, 1, 1 );
|
|
GLenum iError = glGetError();
|
|
if (iError == GL_INVALID_OPERATION)
|
|
{
|
|
LOG->Trace( "Scaling shader failed: glVertexAttrib2fARB returned GL_INVALID_OPERATION" );
|
|
glDeleteObjectARB( g_bTextureMatrixShader );
|
|
g_bTextureMatrixShader = 0;
|
|
}
|
|
else
|
|
{
|
|
ASSERT_M( iError == GL_NO_ERROR, GLToString(iError) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static LocalizedString OBTAIN_AN_UPDATED_VIDEO_DRIVER ( "RageDisplay_Legacy", "Obtain an updated driver from your video card manufacturer." );
|
|
static LocalizedString GLDIRECT_IS_NOT_COMPATIBLE ( "RageDisplay_Legacy", "GLDirect was detected. GLDirect is not compatible with this game and should be disabled." );
|
|
RString RageDisplay_Legacy::Init( const VideoModeParams &p, bool bAllowUnacceleratedRenderer )
|
|
{
|
|
g_pWind = LowLevelWindow::Create();
|
|
|
|
bool bIgnore = false;
|
|
RString sError = SetVideoMode( p, bIgnore );
|
|
if (sError != "")
|
|
return sError;
|
|
|
|
// Log driver details
|
|
g_pWind->LogDebugInformation();
|
|
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 Max texture size: %i", GetMaxTextureSize() );
|
|
LOG->Info( "OGL Texture units: %i", g_iMaxTextureUnits );
|
|
LOG->Info( "GLU Version: %s", gluGetString(GLU_VERSION) );
|
|
|
|
/* Pretty-print the extension string: */
|
|
LOG->Info( "OGL Extensions:" );
|
|
{
|
|
const char *szExtensionString = (const char *) glGetString(GL_EXTENSIONS);
|
|
std::vector<RString> asExtensions;
|
|
split( szExtensionString, " ", asExtensions );
|
|
sort( asExtensions.begin(), asExtensions.end() );
|
|
std::size_t iNextToPrint = 0;
|
|
while( iNextToPrint < asExtensions.size() )
|
|
{
|
|
std::size_t iLastToPrint = iNextToPrint;
|
|
RString sType;
|
|
for( std::size_t i = iNextToPrint; i<asExtensions.size(); ++i )
|
|
{
|
|
std::vector<RString> asBits;
|
|
split( asExtensions[i], "_", asBits );
|
|
RString sThisType;
|
|
if (asBits.size() > 2)
|
|
sThisType = join( "_", asBits.begin(), asBits.begin()+2 );
|
|
if (i > iNextToPrint && sThisType != sType)
|
|
break;
|
|
sType = sThisType;
|
|
iLastToPrint = i;
|
|
}
|
|
|
|
if (iNextToPrint == iLastToPrint)
|
|
{
|
|
LOG->Info( " %s", asExtensions[iNextToPrint].c_str() );
|
|
++iNextToPrint;
|
|
continue;
|
|
}
|
|
|
|
RString sList = ssprintf( " %s: ", sType.c_str() );
|
|
while( iNextToPrint <= iLastToPrint )
|
|
{
|
|
std::vector<RString> asBits;
|
|
split( asExtensions[iNextToPrint], "_", asBits );
|
|
RString sShortExt = join( "_", asBits.begin()+2, asBits.end() );
|
|
sList += sShortExt;
|
|
if (iNextToPrint < iLastToPrint)
|
|
sList += ", ";
|
|
if (iNextToPrint == iLastToPrint || sList.size() + asExtensions[iNextToPrint+1].size() > 120)
|
|
{
|
|
LOG->Info( "%s", sList.c_str() );
|
|
sList = " ";
|
|
}
|
|
++iNextToPrint;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (g_pWind->IsSoftwareRenderer(sError))
|
|
{
|
|
if (!bAllowUnacceleratedRenderer)
|
|
return sError + " " + OBTAIN_AN_UPDATED_VIDEO_DRIVER.GetValue() + "\n\n";
|
|
LOG->Warn( "Low-performance OpenGL renderer: %s", sError.c_str() );
|
|
}
|
|
|
|
#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 ))
|
|
return GLDIRECT_IS_NOT_COMPATIBLE.GetValue() + "\n";
|
|
#endif
|
|
|
|
/* 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_POINT_SIZE_RANGE, g_point_range );
|
|
|
|
return RString();
|
|
}
|
|
|
|
RageDisplay_Legacy::~RageDisplay_Legacy()
|
|
{
|
|
delete g_pWind;
|
|
}
|
|
|
|
void RageDisplay_Legacy::GetDisplaySpecs(DisplaySpecs &out) const
|
|
{
|
|
out.clear();
|
|
g_pWind->GetDisplaySpecs(out);
|
|
}
|
|
|
|
static void CheckPalettedTextures()
|
|
{
|
|
RString sError;
|
|
do
|
|
{
|
|
if (!GLEW_EXT_paletted_texture)
|
|
{
|
|
sError = "GL_EXT_paletted_texture missing";
|
|
break;
|
|
}
|
|
|
|
/* Check to see if paletted textures really work. */
|
|
GLenum glTexFormat = g_GLPixFmtInfo[RagePixelFormat_PAL].internalfmt;
|
|
GLenum glImageFormat = g_GLPixFmtInfo[RagePixelFormat_PAL].format;
|
|
GLenum glImageType = g_GLPixFmtInfo[RagePixelFormat_PAL].type;
|
|
|
|
int iBits = 8;
|
|
|
|
FlushGLErrors();
|
|
#define GL_CHECK_ERROR(f) \
|
|
{ \
|
|
GLenum glError = glGetError(); \
|
|
if (glError != GL_NO_ERROR) { \
|
|
sError = ssprintf(f " failed (%s)", GLToString(glError).c_str() ); \
|
|
break; \
|
|
} \
|
|
}
|
|
|
|
glTexImage2D( GL_PROXY_TEXTURE_2D,
|
|
0, glTexFormat,
|
|
16, 16, 0,
|
|
glImageFormat, glImageType, nullptr );
|
|
GL_CHECK_ERROR( "glTexImage2D" );
|
|
|
|
GLuint iFormat = 0;
|
|
glGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INTERNAL_FORMAT), (GLint *) &iFormat );
|
|
GL_CHECK_ERROR( "glGetTexLevelParameteriv(GL_TEXTURE_INTERNAL_FORMAT)" );
|
|
if (iFormat != glTexFormat)
|
|
{
|
|
sError = ssprintf( "Expected format %s, got %s instead",
|
|
GLToString(glTexFormat).c_str(), GLToString(iFormat).c_str() );
|
|
break;
|
|
}
|
|
|
|
GLubyte palette[256*4];
|
|
memset(palette, 0, sizeof(palette));
|
|
glColorTableEXT(GL_PROXY_TEXTURE_2D, GL_RGBA8, 256, GL_RGBA, GL_UNSIGNED_BYTE, palette);
|
|
GL_CHECK_ERROR( "glColorTableEXT" );
|
|
|
|
GLint iSize = 0;
|
|
glGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INDEX_SIZE_EXT), &iSize );
|
|
GL_CHECK_ERROR( "glGetTexLevelParameteriv(GL_TEXTURE_INDEX_SIZE_EXT)" );
|
|
if (iBits > iSize || iSize > 8)
|
|
{
|
|
sError = ssprintf( "Expected %i-bit palette, got a %i-bit one instead", iBits, int(iSize) );
|
|
break;
|
|
}
|
|
|
|
GLint iRealWidth = 0;
|
|
glGetColorTableParameterivEXT( GL_PROXY_TEXTURE_2D, GL_COLOR_TABLE_WIDTH, &iRealWidth );
|
|
GL_CHECK_ERROR( "glGetColorTableParameterivEXT(GL_COLOR_TABLE_WIDTH)" );
|
|
if (iRealWidth != 1 << iBits)
|
|
{
|
|
sError = ssprintf( "GL_COLOR_TABLE_WIDTH returned %i instead of %i", int(iRealWidth), 1 << iBits );
|
|
break;
|
|
}
|
|
|
|
GLint iRealFormat = 0;
|
|
glGetColorTableParameterivEXT( GL_PROXY_TEXTURE_2D, GL_COLOR_TABLE_FORMAT, &iRealFormat );
|
|
GL_CHECK_ERROR( "glGetColorTableParameterivEXT(GL_COLOR_TABLE_FORMAT)" );
|
|
if (iRealFormat != GL_RGBA8)
|
|
{
|
|
sError = ssprintf( "GL_COLOR_TABLE_FORMAT returned %s instead of GL_RGBA8", GLToString(iRealFormat).c_str() );
|
|
break;
|
|
}
|
|
} while(0);
|
|
#undef GL_CHECK_ERROR
|
|
|
|
if (sError == "")
|
|
return;
|
|
|
|
/* 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. */
|
|
glColorTableEXT = nullptr;
|
|
glGetColorTableParameterivEXT = nullptr;
|
|
LOG->Info( "Paletted textures disabled: %s.", sError.c_str() );
|
|
}
|
|
|
|
static void CheckReversePackedPixels()
|
|
{
|
|
/* Try to create a texture. */
|
|
FlushGLErrors();
|
|
glTexImage2D( GL_PROXY_TEXTURE_2D,
|
|
0, GL_RGBA,
|
|
16, 16, 0,
|
|
GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, nullptr );
|
|
|
|
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() );
|
|
}
|
|
}
|
|
|
|
void SetupExtensions()
|
|
{
|
|
const float fGLVersion = StringToFloat( (const char *) glGetString(GL_VERSION) );
|
|
g_glVersion = std::lrint( fGLVersion * 10 );
|
|
|
|
const float fGLUVersion = StringToFloat( (const char *) gluGetString(GLU_VERSION) );
|
|
g_gluVersion = std::lrint( fGLUVersion * 10 );
|
|
|
|
#ifndef HAVE_X11 // LLW_X11 needs to init GLEW early for GLX exts
|
|
glewInit();
|
|
#endif
|
|
|
|
g_iMaxTextureUnits = 1;
|
|
if (GLEW_ARB_multitexture)
|
|
glGetIntegerv( GL_MAX_TEXTURE_UNITS_ARB, (GLint *) &g_iMaxTextureUnits );
|
|
|
|
CheckPalettedTextures();
|
|
CheckReversePackedPixels();
|
|
|
|
{
|
|
GLint iMaxTableSize = 0;
|
|
glGetIntegerv( GL_MAX_PIXEL_MAP_TABLE, &iMaxTableSize );
|
|
if (iMaxTableSize < 256)
|
|
{
|
|
/* The minimum GL_MAX_PIXEL_MAP_TABLE is 32; if it's not at least 256,
|
|
* we can't fit a palette in it, so we can't send paletted data as input
|
|
* for a non-paletted texture. */
|
|
LOG->Info( "GL_MAX_PIXEL_MAP_TABLE is only %d", int(iMaxTableSize) );
|
|
g_bColorIndexTableWorks = false;
|
|
}
|
|
else
|
|
{
|
|
g_bColorIndexTableWorks = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RageDisplay_Legacy::UseOffscreenRenderTarget()
|
|
{
|
|
if ( !GetActualVideoModeParams().renderOffscreen || !TEXTUREMAN )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !offscreenRenderTarget )
|
|
{
|
|
RenderTargetParam param;
|
|
param.bWithDepthBuffer = true;
|
|
param.bWithAlpha = true;
|
|
param.bFloat = false;
|
|
param.iWidth = GetActualVideoModeParams().width;
|
|
param.iHeight = GetActualVideoModeParams().height;
|
|
RageTextureID id( ssprintf( "FullscreenTexture%dx%d", param.iWidth,
|
|
param.iHeight ) );
|
|
// See if we have this texture loaded already
|
|
// (not GC'd yet). If it exists and we try to recreate
|
|
// it, we'll get an error
|
|
if ( TEXTUREMAN->IsTextureRegistered( id ) )
|
|
{
|
|
offscreenRenderTarget = static_cast<RageTextureRenderTarget*>( TEXTUREMAN->LoadTexture( id ) );
|
|
}
|
|
else
|
|
{
|
|
offscreenRenderTarget = new RageTextureRenderTarget( id, param );
|
|
TEXTUREMAN->RegisterTexture( id, offscreenRenderTarget );
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void RageDisplay_Legacy::ResolutionChanged()
|
|
{
|
|
//LOG->Warn( "RageDisplay_Legacy::ResolutionChanged" );
|
|
|
|
/* Clear any junk that's in the framebuffer. */
|
|
if (BeginFrame())
|
|
EndFrame();
|
|
|
|
RageDisplay::ResolutionChanged();
|
|
|
|
if (offscreenRenderTarget && TEXTUREMAN)
|
|
{
|
|
TEXTUREMAN->UnloadTexture( offscreenRenderTarget );
|
|
offscreenRenderTarget = nullptr;
|
|
}
|
|
|
|
}
|
|
|
|
// Return true if mode change was successful.
|
|
// bNewDeviceOut is set true if a new device was created and textures
|
|
// need to be reloaded.
|
|
RString RageDisplay_Legacy::TryVideoMode( const VideoModeParams &p, bool &bNewDeviceOut )
|
|
{
|
|
//LOG->Warn( "RageDisplay_Legacy::TryVideoMode( %d, %d, %d, %d, %d, %d )", p.windowed, p.width, p.height, p.bpp, p.rate, p.vsync );
|
|
|
|
RString err;
|
|
err = g_pWind->TryVideoMode( p, bNewDeviceOut );
|
|
if (err != "")
|
|
return err; // failed to set video mode
|
|
|
|
/* Now that we've initialized, we can search for extensions. Do this before InvalidateObjects,
|
|
* since AllocateBuffers needs it. */
|
|
SetupExtensions();
|
|
|
|
if (bNewDeviceOut)
|
|
{
|
|
/* We have a new OpenGL context, so we have to tell our textures that
|
|
* their OpenGL texture number is invalid. */
|
|
if (TEXTUREMAN)
|
|
TEXTUREMAN->InvalidateTextures();
|
|
|
|
|
|
/* Delete all render targets. They may have associated resources other than
|
|
* the texture itself. */
|
|
for (std::pair<uintptr_t const, RenderTarget *> &rt : g_mapRenderTargets)
|
|
delete rt.second;
|
|
g_mapRenderTargets.clear();
|
|
|
|
/* Recreate all vertex buffers. */
|
|
InvalidateObjects();
|
|
|
|
InitShaders();
|
|
}
|
|
|
|
// I'm not sure this is correct -Colby
|
|
#if defined(WINDOWS)
|
|
/* Set vsync the Windows way, if we can. (What other extensions are there
|
|
* to do this, for other archs?) */
|
|
if( wglewIsSupported("WGL_EXT_swap_control") )
|
|
wglSwapIntervalEXT(p.vsync);
|
|
else
|
|
return RString("The WGL_EXT_swap_control extension is not supported on your computer.");
|
|
#endif
|
|
|
|
ResolutionChanged();
|
|
|
|
return RString(); // successfully set mode
|
|
}
|
|
|
|
int RageDisplay_Legacy::GetMaxTextureSize() const
|
|
{
|
|
GLint size;
|
|
glGetIntegerv( GL_MAX_TEXTURE_SIZE, &size );
|
|
return size;
|
|
}
|
|
|
|
bool RageDisplay_Legacy::BeginFrame()
|
|
{
|
|
/* We do this in here, rather than ResolutionChanged, or we won't update the
|
|
* viewport for the concurrent rendering context. */
|
|
int fWidth = g_pWind->GetActualVideoModeParams().windowWidth;
|
|
int fHeight = g_pWind->GetActualVideoModeParams().windowHeight;
|
|
|
|
glViewport( 0, 0, fWidth, fHeight );
|
|
|
|
glClearColor( 0,0,0,0 );
|
|
SetZWrite( true );
|
|
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
|
|
|
|
bool beginFrame = RageDisplay::BeginFrame();
|
|
if (beginFrame && UseOffscreenRenderTarget()) {
|
|
offscreenRenderTarget->BeginRenderingTo( false );
|
|
}
|
|
|
|
return beginFrame;
|
|
}
|
|
|
|
void RageDisplay_Legacy::EndFrame()
|
|
{
|
|
if (UseOffscreenRenderTarget())
|
|
{
|
|
offscreenRenderTarget->FinishRenderingTo();
|
|
Sprite fullscreenSprite;
|
|
// We've got a hold of this, don't want sprite deleting it when
|
|
// it's deleted
|
|
offscreenRenderTarget->m_iRefCount++;
|
|
fullscreenSprite.SetTexture(offscreenRenderTarget);
|
|
fullscreenSprite.SetHorizAlign(align_left);
|
|
fullscreenSprite.SetVertAlign(align_top);
|
|
CameraPushMatrix();
|
|
LoadMenuPerspective( 0, GetActualVideoModeParams().width, GetActualVideoModeParams().height,
|
|
static_cast<float> (GetActualVideoModeParams().width) / 2.f,
|
|
static_cast<float> (GetActualVideoModeParams().height) / 2.f );
|
|
fullscreenSprite.Draw();
|
|
CameraPopMatrix();
|
|
}
|
|
|
|
FrameLimitBeforeVsync( g_pWind->GetActualVideoModeParams().rate );
|
|
g_pWind->SwapBuffers();
|
|
FrameLimitAfterVsync();
|
|
|
|
// Some would advise against glFinish(), ever. Those people don't realize
|
|
// the degree of freedom GL hosts are permitted in queueing commands.
|
|
// If left to its own devices, the host could lag behind several frames' worth
|
|
// of commands.
|
|
// glFlush() only forces the host to not wait to execute all commands
|
|
// sent so far; it does NOT block on those commands until they finish.
|
|
// glFinish() blocks. We WANT to block. Why? This puts the engine state
|
|
// reflected by the next frame as close as possible to the on-screen
|
|
// appearance of that frame.
|
|
glFinish();
|
|
|
|
g_pWind->Update();
|
|
|
|
RageDisplay::EndFrame();
|
|
}
|
|
|
|
RageSurface* RageDisplay_Legacy::CreateScreenshot()
|
|
{
|
|
int width = g_pWind->GetActualVideoModeParams().width;
|
|
int height = g_pWind->GetActualVideoModeParams().height;
|
|
|
|
RageSurface *image = nullptr;
|
|
if (offscreenRenderTarget) {
|
|
RageSurface *raw = GetTexture(offscreenRenderTarget->GetTexHandle());
|
|
image = CreateSurface( offscreenRenderTarget->GetImageWidth(), offscreenRenderTarget->GetImageHeight(),
|
|
raw->fmt.BitsPerPixel, raw->fmt.Rmask, raw->fmt.Gmask, raw->fmt.Bmask,
|
|
raw->fmt.Amask );
|
|
RageSurfaceUtils::Blit(raw, image);
|
|
delete raw;
|
|
} else {
|
|
const RagePixelFormatDesc &desc = PIXEL_FORMAT_DESC[RagePixelFormat_RGBA8];
|
|
image = CreateSurface( width, height, desc.bpp,
|
|
desc.masks[0], desc.masks[1], desc.masks[2], 0 );
|
|
|
|
DebugFlushGLErrors();
|
|
|
|
//TODO: revisit for MacOS, where backbuffer size can be less than window size
|
|
glReadBuffer( GL_FRONT );
|
|
DebugAssertNoGLError();
|
|
|
|
glReadPixels( 0, 0, g_pWind->GetActualVideoModeParams().width, g_pWind->GetActualVideoModeParams().height, GL_RGBA,
|
|
GL_UNSIGNED_BYTE, image->pixels );
|
|
DebugAssertNoGLError();
|
|
|
|
RageSurfaceUtils::FlipVertically( image );
|
|
}
|
|
|
|
return image;
|
|
}
|
|
|
|
RageSurface *RageDisplay_Legacy::GetTexture( uintptr_t iTexture )
|
|
{
|
|
if (iTexture == 0)
|
|
return nullptr; // XXX
|
|
|
|
FlushGLErrors();
|
|
|
|
glBindTexture( GL_TEXTURE_2D, static_cast<GLuint>(iTexture) );
|
|
GLint iHeight, iWidth, iAlphaBits;
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &iHeight );
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &iWidth );
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_ALPHA_SIZE, &iAlphaBits );
|
|
int iFormat = iAlphaBits? RagePixelFormat_RGBA8:RagePixelFormat_RGB8;
|
|
|
|
const RagePixelFormatDesc &desc = PIXEL_FORMAT_DESC[iFormat];
|
|
RageSurface *pImage = CreateSurface( iWidth, iHeight, desc.bpp,
|
|
desc.masks[0], desc.masks[1], desc.masks[2], desc.masks[3] );
|
|
|
|
glGetTexImage( GL_TEXTURE_2D, 0, g_GLPixFmtInfo[iFormat].format, GL_UNSIGNED_BYTE, pImage->pixels );
|
|
AssertNoGLError();
|
|
|
|
return pImage;
|
|
}
|
|
|
|
ActualVideoModeParams RageDisplay_Legacy::GetActualVideoModeParams() const
|
|
{
|
|
return g_pWind->GetActualVideoModeParams();
|
|
}
|
|
|
|
static void SetupVertices( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
static float *Vertex, *Texture, *Normal;
|
|
static GLubyte *Color;
|
|
static int Size = 0;
|
|
if (iNumVerts > Size)
|
|
{
|
|
Size = iNumVerts;
|
|
delete [] Vertex;
|
|
delete [] Color;
|
|
delete [] Texture;
|
|
delete [] Normal;
|
|
Vertex = new float[Size*3];
|
|
Color = new GLubyte[Size*4];
|
|
Texture = new float[Size*2];
|
|
Normal = new float[Size*3];
|
|
}
|
|
|
|
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;
|
|
Texture[i*2+0] = v[i].t[0];
|
|
Texture[i*2+1] = v[i].t[1];
|
|
Normal[i*3+0] = v[i].n[0];
|
|
Normal[i*3+1] = v[i].n[1];
|
|
Normal[i*3+2] = v[i].n[2];
|
|
}
|
|
glEnableClientState( GL_VERTEX_ARRAY );
|
|
glVertexPointer( 3, GL_FLOAT, 0, Vertex );
|
|
|
|
glEnableClientState( GL_COLOR_ARRAY );
|
|
glColorPointer( 4, GL_UNSIGNED_BYTE, 0, Color );
|
|
|
|
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
glTexCoordPointer( 2, GL_FLOAT, 0, Texture );
|
|
|
|
if (GLEW_ARB_multitexture)
|
|
{
|
|
glClientActiveTextureARB( GL_TEXTURE1_ARB );
|
|
glEnableClientState( GL_TEXTURE_COORD_ARRAY );
|
|
glTexCoordPointer( 2, GL_FLOAT, 0, Texture );
|
|
glClientActiveTextureARB( GL_TEXTURE0_ARB );
|
|
}
|
|
|
|
glEnableClientState( GL_NORMAL_ARRAY );
|
|
glNormalPointer( GL_FLOAT, 0, Normal );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SendCurrentMatrices()
|
|
{
|
|
RageMatrix projection;
|
|
RageMatrixMultiply( &projection, GetCentering(), GetProjectionTop() );
|
|
|
|
if (g_bInvertY)
|
|
{
|
|
RageMatrix flip;
|
|
RageMatrixScale( &flip, +1, -1, +1 );
|
|
RageMatrixMultiply( &projection, &flip, &projection );
|
|
}
|
|
glMatrixMode( GL_PROJECTION );
|
|
glLoadMatrixf( (const float*)&projection );
|
|
|
|
// OpenGL has just "modelView", whereas D3D has "world" and "view"
|
|
RageMatrix modelView;
|
|
RageMatrixMultiply( &modelView, GetViewTop(), GetWorldTop() );
|
|
glMatrixMode( GL_MODELVIEW );
|
|
glLoadMatrixf( (const float*)&modelView );
|
|
|
|
glMatrixMode( GL_TEXTURE );
|
|
glLoadMatrixf( (const float*)GetTextureTop() );
|
|
}
|
|
|
|
class RageCompiledGeometrySWOGL : public RageCompiledGeometry
|
|
{
|
|
public:
|
|
|
|
void Allocate( const std::vector<msMesh> &vMeshes )
|
|
{
|
|
/* Always allocate at least 1 entry, so &x[0] is valid. */
|
|
const unsigned int verticesCount = std::max<unsigned int>(1u, GetTotalVertices());
|
|
const unsigned int trianglesCount = std::max<unsigned int>(1u, GetTotalTriangles());
|
|
|
|
m_vPosition.resize(verticesCount);
|
|
m_vTexture.resize(verticesCount);
|
|
m_vNormal.resize(verticesCount);
|
|
m_vTexMatrixScale.resize(verticesCount);
|
|
m_vTriangles.resize(trianglesCount);
|
|
}
|
|
void Change( const std::vector<msMesh> &vMeshes )
|
|
{
|
|
for( unsigned i=0; i<vMeshes.size(); i++ )
|
|
{
|
|
const MeshInfo& meshInfo = m_vMeshInfo[i];
|
|
const msMesh& mesh = vMeshes[i];
|
|
const std::vector<RageModelVertex> &Vertices = mesh.Vertices;
|
|
const std::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;
|
|
m_vTexMatrixScale[meshInfo.iVertexStart+j] = Vertices[j].TextureMatrixScale;
|
|
}
|
|
|
|
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] = (uint16_t) iVertexIndexInVBO;
|
|
}
|
|
}
|
|
}
|
|
void Draw( int iMeshIndex ) const
|
|
{
|
|
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]);
|
|
|
|
if (meshInfo.m_bNeedsTextureMatrixScale)
|
|
{
|
|
// Kill the texture translation.
|
|
// XXX: Change me to scale the translation by the TextureTranslationScale of the first vertex.
|
|
RageMatrix mat;
|
|
glGetFloatv( GL_TEXTURE_MATRIX , (float*)mat );
|
|
|
|
/*
|
|
for( int i=0; i<4; i++ )
|
|
{
|
|
RString s;
|
|
for( int j=0; j<4; j++ )
|
|
s += ssprintf( "%f ", mat.m[i][j] );
|
|
LOG->Trace( s );
|
|
}
|
|
*/
|
|
|
|
mat.m[3][0] = 0;
|
|
mat.m[3][1] = 0;
|
|
mat.m[3][2] = 0;
|
|
|
|
glMatrixMode( GL_TEXTURE );
|
|
glLoadMatrixf( (const float*)mat );
|
|
}
|
|
|
|
glDrawElements(
|
|
GL_TRIANGLES,
|
|
meshInfo.iTriangleCount*3,
|
|
GL_UNSIGNED_SHORT,
|
|
&m_vTriangles[0]+meshInfo.iTriangleStart );
|
|
}
|
|
|
|
protected:
|
|
std::vector<RageVector3> m_vPosition;
|
|
std::vector<RageVector2> m_vTexture;
|
|
std::vector<RageVector3> m_vNormal;
|
|
std::vector<msTriangle> m_vTriangles;
|
|
std::vector<RageVector2> m_vTexMatrixScale;
|
|
};
|
|
|
|
class InvalidateObject;
|
|
static std::set<InvalidateObject*> g_InvalidateList;
|
|
class InvalidateObject
|
|
{
|
|
public:
|
|
InvalidateObject() { g_InvalidateList.insert( this ); }
|
|
virtual ~InvalidateObject() { g_InvalidateList.erase( this ); }
|
|
virtual void Invalidate() = 0;
|
|
};
|
|
|
|
static void InvalidateObjects()
|
|
{
|
|
for (InvalidateObject *it : g_InvalidateList)
|
|
it->Invalidate();
|
|
}
|
|
|
|
class RageCompiledGeometryHWOGL : public RageCompiledGeometrySWOGL, public InvalidateObject
|
|
{
|
|
protected:
|
|
// vertex buffer object names
|
|
GLuint m_nPositions;
|
|
GLuint m_nTextureCoords;
|
|
GLuint m_nNormals;
|
|
GLuint m_nTriangles;
|
|
GLuint m_nTextureMatrixScale;
|
|
|
|
void AllocateBuffers();
|
|
void UploadData();
|
|
|
|
public:
|
|
RageCompiledGeometryHWOGL();
|
|
~RageCompiledGeometryHWOGL();
|
|
|
|
/* This is called when our OpenGL context is invalidated. */
|
|
void Invalidate();
|
|
|
|
void Allocate( const std::vector<msMesh> &vMeshes );
|
|
void Change( const std::vector<msMesh> &vMeshes );
|
|
void Draw( int iMeshIndex ) const;
|
|
};
|
|
|
|
RageCompiledGeometryHWOGL::RageCompiledGeometryHWOGL()
|
|
{
|
|
m_nPositions = 0;
|
|
m_nTextureCoords = 0;
|
|
m_nNormals = 0;
|
|
m_nTriangles = 0;
|
|
m_nTextureMatrixScale = 0;
|
|
|
|
AllocateBuffers();
|
|
}
|
|
|
|
RageCompiledGeometryHWOGL::~RageCompiledGeometryHWOGL()
|
|
{
|
|
DebugFlushGLErrors();
|
|
|
|
glDeleteBuffersARB( 1, &m_nPositions );
|
|
DebugAssertNoGLError();
|
|
glDeleteBuffersARB( 1, &m_nTextureCoords );
|
|
DebugAssertNoGLError();
|
|
glDeleteBuffersARB( 1, &m_nNormals );
|
|
DebugAssertNoGLError();
|
|
glDeleteBuffersARB( 1, &m_nTriangles );
|
|
DebugAssertNoGLError();
|
|
glDeleteBuffersARB( 1, &m_nTextureMatrixScale );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
void RageCompiledGeometryHWOGL::AllocateBuffers()
|
|
{
|
|
DebugFlushGLErrors();
|
|
|
|
if (!m_nPositions)
|
|
{
|
|
glGenBuffersARB( 1, &m_nPositions );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
if (!m_nTextureCoords)
|
|
{
|
|
glGenBuffersARB( 1, &m_nTextureCoords );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
if (!m_nNormals)
|
|
{
|
|
glGenBuffersARB( 1, &m_nNormals );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
if (!m_nTriangles)
|
|
{
|
|
glGenBuffersARB( 1, &m_nTriangles );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
if (!m_nTextureMatrixScale)
|
|
{
|
|
glGenBuffersARB( 1, &m_nTextureMatrixScale );
|
|
DebugAssertNoGLError();
|
|
}
|
|
}
|
|
|
|
void RageCompiledGeometryHWOGL::UploadData()
|
|
{
|
|
DebugFlushGLErrors();
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_nPositions);
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector3),
|
|
&m_vPosition[0],
|
|
GL_STATIC_DRAW_ARB);
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_nTextureCoords);
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector2),
|
|
&m_vTexture[0],
|
|
GL_STATIC_DRAW_ARB);
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_nNormals);
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector3),
|
|
&m_vNormal[0],
|
|
GL_STATIC_DRAW_ARB);
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, m_nTriangles);
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ELEMENT_ARRAY_BUFFER_ARB,
|
|
GetTotalTriangles()*sizeof(msTriangle),
|
|
&m_vTriangles[0],
|
|
GL_STATIC_DRAW_ARB);
|
|
DebugAssertNoGLError();
|
|
|
|
|
|
if (m_bAnyNeedsTextureMatrixScale)
|
|
{
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, m_nTextureMatrixScale);
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector2),
|
|
&m_vTexMatrixScale[0],
|
|
GL_STATIC_DRAW_ARB);
|
|
DebugAssertNoGLError();
|
|
}
|
|
}
|
|
|
|
void RageCompiledGeometryHWOGL::Invalidate()
|
|
{
|
|
/* Our vertex buffers no longer exist. Reallocate and reupload. */
|
|
m_nPositions = 0;
|
|
m_nTextureCoords = 0;
|
|
m_nNormals = 0;
|
|
m_nTriangles = 0;
|
|
m_nTextureMatrixScale = 0;
|
|
AllocateBuffers();
|
|
UploadData();
|
|
}
|
|
|
|
void RageCompiledGeometryHWOGL::Allocate( const std::vector<msMesh> &vMeshes )
|
|
{
|
|
DebugFlushGLErrors();
|
|
|
|
RageCompiledGeometrySWOGL::Allocate( vMeshes );
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nPositions );
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector3),
|
|
nullptr,
|
|
GL_STATIC_DRAW_ARB );
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureCoords );
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector2),
|
|
nullptr,
|
|
GL_STATIC_DRAW_ARB );
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nNormals );
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector3),
|
|
nullptr,
|
|
GL_STATIC_DRAW_ARB );
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nTriangles );
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ELEMENT_ARRAY_BUFFER_ARB,
|
|
GetTotalTriangles()*sizeof(msTriangle),
|
|
nullptr,
|
|
GL_STATIC_DRAW_ARB );
|
|
DebugAssertNoGLError();
|
|
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureMatrixScale );
|
|
DebugAssertNoGLError();
|
|
glBufferDataARB(
|
|
GL_ARRAY_BUFFER_ARB,
|
|
GetTotalVertices()*sizeof(RageVector2),
|
|
nullptr,
|
|
GL_STATIC_DRAW_ARB );
|
|
}
|
|
|
|
void RageCompiledGeometryHWOGL::Change( const std::vector<msMesh> &vMeshes )
|
|
{
|
|
RageCompiledGeometrySWOGL::Change( vMeshes );
|
|
|
|
UploadData();
|
|
}
|
|
|
|
void RageCompiledGeometryHWOGL::Draw( int iMeshIndex ) const
|
|
{
|
|
DebugFlushGLErrors();
|
|
|
|
const MeshInfo& meshInfo = m_vMeshInfo[iMeshIndex];
|
|
if (!meshInfo.iVertexCount || !meshInfo.iTriangleCount)
|
|
return;
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
DebugAssertNoGLError();
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nPositions );
|
|
DebugAssertNoGLError();
|
|
glVertexPointer(3, GL_FLOAT, 0, nullptr );
|
|
DebugAssertNoGLError();
|
|
|
|
glDisableClientState(GL_COLOR_ARRAY);
|
|
DebugAssertNoGLError();
|
|
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
DebugAssertNoGLError();
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureCoords );
|
|
DebugAssertNoGLError();
|
|
glTexCoordPointer(2, GL_FLOAT, 0, nullptr);
|
|
DebugAssertNoGLError();
|
|
|
|
// TRICKY: Don't bind and send normals if lighting is disabled. This
|
|
// will save some effort transforming these values.
|
|
// XXX: We should keep track of these ourself and avoid glGet*()
|
|
GLboolean bLighting;
|
|
glGetBooleanv( GL_LIGHTING, &bLighting );
|
|
GLboolean bTextureGenS;
|
|
glGetBooleanv( GL_TEXTURE_GEN_S, &bTextureGenS );
|
|
GLboolean bTextureGenT;
|
|
glGetBooleanv( GL_TEXTURE_GEN_T, &bTextureGenT );
|
|
|
|
if (bLighting || bTextureGenS || bTextureGenT)
|
|
{
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
DebugAssertNoGLError();
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nNormals );
|
|
DebugAssertNoGLError();
|
|
glNormalPointer(GL_FLOAT, 0, nullptr);
|
|
DebugAssertNoGLError();
|
|
}
|
|
else
|
|
{
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
if (meshInfo.m_bNeedsTextureMatrixScale)
|
|
{
|
|
if (g_bTextureMatrixShader != 0)
|
|
{
|
|
/* If we're using texture matrix scales, set up that buffer, too, and enable the
|
|
* vertex shader. This shader doesn't support all OpenGL state, so only enable it
|
|
* if we're using it. */
|
|
glEnableVertexAttribArrayARB( g_iAttribTextureMatrixScale );
|
|
DebugAssertNoGLError();
|
|
glBindBufferARB( GL_ARRAY_BUFFER_ARB, m_nTextureMatrixScale );
|
|
DebugAssertNoGLError();
|
|
glVertexAttribPointerARB( g_iAttribTextureMatrixScale, 2, GL_FLOAT, false, 0, nullptr );
|
|
DebugAssertNoGLError();
|
|
|
|
glUseProgramObjectARB( g_bTextureMatrixShader );
|
|
DebugAssertNoGLError();
|
|
}
|
|
else
|
|
{
|
|
// Kill the texture translation.
|
|
// XXX: Change me to scale the translation by the TextureTranslationScale of the first vertex.
|
|
RageMatrix mat;
|
|
glGetFloatv( GL_TEXTURE_MATRIX , (float*)mat );
|
|
|
|
/*
|
|
for( int i=0; i<4; i++ )
|
|
{
|
|
RString s;
|
|
for( int j=0; j<4; j++ )
|
|
s += ssprintf( "%f ", mat.m[i][j] );
|
|
LOG->Trace( s );
|
|
}
|
|
*/
|
|
|
|
mat.m[3][0] = 0;
|
|
mat.m[3][1] = 0;
|
|
mat.m[3][2] = 0;
|
|
|
|
glMatrixMode( GL_TEXTURE );
|
|
glLoadMatrixf( (const float*)mat );
|
|
DebugAssertNoGLError();
|
|
}
|
|
}
|
|
|
|
glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, m_nTriangles );
|
|
DebugAssertNoGLError();
|
|
|
|
#define BUFFER_OFFSET(o) ((char*)(o))
|
|
|
|
ASSERT( glDrawRangeElements != nullptr );
|
|
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)) );
|
|
DebugAssertNoGLError();
|
|
|
|
if (meshInfo.m_bNeedsTextureMatrixScale && g_bTextureMatrixShader != 0)
|
|
{
|
|
glDisableVertexAttribArrayARB( g_iAttribTextureMatrixScale );
|
|
glUseProgramObjectARB( 0 );
|
|
}
|
|
}
|
|
|
|
RageCompiledGeometry* RageDisplay_Legacy::CreateCompiledGeometry()
|
|
{
|
|
if (GLEW_ARB_vertex_buffer_object)
|
|
return new RageCompiledGeometryHWOGL;
|
|
else
|
|
return new RageCompiledGeometrySWOGL;
|
|
}
|
|
|
|
void RageDisplay_Legacy::DeleteCompiledGeometry( RageCompiledGeometry* p )
|
|
{
|
|
delete p;
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawQuadsInternal( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawArrays( GL_QUADS, 0, iNumVerts );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawQuadStripInternal( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawArrays( GL_QUAD_STRIP, 0, iNumVerts );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawSymmetricQuadStripInternal( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
int iNumPieces = (iNumVerts-3)/3;
|
|
int iNumTriangles = iNumPieces*4;
|
|
int iNumIndices = iNumTriangles*3;
|
|
|
|
// make a temporary index buffer
|
|
static std::vector<uint16_t> vIndices;
|
|
unsigned uOldSize = vIndices.size();
|
|
unsigned uNewSize = std::max(uOldSize,(unsigned)iNumIndices);
|
|
vIndices.resize( uNewSize );
|
|
for( uint16_t i=(uint16_t)uOldSize/12; i<(uint16_t)iNumPieces; i++ )
|
|
{
|
|
// { 1, 3, 0 } { 1, 4, 3 } { 1, 5, 4 } { 1, 2, 5 }
|
|
vIndices[i*12+0] = i*3+1;
|
|
vIndices[i*12+1] = i*3+3;
|
|
vIndices[i*12+2] = i*3+0;
|
|
vIndices[i*12+3] = i*3+1;
|
|
vIndices[i*12+4] = i*3+4;
|
|
vIndices[i*12+5] = i*3+3;
|
|
vIndices[i*12+6] = i*3+1;
|
|
vIndices[i*12+7] = i*3+5;
|
|
vIndices[i*12+8] = i*3+4;
|
|
vIndices[i*12+9] = i*3+1;
|
|
vIndices[i*12+10] = i*3+2;
|
|
vIndices[i*12+11] = i*3+5;
|
|
}
|
|
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawElements(
|
|
GL_TRIANGLES,
|
|
iNumIndices,
|
|
GL_UNSIGNED_SHORT,
|
|
&vIndices[0] );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawFanInternal( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawArrays( GL_TRIANGLE_FAN, 0, iNumVerts );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawStripInternal( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawArrays( GL_TRIANGLE_STRIP, 0, iNumVerts );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawTrianglesInternal( const RageSpriteVertex v[], int iNumVerts )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawArrays( GL_TRIANGLES, 0, iNumVerts );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawCompiledGeometryInternal( const RageCompiledGeometry *p, int iMeshIndex )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
SendCurrentMatrices();
|
|
|
|
p->Draw( iMeshIndex );
|
|
}
|
|
|
|
void RageDisplay_Legacy::DrawLineStripInternal( const RageSpriteVertex v[], int iNumVerts, float fLineWidth )
|
|
{
|
|
TurnOffHardwareVBO();
|
|
|
|
if (!GetActualVideoModeParams().bSmoothLines)
|
|
{
|
|
/* Fall back on the generic polygon-based line strip. */
|
|
RageDisplay::DrawLineStripInternal(v, iNumVerts, fLineWidth );
|
|
return;
|
|
}
|
|
|
|
SendCurrentMatrices();
|
|
|
|
/* 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 );
|
|
|
|
/* fLineWidth is in units relative to object space, but OpenGL line and point sizes
|
|
* are in raster units (actual pixels). Scale the line width by the average ratio;
|
|
* if object space is 640x480, and we have a 1280x960 window, we'll double the
|
|
* width. */
|
|
{
|
|
const RageMatrix* pMat = GetProjectionTop();
|
|
float fW = 2 / pMat->m[0][0];
|
|
float fH = -2 / pMat->m[1][1];
|
|
float fWidthVal = float(g_pWind->GetActualVideoModeParams().width) / fW;
|
|
float fHeightVal = float(g_pWind->GetActualVideoModeParams().height) / fH;
|
|
fLineWidth *= (fWidthVal + fHeightVal) / 2;
|
|
}
|
|
|
|
/* Clamp the width to the hardware max for both lines and points (whichever
|
|
* is more restrictive). */
|
|
fLineWidth = clamp( fLineWidth, g_line_range[0], g_line_range[1] );
|
|
fLineWidth = clamp( fLineWidth, 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( fLineWidth );
|
|
|
|
/* Draw the line loop: */
|
|
SetupVertices( v, iNumVerts );
|
|
glDrawArrays( GL_LINE_STRIP, 0, iNumVerts );
|
|
StatsAddVerts(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( fLineWidth );
|
|
|
|
/* 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 );
|
|
StatsAddVerts(iNumVerts);
|
|
|
|
glDisable( GL_POINT_SMOOTH );
|
|
}
|
|
|
|
static bool SetTextureUnit( TextureUnit tu )
|
|
{
|
|
// If multitexture isn't supported, ignore all textures except for 0.
|
|
if (!GLEW_ARB_multitexture && tu != TextureUnit_1)
|
|
return false;
|
|
|
|
if ((int) tu > g_iMaxTextureUnits)
|
|
return false;
|
|
glActiveTextureARB( enum_add2(GL_TEXTURE0_ARB, tu) );
|
|
return true;
|
|
}
|
|
|
|
void RageDisplay_Legacy::ClearAllTextures()
|
|
{
|
|
FOREACH_ENUM( TextureUnit, i )
|
|
SetTexture( i, 0 );
|
|
|
|
// HACK: Reset the active texture to 0.
|
|
// TODO: Change all texture functions to take a stage number.
|
|
if (GLEW_ARB_multitexture)
|
|
glActiveTextureARB(GL_TEXTURE0_ARB);
|
|
}
|
|
|
|
int RageDisplay_Legacy::GetNumTextureUnits()
|
|
{
|
|
if (GLEW_ARB_multitexture)
|
|
return 1;
|
|
else
|
|
return g_iMaxTextureUnits;
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetTexture( TextureUnit tu, uintptr_t iTexture )
|
|
{
|
|
if (!SetTextureUnit( tu ))
|
|
return;
|
|
|
|
if (iTexture)
|
|
{
|
|
glEnable( GL_TEXTURE_2D );
|
|
glBindTexture( GL_TEXTURE_2D, static_cast<GLuint>(iTexture) );
|
|
}
|
|
else
|
|
{
|
|
glDisable( GL_TEXTURE_2D );
|
|
}
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetTextureMode( TextureUnit tu, TextureMode tm )
|
|
{
|
|
if (!SetTextureUnit( tu ))
|
|
return;
|
|
|
|
switch( tm )
|
|
{
|
|
case TextureMode_Modulate:
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
|
|
break;
|
|
case TextureMode_Add:
|
|
glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD );
|
|
break;
|
|
case TextureMode_Glow:
|
|
// the below function is glowmode,brighten:
|
|
if (!GLEW_ARB_texture_env_combine && !GLEW_EXT_texture_env_combine)
|
|
{
|
|
/* This is changing blend state, instead of texture state, which
|
|
* isn't great, but it's better than doing nothing. */
|
|
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
|
|
return;
|
|
}
|
|
|
|
// and this is glowmode,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);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetTextureFiltering( TextureUnit tu, bool b )
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, b ? GL_LINEAR : GL_NEAREST);
|
|
|
|
GLint iMinFilter;
|
|
if (b)
|
|
{
|
|
GLint iWidth1 = -1;
|
|
GLint iWidth2 = -1;
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &iWidth1);
|
|
glGetTexLevelParameteriv(GL_TEXTURE_2D, 1, GL_TEXTURE_WIDTH, &iWidth2);
|
|
if (iWidth1 > 1 && iWidth2 != 0)
|
|
{
|
|
/* Mipmaps are enabled. */
|
|
if (g_pWind->GetActualVideoModeParams().bTrilinearFiltering)
|
|
iMinFilter = GL_LINEAR_MIPMAP_LINEAR;
|
|
else
|
|
iMinFilter = GL_LINEAR_MIPMAP_NEAREST;
|
|
}
|
|
else
|
|
{
|
|
iMinFilter = GL_LINEAR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
iMinFilter = GL_NEAREST;
|
|
}
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, iMinFilter );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetEffectMode( EffectMode effect )
|
|
{
|
|
if (!GLEW_ARB_fragment_program || !GLEW_ARB_shading_language_100 || !GLEW_ARB_shader_objects)
|
|
return;
|
|
|
|
GLhandleARB hShader = 0;
|
|
switch (effect)
|
|
{
|
|
case EffectMode_Normal:
|
|
hShader = 0;
|
|
break;
|
|
case EffectMode_Unpremultiply:
|
|
hShader = g_bUnpremultiplyShader;
|
|
break;
|
|
case EffectMode_ColorBurn:
|
|
hShader = g_bColorBurnShader;
|
|
break;
|
|
case EffectMode_ColorDodge:
|
|
hShader = g_bColorDodgeShader;
|
|
break;
|
|
case EffectMode_VividLight:
|
|
hShader = g_bVividLightShader;
|
|
break;
|
|
case EffectMode_HardMix:
|
|
hShader = g_hHardMixShader;
|
|
break;
|
|
case EffectMode_Overlay:
|
|
hShader = g_hOverlayShader;
|
|
break;
|
|
case EffectMode_Screen:
|
|
hShader = g_hScreenShader;
|
|
break;
|
|
case EffectMode_YUYV422:
|
|
hShader = g_hYUYV422Shader;
|
|
break;
|
|
case EffectMode_DistanceField:
|
|
hShader = g_gDistanceFieldShader;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
DebugFlushGLErrors();
|
|
glUseProgramObjectARB( hShader );
|
|
if (hShader == 0)
|
|
return;
|
|
GLint iTexture1 = glGetUniformLocationARB( hShader, "Texture1" );
|
|
GLint iTexture2 = glGetUniformLocationARB( hShader, "Texture2" );
|
|
glUniform1iARB( iTexture1, 0 );
|
|
glUniform1iARB( iTexture2, 1 );
|
|
|
|
if (effect == EffectMode_YUYV422)
|
|
{
|
|
GLint iTextureWidthUniform = glGetUniformLocationARB( hShader, "TextureWidth" );
|
|
GLint iWidth;
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &iWidth );
|
|
glUniform1iARB( iTextureWidthUniform, iWidth );
|
|
}
|
|
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
bool RageDisplay_Legacy::IsEffectModeSupported( EffectMode effect )
|
|
{
|
|
switch( effect )
|
|
{
|
|
case EffectMode_Normal:
|
|
return true;
|
|
case EffectMode_Unpremultiply:
|
|
return g_bUnpremultiplyShader != 0;
|
|
case EffectMode_ColorBurn:
|
|
return g_bColorBurnShader != 0;
|
|
case EffectMode_ColorDodge:
|
|
return g_bColorDodgeShader != 0;
|
|
case EffectMode_VividLight:
|
|
return g_bVividLightShader != 0;
|
|
case EffectMode_HardMix:
|
|
return g_hHardMixShader != 0;
|
|
case EffectMode_Overlay:
|
|
return g_hOverlayShader != 0;
|
|
case EffectMode_Screen:
|
|
return g_hScreenShader != 0;
|
|
case EffectMode_YUYV422:
|
|
return g_hYUYV422Shader != 0;
|
|
case EffectMode_DistanceField:
|
|
return g_gDistanceFieldShader != 0;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetBlendMode( BlendMode mode )
|
|
{
|
|
glEnable(GL_BLEND);
|
|
|
|
if (glBlendEquation != nullptr)
|
|
{
|
|
if (mode == BLEND_INVERT_DEST)
|
|
glBlendEquation( GL_FUNC_SUBTRACT );
|
|
else if (mode == BLEND_SUBTRACT)
|
|
glBlendEquation( GL_FUNC_REVERSE_SUBTRACT );
|
|
else
|
|
glBlendEquation( GL_FUNC_ADD );
|
|
}
|
|
|
|
int iSourceRGB, iDestRGB;
|
|
int iSourceAlpha = GL_ONE, iDestAlpha = GL_ONE_MINUS_SRC_ALPHA;
|
|
switch( mode )
|
|
{
|
|
case BLEND_NORMAL:
|
|
iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ONE_MINUS_SRC_ALPHA;
|
|
break;
|
|
case BLEND_ADD:
|
|
iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ONE;
|
|
break;
|
|
case BLEND_SUBTRACT:
|
|
iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ONE_MINUS_SRC_ALPHA;
|
|
break;
|
|
case BLEND_MODULATE:
|
|
iSourceRGB = GL_ZERO; iDestRGB = GL_SRC_COLOR;
|
|
break;
|
|
case BLEND_COPY_SRC:
|
|
iSourceRGB = GL_ONE; iDestRGB = GL_ZERO;
|
|
iSourceAlpha = GL_ONE; iDestAlpha = GL_ZERO;
|
|
break;
|
|
case BLEND_ALPHA_MASK:
|
|
iSourceRGB = GL_ZERO; iDestRGB = GL_ONE;
|
|
iSourceAlpha = GL_ZERO; iDestAlpha = GL_SRC_ALPHA;
|
|
break;
|
|
case BLEND_ALPHA_KNOCK_OUT:
|
|
iSourceRGB = GL_ZERO; iDestRGB = GL_ONE;
|
|
iSourceAlpha = GL_ZERO; iDestAlpha = GL_ONE_MINUS_SRC_ALPHA;
|
|
break;
|
|
case BLEND_ALPHA_MULTIPLY:
|
|
iSourceRGB = GL_SRC_ALPHA; iDestRGB = GL_ZERO;
|
|
break;
|
|
case BLEND_WEIGHTED_MULTIPLY:
|
|
/* output = 2*(dst*src). 0.5,0.5,0.5 is identity; darker colors darken the image,
|
|
* and brighter colors lighten the image. */
|
|
iSourceRGB = GL_DST_COLOR; iDestRGB = GL_SRC_COLOR;
|
|
break;
|
|
case BLEND_INVERT_DEST:
|
|
/* out = src - dst. The source color should almost always be #FFFFFF, to make it "1 - dst". */
|
|
iSourceRGB = GL_ONE; iDestRGB = GL_ONE;
|
|
break;
|
|
case BLEND_NO_EFFECT:
|
|
iSourceRGB = GL_ZERO; iDestRGB = GL_ONE;
|
|
iSourceAlpha = GL_ZERO; iDestAlpha = GL_ONE;
|
|
break;
|
|
DEFAULT_FAIL( mode );
|
|
}
|
|
|
|
if (GLEW_EXT_blend_equation_separate)
|
|
glBlendFuncSeparateEXT( iSourceRGB, iDestRGB, iSourceAlpha, iDestAlpha );
|
|
else
|
|
glBlendFunc( iSourceRGB, iDestRGB );
|
|
}
|
|
|
|
bool RageDisplay_Legacy::IsZWriteEnabled() const
|
|
{
|
|
bool a;
|
|
glGetBooleanv( GL_DEPTH_WRITEMASK, (unsigned char*)&a );
|
|
return a;
|
|
}
|
|
|
|
bool RageDisplay_Legacy::IsZTestEnabled() const
|
|
{
|
|
GLenum a;
|
|
glGetIntegerv( GL_DEPTH_FUNC, (GLint*)&a );
|
|
return a != GL_ALWAYS;
|
|
}
|
|
|
|
void RageDisplay_Legacy::ClearZBuffer()
|
|
{
|
|
bool write = IsZWriteEnabled();
|
|
SetZWrite( true );
|
|
glClear( GL_DEPTH_BUFFER_BIT );
|
|
SetZWrite( write );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetZWrite( bool b )
|
|
{
|
|
glDepthMask( b );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetZBias( float f )
|
|
{
|
|
float fNear = SCALE( f, 0.0f, 1.0f, 0.05f, 0.0f );
|
|
float fFar = SCALE( f, 0.0f, 1.0f, 1.0f, 0.95f );
|
|
|
|
glDepthRange( fNear, fFar );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetZTestMode( ZTestMode mode )
|
|
{
|
|
glEnable( GL_DEPTH_TEST );
|
|
switch( mode )
|
|
{
|
|
case ZTEST_OFF: glDepthFunc( GL_ALWAYS ); break;
|
|
case ZTEST_WRITE_ON_PASS: glDepthFunc( GL_LEQUAL ); break;
|
|
case ZTEST_WRITE_ON_FAIL: glDepthFunc( GL_GREATER ); break;
|
|
default:
|
|
FAIL_M(ssprintf("Invalid ZTestMode: %i", mode));
|
|
}
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetTextureWrapping( TextureUnit tu, bool b )
|
|
{
|
|
/* This should be per-texture-unit state, but it's per-texture state in OpenGl,
|
|
* so we'll behave incorrectly if the same texture is used in more than one texture
|
|
* unit simultaneously with different wrapping. */
|
|
SetTextureUnit( tu );
|
|
|
|
GLenum mode = b ? GL_REPEAT : GL_CLAMP_TO_EDGE;
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetMaterial(
|
|
const RageColor &emissive,
|
|
const RageColor &ambient,
|
|
const RageColor &diffuse,
|
|
const RageColor &specular,
|
|
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.
|
|
// XXX: unintended: SetLighting must be called before SetMaterial
|
|
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
|
|
{
|
|
RageColor c = diffuse;
|
|
c.r += emissive.r + ambient.r;
|
|
c.g += emissive.g + ambient.g;
|
|
c.b += emissive.b + ambient.b;
|
|
glColor4fv( c );
|
|
}
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetLighting( bool b )
|
|
{
|
|
if (b)
|
|
glEnable(GL_LIGHTING);
|
|
else
|
|
glDisable(GL_LIGHTING);
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetLightOff( int index )
|
|
{
|
|
glDisable( GL_LIGHT0+index );
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetLightDirectional(
|
|
int index,
|
|
const RageColor &ambient,
|
|
const RageColor &diffuse,
|
|
const RageColor &specular,
|
|
const RageVector3 &dir )
|
|
{
|
|
// Light coordinates are transformed by the modelview matrix, but
|
|
// we are being passed in world-space coords.
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glEnable( GL_LIGHT0+index );
|
|
glLightfv( GL_LIGHT0+index, GL_AMBIENT, ambient );
|
|
glLightfv( GL_LIGHT0+index, GL_DIFFUSE, diffuse );
|
|
glLightfv( GL_LIGHT0+index, GL_SPECULAR, specular );
|
|
float position[4] = {dir.x, dir.y, dir.z, 0};
|
|
glLightfv( GL_LIGHT0+index, GL_POSITION, position );
|
|
|
|
glPopMatrix();
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetCullMode( CullMode mode )
|
|
{
|
|
if (mode != CULL_NONE)
|
|
glEnable(GL_CULL_FACE);
|
|
switch( mode )
|
|
{
|
|
case CULL_BACK:
|
|
glCullFace( GL_BACK );
|
|
break;
|
|
case CULL_FRONT:
|
|
glCullFace( GL_FRONT );
|
|
break;
|
|
case CULL_NONE:
|
|
glDisable( GL_CULL_FACE );
|
|
break;
|
|
default:
|
|
FAIL_M(ssprintf("Invalid CullMode: %i", mode));
|
|
}
|
|
}
|
|
|
|
const RageDisplay::RagePixelFormatDesc *RageDisplay_Legacy::GetPixelFormatDesc(RagePixelFormat pf) const
|
|
{
|
|
ASSERT( pf < NUM_RagePixelFormat );
|
|
return &PIXEL_FORMAT_DESC[pf];
|
|
}
|
|
|
|
bool RageDisplay_Legacy::SupportsThreadedRendering()
|
|
{
|
|
return g_pWind->SupportsThreadedRendering();
|
|
}
|
|
|
|
void RageDisplay_Legacy::BeginConcurrentRenderingMainThread()
|
|
{
|
|
g_pWind->BeginConcurrentRenderingMainThread();
|
|
}
|
|
|
|
void RageDisplay_Legacy::EndConcurrentRenderingMainThread()
|
|
{
|
|
g_pWind->EndConcurrentRenderingMainThread();
|
|
}
|
|
|
|
void RageDisplay_Legacy::BeginConcurrentRendering()
|
|
{
|
|
g_pWind->BeginConcurrentRendering();
|
|
RageDisplay::BeginConcurrentRendering();
|
|
}
|
|
|
|
void RageDisplay_Legacy::EndConcurrentRendering()
|
|
{
|
|
g_pWind->EndConcurrentRendering();
|
|
}
|
|
|
|
void RageDisplay_Legacy::DeleteTexture( uintptr_t iTexture )
|
|
{
|
|
if (iTexture == 0)
|
|
return;
|
|
|
|
if (g_mapRenderTargets.find(iTexture) != g_mapRenderTargets.end())
|
|
{
|
|
delete g_mapRenderTargets[iTexture];
|
|
g_mapRenderTargets.erase( iTexture );
|
|
return;
|
|
}
|
|
|
|
DebugFlushGLErrors();
|
|
glDeleteTextures( 1, reinterpret_cast<GLuint*>(&iTexture) );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
|
|
RagePixelFormat RageDisplay_Legacy::GetImgPixelFormat( RageSurface* &img, bool &bFreeImg, int width, int height, bool bPalettedTexture )
|
|
{
|
|
RagePixelFormat pixfmt = FindPixelFormat( img->format->BitsPerPixel, img->format->Rmask, img->format->Gmask, img->format->Bmask, img->format->Amask );
|
|
|
|
/* If img is paletted, we're setting up a non-paletted texture, and color indexes
|
|
* are too small, depalettize. */
|
|
bool bSupported = true;
|
|
if (!bPalettedTexture && img->fmt.BytesPerPixel == 1 && !g_bColorIndexTableWorks)
|
|
bSupported = false;
|
|
|
|
if (pixfmt == RagePixelFormat_Invalid || !SupportsSurfaceFormat(pixfmt))
|
|
bSupported = false;
|
|
|
|
if (!bSupported)
|
|
{
|
|
/* 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 = RagePixelFormat_RGBA8;
|
|
ASSERT( SupportsSurfaceFormat(pixfmt) );
|
|
|
|
const RagePixelFormatDesc *pfd = DISPLAY->GetPixelFormatDesc(pixfmt);
|
|
|
|
RageSurface *imgconv = CreateSurface( img->w, img->h,
|
|
pfd->bpp, pfd->masks[0], pfd->masks[1], pfd->masks[2], pfd->masks[3] );
|
|
RageSurfaceUtils::Blit( img, imgconv, width, height );
|
|
img = imgconv;
|
|
bFreeImg = true;
|
|
}
|
|
else
|
|
{
|
|
bFreeImg = false;
|
|
}
|
|
|
|
return pixfmt;
|
|
}
|
|
|
|
/* If we're sending a paletted surface to a non-paletted texture, set the palette. */
|
|
void SetPixelMapForSurface( int glImageFormat, int glTexFormat, const RageSurfacePalette *palette )
|
|
{
|
|
if (glImageFormat != GL_COLOR_INDEX || glTexFormat == GL_COLOR_INDEX8_EXT)
|
|
{
|
|
glPixelTransferi( GL_MAP_COLOR, false );
|
|
return;
|
|
}
|
|
|
|
GLushort buf[4][256];
|
|
memset( buf, 0, sizeof(buf) );
|
|
|
|
for( int i = 0; i < palette->ncolors; ++i )
|
|
{
|
|
buf[0][i] = SCALE( palette->colors[i].r, 0, 255, 0, 65535 );
|
|
buf[1][i] = SCALE( palette->colors[i].g, 0, 255, 0, 65535 );
|
|
buf[2][i] = SCALE( palette->colors[i].b, 0, 255, 0, 65535 );
|
|
buf[3][i] = SCALE( palette->colors[i].a, 0, 255, 0, 65535 );
|
|
}
|
|
|
|
DebugFlushGLErrors();
|
|
glPixelMapusv( GL_PIXEL_MAP_I_TO_R, 256, buf[0] );
|
|
glPixelMapusv( GL_PIXEL_MAP_I_TO_G, 256, buf[1] );
|
|
glPixelMapusv( GL_PIXEL_MAP_I_TO_B, 256, buf[2] );
|
|
glPixelMapusv( GL_PIXEL_MAP_I_TO_A, 256, buf[3] );
|
|
glPixelTransferi( GL_MAP_COLOR, true );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
uintptr_t RageDisplay_Legacy::CreateTexture(
|
|
RagePixelFormat pixfmt,
|
|
RageSurface* pImg,
|
|
bool bGenerateMipMaps )
|
|
{
|
|
ASSERT( pixfmt < NUM_RagePixelFormat );
|
|
|
|
|
|
/* Find the pixel format of the surface we've been given. */
|
|
bool bFreeImg;
|
|
RagePixelFormat SurfacePixFmt = GetImgPixelFormat( pImg, bFreeImg, pImg->w, pImg->h, pixfmt == RagePixelFormat_PAL );
|
|
ASSERT( SurfacePixFmt != RagePixelFormat_Invalid );
|
|
|
|
GLenum glTexFormat = g_GLPixFmtInfo[pixfmt].internalfmt;
|
|
GLenum glImageFormat = g_GLPixFmtInfo[SurfacePixFmt].format;
|
|
GLenum glImageType = g_GLPixFmtInfo[SurfacePixFmt].type;
|
|
|
|
/* If the image is paletted, but we're not sending it to a paletted image,
|
|
* set up glPixelMap. */
|
|
SetPixelMapForSurface( glImageFormat, glTexFormat, pImg->format->palette.get() );
|
|
|
|
// 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
|
|
if (bGenerateMipMaps && g_gluVersion < 13)
|
|
{
|
|
switch( pixfmt )
|
|
{
|
|
// OpenGL 1.1 types
|
|
case RagePixelFormat_RGBA8:
|
|
case RagePixelFormat_RGB8:
|
|
case RagePixelFormat_PAL:
|
|
case RagePixelFormat_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;
|
|
}
|
|
}
|
|
|
|
SetTextureUnit( TextureUnit_1 );
|
|
|
|
// allocate OpenGL texture resource
|
|
uintptr_t iTexHandle;
|
|
glGenTextures( 1, reinterpret_cast<GLuint*>(&iTexHandle) );
|
|
ASSERT( iTexHandle != 0 );
|
|
|
|
glBindTexture( GL_TEXTURE_2D, static_cast<GLuint>(iTexHandle) );
|
|
|
|
if (g_pWind->GetActualVideoModeParams().bAnisotropicFiltering &&
|
|
GLEW_EXT_texture_filter_anisotropic )
|
|
{
|
|
GLfloat fLargestSupportedAnisotropy;
|
|
glGetFloatv( GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargestSupportedAnisotropy );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargestSupportedAnisotropy );
|
|
}
|
|
|
|
SetTextureFiltering( TextureUnit_1, true );
|
|
SetTextureWrapping( TextureUnit_1, false );
|
|
|
|
glPixelStorei( GL_UNPACK_ROW_LENGTH, pImg->pitch / pImg->format->BytesPerPixel );
|
|
|
|
|
|
if (pixfmt == RagePixelFormat_PAL)
|
|
{
|
|
/* The texture is paletted; set the texture palette. */
|
|
GLubyte palette[256*4];
|
|
memset( palette, 0, sizeof(palette) );
|
|
int p = 0;
|
|
/* Copy the palette to the format OpenGL expects. */
|
|
for( int i = 0; i < pImg->format->palette->ncolors; ++i )
|
|
{
|
|
palette[p++] = pImg->format->palette->colors[i].r;
|
|
palette[p++] = pImg->format->palette->colors[i].g;
|
|
palette[p++] = pImg->format->palette->colors[i].b;
|
|
palette[p++] = pImg->format->palette->colors[i].a;
|
|
}
|
|
|
|
/* Set the palette. */
|
|
glColorTableEXT( GL_TEXTURE_2D, GL_RGBA8, 256, GL_RGBA, GL_UNSIGNED_BYTE, palette );
|
|
|
|
GLint iRealFormat = 0;
|
|
glGetColorTableParameterivEXT( GL_TEXTURE_2D, GL_COLOR_TABLE_FORMAT, &iRealFormat );
|
|
ASSERT( iRealFormat == GL_RGBA8 );
|
|
}
|
|
|
|
LOG->Trace( "%s (format %s, %ix%i, format %s, type %s, pixfmt %i, imgpixfmt %i)",
|
|
bGenerateMipMaps? "gluBuild2DMipmaps":"glTexImage2D",
|
|
GLToString(glTexFormat).c_str(),
|
|
pImg->w, pImg->h,
|
|
GLToString(glImageFormat).c_str(),
|
|
GLToString(glImageType).c_str(), pixfmt, SurfacePixFmt );
|
|
|
|
DebugFlushGLErrors();
|
|
|
|
if (bGenerateMipMaps)
|
|
{
|
|
GLenum error = gluBuild2DMipmaps(
|
|
GL_TEXTURE_2D, glTexFormat,
|
|
pImg->w, pImg->h,
|
|
glImageFormat, glImageType, pImg->pixels );
|
|
ASSERT_M( error == 0, (char *) gluErrorString(error) );
|
|
}
|
|
else
|
|
{
|
|
glTexImage2D(
|
|
GL_TEXTURE_2D, 0, glTexFormat,
|
|
power_of_two(pImg->w), power_of_two(pImg->h), 0,
|
|
glImageFormat, glImageType, nullptr );
|
|
if (pImg->pixels)
|
|
glTexSubImage2D( GL_TEXTURE_2D, 0,
|
|
0, 0,
|
|
pImg->w, pImg->h,
|
|
glImageFormat, glImageType, pImg->pixels );
|
|
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
|
|
/* Sanity check: */
|
|
if (pixfmt == RagePixelFormat_PAL)
|
|
{
|
|
GLint iSize = 0;
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INDEX_SIZE_EXT), &iSize );
|
|
if (iSize != 8)
|
|
RageException::Throw( "Thought paletted textures worked, but they don't." );
|
|
}
|
|
|
|
glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
|
|
glFlush();
|
|
|
|
if (bFreeImg)
|
|
delete pImg;
|
|
return iTexHandle;
|
|
}
|
|
|
|
struct RageTextureLock_OGL: public RageTextureLock, public InvalidateObject
|
|
{
|
|
public:
|
|
RageTextureLock_OGL()
|
|
{
|
|
m_iTexHandle = 0;
|
|
m_iBuffer = 0;
|
|
|
|
CreateObject();
|
|
}
|
|
|
|
~RageTextureLock_OGL()
|
|
{
|
|
ASSERT( m_iTexHandle == 0 ); // locked!
|
|
glDeleteBuffersARB( 1, &m_iBuffer );
|
|
}
|
|
|
|
/* This is called when our OpenGL context is invalidated. */
|
|
void Invalidate()
|
|
{
|
|
m_iTexHandle = 0;
|
|
}
|
|
|
|
void Lock( uintptr_t iTexHandle, RageSurface *pSurface )
|
|
{
|
|
ASSERT( m_iTexHandle == 0 );
|
|
ASSERT( pSurface->pixels == nullptr );
|
|
|
|
CreateObject();
|
|
|
|
m_iTexHandle = iTexHandle;
|
|
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, m_iBuffer );
|
|
|
|
int iSize = pSurface->h * pSurface->pitch;
|
|
glBufferDataARB( GL_PIXEL_UNPACK_BUFFER_ARB, iSize, nullptr, GL_STREAM_DRAW );
|
|
|
|
void *pSurfaceMemory = glMapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY );
|
|
pSurface->pixels = (uint8_t *) pSurfaceMemory;
|
|
pSurface->pixels_owned = false;
|
|
}
|
|
|
|
void Unlock( RageSurface *pSurface, bool bChanged )
|
|
{
|
|
glUnmapBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB );
|
|
|
|
pSurface->pixels = (uint8_t *) BUFFER_OFFSET(0);
|
|
|
|
if (bChanged)
|
|
DISPLAY->UpdateTexture( m_iTexHandle, pSurface, 0, 0, pSurface->w, pSurface->h );
|
|
|
|
pSurface->pixels = nullptr;
|
|
|
|
m_iTexHandle = 0;
|
|
glBindBufferARB( GL_PIXEL_UNPACK_BUFFER_ARB, 0 );
|
|
}
|
|
|
|
private:
|
|
void CreateObject()
|
|
{
|
|
if (m_iBuffer != 0)
|
|
return;
|
|
|
|
DebugFlushGLErrors();
|
|
glGenBuffersARB( 1, &m_iBuffer );
|
|
DebugAssertNoGLError();
|
|
}
|
|
|
|
GLuint m_iBuffer;
|
|
|
|
uintptr_t m_iTexHandle;
|
|
};
|
|
|
|
RageTextureLock *RageDisplay_Legacy::CreateTextureLock()
|
|
{
|
|
if (!GLEW_ARB_pixel_buffer_object)
|
|
return nullptr;
|
|
|
|
return new RageTextureLock_OGL;
|
|
}
|
|
|
|
void RageDisplay_Legacy::UpdateTexture(
|
|
uintptr_t iTexHandle,
|
|
RageSurface* pImg,
|
|
int iXOffset, int iYOffset, int iWidth, int iHeight )
|
|
{
|
|
glBindTexture( GL_TEXTURE_2D, static_cast<GLuint>(iTexHandle) );
|
|
|
|
bool bFreeImg;
|
|
RagePixelFormat SurfacePixFmt = GetImgPixelFormat( pImg, bFreeImg, iWidth, iHeight, false );
|
|
|
|
glPixelStorei( GL_UNPACK_ROW_LENGTH, pImg->pitch / pImg->format->BytesPerPixel );
|
|
|
|
GLenum glImageFormat = g_GLPixFmtInfo[SurfacePixFmt].format;
|
|
GLenum glImageType = g_GLPixFmtInfo[SurfacePixFmt].type;
|
|
|
|
/* If the image is paletted, but we're not sending it to a paletted image,
|
|
* set up glPixelMap. */
|
|
if (pImg->format->palette)
|
|
{
|
|
GLenum glTexFormat = 0;
|
|
glGetTexLevelParameteriv( GL_PROXY_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INTERNAL_FORMAT), (GLint *) &glTexFormat );
|
|
SetPixelMapForSurface( glImageFormat, glTexFormat, pImg->format->palette.get() );
|
|
}
|
|
|
|
glTexSubImage2D( GL_TEXTURE_2D, 0,
|
|
iXOffset, iYOffset,
|
|
iWidth, iHeight,
|
|
glImageFormat, glImageType, pImg->pixels );
|
|
|
|
/* Must unset PixelStore when we're done! */
|
|
glPixelStorei( GL_UNPACK_ROW_LENGTH, 0 );
|
|
glFlush();
|
|
|
|
if (bFreeImg)
|
|
delete pImg;
|
|
}
|
|
|
|
class RenderTarget_FramebufferObject: public RenderTarget
|
|
{
|
|
public:
|
|
RenderTarget_FramebufferObject();
|
|
~RenderTarget_FramebufferObject();
|
|
void Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut );
|
|
uintptr_t GetTexture() const { return m_iTexHandle; }
|
|
void StartRenderingTo();
|
|
void FinishRenderingTo();
|
|
|
|
virtual bool InvertY() const { return true; }
|
|
|
|
private:
|
|
uintptr_t m_iFrameBufferHandle;
|
|
uintptr_t m_iTexHandle;
|
|
uintptr_t m_iDepthBufferHandle;
|
|
};
|
|
|
|
RenderTarget_FramebufferObject::RenderTarget_FramebufferObject()
|
|
{
|
|
m_iFrameBufferHandle = 0;
|
|
m_iTexHandle = 0;
|
|
m_iDepthBufferHandle = 0;
|
|
}
|
|
|
|
RenderTarget_FramebufferObject::~RenderTarget_FramebufferObject()
|
|
{
|
|
if (m_iDepthBufferHandle)
|
|
glDeleteRenderbuffersEXT( 1, reinterpret_cast<GLuint*>(&m_iDepthBufferHandle) );
|
|
if (m_iFrameBufferHandle)
|
|
glDeleteFramebuffersEXT( 1, reinterpret_cast<GLuint*>(&m_iFrameBufferHandle) );
|
|
if (m_iTexHandle)
|
|
glDeleteTextures( 1, reinterpret_cast<GLuint*>(&m_iTexHandle) );
|
|
}
|
|
|
|
void RenderTarget_FramebufferObject::Create( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut )
|
|
{
|
|
m_Param = param;
|
|
|
|
DebugFlushGLErrors();
|
|
|
|
// Allocate OpenGL texture resource
|
|
glGenTextures( 1, reinterpret_cast<GLuint*>(&m_iTexHandle) );
|
|
ASSERT( m_iTexHandle != 0 );
|
|
|
|
int iTextureWidth = power_of_two( param.iWidth );
|
|
int iTextureHeight = power_of_two( param.iHeight );
|
|
|
|
iTextureWidthOut = iTextureWidth;
|
|
iTextureHeightOut = iTextureHeight;
|
|
|
|
glBindTexture( GL_TEXTURE_2D, static_cast<GLuint>(m_iTexHandle) );
|
|
GLenum internalformat;
|
|
GLenum type = param.bWithAlpha? GL_RGBA:GL_RGB;
|
|
if (param.bFloat && GLEW_ARB_texture_float)
|
|
internalformat = param.bWithAlpha? GL_RGBA16F_ARB:GL_RGB16F_ARB;
|
|
else
|
|
internalformat = param.bWithAlpha? GL_RGBA8:GL_RGB8;
|
|
|
|
glTexImage2D( GL_TEXTURE_2D, 0, internalformat,
|
|
iTextureWidth, iTextureHeight, 0, type, GL_UNSIGNED_BYTE, nullptr );
|
|
DebugAssertNoGLError();
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
|
|
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
|
|
/* Create the framebuffer object. */
|
|
glGenFramebuffersEXT( 1, reinterpret_cast<GLuint*>(&m_iFrameBufferHandle) );
|
|
ASSERT( m_iFrameBufferHandle != 0 );
|
|
|
|
/* Attach the texture to it. */
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, static_cast<GLuint>(m_iFrameBufferHandle) );
|
|
glFramebufferTexture2DEXT( GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, static_cast<GLuint>(m_iTexHandle), 0 );
|
|
DebugAssertNoGLError();
|
|
|
|
/* Attach a depth buffer, if requested. */
|
|
if (param.bWithDepthBuffer)
|
|
{
|
|
glGenRenderbuffersEXT( 1, reinterpret_cast<GLuint*>(&m_iDepthBufferHandle) );
|
|
ASSERT( m_iDepthBufferHandle != 0 );
|
|
|
|
glBindRenderbufferEXT( GL_RENDERBUFFER, static_cast<GLuint>(m_iDepthBufferHandle) );
|
|
glRenderbufferStorageEXT( GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT16, iTextureWidth, iTextureHeight );
|
|
glFramebufferRenderbufferEXT( GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, static_cast<GLuint>(m_iDepthBufferHandle) );
|
|
}
|
|
|
|
GLenum status = glCheckFramebufferStatusEXT( GL_FRAMEBUFFER_EXT );
|
|
switch(status)
|
|
{
|
|
case GL_FRAMEBUFFER_COMPLETE_EXT:
|
|
break;
|
|
case GL_FRAMEBUFFER_UNSUPPORTED_EXT:
|
|
FAIL_M( "GL_FRAMEBUFFER_UNSUPPORTED_EXT" );
|
|
break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: FAIL_M( "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT" ); break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: FAIL_M( "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT" ); break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: FAIL_M( "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT" ); break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: FAIL_M( "GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT" ); break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: FAIL_M( "GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT" ); break;
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: FAIL_M( "GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT" ); break;
|
|
default:
|
|
FAIL_M(ssprintf("Unexpected GL framebuffer status: %i", status));
|
|
}
|
|
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
|
|
}
|
|
|
|
void RenderTarget_FramebufferObject::StartRenderingTo()
|
|
{
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, static_cast<GLuint>(m_iFrameBufferHandle) );
|
|
}
|
|
|
|
void RenderTarget_FramebufferObject::FinishRenderingTo()
|
|
{
|
|
glBindFramebufferEXT( GL_FRAMEBUFFER_EXT, 0 );
|
|
}
|
|
|
|
bool RageDisplay_Legacy::SupportsRenderToTexture() const
|
|
{
|
|
return GLEW_EXT_framebuffer_object || g_pWind->SupportsRenderToTexture();
|
|
}
|
|
|
|
bool RageDisplay_Legacy::SupportsFullscreenBorderlessWindow() const
|
|
{
|
|
// In order to support FSBW, we're going to need the LowLevelWindow implementation
|
|
// to support creating a fullscreen borderless window, and we're going to need
|
|
// RenderToTexture support in order to render in alternative resolutions
|
|
return g_pWind->SupportsFullscreenBorderlessWindow() && SupportsRenderToTexture();
|
|
}
|
|
|
|
/*
|
|
* Render-to-texture can be implemented in several ways: the generic GL_ARB_pixel_buffer_object,
|
|
* or platform-specifically. PBO is not available on all hardware that supports RTT,
|
|
* particularly GeForce 2, but is simpler and faster when available.
|
|
*/
|
|
|
|
uintptr_t RageDisplay_Legacy::CreateRenderTarget( const RenderTargetParam ¶m, int &iTextureWidthOut, int &iTextureHeightOut )
|
|
{
|
|
RenderTarget *pTarget;
|
|
if (GLEW_EXT_framebuffer_object)
|
|
pTarget = new RenderTarget_FramebufferObject;
|
|
else
|
|
pTarget = g_pWind->CreateRenderTarget();
|
|
|
|
pTarget->Create( param, iTextureWidthOut, iTextureHeightOut );
|
|
|
|
uintptr_t iTexture = pTarget->GetTexture();
|
|
|
|
ASSERT( g_mapRenderTargets.find(iTexture) == g_mapRenderTargets.end() );
|
|
g_mapRenderTargets[iTexture] = pTarget;
|
|
return iTexture;
|
|
}
|
|
|
|
uintptr_t RageDisplay_Legacy::GetRenderTarget()
|
|
{
|
|
for( std::map<uintptr_t, RenderTarget*>::const_iterator it = g_mapRenderTargets.begin(); it != g_mapRenderTargets.end(); ++it )
|
|
if( it->second == g_pCurrentRenderTarget )
|
|
return it->first;
|
|
return 0;
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetRenderTarget( uintptr_t iTexture, bool bPreserveTexture )
|
|
{
|
|
if (iTexture == 0)
|
|
{
|
|
g_bInvertY = false;
|
|
glFrontFace( GL_CCW );
|
|
|
|
/* Pop matrixes affected by SetDefaultRenderStates. */
|
|
DISPLAY->CameraPopMatrix();
|
|
|
|
/* Reset the viewport. */
|
|
int fWidth = g_pWind->GetActualVideoModeParams().windowWidth;
|
|
int fHeight = g_pWind->GetActualVideoModeParams().windowHeight;
|
|
glViewport( 0, 0, fWidth, fHeight );
|
|
|
|
if (g_pCurrentRenderTarget)
|
|
g_pCurrentRenderTarget->FinishRenderingTo();
|
|
g_pCurrentRenderTarget = nullptr;
|
|
return;
|
|
}
|
|
|
|
/* If we already had a render target, disable it. */
|
|
if (g_pCurrentRenderTarget != nullptr)
|
|
SetRenderTarget(0, true);
|
|
|
|
/* Enable the new render target. */
|
|
ASSERT(g_mapRenderTargets.find(iTexture) != g_mapRenderTargets.end());
|
|
RenderTarget *pTarget = g_mapRenderTargets[iTexture];
|
|
pTarget->StartRenderingTo();
|
|
g_pCurrentRenderTarget = pTarget;
|
|
|
|
/* Set the viewport to the size of the render target. */
|
|
glViewport(0, 0, pTarget->GetParam().iWidth, pTarget->GetParam().iHeight);
|
|
|
|
/* If this render target implementation flips Y, compensate. Inverting will
|
|
* switch the winding order. */
|
|
g_bInvertY = pTarget->InvertY();
|
|
if (g_bInvertY)
|
|
glFrontFace(GL_CW);
|
|
|
|
/* The render target may be in a different OpenGL context, so re-send
|
|
* state. Push matrixes affected by SetDefaultRenderStates. */
|
|
DISPLAY->CameraPushMatrix();
|
|
SetDefaultRenderStates();
|
|
|
|
/* Clear the texture, if requested. Always set the associated state, for
|
|
* consistency. */
|
|
glClearColor(0,0,0,0);
|
|
SetZWrite(true);
|
|
|
|
/* If bPreserveTexture is false, clear the render target. Only clear the depth
|
|
* buffer if the target has one; otherwise we're clearing the real depth buffer. */
|
|
if (!bPreserveTexture)
|
|
{
|
|
int iBit = GL_COLOR_BUFFER_BIT;
|
|
if (pTarget->GetParam().bWithDepthBuffer)
|
|
iBit |= GL_DEPTH_BUFFER_BIT;
|
|
glClear(iBit);
|
|
}
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetPolygonMode(PolygonMode pm)
|
|
{
|
|
GLenum m;
|
|
switch (pm)
|
|
{
|
|
case POLYGON_FILL: m = GL_FILL; break;
|
|
case POLYGON_LINE: m = GL_LINE; break;
|
|
default:
|
|
FAIL_M(ssprintf("Invalid PolygonMode: %i", pm));
|
|
}
|
|
glPolygonMode(GL_FRONT_AND_BACK, m);
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetLineWidth(float fWidth)
|
|
{
|
|
glLineWidth(fWidth);
|
|
}
|
|
|
|
RString RageDisplay_Legacy::GetTextureDiagnostics(uintptr_t iTexture) const
|
|
{
|
|
/*
|
|
s << (bGenerateMipMaps? "gluBuild2DMipmaps":"glTexImage2D");
|
|
s << "(format " << GLToString(glTexFormat) <<
|
|
", " << pImg->w << "x" << pImg->h <<
|
|
", format " << GLToString(iFormat) <<
|
|
", type " << GLToString(glImageType) <<
|
|
", pixfmt " << pixfmt <<
|
|
", imgpixfmt " << SurfacePixFmt <<
|
|
")";
|
|
LOG->Trace( "%s", s.str().c_str() );
|
|
|
|
glBindTexture( GL_TEXTURE_2D, iTexture );
|
|
|
|
GLint iWidth;
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GLenum(GL_TEXTURE_WIDTH), (GLint *) &iWidth );
|
|
GLint iHeight;
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GLenum(GL_TEXTURE_HEIGHT), (GLint *) &iHeight );
|
|
GLint iFormat;
|
|
glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GLenum(GL_TEXTURE_INTERNAL_FORMAT), (GLint *) &iFormat );
|
|
|
|
GL_CHECK_ERROR( "glGetTexLevelParameteriv(GL_TEXTURE_INTERNAL_FORMAT)" );
|
|
if (iFormat != glTexFormat)
|
|
{
|
|
sError = ssprintf( "Expected format %s, got %s instead",
|
|
GLToString(glTexFormat).c_str(), GLToString(iFormat).c_str() );
|
|
break;
|
|
}
|
|
*/
|
|
return RString();
|
|
}
|
|
|
|
/*
|
|
* XXX: Things like this only have to be set once per context - making
|
|
* SetDefault call These kinds of functions is wasteful. -Colby
|
|
*/
|
|
void RageDisplay_Legacy::SetAlphaTest(bool b)
|
|
{
|
|
// Previously this was 0.01, rather than 0x01.
|
|
glAlphaFunc(GL_GREATER, 0.00390625 /* 1/256 */);
|
|
if (b)
|
|
glEnable(GL_ALPHA_TEST);
|
|
else
|
|
glDisable(GL_ALPHA_TEST);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_Legacy::SupportsSurfaceFormat(RagePixelFormat pixfmt)
|
|
{
|
|
switch (g_GLPixFmtInfo[pixfmt].type)
|
|
{
|
|
case GL_UNSIGNED_SHORT_1_5_5_5_REV:
|
|
return GLEW_EXT_bgra && g_bReversePackedPixelsWorks;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
bool RageDisplay_Legacy::SupportsTextureFormat(RagePixelFormat pixfmt, bool bRealtime)
|
|
{
|
|
/* 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 (bRealtime && !SupportsSurfaceFormat(pixfmt))
|
|
return false;
|
|
|
|
switch (g_GLPixFmtInfo[pixfmt].format)
|
|
{
|
|
case GL_COLOR_INDEX:
|
|
return glColorTableEXT && glGetColorTableParameterivEXT;
|
|
case GL_BGR:
|
|
case GL_BGRA:
|
|
return !!GLEW_EXT_bgra;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool RageDisplay_Legacy::SupportsPerVertexMatrixScale()
|
|
{
|
|
// Intel i915 on OSX 10.4.4 supports vertex programs but not hardware vertex buffers.
|
|
// Our software vertex rendering doesn't support vertex programs.
|
|
return glGenBuffersARB && g_bTextureMatrixShader != 0;
|
|
}
|
|
|
|
void RageDisplay_Legacy::SetSphereEnvironmentMapping(TextureUnit tu, bool b)
|
|
{
|
|
if (!SetTextureUnit(tu))
|
|
return;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
GLint iCelTexture1, iCelTexture2 = 0;
|
|
|
|
void RageDisplay_Legacy::SetCelShaded( int stage )
|
|
{
|
|
if (!GLEW_ARB_fragment_program && !GL_ARB_shading_language_100)
|
|
return; // not supported
|
|
|
|
switch (stage)
|
|
{
|
|
case 1:
|
|
glUseProgramObjectARB(g_gShellShader);
|
|
break;
|
|
case 2:
|
|
glUseProgramObjectARB(g_gCelShader);
|
|
break;
|
|
default:
|
|
glUseProgramObjectARB(0);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Copyright (c) 2001-2011 Chris Danford, Glenn Maynard, Colby Klein
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, and/or sell copies of the Software, and to permit persons to
|
|
* whom the Software is furnished to do so, provided that the above
|
|
* copyright notice(s) and this permission notice appear in all copies of
|
|
* the Software and that both the above copyright notice(s) and this
|
|
* permission notice appear in supporting documentation.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
|
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
|
|
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
|
|
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
* PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|