📄 opengl.cpp
字号:
/*=============================================================================
OpenGL.cpp: Unreal OpenGL support implementation.
Copyright 1997-1999 Epic Games, Inc. All Rights Reserved.
The UOpenGLRenderDevice class implements Unreal's abstract
URenderDevice class, which abstracts the concept of a "3d rendering
device".
Other URenderDevice subclasses include:
* USoftwareRenderDevice: Software renderer.
* UGlideRenderDevice: 3dfx Glide renderer.
* UDirect3DRenderDevice: Direct3D renderer.
Revision history:
* Created by Tim Sweeney
* Inspired by code written by Keith Leonard
=============================================================================*/
#include "OpenGLDrv.h"
/*-----------------------------------------------------------------------------
Globals.
-----------------------------------------------------------------------------*/
#define STDGL 1 /* Use standard GL driver or minidriver by default */
#define DYNAMIC_BIND 1 /* If 0, must static link to OpenGL32, Gdi32 */
#define GL_DLL (STDGL ? "OpenGL32.dll" : "3dfxgl.dll")
#define PYR(n) ((n)*((n+1))/2) /* Pyramid scaling function */
BYTE ScaleByteNormal [PYR(256)]; /* Regular normalization table */
BYTE ScaleByteBrighten[PYR(256)]; /* 2X overbrightening normalization table */
FPlane One4(1,1,1,1); /* 4 consecutive floats for OpenGL */
#define OVERBRIGHT 1.4f
/*-----------------------------------------------------------------------------
UOpenGLDrv.
-----------------------------------------------------------------------------*/
//
// A OpenGL rendering device attached to a viewport.
//
class UOpenGLRenderDevice : public URenderDevice
{
DECLARE_CLASS(UOpenGLRenderDevice,URenderDevice,CLASS_Config)
// Information about a cached texture.
struct FCachedTexture
{
GLuint Id;
INT BaseMip;
INT UBits, VBits;
INT UCopyBits, VCopyBits;
FPlane ColorNorm, ColorRenorm;
};
// Permanent variables.
HGLRC hRC;
HWND hWnd;
HDC hDC;
UBOOL WasFullscreen;
TMap<QWORD,FCachedTexture> LocalBindMap, *BindMap;
TArray<FPlane> Modes;
UViewport* Viewport;
// Timing.
DWORD BindCycles, ImageCycles, ComplexCycles, GouraudCycles, TileCycles;
// Hardware constraints.
INT MaxLogUOverV;
INT MaxLogVOverU;
INT MinLogTextureSize;
INT MaxLogTextureSize;
UBOOL UseZTrick;
UBOOL UseMultiTexture;
UBOOL UsePalette;
UBOOL Multipass1X;
UBOOL DoPrecache;
UBOOL ShareLists;
UBOOL AlwaysMipmap;
BYTE* ScaleByte;
// Hit info.
BYTE* HitData;
INT* HitSize;
// Lock variables.
UBOOL ZTrickToggle;
INT ZTrickFunc;
FPlane FlashScale, FlashFog;
FLOAT RProjZ, Aspect;
DWORD CurrentPolyFlags;
TArray<INT> GLHitData;
struct FTexInfo
{
QWORD CurrentCacheID;
FLOAT UMult;
FLOAT VMult;
FLOAT UPan;
FLOAT VPan;
FPlane ColorNorm;
FPlane ColorRenorm;
} TexInfo[4];
FLOAT RFX2, RFY2;
// Static variables.
static TMap<QWORD,FCachedTexture> SharedBindMap;
static TArray<HGLRC> AllContexts;
static INT NumDevices;
static INT LockCount;
static HGLRC hCurrentRC;
static HMODULE hModuleGlMain;
static HMODULE hModuleGlGdi;
// GL functions.
#define GL_EXT(name) static UBOOL SUPPORTS##name;
#define GL_PROC(ext,ret,func,parms) static ret (__stdcall *func)parms;
#include "OpenGLFuncs.h"
#undef GL_EXT
#undef GL_PROC
// Implementation.
UBOOL FindExt( const TCHAR* Name )
{
guard(UOpenGLRenderDevice::FindExt);
UBOOL Result = appStrfind(appFromAnsi((char*)glGetString(GL_EXTENSIONS)),Name)!=NULL;
if( Result )
debugf( NAME_Init, TEXT("Device supports: %s"), Name );
return Result;
unguard;
}
void FindProc( void*& ProcAddress, char* Name, char* SupportName, UBOOL& Supports, UBOOL AllowExt )
{
guard(UOpenGLRenderDevice::FindProc);
#if DYNAMIC_BIND
if( !ProcAddress )
ProcAddress = GetProcAddress( hModuleGlMain, Name );
if( !ProcAddress )
ProcAddress = GetProcAddress( hModuleGlGdi, Name );
#endif
if( !ProcAddress && Supports && AllowExt )
ProcAddress = wglGetProcAddress( Name );
if( !ProcAddress )
{
if( Supports )
debugf( TEXT(" Missing function '%s' for '%s' support"), appFromAnsi(Name), appFromAnsi(SupportName) );
Supports = 0;
}
unguard;
}
void FindProcs( UBOOL AllowExt )
{
guard(UOpenGLDriver::FindProcs);
#define GL_EXT(name) if( AllowExt ) SUPPORTS##name = FindExt( TEXT(#name)+1 );
#define GL_PROC(ext,ret,func,parms) FindProc( *(void**)&func, #func, #ext, SUPPORTS##ext, AllowExt );
#include "OpenGLFuncs.h"
#undef GL_EXT
#undef GL_PROC
unguard;
}
UBOOL FailedInitf( const TCHAR* Fmt, ... )
{
TCHAR TempStr[4096];
GET_VARARGS( TempStr, ARRAY_COUNT(TempStr), Fmt );
debugf( NAME_Init, TempStr );
Exit();
return 0;
}
void MakeCurrent()
{
guard(UOpenGLRenderDevice::MakeCurrent);
check(hRC);
check(hDC);
if( hCurrentRC!=hRC )
{
verify(wglMakeCurrent(hDC,hRC));
hCurrentRC = hRC;
}
unguard;
}
void Check( const char* Tag )
{
GLenum Error = glGetError();
if( Error!=GL_NO_ERROR )
{
const TCHAR* Msg=TEXT("Unknown");
switch( Error )
{
case GL_NO_ERROR:
Msg = TEXT("GL_NO_ERROR");
break;
case GL_INVALID_ENUM:
Msg = TEXT("GL_INVALID_ENUM");
break;
case GL_INVALID_VALUE:
Msg = TEXT("GL_INVALID_VALUE");
break;
case GL_INVALID_OPERATION:
Msg = TEXT("GL_INVALID_OPERATION");
break;
case GL_STACK_OVERFLOW:
Msg = TEXT("GL_STACK_OVERFLOW");
break;
case GL_STACK_UNDERFLOW:
Msg = TEXT("GL_STACK_UNDERFLOW");
break;
case GL_OUT_OF_MEMORY:
Msg = TEXT("GL_OUT_OF_MEMORY");
break;
};
appErrorf( TEXT("OpenGL Error: %s (%s)"), Msg, Tag );
}
}
void SetNoTexture( INT Multi )
{
guard(UOpenGLRenderDevice::SetNoTexture);
if( TexInfo[Multi].CurrentCacheID != 0 )
{
// Set small white texture.
clock(BindCycles);
glBindTexture( GL_TEXTURE_2D, 0 );
TexInfo[Multi].CurrentCacheID = 0;
unclock(BindCycles);
}
unguard;
}
void SetTexture( INT Multi, FTextureInfo& Info, DWORD PolyFlags, FLOAT PanBias )
{
guard(UOpenGLRenderDevice::SetTexture);
// Set panning.
FTexInfo& Tex = TexInfo[Multi];
Tex.UPan = Info.Pan.X + PanBias*Info.UScale;
Tex.VPan = Info.Pan.Y + PanBias*Info.VScale;
// Find in cache.
if( Info.CacheID==Tex.CurrentCacheID && !Info.bRealtimeChanged )
return;
// Make current.
clock(BindCycles);
Tex.CurrentCacheID = Info.CacheID;
FCachedTexture *Bind=BindMap->Find(Info.CacheID), *ExistingBind=Bind;
if( !Bind )
{
// Figure out OpenGL-related scaling for the texture.
Bind = &BindMap->Add( Info.CacheID, FCachedTexture() );
glGenTextures( 1, &Bind->Id );
Bind->BaseMip = Min(0,Info.NumMips-1);
Bind->UCopyBits = 0;
Bind->VCopyBits = 0;
Bind->UBits = Info.Mips[Bind->BaseMip]->UBits;
Bind->VBits = Info.Mips[Bind->BaseMip]->VBits;
if( Bind->UBits-Bind->VBits > MaxLogUOverV )
{
Bind->VCopyBits += (Bind->UBits-Bind->VBits)-MaxLogUOverV;
Bind->VBits = Bind->UBits-MaxLogUOverV;
}
if( Bind->VBits-Bind->UBits > MaxLogVOverU )
{
Bind->UCopyBits += (Bind->VBits-Bind->UBits)-MaxLogVOverU;
Bind->UBits = Bind->VBits-MaxLogVOverU;
}
if( Bind->UBits < MinLogTextureSize )
{
Bind->UCopyBits += MinLogTextureSize - Bind->UBits;
Bind->UBits += MinLogTextureSize - Bind->UBits;
}
if( Bind->VBits < MinLogTextureSize )
{
Bind->VCopyBits += MinLogTextureSize - Bind->VBits;
Bind->VBits += MinLogTextureSize - Bind->VBits;
}
if( Bind->UBits > MaxLogTextureSize )
{
Bind->BaseMip += Bind->UBits-MaxLogTextureSize;
Bind->VBits -= Bind->UBits-MaxLogTextureSize;
Bind->UBits = MaxLogTextureSize;
if( Bind->VBits < 0 )
{
Bind->VCopyBits = -Bind->VBits;
Bind->VBits = 0;
}
}
if( Bind->VBits > MaxLogTextureSize )
{
Bind->BaseMip += Bind->VBits-MaxLogTextureSize;
Bind->UBits -= Bind->VBits-MaxLogTextureSize;
Bind->VBits = MaxLogTextureSize;
if( Bind->UBits < 0 )
{
Bind->UCopyBits = -Bind->UBits;
Bind->UBits = 0;
}
}
}
glBindTexture( GL_TEXTURE_2D, Bind->Id );
unclock(BindCycles);
// Account for all the impact on scale normalization.
Tex.UMult = 1.0 / (Info.UScale * (Info.USize << Bind->UCopyBits));
Tex.VMult = 1.0 / (Info.VScale * (Info.VSize << Bind->VCopyBits));
// Upload if needed.
if( !ExistingBind || Info.bRealtimeChanged )
{
// Cleanup texture flags.
Info.Load();
Info.bRealtimeChanged = 0;
// Set maximum color.
Info.CacheMaxColor();
Bind->ColorNorm = Info.Palette ? Info.MaxColor->Plane() : FPlane(Info.MaxColor->B/127.f,Info.MaxColor->G/127.f,Info.MaxColor->R/127.f,0);
Bind->ColorNorm.W = 1;
if( Multipass1X )
{
Bind->ColorRenorm.X = Min( Bind->ColorNorm.X * OVERBRIGHT, 1.0f );
Bind->ColorRenorm.Y = Min( Bind->ColorNorm.Y * OVERBRIGHT, 1.0f );
Bind->ColorRenorm.Z = Min( Bind->ColorNorm.Z * OVERBRIGHT, 1.0f );
}
else Bind->ColorRenorm = Bind->ColorNorm;
// Setup scaling.
BYTE* ScaleR = &ScaleByte[PYR(Info.MaxColor->R)];
BYTE* ScaleG = &ScaleByte[PYR(Info.MaxColor->G)];
BYTE* ScaleB = &ScaleByte[PYR(Info.MaxColor->B)];
// Generate the palette.
FColor LocalPal[256], *NewPal=Info.Palette, TempColor(0,0,0,0);
UBOOL Paletted = UsePalette && Info.Palette && !(PolyFlags & PF_Masked) && Info.Palette[0].A==255;//!!hw might support alpha palettes?
if( Info.Palette )
{
TempColor = Info.Palette[0];
if( PolyFlags & PF_Masked )
Info.Palette[0] = FColor(0,0,0,0);
NewPal = LocalPal;
for( INT i=0; i<256; i++ )
{
FColor& Src = Info.Palette[i];
NewPal[i].R = ScaleR[Src.R];
NewPal[i].G = ScaleG[Src.G];
NewPal[i].B = ScaleB[Src.B];
NewPal[i].A = Src.A;
}
if( Paletted )
glColorTableEXT( GL_TEXTURE_2D, GL_RGBA, 256, GL_RGBA, GL_UNSIGNED_BYTE, NewPal );
}
// Download the texture.
clock(ImageCycles);
FMemMark Mark(GMem);
BYTE* Compose = New<BYTE>( GMem, (1<<(Bind->UBits+Bind->VBits))*4 );
UBOOL SkipMipmaps = Info.NumMips==1 && !AlwaysMipmap;
for( INT Level=0; Level<=Max(Bind->UBits,Bind->VBits); Level++ )
{
// Convert the mipmap.
INT MipIndex=Bind->BaseMip+Level, StepBits=0;
if( MipIndex>=Info.NumMips )
{
StepBits = MipIndex - (Info.NumMips - 1);
MipIndex = Info.NumMips - 1;
}
FMipmapBase* Mip = Info.Mips[MipIndex];
BYTE* Src = (BYTE*)Compose;
DWORD Mask = Mip->USize-1;
GLuint SourceFormat, InternalFormat;
if( Paletted )
{
guard(ConvertP8_P8);
SourceFormat = GL_COLOR_INDEX;
InternalFormat = GL_COLOR_INDEX8_EXT;
BYTE* Ptr = (BYTE*)Compose;
for( INT i=0; i<(1<<Max(0,Bind->VBits-Level)); i++ )
{
BYTE* Base = (BYTE*)Mip->DataPtr + ((i<<StepBits)&(Mip->VSize-1))*Mip->USize;
for( INT j=0; j<(1<<Max(0,Bind->UBits-Level+StepBits)); j+=(1<<StepBits) )
*Ptr++ = Base[j&Mask];
}
unguard;
}
else if( Info.Palette )
{
guard(ConvertP8_RGBA8888);
SourceFormat = GL_RGBA;
InternalFormat = GL_RGBA8; // GL_RGBA4!!
FColor* Ptr = (FColor*)Compose;
for( INT i=0; i<(1<<Max(0,Bind->VBits-Level)); i++ )
{
BYTE* Base = (BYTE*)Mip->DataPtr + ((i<<StepBits)&(Mip->VSize-1))*Mip->USize;
for( INT j=0; j<(1<<Max(0,Bind->UBits-Level+StepBits)); j+=(1<<StepBits) )
*Ptr++ = NewPal[Base[j&Mask]];
}
unguard;
}
else
{
guard(ConvertBGRA7777_RGBA8888);
SourceFormat = GL_RGBA;
InternalFormat = GL_RGBA8;
FColor* Ptr = (FColor*)Compose;
for( INT i=0; i<(1<<Max(0,Bind->VBits-Level)); i++ )
{
FColor* Base = (FColor*)Mip->DataPtr + Min<DWORD>((i<<StepBits)&(Mip->VSize-1),Info.VClamp-1)*Mip->USize;
for( INT j=0; j<(1<<Max(0,Bind->UBits-Level+StepBits)); j+=(1<<StepBits) )
{
FColor& Src = Base[Min<DWORD>(j&Mask,Info.UClamp-1)];
Ptr->R = ScaleB[Src.B];
Ptr->G = ScaleG[Src.G];
Ptr->B = ScaleR[Src.R];
Ptr->A = Src.A*2;
Ptr++;
}
}
unguard;
}
if( ExistingBind && 0 )//crashes frequently on TNT for Apache. My bug or theirs?
{
guard(glTexSubImage2D);
glTexSubImage2D( GL_TEXTURE_2D, Level, 0, 0, 1<<Max(0,Bind->UBits-Level), 1<<Max(0,Bind->VBits-Level), SourceFormat, GL_UNSIGNED_BYTE, Src );
unguard;
}
else
{
guard(glTexImage2D);
glTexImage2D( GL_TEXTURE_2D, Level, InternalFormat, 1<<Max(0,Bind->UBits-Level), 1<<Max(0,Bind->VBits-Level), 0, SourceFormat, GL_UNSIGNED_BYTE, Src );
unguard;
}
if( SkipMipmaps )
break;
}
Mark.Pop();
unclock(ImageCycles);
// Set texture state.
if( !(PolyFlags & PF_NoSmooth) )
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SkipMipmaps ? GL_LINEAR : GL_LINEAR_MIPMAP_NEAREST );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, SkipMipmaps ? GL_NEAREST : GL_NEAREST_MIPMAP_NEAREST );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
// Cleanup.
if( Info.Palette )
Info.Palette[0] = TempColor;
if( SupportsLazyTextures )
Info.Unload();
}
// Copy color norm.
Tex.ColorNorm = Bind->ColorNorm;
Tex.ColorRenorm = Bind->ColorRenorm;
unguard;
}
void SetBlend( DWORD PolyFlags )
{
guardSlow(UOpenGLRenderDevice::SetBlend);
// Adjust PolyFlags according to Unreal's precedence rules.
if( !(PolyFlags & (PF_Translucent|PF_Modulated)) )
PolyFlags |= PF_Occlude;
else if( PolyFlags & PF_Translucent )
PolyFlags &= ~PF_Masked;
// Detect changes in the blending modes.
DWORD Xor = CurrentPolyFlags^PolyFlags;
if( Xor & (PF_Translucent|PF_Modulated|PF_Invisible|PF_Occlude|PF_Masked|PF_Environment) )
{
if( Xor&(PF_Translucent|PF_Modulated|PF_Environment) )
{
if( PolyFlags & PF_Translucent )
{
glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_COLOR );
}
else if( PolyFlags & PF_Modulated )
{
glBlendFunc( GL_DST_COLOR, GL_SRC_COLOR );
}
else if( PolyFlags & PF_Environment )
{
glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
}
else
{
glBlendFunc( GL_ONE, GL_ZERO );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -