622 lines
16 KiB
C++
622 lines
16 KiB
C++
#include "global.h"
|
|
/*
|
|
-----------------------------------------------------------------------------
|
|
Class: RageDisplay
|
|
|
|
Desc: Methods common to all RageDisplays
|
|
|
|
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
|
|
Chris Danford
|
|
Glenn Maynard
|
|
-----------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "RageDisplay.h"
|
|
#include "RageTimer.h"
|
|
#include "RageLog.h"
|
|
#include "RageMath.h"
|
|
#include "RageUtil.h"
|
|
#include "GameConstantsAndTypes.h"
|
|
#include "SDL_utils.h"
|
|
|
|
//
|
|
// Statistics stuff
|
|
//
|
|
RageTimer g_LastCheckTimer;
|
|
int g_iNumVerts;
|
|
int g_iFPS, g_iVPF, g_iCFPS;
|
|
|
|
int RageDisplay::GetFPS() const { return g_iFPS; }
|
|
int RageDisplay::GetVPF() const { return g_iVPF; }
|
|
int RageDisplay::GetCumFPS() const { return g_iCFPS; }
|
|
|
|
static int g_iFramesRenderedSinceLastCheck,
|
|
g_iFramesRenderedSinceLastReset,
|
|
g_iVertsRenderedSinceLastCheck,
|
|
g_iNumChecksSinceLastReset;
|
|
|
|
RageDisplay* DISPLAY = NULL;
|
|
|
|
CString RageDisplay::PixelFormatToString( PixelFormat pixfmt )
|
|
{
|
|
const CString s[NUM_PIX_FORMATS] = {
|
|
"FMT_RGBA8",
|
|
"FMT_RGBA4",
|
|
"FMT_RGB5A1",
|
|
"FMT_RGB5",
|
|
"FMT_RGB8",
|
|
"FMT_PAL" };
|
|
return s[pixfmt];
|
|
};
|
|
|
|
// Return true if device was re-created and we need to reload textures.
|
|
bool RageDisplay::SetVideoMode( VideoModeParams p )
|
|
{
|
|
/* Round to the nearest valid fullscreen resolution */
|
|
if( !p.windowed )
|
|
{
|
|
if( p.width <= 320 ) p.width = 320;
|
|
else if( p.width <= 400 ) p.width = 400;
|
|
else if( p.width <= 512 ) p.width = 512;
|
|
else if( p.width <= 640 ) p.width = 640;
|
|
else if( p.width <= 800 ) p.width = 800;
|
|
else if( p.width <= 1024 ) p.width = 1024;
|
|
else if( p.width <= 1280 ) p.width = 1280;
|
|
|
|
switch( p.width )
|
|
{
|
|
case 320: p.height = 240; break;
|
|
case 400: p.height = 300; break;
|
|
case 512: p.height = 384; break;
|
|
case 640: p.height = 480; break;
|
|
case 800: p.height = 600; break;
|
|
case 1024: p.height = 768; break;
|
|
case 1280: p.height = 960; break;
|
|
default: ASSERT(0);
|
|
}
|
|
}
|
|
|
|
bool bNeedReloadTextures;
|
|
|
|
CString err;
|
|
err = this->TryVideoMode(p,bNeedReloadTextures);
|
|
if( err == "" )
|
|
return bNeedReloadTextures;
|
|
|
|
// fall back
|
|
p.windowed = false;
|
|
if( this->TryVideoMode(p,bNeedReloadTextures) == "" )
|
|
return bNeedReloadTextures;
|
|
p.bpp = 16;
|
|
if( this->TryVideoMode(p,bNeedReloadTextures) == "" )
|
|
return bNeedReloadTextures;
|
|
p.width = 640;
|
|
p.height = 480;
|
|
if( this->TryVideoMode(p,bNeedReloadTextures) == "" )
|
|
return bNeedReloadTextures;
|
|
|
|
RageException::ThrowNonfatal( "SetVideoMode failed: %s", err.c_str() );
|
|
}
|
|
|
|
void RageDisplay::ProcessStatsOnFlip()
|
|
{
|
|
g_iFramesRenderedSinceLastCheck++;
|
|
g_iFramesRenderedSinceLastReset++;
|
|
|
|
if( g_LastCheckTimer.PeekDeltaTime() >= 1.0f ) // update stats every 1 sec.
|
|
{
|
|
g_LastCheckTimer.GetDeltaTime();
|
|
g_iNumChecksSinceLastReset++;
|
|
g_iFPS = g_iFramesRenderedSinceLastCheck;
|
|
g_iCFPS = g_iFramesRenderedSinceLastReset / g_iNumChecksSinceLastReset;
|
|
g_iVPF = g_iVertsRenderedSinceLastCheck / g_iFPS;
|
|
g_iFramesRenderedSinceLastCheck = g_iVertsRenderedSinceLastCheck = 0;
|
|
LOG->Trace( "FPS: %d, CFPS %d, VPF: %d", g_iFPS, g_iCFPS, g_iVPF );
|
|
}
|
|
}
|
|
|
|
void RageDisplay::ResetStats()
|
|
{
|
|
g_iFPS = g_iVPF = 0;
|
|
g_iFramesRenderedSinceLastCheck = g_iFramesRenderedSinceLastReset = 0;
|
|
g_iNumChecksSinceLastReset = 0;
|
|
g_iVertsRenderedSinceLastCheck = 0;
|
|
g_LastCheckTimer.GetDeltaTime();
|
|
}
|
|
|
|
void RageDisplay::StatsAddVerts( int iNumVertsRendered ) { g_iVertsRenderedSinceLastCheck += iNumVertsRendered; }
|
|
|
|
/* Draw a line as a quad. GL_LINES with antialiasing 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::DrawLineStrip( 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. */
|
|
int i;
|
|
for(i = 0; i < iNumVerts-1; ++i)
|
|
DrawPolyLine(v[i], v[i+1], LineWidth);
|
|
|
|
/* Join the lines with circles so we get rounded corners. */
|
|
for(i = 0; i < iNumVerts; ++i)
|
|
DrawCircle( v[i], LineWidth/2 );
|
|
}
|
|
|
|
void RageDisplay::DrawCircle( 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 = cosf(fRotation) * radius;
|
|
const float fY = -sinf(fRotation) * radius;
|
|
v[1+i] = v[0];
|
|
v[1+i].p.x += fX;
|
|
v[1+i].p.y += fY;
|
|
}
|
|
|
|
this->DrawFan( v, subdivisions+2 );
|
|
}
|
|
|
|
|
|
|
|
void RageDisplay::SetDefaultRenderStates()
|
|
{
|
|
SetLighting( false );
|
|
SetBackfaceCull( false );
|
|
SetZBuffer( false );
|
|
SetAlphaTest( true );
|
|
SetBlendMode( BLEND_NORMAL );
|
|
SetTextureFiltering( true );
|
|
LoadMenuPerspective(0, CENTER_X, CENTER_Y); // 0 FOV = ortho
|
|
}
|
|
|
|
|
|
//
|
|
// Matrix stuff
|
|
//
|
|
class MatrixStack
|
|
{
|
|
vector<RageMatrix> stack;
|
|
public:
|
|
|
|
MatrixStack()
|
|
{
|
|
stack.resize(1);
|
|
LoadIdentity();
|
|
}
|
|
|
|
// Pops the top of the stack.
|
|
void Pop()
|
|
{
|
|
stack.pop_back();
|
|
ASSERT( stack.size() > 0 ); // underflow
|
|
}
|
|
|
|
// Pushes the stack by one, duplicating the current matrix.
|
|
void Push()
|
|
{
|
|
stack.push_back( stack.back() );
|
|
ASSERT( stack.size() < 100 ); // overflow
|
|
}
|
|
|
|
// Loads identity in the current matrix.
|
|
void LoadIdentity()
|
|
{
|
|
RageMatrixIdentity( &stack.back() );
|
|
}
|
|
|
|
// Loads the given matrix into the current matrix
|
|
void LoadMatrix( const RageMatrix& m )
|
|
{
|
|
stack.back() = m;
|
|
}
|
|
|
|
// Right-Multiplies the given matrix to the current matrix.
|
|
// (transformation is about the current world origin)
|
|
void MultMatrix( const RageMatrix& m )
|
|
{
|
|
RageMatrixMultiply( &stack.back(), &m, &stack.back() );
|
|
}
|
|
|
|
// Left-Multiplies the given matrix to the current matrix
|
|
// (transformation is about the local origin of the object)
|
|
void MultMatrixLocal( const RageMatrix& m )
|
|
{
|
|
RageMatrixMultiply( &stack.back(), &stack.back(), &m );
|
|
}
|
|
|
|
// 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 )
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixRotationX( &m, degrees );
|
|
MultMatrix( m );
|
|
}
|
|
void RotateY( float degrees )
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixRotationY( &m, degrees );
|
|
MultMatrix( m );
|
|
}
|
|
void RotateZ( float degrees )
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixRotationZ( &m, degrees );
|
|
MultMatrix( m );
|
|
}
|
|
|
|
// Left multiply the current matrix with the computed rotation
|
|
// matrix. All angles are counterclockwise. (rotation is about the
|
|
// local origin of the object)
|
|
void RotateXLocal( float degrees )
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixRotationX( &m, degrees );
|
|
MultMatrixLocal( m );
|
|
}
|
|
void RotateYLocal( float degrees )
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixRotationY( &m, degrees );
|
|
MultMatrixLocal( m );
|
|
}
|
|
void RotateZLocal( float degrees )
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixRotationZ( &m, degrees );
|
|
MultMatrixLocal( m );
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixScaling( &m, x, y, z );
|
|
MultMatrix( m );
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixScaling( &m, x, y, z );
|
|
MultMatrixLocal( m );
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixTranslation( &m, x, y, z );
|
|
MultMatrix( m );
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
RageMatrix m;
|
|
RageMatrixTranslation( &m, x, y, z );
|
|
MultMatrixLocal( m );
|
|
}
|
|
|
|
// Obtain the current matrix at the top of the stack
|
|
const RageMatrix* GetTop() { return &stack.back(); }
|
|
};
|
|
|
|
|
|
MatrixStack g_ProjectionStack;
|
|
MatrixStack g_ViewStack;
|
|
MatrixStack g_WorldStack;
|
|
|
|
const RageMatrix* RageDisplay::GetProjectionTop()
|
|
{
|
|
return g_ProjectionStack.GetTop();
|
|
}
|
|
|
|
const RageMatrix* RageDisplay::GetViewTop()
|
|
{
|
|
return g_ViewStack.GetTop();
|
|
}
|
|
|
|
const RageMatrix* RageDisplay::GetWorldTop()
|
|
{
|
|
return g_WorldStack.GetTop();
|
|
}
|
|
|
|
void RageDisplay::PushMatrix()
|
|
{
|
|
g_WorldStack.Push();
|
|
}
|
|
|
|
void RageDisplay::PopMatrix()
|
|
{
|
|
g_WorldStack.Pop();
|
|
}
|
|
|
|
void RageDisplay::Translate( float x, float y, float z )
|
|
{
|
|
g_WorldStack.TranslateLocal(x, y, z);
|
|
}
|
|
|
|
void RageDisplay::TranslateWorld( float x, float y, float z )
|
|
{
|
|
g_WorldStack.Translate(x, y, z);
|
|
}
|
|
|
|
void RageDisplay::Scale( float x, float y, float z )
|
|
{
|
|
g_WorldStack.ScaleLocal(x, y, z);
|
|
}
|
|
|
|
void RageDisplay::RotateX( float deg )
|
|
{
|
|
g_WorldStack.RotateXLocal( deg );
|
|
}
|
|
|
|
void RageDisplay::RotateY( float deg )
|
|
{
|
|
g_WorldStack.RotateYLocal( deg );
|
|
}
|
|
|
|
void RageDisplay::RotateZ( float deg )
|
|
{
|
|
g_WorldStack.RotateZLocal( deg );
|
|
}
|
|
|
|
void RageDisplay::PostMultMatrix( const RageMatrix &m )
|
|
{
|
|
g_WorldStack.MultMatrix( m );
|
|
}
|
|
|
|
void RageDisplay::PreMultMatrix( const RageMatrix &m )
|
|
{
|
|
g_WorldStack.MultMatrixLocal( m );
|
|
}
|
|
|
|
void RageDisplay::LoadIdentity()
|
|
{
|
|
g_WorldStack.LoadIdentity();
|
|
}
|
|
|
|
void RageDisplay::LoadMenuPerspective( float fovDegrees, float fVanishPointX, float fVanishPointY )
|
|
{
|
|
/* fovDegrees == 0 looks the same as an ortho projection. However,
|
|
* we don't want to mess with the ModelView stack because
|
|
* EnterPerspectiveMode's preserve location feature expectes there
|
|
* not to be any camera transforms. So, do a true ortho projection
|
|
* if fovDegrees == 0. Perhaps it would be more convenient to keep
|
|
* separate model and view stacks like D3D?
|
|
*/
|
|
if( fovDegrees == 0 )
|
|
{
|
|
float left = 0, right = SCREEN_WIDTH, bottom = SCREEN_HEIGHT, top = 0;
|
|
g_ProjectionStack.LoadMatrix( GetOrthoMatrix(left, right, bottom, top, SCREEN_NEAR, SCREEN_FAR) );
|
|
g_ViewStack.LoadIdentity();
|
|
}
|
|
else
|
|
{
|
|
CLAMP( fovDegrees, 0.1f, 179.9f );
|
|
float fovRadians = fovDegrees / 180.f * PI;
|
|
float theta = fovRadians/2;
|
|
float fDistCameraFromImage = SCREEN_WIDTH/2 / tanf( theta );
|
|
|
|
fVanishPointX = SCALE( fVanishPointX, SCREEN_LEFT, SCREEN_RIGHT, SCREEN_RIGHT, SCREEN_LEFT );
|
|
fVanishPointY = SCALE( fVanishPointY, SCREEN_TOP, SCREEN_BOTTOM, SCREEN_BOTTOM, SCREEN_TOP );
|
|
|
|
fVanishPointX -= CENTER_X;
|
|
fVanishPointY -= CENTER_Y;
|
|
|
|
|
|
/* It's the caller's responsibility to push first. */
|
|
g_ProjectionStack.LoadMatrix(
|
|
GetFrustumMatrix(
|
|
(fVanishPointX-SCREEN_WIDTH/2)/fDistCameraFromImage,
|
|
(fVanishPointX+SCREEN_WIDTH/2)/fDistCameraFromImage,
|
|
(fVanishPointY+SCREEN_HEIGHT/2)/fDistCameraFromImage,
|
|
(fVanishPointY-SCREEN_HEIGHT/2)/fDistCameraFromImage,
|
|
1,
|
|
fDistCameraFromImage+1000 ) );
|
|
|
|
g_ViewStack.LoadMatrix(
|
|
RageLookAt(
|
|
-fVanishPointX+CENTER_X, -fVanishPointY+CENTER_Y, fDistCameraFromImage,
|
|
-fVanishPointX+CENTER_X, -fVanishPointY+CENTER_Y, 0,
|
|
0.0f, 1.0f, 0.0f) );
|
|
}
|
|
}
|
|
|
|
|
|
/* Switch from orthogonal to perspective view.
|
|
*
|
|
* Tricky: we want to maintain all of the zooms, rotations and translations
|
|
* that have been applied already. They're in our internal screen space (that
|
|
* is, 640x480 ortho). We can't simply leave them where they are, since they'll
|
|
* be applied before the perspective transform, which means they'll be in the
|
|
* wrong coordinate space.
|
|
*
|
|
* Handle translations (the right column of the transform matrix) to the viewport.
|
|
* Move rotations and scaling (0,0 through 1,1) to after the perspective transform.
|
|
* Actually, those might be able to stay where they are, I'm not sure; it's translations
|
|
* that are annoying. So, XXX: see if rots and scales can be left on the modelview
|
|
* matrix instead of messing with the projection matrix.
|
|
*
|
|
* When finished, the current position will be the "viewpoint" (at 0,0). negative
|
|
* Z goes into the screen, positive X and Y is right and down.
|
|
*/
|
|
//void RageDisplay::EnterPerspective(float fov, bool preserve_loc, float near_clip, float far_clip)
|
|
//{
|
|
// g_ProjectionStack.Push();
|
|
// g_WorldStack.Push();
|
|
//
|
|
// float aspect = SCREEN_WIDTH/(float)SCREEN_HEIGHT;
|
|
// g_ProjectionStack.LoadMatrix( GetPerspectiveMatrix(fov, aspect, near_clip, far_clip) );
|
|
// /* Flip the Y coordinate, so positive numbers go down. */
|
|
// g_ProjectionStack.Scale(1, -1, 1);
|
|
//
|
|
// if( preserve_loc )
|
|
// {
|
|
//
|
|
//RageMatrix matTop = *g_WorldStack.GetTop();
|
|
/* TODO: Come up with a more general way to handle this.
|
|
* It looks kind of hacky. -Chris */
|
|
// {
|
|
// /* Pull out the 2d translation. */
|
|
// float x = matTop.m[3][0], y = matTop.m[3][1];
|
|
//
|
|
// /* These values are where the viewpoint should be. By default, it's in the
|
|
// * center of the screen, and these values are from the top-left, so subtract
|
|
// * the difference. */
|
|
// x -= SCREEN_WIDTH/2;
|
|
// y -= SCREEN_HEIGHT/2;
|
|
// SetViewport(int(x), int(y));
|
|
// }
|
|
//
|
|
// /* Pull out the 2d rotations and scales. */
|
|
// {
|
|
// RageMatrix mat;
|
|
// RageMatrixIdentity(&mat);
|
|
// mat.m[0][0] = matTop.m[0][0];
|
|
// mat.m[0][1] = matTop.m[0][1];
|
|
// mat.m[1][0] = matTop.m[1][0];
|
|
// mat.m[1][1] = matTop.m[1][1];
|
|
// this->MultMatrix(mat);
|
|
// }
|
|
//
|
|
// /* We can't cope with perspective matrices or things that touch Z. (We shouldn't
|
|
// * have touched those while in 2d, anyway.) */
|
|
// ASSERT(matTop.m[0][2] == 0.f && matTop.m[0][3] == 0.f && matTop.m[1][2] == 0.f &&
|
|
// matTop.m[1][3] == 0.f && matTop.m[2][0] == 0.f && matTop.m[2][1] == 0.f &&
|
|
// matTop.m[2][2] == 1.f && matTop.m[3][2] == 0.f && matTop.m[3][3] == 1.f);
|
|
//
|
|
// /* Reset the matrix back to identity. */
|
|
// glMatrixMode( GL_MODELVIEW );
|
|
// glLoadIdentity();
|
|
// }
|
|
//}
|
|
|
|
void RageDisplay::CameraPushMatrix()
|
|
{
|
|
g_ProjectionStack.Push();
|
|
g_ViewStack.Push();
|
|
}
|
|
|
|
void RageDisplay::CameraPopMatrix()
|
|
{
|
|
g_ProjectionStack.Pop();
|
|
g_ViewStack.Pop();
|
|
}
|
|
|
|
|
|
/* gluLookAt. The result is pre-multiplied to the matrix (M = L * M) instead of
|
|
* post-multiplied. */
|
|
void RageDisplay::LoadLookAt(float fov, const RageVector3 &Eye, const RageVector3 &At, const RageVector3 &Up)
|
|
{
|
|
float aspect = SCREEN_WIDTH/(float)SCREEN_HEIGHT;
|
|
g_ProjectionStack.LoadMatrix( GetPerspectiveMatrix(fov, aspect, 1, 1000) );
|
|
/* Flip the Y coordinate, so positive numbers go down. */
|
|
g_ProjectionStack.Scale(1, -1, 1);
|
|
|
|
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;
|
|
|
|
return GetFrustumMatrix(xmin, xmax, ymin, ymax, zNear, zFar);
|
|
}
|
|
|
|
SDL_Surface *RageDisplay::CreateSurfaceFromPixfmt( PixelFormat pixfmt,
|
|
void *pixels, int width, int height, int pitch )
|
|
{
|
|
const PixelFormatDesc *tpf = GetPixelFormatDesc(pixfmt);
|
|
|
|
SDL_Surface *surf = SDL_CreateRGBSurfaceFrom(
|
|
pixels, width, height, tpf->bpp, pitch,
|
|
tpf->masks[0], tpf->masks[1], tpf->masks[2], tpf->masks[3]);
|
|
|
|
return surf;
|
|
}
|
|
|
|
RageDisplay::PixelFormat RageDisplay::FindPixelFormat(
|
|
int bpp, int Rmask, int Gmask, int Bmask, int Amask )
|
|
{
|
|
PixelFormatDesc tmp = { bpp, Rmask, Gmask, Bmask, Amask };
|
|
|
|
for(int pixfmt = 0; pixfmt < NUM_PIX_FORMATS; ++pixfmt)
|
|
{
|
|
const PixelFormatDesc *pf = GetPixelFormatDesc(PixelFormat(pixfmt));
|
|
if(!SupportsTextureFormat( PixelFormat(pixfmt) ))
|
|
continue;
|
|
|
|
if(memcmp(pf, &tmp, sizeof(tmp)))
|
|
continue;
|
|
return PixelFormat(pixfmt);
|
|
}
|
|
|
|
return NUM_PIX_FORMATS;
|
|
}
|
|
|
|
RageMatrix RageDisplay::GetFrustumMatrix( float l, float r, float b, float t, float zn, float zf )
|
|
{
|
|
// glFrustrum
|
|
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;
|
|
}
|