Files
itgmania212121/stepmania/src/RageDisplay.cpp
T

889 lines
22 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-05-20 08:59:37 +00:00
#include "RageDisplay.h"
2002-07-27 19:29:51 +00:00
#include "RageTimer.h"
2003-05-22 05:28:37 +00:00
#include "RageLog.h"
#include "RageMath.h"
2003-05-22 05:28:37 +00:00
#include "RageUtil.h"
2004-02-12 06:49:13 +00:00
#include "RageFile.h"
2004-06-14 22:27:16 +00:00
#include "RageSurface_Save_BMP.h"
2005-01-20 01:35:49 +00:00
#include "RageSurface_Save_JPEG.h"
#include "RageSurfaceUtils_Zoom.h"
2004-06-14 00:51:00 +00:00
#include "RageSurface.h"
2004-09-13 03:58:39 +00:00
#include "Preference.h"
2006-02-06 21:02:50 +00:00
#include "LocalizedString.h"
#include "arch/ArchHooks/ArchHooks.h"
2002-06-14 22:25:22 +00:00
2003-05-22 05:28:37 +00:00
//
// Statistics stuff
//
2006-02-06 21:02:50 +00:00
RageTimer g_LastCheckTimer;
int g_iNumVerts;
int g_iFPS, g_iVPF, g_iCFPS;
2002-11-11 20:43:30 +00:00
int RageDisplay::GetFPS() const { return g_iFPS; }
int RageDisplay::GetVPF() const { return g_iVPF; }
2003-01-11 05:12:17 +00:00
int RageDisplay::GetCumFPS() const { return g_iCFPS; }
2002-11-11 20:43:30 +00:00
static int g_iFramesRenderedSinceLastCheck,
2003-01-11 05:12:17 +00:00
g_iFramesRenderedSinceLastReset,
g_iVertsRenderedSinceLastCheck,
g_iNumChecksSinceLastReset;
static RageTimer g_LastFrameEndedAt( RageZeroTimer );
2003-01-08 23:16:28 +00:00
static int g_iTranslateX = 0, g_iTranslateY = 0, g_iAddWidth = 0, g_iAddHeight = 0;
RageDisplay* DISPLAY = NULL;
2005-05-19 01:26:41 +00:00
Preference<bool> LOG_FPS( "LogFPS", true );
2005-10-13 04:02:23 +00:00
Preference<float> g_fFrameLimitPercent( "FrameLimitPercent", 0.0f );
2004-09-13 03:58:39 +00:00
static const char *PixelFormatNames[] = {
"RGBA8",
"RGBA4",
"RGB5A1",
"RGB5",
"RGB8",
"PAL",
"BGR8",
"A1BGR5",
};
XToString( PixelFormat, NUM_PixelFormat );
2004-11-30 20:01:07 +00:00
/* bNeedReloadTextures is set to true if the device was re-created and we need
* to reload textures. On failure, an error message is returned.
* XXX: the renderer itself should probably be the one to try fallback modes */
2006-02-06 21:02:50 +00:00
static LocalizedString SETVIDEOMODE_FAILED ( "RageDisplay", "SetVideoMode failed:" );
2006-01-22 01:00:06 +00:00
RString RageDisplay::SetVideoMode( VideoModeParams p, bool &bNeedReloadTextures )
2003-06-05 19:29:27 +00:00
{
2006-01-22 01:00:06 +00:00
RString err;
err = this->TryVideoMode(p,bNeedReloadTextures);
if( err == "" )
2006-01-22 01:00:06 +00:00
return RString();
LOG->Trace( "TryVideoMode failed: %s", err.c_str() );
2003-06-05 19:29:27 +00:00
// fall back
if( this->TryVideoMode(p,bNeedReloadTextures) == "" )
2006-01-22 01:00:06 +00:00
return RString();
2003-06-05 19:29:27 +00:00
p.bpp = 16;
if( this->TryVideoMode(p,bNeedReloadTextures) == "" )
2006-01-22 01:00:06 +00:00
return RString();
2003-06-05 19:29:27 +00:00
p.width = 640;
p.height = 480;
if( this->TryVideoMode(p,bNeedReloadTextures) == "" )
2006-01-22 01:00:06 +00:00
return RString();
2003-06-05 19:29:27 +00:00
2006-02-06 21:02:50 +00:00
return SETVIDEOMODE_FAILED.GetValue() + " " + err;
2003-06-05 19:29:27 +00:00
}
2003-05-22 05:28:37 +00:00
void RageDisplay::ProcessStatsOnFlip()
2002-08-29 22:30:06 +00:00
{
g_iFramesRenderedSinceLastCheck++;
2003-01-11 05:12:17 +00:00
g_iFramesRenderedSinceLastReset++;
if( g_LastCheckTimer.PeekDeltaTime() >= 1.0f ) // update stats every 1 sec.
{
2006-06-26 18:55:17 +00:00
float fActualTime = g_LastCheckTimer.GetDeltaTime();
2003-01-11 05:12:17 +00:00
g_iNumChecksSinceLastReset++;
2006-06-26 18:55:17 +00:00
g_iFPS = lrintf( g_iFramesRenderedSinceLastCheck / fActualTime );
2003-01-11 05:12:17 +00:00
g_iCFPS = g_iFramesRenderedSinceLastReset / g_iNumChecksSinceLastReset;
2006-06-26 18:55:17 +00:00
g_iCFPS = lrintf( g_iCFPS / fActualTime );
g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFramesRenderedSinceLastCheck;
2003-01-08 23:16:28 +00:00
g_iFramesRenderedSinceLastCheck = g_iVertsRenderedSinceLastCheck = 0;
2004-09-13 04:38:50 +00:00
if( LOG_FPS )
2005-08-23 05:15:16 +00:00
{
2006-01-22 01:00:06 +00:00
RString sStats = GetStats();
2005-08-23 05:15:16 +00:00
sStats.Replace( "\n", ", " );
LOG->Trace( "%s", sStats.c_str() );
}
}
2002-05-20 08:59:37 +00:00
}
2002-12-21 07:54:24 +00:00
void RageDisplay::ResetStats()
{
2003-01-08 23:16:28 +00:00
g_iFPS = g_iVPF = 0;
2003-01-11 05:12:17 +00:00
g_iFramesRenderedSinceLastCheck = g_iFramesRenderedSinceLastReset = 0;
g_iNumChecksSinceLastReset = 0;
g_iVertsRenderedSinceLastCheck = 0;
2002-12-21 07:54:24 +00:00
g_LastCheckTimer.GetDeltaTime();
}
2006-01-22 01:00:06 +00:00
RString RageDisplay::GetStats() const
{
2006-01-22 01:00:06 +00:00
RString s;
/* If FPS == 0, we don't have stats yet. */
if( !GetFPS() )
s = "-- FPS\n-- av FPS\n-- VPF";
s = ssprintf( "%i FPS\n%i av FPS\n%i VPF", GetFPS(), GetCumFPS(), GetVPF() );
s += "\n"+this->GetApiDescription();
return s;
}
2005-07-13 00:37:43 +00:00
void RageDisplay::EndFrame()
{
ProcessStatsOnFlip();
}
2003-05-22 05:28:37 +00:00
void RageDisplay::StatsAddVerts( int iNumVertsRendered ) { g_iVertsRenderedSinceLastCheck += iNumVertsRendered; }
2003-01-22 22:41:05 +00:00
2003-12-20 21:29:55 +00:00
/* Draw a line as a quad. GL_LINES with SmoothLines off can draw line
* ends at odd angles--they're forced to axis-alignment regardless of the
* angle of the line. */
void RageDisplay::DrawPolyLine(const RageSpriteVertex &p1, const RageSpriteVertex &p2, float LineWidth )
{
/* soh cah toa strikes strikes again! */
float opp = p2.p.x - p1.p.x;
float adj = p2.p.y - p1.p.y;
float hyp = powf(opp*opp + adj*adj, 0.5f);
float lsin = opp/hyp;
float lcos = adj/hyp;
RageSpriteVertex v[4];
v[0] = v[1] = p1;
v[2] = v[3] = p2;
float ydist = lsin * LineWidth/2;
float xdist = lcos * LineWidth/2;
v[0].p.x += xdist;
v[0].p.y -= ydist;
v[1].p.x -= xdist;
v[1].p.y += ydist;
v[2].p.x -= xdist;
v[2].p.y += ydist;
v[3].p.x += xdist;
v[3].p.y -= ydist;
this->DrawQuad(v);
}
void RageDisplay::DrawLineStripInternal( const RageSpriteVertex v[], int iNumVerts, float LineWidth )
{
ASSERT( iNumVerts >= 2 );
/* Draw a line strip with rounded corners using polys. This is used on
* cards that have strange allergic reactions to antialiased points and
* lines. */
2004-09-21 07:53:39 +00:00
for( int i = 0; i < iNumVerts-1; ++i )
DrawPolyLine(v[i], v[i+1], LineWidth);
/* Join the lines with circles so we get rounded corners. */
2004-09-21 07:53:39 +00:00
for( int i = 0; i < iNumVerts; ++i )
DrawCircle( v[i], LineWidth/2 );
}
void RageDisplay::DrawCircleInternal( const RageSpriteVertex &p, float radius )
{
const int subdivisions = 32;
RageSpriteVertex v[subdivisions+2];
v[0] = p;
for(int i = 0; i < subdivisions+1; ++i)
{
const float fRotation = float(i) / subdivisions * 2*PI;
const float fX = RageFastCos(fRotation) * radius;
const float fY = -RageFastSin(fRotation) * radius;
v[1+i] = v[0];
v[1+i].p.x += fX;
v[1+i].p.y += fY;
}
this->DrawFan( v, subdivisions+2 );
}
2003-05-22 05:28:37 +00:00
void RageDisplay::SetDefaultRenderStates()
2002-05-20 08:59:37 +00:00
{
2003-05-22 05:28:37 +00:00
SetLighting( false );
2004-02-04 11:05:33 +00:00
SetCullMode( CULL_NONE );
2004-05-15 18:35:03 +00:00
SetZWrite( false );
SetZTestMode( ZTEST_OFF );
2003-05-22 05:28:37 +00:00
SetAlphaTest( true );
SetBlendMode( BLEND_NORMAL );
SetTextureFiltering( true );
2005-11-04 15:42:20 +00:00
SetZBias( 0 );
LoadMenuPerspective( 0, 640, 480, 320, 240 ); // 0 FOV = ortho
ChangeCentering(0,0,0,0);
2002-05-20 08:59:37 +00:00
}
2003-03-15 00:19:48 +00:00
2003-01-08 23:16:28 +00:00
2003-05-22 05:28:37 +00:00
//
// Matrix stuff
//
class MatrixStack
{
2003-05-22 05:28:37 +00:00
vector<RageMatrix> stack;
public:
2003-05-22 05:28:37 +00:00
MatrixStack()
{
2003-05-22 05:28:37 +00:00
stack.resize(1);
LoadIdentity();
}
2005-01-28 07:31:41 +00:00
// Pops the top of the stack.
void Pop()
{
2003-05-22 05:28:37 +00:00
stack.pop_back();
ASSERT( stack.size() > 0 ); // underflow
}
2005-01-28 07:31:41 +00:00
// Pushes the stack by one, duplicating the current matrix.
void Push()
2003-05-22 05:28:37 +00:00
{
stack.push_back( stack.back() );
ASSERT( stack.size() < 100 ); // overflow
}
2003-01-25 03:57:14 +00:00
2005-01-28 07:31:41 +00:00
// Loads identity in the current matrix.
void LoadIdentity()
2003-05-22 05:28:37 +00:00
{
RageMatrixIdentity( &stack.back() );
}
2003-01-25 03:57:14 +00:00
2005-01-28 07:31:41 +00:00
// Loads the given matrix into the current matrix
void LoadMatrix( const RageMatrix& m )
2003-05-22 05:28:37 +00:00
{
stack.back() = m;
}
2003-01-25 03:57:14 +00:00
2005-01-28 07:31:41 +00:00
// Right-Multiplies the given matrix to the current matrix.
// (transformation is about the current world origin)
void MultMatrix( const RageMatrix& m )
2003-05-22 05:28:37 +00:00
{
RageMatrixMultiply( &stack.back(), &m, &stack.back() );
}
2003-01-25 03:57:14 +00:00
2005-01-28 07:31:41 +00:00
// Left-Multiplies the given matrix to the current matrix
// (transformation is about the local origin of the object)
void MultMatrixLocal( const RageMatrix& m )
2003-05-22 05:28:37 +00:00
{
RageMatrixMultiply( &stack.back(), &stack.back(), &m );
}
2003-01-25 03:57:14 +00:00
2005-01-28 07:31:41 +00:00
// Right multiply the current matrix with the computed rotation
// matrix, counterclockwise about the given axis with the given angle.
// (rotation is about the current world origin)
void RotateX( float degrees )
2003-05-22 05:28:37 +00:00
{
RageMatrix m;
RageMatrixRotationX( &m, degrees );
MultMatrix( m );
}
2005-01-28 07:31:41 +00:00
void RotateY( float degrees )
{
2003-05-22 05:28:37 +00:00
RageMatrix m;
RageMatrixRotationY( &m, degrees );
MultMatrix( m );
}
void RotateZ( float degrees )
{
RageMatrix m;
RageMatrixRotationZ( &m, degrees );
MultMatrix( m );
}
2003-02-14 07:04:19 +00:00
2003-05-22 05:28:37 +00:00
// Left multiply the current matrix with the computed rotation
2005-01-28 07:31:41 +00:00
// matrix. All angles are counterclockwise. (rotation is about the
// local origin of the object)
2003-05-22 05:28:37 +00:00
void RotateXLocal( float degrees )
2003-01-25 03:57:14 +00:00
{
2003-05-22 05:28:37 +00:00
RageMatrix m;
RageMatrixRotationX( &m, degrees );
MultMatrixLocal( m );
}
2005-01-28 07:31:41 +00:00
void RotateYLocal( float degrees )
2003-05-22 05:28:37 +00:00
{
RageMatrix m;
RageMatrixRotationY( &m, degrees );
MultMatrixLocal( m );
}
void RotateZLocal( float degrees )
{
RageMatrix m;
RageMatrixRotationZ( &m, degrees );
MultMatrixLocal( m );
2003-01-25 03:57:14 +00:00
}
2005-01-28 07:31:41 +00:00
// Right multiply the current matrix with the computed scale
// matrix. (transformation is about the current world origin)
void Scale( float x, float y, float z)
2003-05-22 05:28:37 +00:00
{
RageMatrix m;
RageMatrixScaling( &m, x, y, z );
MultMatrix( m );
}
2005-01-28 07:31:41 +00:00
// Left multiply the current matrix with the computed scale
// matrix. (transformation is about the local origin of the object)
void ScaleLocal( float x, float y, float z)
2003-05-22 05:28:37 +00:00
{
RageMatrix m;
RageMatrixScaling( &m, x, y, z );
MultMatrixLocal( m );
}
2002-11-18 20:55:23 +00:00
2005-01-28 07:31:41 +00:00
// Right multiply the current matrix with the computed translation
// matrix. (transformation is about the current world origin)
void Translate( float x, float y, float z)
2003-05-22 05:28:37 +00:00
{
RageMatrix m;
RageMatrixTranslation( &m, x, y, z );
MultMatrix( m );
}
2002-11-18 20:55:23 +00:00
2005-01-28 07:31:41 +00:00
// Left multiply the current matrix with the computed translation
// matrix. (transformation is about the local origin of the object)
void TranslateLocal( float x, float y, float z)
2003-05-22 05:28:37 +00:00
{
RageMatrix m;
RageMatrixTranslation( &m, x, y, z );
MultMatrixLocal( m );
}
2003-01-08 22:30:32 +00:00
2005-05-02 21:43:27 +00:00
void SkewX( float fAmount )
{
RageMatrix m;
RageMatrixSkewX( &m, fAmount );
MultMatrixLocal( m );
}
2005-01-28 07:31:41 +00:00
// Obtain the current matrix at the top of the stack
const RageMatrix* GetTop() { return &stack.back(); }
2003-05-22 05:28:37 +00:00
};
2002-11-18 20:55:23 +00:00
2005-12-29 19:34:28 +00:00
static MatrixStack g_ProjectionStack;
static MatrixStack g_ViewStack;
static MatrixStack g_WorldStack;
static MatrixStack g_TextureStack;
const RageMatrix* RageDisplay::GetProjectionTop()
2003-05-22 05:28:37 +00:00
{
return g_ProjectionStack.GetTop();
}
const RageMatrix* RageDisplay::GetViewTop()
2003-01-25 03:57:14 +00:00
{
return g_ViewStack.GetTop();
}
const RageMatrix* RageDisplay::GetWorldTop()
{
return g_WorldStack.GetTop();
2003-01-25 03:57:14 +00:00
}
const RageMatrix* RageDisplay::GetTextureTop()
{
return g_TextureStack.GetTop();
}
void RageDisplay::PushMatrix()
{
g_WorldStack.Push();
}
void RageDisplay::PopMatrix()
{
g_WorldStack.Pop();
}
2003-05-22 05:28:37 +00:00
void RageDisplay::Translate( float x, float y, float z )
2003-04-25 08:17:27 +00:00
{
g_WorldStack.TranslateLocal(x, y, z);
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::TranslateWorld( float x, float y, float z )
{
g_WorldStack.Translate(x, y, z);
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::Scale( float x, float y, float z )
{
g_WorldStack.ScaleLocal(x, y, z);
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::RotateX( float deg )
{
g_WorldStack.RotateXLocal( deg );
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::RotateY( float deg )
{
g_WorldStack.RotateYLocal( deg );
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::RotateZ( float deg )
{
g_WorldStack.RotateZLocal( deg );
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2005-05-02 21:43:27 +00:00
void RageDisplay::SkewX( float fAmount )
{
g_WorldStack.SkewX( fAmount );
}
2003-05-22 05:28:37 +00:00
void RageDisplay::PostMultMatrix( const RageMatrix &m )
{
g_WorldStack.MultMatrix( m );
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::PreMultMatrix( const RageMatrix &m )
{
g_WorldStack.MultMatrixLocal( m );
2003-05-22 05:28:37 +00:00
}
2003-04-25 08:17:27 +00:00
2003-05-22 05:28:37 +00:00
void RageDisplay::LoadIdentity()
{
g_WorldStack.LoadIdentity();
2003-04-25 08:17:27 +00:00
}
void RageDisplay::TexturePushMatrix()
{
g_TextureStack.Push();
}
void RageDisplay::TexturePopMatrix()
{
g_TextureStack.Pop();
}
void RageDisplay::TextureTranslate( float x, float y )
{
g_TextureStack.TranslateLocal(x, y, 0);
}
void RageDisplay::LoadMenuPerspective( float fovDegrees, float fWidth, float fHeight, float fVanishPointX, float fVanishPointY )
{
/* fovDegrees == 0 gives ortho projection. */
if( fovDegrees == 0 )
{
float left = 0, right = fWidth, bottom = fHeight, top = 0;
g_ProjectionStack.LoadMatrix( GetOrthoMatrix(left, right, bottom, top, -1000, +1000) );
g_ViewStack.LoadIdentity();
}
else
{
CLAMP( fovDegrees, 0.1f, 179.9f );
float fovRadians = fovDegrees / 180.f * PI;
float theta = fovRadians/2;
float fDistCameraFromImage = fWidth/2 / tanf( theta );
fVanishPointX = SCALE( fVanishPointX, 0, fWidth, fWidth, 0 );
fVanishPointY = SCALE( fVanishPointY, 0, fHeight, fHeight, 0 );
fVanishPointX -= fWidth/2;
fVanishPointY -= fHeight/2;
/* It's the caller's responsibility to push first. */
2003-05-22 05:28:37 +00:00
g_ProjectionStack.LoadMatrix(
2003-09-21 18:07:29 +00:00
GetFrustumMatrix(
(fVanishPointX-fWidth/2)/fDistCameraFromImage,
(fVanishPointX+fWidth/2)/fDistCameraFromImage,
(fVanishPointY+fHeight/2)/fDistCameraFromImage,
(fVanishPointY-fHeight/2)/fDistCameraFromImage,
2003-05-22 05:28:37 +00:00
1,
fDistCameraFromImage+1000 ) );
g_ViewStack.LoadMatrix(
2003-05-22 05:28:37 +00:00
RageLookAt(
-fVanishPointX+fWidth/2, -fVanishPointY+fHeight/2, fDistCameraFromImage,
-fVanishPointX+fWidth/2, -fVanishPointY+fHeight/2, 0,
2003-05-22 05:28:37 +00:00
0.0f, 1.0f, 0.0f) );
}
}
2003-04-25 06:58:19 +00:00
void RageDisplay::CameraPushMatrix()
{
g_ProjectionStack.Push();
g_ViewStack.Push();
2002-11-15 08:01:34 +00:00
}
void RageDisplay::CameraPopMatrix()
2002-11-15 08:01:34 +00:00
{
2003-05-22 05:28:37 +00:00
g_ProjectionStack.Pop();
g_ViewStack.Pop();
2002-11-15 08:01:34 +00:00
}
2003-04-10 17:58:27 +00:00
/* gluLookAt. The result is pre-multiplied to the matrix (M = L * M) instead of
* post-multiplied. */
2005-12-04 16:59:53 +00:00
void RageDisplay::LoadLookAt( float fFOV, const RageVector3 &Eye, const RageVector3 &At, const RageVector3 &Up )
2002-11-15 08:01:34 +00:00
{
float fAspect = GetActualVideoModeParams().fDisplayAspectRatio;
2005-12-04 16:59:53 +00:00
g_ProjectionStack.LoadMatrix( GetPerspectiveMatrix(fFOV, fAspect, 1, 1000) );
/* Flip the Y coordinate, so positive numbers go down. */
2005-12-04 16:59:53 +00:00
g_ProjectionStack.Scale( 1, -1, 1 );
2005-12-04 16:59:53 +00:00
g_ViewStack.LoadMatrix( RageLookAt(Eye.x, Eye.y, Eye.z, At.x, At.y, At.z, Up.x, Up.y, Up.z) );
}
RageMatrix RageDisplay::GetPerspectiveMatrix(float fovy, float aspect, float zNear, float zFar)
{
float ymax = zNear * tanf(fovy * PI / 360.0f);
float ymin = -ymax;
float xmin = ymin * aspect;
float xmax = ymax * aspect;
2003-09-21 18:07:29 +00:00
return GetFrustumMatrix(xmin, xmax, ymin, ymax, zNear, zFar);
}
2004-06-14 00:51:00 +00:00
RageSurface *RageDisplay::CreateSurfaceFromPixfmt( PixelFormat pixfmt,
void *pixels, int width, int height, int pitch )
{
const PixelFormatDesc *tpf = GetPixelFormatDesc(pixfmt);
2004-06-14 00:51:00 +00:00
RageSurface *surf = CreateSurfaceFrom(
width, height, tpf->bpp,
tpf->masks[0], tpf->masks[1], tpf->masks[2], tpf->masks[3],
(uint8_t *) pixels, pitch );
return surf;
}
2005-12-29 19:38:58 +00:00
PixelFormat RageDisplay::FindPixelFormat( int iBPP, int iRmask, int iGmask, int iBmask, int iAmask, bool bRealtime )
{
2005-12-29 19:38:58 +00:00
PixelFormatDesc tmp = { iBPP, { iRmask, iGmask, iBmask, iAmask } };
2005-12-29 19:38:58 +00:00
FOREACH_ENUM2( PixelFormat, iPixFmt )
{
2005-12-29 19:38:58 +00:00
const PixelFormatDesc *pf = GetPixelFormatDesc( PixelFormat(iPixFmt) );
if( !SupportsTextureFormat(PixelFormat(iPixFmt), bRealtime) )
continue;
2005-12-29 19:38:58 +00:00
if( memcmp(pf, &tmp, sizeof(tmp)) )
continue;
2005-12-29 19:38:58 +00:00
return iPixFmt;
}
return PixelFormat_INVALID;
}
2003-09-21 18:07:29 +00:00
/* These convert to OpenGL's coordinate system: -1,-1 is the bottom-left, +1,+1 is the
* top-right, and Z goes from -1 (viewer) to +1 (distance). It's a little odd, but
* very well-defined. */
RageMatrix RageDisplay::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-09-21 18:07:29 +00:00
RageMatrix RageDisplay::GetFrustumMatrix( float l, float r, float b, float t, float zn, float zf )
{
2004-11-26 19:47:26 +00:00
// glFrustum
2003-09-21 18:07:29 +00:00
float A = (r+l) / (r-l);
float B = (t+b) / (t-b);
float C = -1 * (zf+zn) / (zf-zn);
float D = -1 * (2*zf*zn) / (zf-zn);
RageMatrix m(
2*zn/(r-l), 0, 0, 0,
0, 2*zn/(t-b), 0, 0,
A, B, C, -1,
0, 0, D, 0 );
return m;
}
2005-12-29 08:14:52 +00:00
void RageDisplay::ResolutionChanged()
{
/* The centering matrix depends on the resolution. */
UpdateCentering();
}
void RageDisplay::ChangeCentering( int iTranslateX, int iTranslateY, int iAddWidth, int iAddHeight )
{
g_iTranslateX = iTranslateX;
g_iTranslateY = iTranslateY;
g_iAddWidth = iAddWidth;
g_iAddHeight = iAddHeight;
UpdateCentering();
}
2006-03-26 21:59:29 +00:00
RageMatrix RageDisplay::GetCenteringMatrix( float fTranslateX, float fTranslateY, float fAddWidth, float fAddHeight ) const
{
// in screen space, left edge = -1, right edge = 1, bottom edge = -1. top edge = 1
float fWidth = (float) GetActualVideoModeParams().width;
float fHeight = (float) GetActualVideoModeParams().height;
2006-03-26 21:59:29 +00:00
float fPercentShiftX = SCALE( fTranslateX, 0, fWidth, 0, +2.0f );
float fPercentShiftY = SCALE( fTranslateY, 0, fHeight, 0, -2.0f );
float fPercentScaleX = SCALE( fAddWidth, 0, fWidth, 1.0f, 2.0f );
float fPercentScaleY = SCALE( fAddHeight, 0, fHeight, 1.0f, 2.0f );
2003-10-11 07:47:34 +00:00
RageMatrix m1;
RageMatrix m2;
RageMatrixTranslation(
&m1,
fPercentShiftX,
fPercentShiftY,
0 );
RageMatrixScaling(
&m2,
fPercentScaleX,
fPercentScaleY,
1 );
2006-03-26 21:59:29 +00:00
RageMatrix mOut;
RageMatrixMultiply( &mOut, &m1, &m2 );
return mOut;
}
void RageDisplay::UpdateCentering()
{
m_Centering = GetCenteringMatrix( (float) g_iTranslateX, (float) g_iTranslateY, (float) g_iAddWidth, (float) g_iAddHeight );
}
2004-02-12 06:49:13 +00:00
2006-01-22 01:00:06 +00:00
bool RageDisplay::SaveScreenshot( RString sPath, GraphicsFileFormat format )
2004-02-12 06:49:13 +00:00
{
2004-06-14 00:51:00 +00:00
RageSurface* surface = this->CreateScreenshot();
2004-02-12 06:49:13 +00:00
2004-05-24 21:47:49 +00:00
/* Unless we're in lossless, resize the image to 640x480. If we're saving lossy,
* there's no sense in saving 1280x960 screenshots, and we don't want to output
* screenshots in a strange (non-1) sample aspect ratio. */
if( format != SAVE_LOSSLESS )
{
/* Maintain the DAR. */
2005-12-25 18:59:52 +00:00
ASSERT( GetActualVideoModeParams().fDisplayAspectRatio > 0 );
int iHeight = lrintf( 640 / GetActualVideoModeParams().fDisplayAspectRatio );
LOG->Trace( "%ix%i -> %ix%i (%.3f)", surface->w, surface->h, 640, iHeight, GetActualVideoModeParams().fDisplayAspectRatio );
2005-12-23 13:10:21 +00:00
RageSurfaceUtils::Zoom( surface, 640, iHeight );
}
2004-05-24 21:47:49 +00:00
2004-06-14 22:27:16 +00:00
RageFile out;
if( !out.Open( sPath, RageFile::WRITE ) )
{
LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() );
return false;
}
bool bSuccess = false;
2004-02-12 06:49:13 +00:00
switch( format )
{
2004-03-23 22:35:30 +00:00
case SAVE_LOSSLESS:
2005-01-20 01:42:05 +00:00
bSuccess = RageSurfaceUtils::SaveBMP( surface, out );
2004-02-12 06:49:13 +00:00
break;
2004-03-23 22:35:30 +00:00
case SAVE_LOSSY_LOW_QUAL:
2005-01-20 01:42:05 +00:00
bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, false );
2004-06-14 22:27:16 +00:00
break;
2004-03-23 22:35:30 +00:00
case SAVE_LOSSY_HIGH_QUAL:
2005-01-20 01:42:05 +00:00
bSuccess = RageSurfaceUtils::SaveJPEG( surface, out, true );
2004-02-12 06:49:13 +00:00
break;
default:
ASSERT(0);
return false;
}
2004-06-14 00:51:00 +00:00
delete surface;
2004-02-12 06:49:13 +00:00
surface = NULL;
2004-06-14 22:27:16 +00:00
if( !bSuccess )
2004-02-12 06:49:13 +00:00
{
LOG->Trace("Couldn't write %s: %s", sPath.c_str(), out.GetError().c_str() );
return false;
}
return true;
}
void RageDisplay::DrawQuads( const RageSpriteVertex v[], int iNumVerts )
{
ASSERT( (iNumVerts%4) == 0 );
if(iNumVerts == 0)
return;
this->DrawQuadsInternal(v,iNumVerts);
StatsAddVerts(iNumVerts);
}
void RageDisplay::DrawQuadStrip( const RageSpriteVertex v[], int iNumVerts )
{
ASSERT( (iNumVerts%2) == 0 );
if(iNumVerts < 4)
return;
this->DrawQuadStripInternal(v,iNumVerts);
StatsAddVerts(iNumVerts);
}
void RageDisplay::DrawFan( const RageSpriteVertex v[], int iNumVerts )
{
ASSERT( iNumVerts >= 3 );
this->DrawFanInternal(v,iNumVerts);
StatsAddVerts(iNumVerts);
}
void RageDisplay::DrawStrip( const RageSpriteVertex v[], int iNumVerts )
{
ASSERT( iNumVerts >= 3 );
this->DrawStripInternal(v,iNumVerts);
StatsAddVerts(iNumVerts);
}
void RageDisplay::DrawTriangles( const RageSpriteVertex v[], int iNumVerts )
{
if( iNumVerts == 0 )
return;
ASSERT( iNumVerts >= 3 );
this->DrawTrianglesInternal(v,iNumVerts);
StatsAddVerts(iNumVerts);
}
void RageDisplay::DrawCompiledGeometry( const RageCompiledGeometry *p, int iMeshIndex, const vector<msMesh> &vMeshes )
{
this->DrawCompiledGeometryInternal( p, iMeshIndex );
StatsAddVerts( vMeshes[iMeshIndex].Triangles.size() );
}
void RageDisplay::DrawLineStrip( const RageSpriteVertex v[], int iNumVerts, float LineWidth )
{
ASSERT( iNumVerts >= 2 );
this->DrawLineStripInternal( v, iNumVerts, LineWidth );
}
void RageDisplay::DrawCircle( const RageSpriteVertex &v, float radius )
{
this->DrawCircleInternal( v, radius );
}
2004-05-06 00:42:06 +00:00
void RageDisplay::FrameLimitBeforeVsync( int iFPS )
{
ASSERT( iFPS != 0 );
int iDelayMicroseconds = 0;
if( g_fFrameLimitPercent.Get() > 0.0f && !g_LastFrameEndedAt.IsZero() )
{
float fFrameTime = g_LastFrameEndedAt.GetDeltaTime();
float fExpectedTime = 1.0f / iFPS;
/* This is typically used to turn some of the delay that would normally
* be waiting for vsync and turn it into a usleep, to make sure we give
* up the CPU. If we overshoot the sleep, then we'll miss the vsync,
* so allow tweaking the amount of time we expect a frame to take.
* Frame limiting is disabled by setting this to 0. */
fExpectedTime *= g_fFrameLimitPercent.Get();
float fExtraTime = fExpectedTime - fFrameTime;
iDelayMicroseconds = int(fExtraTime * 1000000);
}
if( !HOOKS->AppHasFocus() )
iDelayMicroseconds = max( iDelayMicroseconds, 10000 ); // give some time to other processes and threads
#if defined(_WINDOWS)
/* In Windows, always explicitly give up a minimum amount of CPU for other threads. */
iDelayMicroseconds = max( iDelayMicroseconds, 1000 );
#endif
if( iDelayMicroseconds > 0 )
usleep( iDelayMicroseconds );
}
void RageDisplay::FrameLimitAfterVsync()
{
if( g_fFrameLimitPercent.Get() == 0.0f )
return;
g_LastFrameEndedAt.Touch();
}
2005-01-31 22:58:12 +00:00
RageCompiledGeometry::~RageCompiledGeometry()
{
m_bNeedsNormals = false;
}
void RageCompiledGeometry::Set( const vector<msMesh> &vMeshes, bool bNeedsNormals )
{
m_bNeedsNormals = bNeedsNormals;
size_t totalVerts = 0;
size_t totalTriangles = 0;
m_bAnyNeedsTextureMatrixScale = false;
2005-01-31 22:58:12 +00:00
m_vMeshInfo.resize( vMeshes.size() );
for( unsigned i=0; i<vMeshes.size(); i++ )
{
const msMesh& mesh = vMeshes[i];
const vector<RageModelVertex> &Vertices = mesh.Vertices;
const vector<msTriangle> &Triangles = mesh.Triangles;
MeshInfo& meshInfo = m_vMeshInfo[i];
meshInfo.m_bNeedsTextureMatrixScale = false;
2005-01-31 22:58:12 +00:00
meshInfo.iVertexStart = totalVerts;
meshInfo.iVertexCount = Vertices.size();
meshInfo.iTriangleStart = totalTriangles;
meshInfo.iTriangleCount = Triangles.size();
totalVerts += Vertices.size();
totalTriangles += Triangles.size();
2005-02-01 02:47:30 +00:00
for( unsigned j = 0; j < Vertices.size(); ++j )
{
2005-02-01 02:47:30 +00:00
if( Vertices[j].TextureMatrixScale.x != 1.0f || Vertices[j].TextureMatrixScale.y != 1.0f )
{
meshInfo.m_bNeedsTextureMatrixScale = true;
m_bAnyNeedsTextureMatrixScale = true;
}
}
2005-01-31 22:58:12 +00:00
}
this->Allocate( vMeshes );
Change( vMeshes );
}
2004-05-06 00:42:06 +00:00
/*
* Copyright (c) 2001-2004 Chris Danford, Glenn Maynard
* 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.
*/