tenebrae2/macosx/gl_vidosx.m
2003-01-17 21:18:53 +00:00

2104 lines
69 KiB
Objective-C

//___________________________________________________________________________________________________________nFO
// "gl_vidosx.m" - MacOS X OpenGL Video driver
//
// Written by: Axel "awe" Wefers [mailto:awe@fruitz-of-dojo.de].
// (C)2001-2002 Fruitz Of Dojo [http://www.fruitz-of-dojo.de].
//
// Quakeª is copyrighted by id software [http://www.idsoftware.com].
//
// Version History:
//
// TenebraeQuake:
// v1.0.0: Initial release.
//
// Standard Quake branch [pre-TenebraeQuake]:
// v1.0.8: Fixed an issue with the console aspect, if apspect ratio was not 4:3.
// Added support for gl_arb_multisample.
// v1.0.7: Brings back the video options menu.
// "vid_wait" now availavle via video options.
// ATI Radeon only:
// Added support for FSAA, via variable "gl_fsaa" or video options.
// Added support for Truform, via variable "gl_truform" or video options.
// Added support for anisotropic texture filtering, via variable "gl_anisotropic" or options.
// v1.0.5: Added "minimized in Dock" mode.
// Displays are now catured manually due to a bug with CGReleaseAllDisplays().
// Reduced the fade duration to 1.0s.
// v1.0.4: Fixed continuous console output, if gamma setting fails.
// Fixed a multi-monitor issue.
// v1.0.3: Enables setting the gamma via the brightness slider at the options dialog.
// Enable/Disable VBL syncing via "vid_wait".
// v1.0.2: GLQuake/GLQuakeWorld:
// Fixed a performance issue [see "gl_rsurf.c"].
// Default value of "gl_keeptjunctions" is now "1" [see "gl_rmain.c"].
// Added "DrawSprocket" style gamma fading at game start/end.
// Some internal changes.
// GLQuakeWorld:
// Fixed console width/height bug with resolutions other than 640x480 [was always 640x480].
// v1.0.1: Initial release.
//______________________________________________________________________________________________________iNCLUDES
#pragma mark =Includes=
#include "quakedef.h"
#import <AppKit/AppKit.h>
#import <IOKit/graphics/IOGraphicsTypes.h>
#import <mach-o/dyld.h>
#import <OpenGL/OpenGL.h>
#import <OpenGL/gl.h>
#import <OpenGL/glu.h>
#import <OpenGL/glext.h>
#pragma mark -
//_______________________________________________________________________________________________________dEFINES
#pragma mark =Defines=
#define HWA_OPENGL_ONLY // allow only systems with HWA OpenGL.
#define USE_BUFFERED_WINDOW // the appearance of buffered windows is nicer.
#define ALLOW_MULTITEXTURE_EXTENSION // doesn't seem to work with current MacOS X [10.1].
#define FADE_DURATION 1.0f
#define MAX_DISPLAYS 100 // max displays for gamma fading.
#define WARP_WIDTH 320
#define WARP_HEIGHT 200
#define CONSOLE_MIN_WIDTH 320
#define CONSOLE_MIN_HEIGHT 200
#define ATI_FSAA_LEVEL 510 // required for CGLSetParamter () to enable FSAA.
#define FONT_WIDTH 8
#define FONT_HEIGHT 8
#define FIRST_MENU_LINE 40
#pragma mark -
//________________________________________________________________________________________________________mACROS
#pragma mark =Macros=
#define SQUARE(A) ((A) * (A))
#pragma mark -
//_________________________________________________________________________________________________________eNUMS
#pragma mark =Enums=
enum {
VIDEO_WAIT,
VIDEO_FSAA,
VIDEO_ANISOTROPIC,
VIDEO_TRUFORM
};
#pragma mark -
//__________________________________________________________________________________________________________mISC
#pragma mark =TypeDefs=
typedef unsigned int UInt;
typedef signed int SInt;
typedef struct {
CGDirectDisplayID displayID;
CGGammaValue component[9];
} osxCGGammaStruct;
typedef struct {
UInt16 Width;
UInt16 Height;
float Refresh;
char Desc[32];
} osxCGModeListStruct;
#pragma mark -
//____________________________________________________________________________________________________iNTERFACES
#pragma mark =ObjC Interfaces=
@interface NSOpenGLContext (CGLContextAccess)
- (CGLContextObj) cglContext;
@end
@interface QuakeView : NSView
@end
#pragma mark -
//_____________________________________________________________________________________________________vARIABLES
#pragma mark =Variables=
const char *gl_renderer,
*gl_vendor,
*gl_version,
*gl_extensions;
extern qboolean gMouseEnabled;
cvar_t vid_mode = { "vid_mode", "0", 0 };
cvar_t vid_redrawfull = { "vid_redrawfull", "0", 0 };
cvar_t vid_wait = { "vid_wait", "1", 1 };
cvar_t _vid_default_mode = { "_vid_default_mode", "0", 1 };
cvar_t _vid_default_blit_mode = { "_vid_default_blit_mode", "0", 1 };
cvar_t _windowed_mouse = { "_windowed_mouse","0", 0 };
cvar_t gl_anisotropic = { "gl_anisotropic", "0", 1 };
cvar_t gl_fsaa = { "gl_fsaa", "1", 0 };
cvar_t gl_truform = { "gl_truform", "-1", 1 };
cvar_t gl_ztrick = { "gl_ztrick", "1" };
unsigned d_8to24table[256];
unsigned char d_15to8table[65536];
unsigned char d_8to8graytable[256];
NSDictionary *gDisplayMode;
CGDirectDisplayID gDisplayList[MAX_DISPLAYS];
CGDisplayCount gDisplayCount;
NSWindow *gWindow;
qboolean gDisplayFullscreen,
isPermedia = NO,
gl_palettedtex = NO,
gl_nvcombiner = NO,
gl_geforce3 = NO,
gl_radeon = NO,
gl_var = NO,
gl_fsaaavailable = NO,
gl_mtexable = NO,
gl_pntriangles = NO,
gl_texturefilteranisotropic = NO;
Boolean gFadeAllDisplays,
gWindowedMouse = NO,
gIsMinimized = NO;
UInt gDisplay;
SInt texture_extension_number = 1,
texture_mode = GL_LINEAR,
gARBMultiSamples = 0;
float gWindowPosX,
gWindowPosY;
GLfloat gldepthmin,
gldepthmax,
gl_texureanisotropylevel = 1.0f;
void * AGP_Buffer = NULL;
static const float gGLTruformAmbient[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
static CGDirectDisplayID gCapturedDisplayList[MAX_DISPLAYS];
static CGDisplayCount gCapturedDisplayCount = 0;
static NSDictionary *gOriginalMode;
static NSOpenGLContext *gGLContext;
static osxCGGammaStruct *gOriginalGamma = NULL;
static QuakeView *gWindowView = NULL;
static NSImage *gMiniWindow = NULL,
*gOldIcon = NULL;
static Boolean gDisplayIs8Bit,
gGLAnisotropic = NO;
static UInt gDisplayWidth,
gDisplayHeight,
gDisplayDepth;
static UInt32 *gMiniBuffer = NULL;
static float vid_gamma = 1.0f,
gVidWait = 0.0f,
gFSAALevel = 1.0f,
gPNTriangleLevel = -1.0f;
static SInt8 gMenuMaxLine,
gMenuLine = FIRST_MENU_LINE,
gMenuItem = VIDEO_WAIT;
#pragma mark -
//___________________________________________________________________________________________fUNCTION_pROTOTYPES
#pragma mark =Function ProtoTypes =
extern void M_Menu_Options_f (void);
extern void M_Print (int cx, int cy, char *str);
extern void M_DrawCharacter (int cx, int line, int num);
extern void M_DrawPic (int x, int y, qpic_t *pic);
extern void IN_ShowCursor (Boolean);
extern double Sys_FloatTime (void);
extern void GL_CreateShadersRadeon (void);
extern qboolean GL_LookupRadeonSymbols (void);
static void VID_CheckGamma (unsigned char *);
static Boolean VID_CaptureDisplays (Boolean);
static Boolean VID_ReleaseDisplays (Boolean);
static Boolean VID_FadeGammaInit (Boolean);
static void VID_FadeGammaOut (Boolean, float);
static void VID_FadeGammaIn (Boolean, float);
static void VID_SetWait (UInt32);
static Boolean VID_SetDisplayMode (void);
static void VID_RestoreOldIcon (void);
static void VID_MenuDraw (void);
static void VID_MenuKey (int theKey);
Boolean GL_CheckARBMultisampleExtension (CGDirectDisplayID theDisplay);
void * GL_GetProcAddress (const char *theName, qboolean theSafeMode);
static void GL_CheckPalettedTexture (void);
static void GL_CheckMultiTextureExtensions (void);
static void GL_CheckDiffuseBumpMappingExtensions (void);
static void GL_CheckSpecularBumpMappingExtensions (void);
static void GL_CheckGeforce3Extensions (void);
static void GL_CheckRadeonExtensions (void);
static void GL_CheckVertexArrayRange (void);
static void GL_CheckPNTrianglesExtensions (void);
static void GL_CheckSwitchFSAAOnTheFly (void);
static void GL_CheckTextureFilterAnisotropic (void);
static void GL_Init (void);
static void GL_SetFSAA (UInt32 theFSAALevel);
static void GL_RenderInsideDock (void);
#pragma mark -
//______________________________________________________________________________________________VID_LockBuffer()
#ifdef QUAKE_WORLD
void VID_LockBuffer (void)
{
}
#endif /* QUAKE_WORLD */
//____________________________________________________________________________________________VID_UnlockBuffer()
#ifdef QUAKE_WORLD
void VID_UnlockBuffer (void)
{
}
#endif /* QUAKE_WORLD */
//__________________________________________________________________________________________________VID_Is8bit()
qboolean VID_Is8bit (void)
{
return (gDisplayIs8Bit);
}
//______________________________________________________________________________________________VID_CheckGamma()
void VID_CheckGamma (unsigned char *thePalette)
{
float myNewValue;
unsigned char myPalette[768];
SInt i;
if ((i = COM_CheckParm ("-gamma")) == 0)
{
if ((gl_renderer && strstr (gl_renderer, "Voodoo")) ||
(gl_vendor && strstr (gl_vendor, "3Dfx")))
{
vid_gamma = 1.0f;
}
else
{
vid_gamma = 0.6f;
}
}
else
{
vid_gamma = Q_atof (com_argv[i+1]);
}
for (i = 0 ; i < 768 ; i++)
{
myNewValue = pow ((thePalette[i] + 1) / 256.0f, vid_gamma) * 255 + 0.5f;
if (myNewValue < 0.0f)
myNewValue = 0.0f;
if (myNewValue > 255.0f)
myNewValue = 255.0f;
myPalette[i] = (unsigned char) myNewValue;
}
memcpy (thePalette, myPalette, sizeof (myPalette));
}
//______________________________________________________________________________________________VID_SetPalette()
void VID_SetPalette (UInt8 *thePalette)
{
UInt myRedComp, myGreenComp, myBlueComp,
myColorValue, myBestValue,
*myColorTable;
SInt myNewRedComp, myNewGreenComp, myNewBlueComp,
myCurDistance, myBestDistance;
UInt16 i;
UInt8 *myPalette,
*myShadeTable;
myPalette = thePalette;
myShadeTable = d_8to8graytable;
myColorTable = d_8to24table;
for (i = 0; i < 256; i++)
{
myRedComp = myPalette[0];
myGreenComp = myPalette[1];
myBlueComp = myPalette[2];
myColorValue = (myRedComp << 24) + (myGreenComp << 16) + (myBlueComp << 8) + (0xFF << 0);
*myColorTable++ = myColorValue;
*myShadeTable++ = (myRedComp + myGreenComp + myBlueComp) / 3;
myPalette += 3;
}
d_8to24table[255] &= 0xffffff00;
for (i = 0; i < (1 << 15); i++)
{
myRedComp = ((i & 0x001F) << 3) + 4;
myGreenComp = ((i & 0x03E0) >> 2) + 4;
myBlueComp = ((i & 0x7C00) >> 7) + 4;
myPalette = (UInt8 *) d_8to24table;
for (myColorValue = 0 , myBestValue = 0, myBestDistance = SQUARE(10000);
myColorValue < 256;
myColorValue++, myPalette += 4)
{
myNewRedComp = (SInt) myRedComp - (SInt) myPalette[0];
myNewGreenComp = (SInt) myGreenComp - (SInt) myPalette[1];
myNewBlueComp = (SInt) myBlueComp - (SInt) myPalette[2];
myCurDistance = SQUARE(myNewRedComp) + SQUARE(myNewGreenComp) + SQUARE(myNewBlueComp);
if (myCurDistance < myBestDistance)
{
myBestValue = myColorValue;
myBestDistance = myCurDistance;
}
}
d_15to8table[i] = myBestValue;
}
}
//____________________________________________________________________________________________VID_ShiftPalette()
void VID_ShiftPalette (UInt8 *thePalette)
{
// nothing to do here...
}
//_________________________________________________________________________________________________VID_SetMode()
SInt VID_SetMode (SInt modenum, UInt8 *thePalette)
{
return (1);
}
//_________________________________________________________________________________________VID_CaptureDisplays()
Boolean VID_CaptureDisplays (Boolean theCaptureAllDisplays)
{
CGDisplayErr myError;
SInt16 i;
if (theCaptureAllDisplays == NO)
{
if (CGDisplayIsCaptured (gDisplayList[gDisplay]) == NO)
{
myError = CGDisplayCapture (gDisplayList[gDisplay]);
if (myError != CGDisplayNoErr)
{
return (NO);
}
}
return (YES);
}
// are the displays already captured?
if (gCapturedDisplayCount != 0)
{
return (YES);
}
// we have to loop manually thru each display, since there is a bug with CGReleaseAllDisplays().
// [only active displays will be released and not all captured!]
// get the active display list:
myError = CGGetActiveDisplayList (MAX_DISPLAYS, gCapturedDisplayList, &gCapturedDisplayCount);
if (myError != CGDisplayNoErr || gCapturedDisplayCount == 0)
{
gCapturedDisplayCount = 0;
return (NO);
}
// capture each active display:
for (i = 0; i < gCapturedDisplayCount; i++)
{
myError = CGDisplayCapture (gCapturedDisplayList[i]);
if (myError != CGDisplayNoErr)
{
for (; i >= 0; i--)
{
CGDisplayRelease (gCapturedDisplayList[i]);
}
gCapturedDisplayCount = 0;
return (NO);
}
}
return (YES);
}
//_________________________________________________________________________________________VID_ReleaseDisplays()
Boolean VID_ReleaseDisplays (Boolean theCaptureAllDisplays)
{
CGDisplayErr myError;
SInt16 i;
if (theCaptureAllDisplays == NO)
{
if (CGDisplayIsCaptured (gDisplayList[gDisplay]) == YES)
{
myError = CGDisplayRelease (gDisplayList[gDisplay]);
if (myError != CGDisplayNoErr)
{
return (NO);
}
}
return (YES);
}
// are the displays already released?
if (gCapturedDisplayCount == 0)
{
return (YES);
}
// release each captured display:
for (i = 0; i < gCapturedDisplayCount; i++)
{
CGDisplayRelease (gCapturedDisplayList[i]);
}
gCapturedDisplayCount = 0;
return (YES);
}
//___________________________________________________________________________________________VID_FadeGammaInit()
Boolean VID_FadeGammaInit (Boolean theFadeOnAllDisplays)
{
static Boolean myFadeOnAllDisplays;
CGDisplayErr myError;
UInt32 i;
// if init fails, no gamma fading will be used!
if (gOriginalGamma != NULL)
{
// initialized, but did we change the number of displays?
if (theFadeOnAllDisplays == myFadeOnAllDisplays)
{
return (YES);
}
free (gOriginalGamma);
gOriginalGamma = NULL;
}
// get the list of displays, in case something bad is going on:
if (gDisplayList == NULL || gDisplayCount == 0)
{
return (NO);
}
if (gDisplay > gDisplayCount)
{
gDisplay = 0;
}
if (theFadeOnAllDisplays == NO)
{
gDisplayList[0] = gDisplayList[gDisplay];
gDisplayCount = 1;
gDisplay = 0;
}
// get memory for our original gamma table(s):
gOriginalGamma = malloc (sizeof (osxCGGammaStruct) * gDisplayCount);
if (gOriginalGamma == NULL)
{
return (NO);
}
// store the original gamma values within this table(s):
for (i = 0; i < gDisplayCount; i++)
{
if (gDisplayCount == 1)
{
gOriginalGamma[i].displayID = gDisplayList[gDisplay];
}
else
{
gOriginalGamma[i].displayID = gDisplayList[i];
}
myError = CGGetDisplayTransferByFormula (gOriginalGamma[i].displayID,
&gOriginalGamma[i].component[0],
&gOriginalGamma[i].component[1],
&gOriginalGamma[i].component[2],
&gOriginalGamma[i].component[3],
&gOriginalGamma[i].component[4],
&gOriginalGamma[i].component[5],
&gOriginalGamma[i].component[6],
&gOriginalGamma[i].component[7],
&gOriginalGamma[i].component[8]);
if (myError != CGDisplayNoErr)
{
free (gOriginalGamma);
gOriginalGamma = NULL;
return (NO);
}
}
myFadeOnAllDisplays = theFadeOnAllDisplays;
return (YES);
}
//____________________________________________________________________________________________VID_FadeGammaOut()
void VID_FadeGammaOut (Boolean theFadeOnAllDisplays, float theDuration)
{
osxCGGammaStruct myCurGamma;
float myStartTime = 0.0f, myCurScale = 0.0f;
UInt32 i, j;
// check if initialized:
if (VID_FadeGammaInit (theFadeOnAllDisplays) == NO)
{
return;
}
// get the time of the fade start:
myStartTime = Sys_FloatTime ();
// fade for the choosen duration:
while (1)
{
// calculate the current scale and clamp it:
if (theDuration > 0.0f)
{
myCurScale = 1.0f - (Sys_FloatTime () - myStartTime) / theDuration;
if (myCurScale < 0.0f)
{
myCurScale = 0.0f;
}
}
// fade the gamma for each display:
for (i = 0; i < gDisplayCount; i++)
{
// calculate the current intensity for each color component:
for (j = 1; j < 9; j += 3)
{
myCurGamma.component[j] = myCurScale * gOriginalGamma[i].component[j];
}
// set the current gamma:
CGSetDisplayTransferByFormula (gOriginalGamma[i].displayID,
gOriginalGamma[i].component[0],
myCurGamma.component[1],
gOriginalGamma[i].component[2],
gOriginalGamma[i].component[3],
myCurGamma.component[4],
gOriginalGamma[i].component[5],
gOriginalGamma[i].component[6],
myCurGamma.component[7],
gOriginalGamma[i].component[8]);
}
// are we finished?
if(myCurScale <= 0.0f)
{
break;
}
}
}
//_____________________________________________________________________________________________VID_FadeGammaIn()
void VID_FadeGammaIn (Boolean theFadeOnAllDisplays, float theDuration)
{
osxCGGammaStruct myCurGamma;
float myStartTime = 0.0f, myCurScale = 1.0f;
UInt32 i, j;
// check if initialized:
if (gOriginalGamma == NULL)
{
return;
}
// get the time of the fade start:
myStartTime = Sys_FloatTime ();
// fade for the choosen duration:
while (1)
{
// calculate the current scale and clamp it:
if (theDuration > 0.0f)
{
myCurScale = (Sys_FloatTime () - myStartTime) / theDuration;
if (myCurScale > 1.0f)
{
myCurScale = 1.0f;
}
}
// fade the gamma for each display:
for (i = 0; i < gDisplayCount; i++)
{
// calculate the current intensity for each gamma component:
for (j = 1; j < 9; j += 3)
{
myCurGamma.component[j] = myCurScale * gOriginalGamma[i].component[j];
}
// set the current gamma:
CGSetDisplayTransferByFormula (gOriginalGamma[i].displayID,
gOriginalGamma[i].component[0],
myCurGamma.component[1],
gOriginalGamma[i].component[2],
gOriginalGamma[i].component[3],
myCurGamma.component[4],
gOriginalGamma[i].component[5],
gOriginalGamma[i].component[6],
myCurGamma.component[7],
gOriginalGamma[i].component[8]);
}
// are we finished?
if(myCurScale >= 1.0f)
{
break;
}
}
}
//_____________________________________________________________________________________VID_CreateGLPixelFormat()
NSOpenGLPixelFormat * VID_CreateGLPixelFormat (void)
{
NSOpenGLPixelFormat *myPixelFormat;
NSOpenGLPixelFormatAttribute myAttributeList[32];
UInt i = 0;
if (gARBMultiSamples > 0)
{
if (gARBMultiSamples > 8)
gARBMultiSamples = 8;
myAttributeList[i++] = NSOpenGLPFASampleBuffers;
myAttributeList[i++] = 1;
myAttributeList[i++] = NSOpenGLPFASamples;
myAttributeList[i++] = gARBMultiSamples;
}
myAttributeList[i++] = NSOpenGLPFANoRecovery;
myAttributeList[i++] = NSOpenGLPFAClosestPolicy;
myAttributeList[i++] = NSOpenGLPFAAccelerated;
myAttributeList[i++] = NSOpenGLPFADoubleBuffer;
myAttributeList[i++] = NSOpenGLPFAColorSize;
myAttributeList[i++] = gDisplayDepth;
myAttributeList[i++] = NSOpenGLPFADepthSize;
myAttributeList[i++] = 24;
myAttributeList[i++] = NSOpenGLPFAAlphaSize;
myAttributeList[i++] = 8;
myAttributeList[i++] = NSOpenGLPFAStencilSize;
myAttributeList[i++] = 8;
myAttributeList[i++] = NSOpenGLPFAAccumSize;
myAttributeList[i++] = 0;
if (gDisplayFullscreen == YES)
{
myAttributeList[i++] = NSOpenGLPFAFullScreen;
myAttributeList[i++] = NSOpenGLPFAScreenMask;
myAttributeList[i++] = CGDisplayIDToOpenGLDisplayMask (gDisplayList[gDisplay]);
}
else
{
myAttributeList[i++] = NSOpenGLPFAWindow;
}
myAttributeList[i++] = 0;
myPixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes: myAttributeList];
return (myPixelFormat);
}
//_________________________________________________________________________________________________VID_SetWait()
void VID_SetWait (UInt32 theState)
{
// set theState to 1 to enable, to 0 to disable VBL syncing.
[gGLContext makeCurrentContext];
if(CGLSetParameter (CGLGetCurrentContext (), kCGLCPSwapInterval, &theState) == CGDisplayNoErr)
{
gVidWait = vid_wait.value;
if (theState == 0)
{
Con_Printf ("video wait successfully disabled!\n");
}
else
{
Con_Printf ("video wait successfully enabled!\n");
}
}
else
{
vid_wait.value = gVidWait;
Con_Printf ("Error while trying to change video wait!\n");
}
}
//_________________________________________________________________________________________________VID_SetMode()
Boolean VID_SetDisplayMode (void)
{
NSOpenGLPixelFormat *myPixelFormat;
// just for security:
if (gDisplayList == NULL || gDisplayCount == 0)
{
Sys_Error ("Invalid list of active displays!");
}
// save the old display mode:
gOriginalMode = (NSDictionary *) CGDisplayCurrentMode (gDisplayList[gDisplay]);
if (gDisplayFullscreen)
{
// hide cursor:
IN_ShowCursor (NO);
// fade the display(s) to black:
VID_FadeGammaOut (gFadeAllDisplays, FADE_DURATION);
// capture display(s);
if (VID_CaptureDisplays (gFadeAllDisplays) == NO)
Sys_Error ("Unable to capture display(s)!");
// switch the display mode:
if (CGDisplaySwitchToMode (gDisplayList[gDisplay], (CFDictionaryRef) gDisplayMode)
!= CGDisplayNoErr)
{
Sys_Error ("Unable to switch the displaymode!");
}
gDisplayDepth = [[gDisplayMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue];
}
else
{
gDisplayDepth = [[gOriginalMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue];
}
// 8 bit display depth is possible with windowed modes:
if (gDisplayDepth == 8) gDisplayIs8Bit = YES;
else gDisplayIs8Bit = NO;
// get the pixel format:
if ((myPixelFormat = VID_CreateGLPixelFormat ()) == NULL)
{
if (gARBMultiSamples > 0)
Sys_Error ("Unable to find a matching pixelformat. Set FSAA samples to zero and try again.");
else
Sys_Error ("Unable to find a matching pixelformat. Probably your graphics board is not supported.");
}
// initialize the OpenGL context:
if (!(gGLContext = [[NSOpenGLContext alloc] initWithFormat: myPixelFormat shareContext: nil]))
{
if (gARBMultiSamples > 0)
Sys_Error ("Unable to create an OpenGL context. Set FSAA samples to zero and try again.");
else
Sys_Error ("Unable to create an OpenGL context. Probably your graphics board is not supported.");
}
// get rid of the pixel format:
[myPixelFormat release];
if (gDisplayFullscreen)
{
// attach the OpenGL context to fullscreen:
if (CGLSetFullScreen ([gGLContext cglContext]) != CGDisplayNoErr)
Sys_Error ("Unable to use the selected displaymode for fullscreen OpenGL.");
// fade the gamma back:
VID_FadeGammaIn (gFadeAllDisplays, 0.0f);
}
else
{
NSRect myContentRect;
// setup the window according to our settings:
myContentRect = NSMakeRect (0, 0, gDisplayWidth, gDisplayHeight);
gWindow = [[NSWindow alloc] initWithContentRect: myContentRect
styleMask: NSTitledWindowMask | NSMiniaturizableWindowMask
#ifdef USE_BUFFERED_WINDOW
backing: NSBackingStoreBuffered
#else
backing: NSBackingStoreRetained
#endif /* USE_BUFFERED_WINDOW */
defer: NO];
[gWindow setTitle: @"TenebraeQuake"];
gWindowView = [[QuakeView alloc] initWithFrame: myContentRect];
if (gWindowView == NULL)
{
Sys_Error ("Unable to create content view!\n");
}
// setup the view for tracking the window location:
[gWindow setContentView: gWindowView];
[gWindow makeFirstResponder: gWindowView];
[gWindow setDelegate: gWindowView];
// attach the OpenGL context to the window:
[gGLContext setView: [gWindow contentView]];
// finally show the window:
[gWindow center];
[gWindow setAcceptsMouseMovedEvents: YES];
[gWindow makeKeyAndOrderFront: nil];
[gWindow makeMainWindow];
[gWindow flushWindow];
// setup the miniwindow [if one alloc fails, the miniwindow will not be drawn]:
gMiniWindow = [[NSImage alloc] initWithSize: NSMakeSize (128, 128)];
gOldIcon = [NSImage imageNamed: @"NSApplicationIcon"];
gMiniBuffer = malloc (gDisplayWidth * gDisplayHeight * 4);
}
// Lock the OpenGL context to the refresh rate of the display [for clean rendering], if desired:
VID_SetWait((UInt32) vid_wait.value);
return (YES);
}
//____________________________________________________________________________________________________VID_Init()
void VID_Init (unsigned char *thePalette)
{
char myGLDir[MAX_OSPATH];
UInt i;
// register miscelanous vars:
Cvar_RegisterVariable (&vid_mode);
Cvar_RegisterVariable (&_vid_default_mode);
Cvar_RegisterVariable (&_vid_default_blit_mode);
Cvar_RegisterVariable (&vid_wait);
Cvar_RegisterVariable (&vid_redrawfull);
Cvar_RegisterVariable (&_windowed_mouse);
Cvar_RegisterVariable (&gl_anisotropic);
Cvar_RegisterVariable (&gl_fsaa);
Cvar_RegisterVariable (&gl_truform);
Cvar_RegisterVariable (&gl_ztrick);
// setup basic width/height:
vid.maxwarpwidth = WARP_WIDTH;
vid.maxwarpheight = WARP_HEIGHT;
vid.colormap = host_colormap;
vid.fullbright = 256 - LittleLong (*((SInt *)vid.colormap + 2048));
gDisplayWidth = [[gDisplayMode objectForKey: (id)kCGDisplayWidth] intValue];
gDisplayHeight = [[gDisplayMode objectForKey: (id)kCGDisplayHeight] intValue];
// get width from command line parameters [only for windowed mode]:
if ((i = COM_CheckParm("-width")))
gDisplayWidth = atoi (com_argv[i+1]);
vid.width = gDisplayWidth;
// get height from command line parameters [only for windowed mode]:
if ((i = COM_CheckParm("-height")))
gDisplayHeight = atoi (com_argv[i+1]);
vid.height = gDisplayHeight;
vid.aspect = ((float) vid.height / (float) vid.width) * (320.0f / 240.0f);
vid.numpages = 2;
// switch the video mode:
if (!VID_SetDisplayMode())
Sys_Error ("Can\'t initialize video!");
// setup console width according to display width:
if ((i = COM_CheckParm("-conwidth")))
vid.conwidth = Q_atoi (com_argv[i+1]);
else
vid.conwidth = vid.width;
vid.conwidth &= 0xfff8;
// setup console height according to display height:
if ((i = COM_CheckParm ("-conheight")))
vid.conheight = Q_atoi (com_argv[i+1]);
else
vid.conheight = vid.height;
// check the console size:
if (vid.conwidth > gDisplayWidth)
vid.conwidth = gDisplayWidth;
if (vid.conheight > gDisplayHeight)
vid.conheight = gDisplayHeight;
if (vid.conwidth < CONSOLE_MIN_WIDTH)
vid.conwidth = CONSOLE_MIN_WIDTH;
if (vid.conheight < CONSOLE_MIN_HEIGHT)
vid.conheight = CONSOLE_MIN_HEIGHT;
// setup OpenGL:
GL_Init ();
// setup the "glquake" folder within the "id1" folder:
sprintf (myGLDir, "%s/glquake", com_gamedir);
Sys_mkdir (myGLDir);
// enable the video options menu:
vid_menudrawfn = VID_MenuDraw;
vid_menukeyfn = VID_MenuKey;
// finish up initialization:
VID_CheckGamma (thePalette);
VID_SetPalette (thePalette);
Con_SafePrintf ("Video mode %dx%d initialized.\n", gDisplayWidth, gDisplayHeight);
vid.recalc_refdef = 1;
}
//________________________________________________________________________________________________VID_Shutdown()
void VID_Shutdown (void)
{
// restore old application icon, if changed:
VID_RestoreOldIcon ();
// release the buffer of the mini window:
if (gMiniBuffer != NULL)
{
free (gMiniBuffer);
gMiniBuffer = NULL;
}
// release the miniwindow:
if (gMiniWindow != NULL)
{
[gMiniWindow release];
gMiniWindow = NULL;
}
// release the old icon:
if (gOldIcon != NULL)
{
[gOldIcon release];
gOldIcon = NULL;
}
// close the old window:
if (gWindow != NULL)
{
[gWindow close];
[gWindow release];
gWindow = NULL;
}
// close the content view:
if (gWindowView != NULL)
{
[gWindowView release];
gWindowView = NULL;
}
// fade gamma out, to avoid splash:
if (gDisplayFullscreen == YES && gOriginalMode != NULL)
{
VID_FadeGammaOut (gFadeAllDisplays, 0.0f);
}
// clean up the OpenGL context:
if (gGLContext != NULL)
{
[NSOpenGLContext clearCurrentContext];
[gGLContext clearDrawable];
[gGLContext release];
gGLContext = NULL;
}
// free the vertexarray buffer:
if (gl_var == YES && AGP_Buffer != NULL)
{
free (AGP_Buffer);
AGP_Buffer = NULL;
}
// restore the old display mode:
if (gDisplayFullscreen == YES)
{
if (gOriginalMode != NULL)
{
CGDisplaySwitchToMode (gDisplayList[gDisplay], (CFDictionaryRef) gOriginalMode);
VID_ReleaseDisplays (gFadeAllDisplays);
VID_FadeGammaIn (gFadeAllDisplays, FADE_DURATION);
// clean up the gamma fading:
if (gOriginalGamma != NULL)
{
free (gOriginalGamma);
gOriginalGamma = NULL;
}
}
}
else
{
if (gWindow != NULL)
{
[gWindow release];
}
}
}
//__________________________________________________________________________________________VID_RestoreOldIcon()
void VID_RestoreOldIcon (void)
{
if (gIsMinimized == YES)
{
if (gOldIcon != NULL)
{
[NSApp setApplicationIconImage: gOldIcon];
}
gIsMinimized = NO;
}
}
//________________________________________________________________________________________________VID_MenuDraw()
void VID_MenuDraw (void)
{
qpic_t *myPicture;
UInt8 myRow = FIRST_MENU_LINE, myString[16];
myPicture = Draw_CachePic ("gfx/vidmodes.lmp");
M_DrawPic ((320 - myPicture->width) / 2, 4, myPicture);
// draw vid_wait option:
M_Print (FONT_WIDTH, myRow, "Video Sync:");
if (vid_wait.value) M_Print ((39 - 2) * FONT_WIDTH, myRow, "On");
else M_Print ((39 - 3) * FONT_WIDTH, myRow, "Off");
if (gMenuLine == myRow) gMenuItem = VIDEO_WAIT;
// draw FSAA option:
if (gl_fsaaavailable == YES)
{
myRow += FONT_HEIGHT;
M_Print (FONT_WIDTH, myRow, "FSAA:");
if (gl_fsaa.value == 1.0f)
{
M_Print ((39 - 3) * FONT_WIDTH, myRow, "Off");
}
else
{
snprintf (myString, 16, "%dx", (int) gl_fsaa.value);
M_Print ((39 - strlen (myString)) * FONT_WIDTH, myRow, myString);
}
if (gMenuLine == myRow) gMenuItem = VIDEO_FSAA;
}
// draw anisotropic option:
if (gl_texturefilteranisotropic == YES)
{
myRow += FONT_HEIGHT;
M_Print (FONT_WIDTH, myRow, "Anisotropic Texture Filtering:");
if (gl_anisotropic.value) M_Print ((39 - 2) * FONT_WIDTH, myRow, "On");
else M_Print ((39 - 3) * FONT_WIDTH, myRow, "Off");
if (gMenuLine == myRow) gMenuItem = VIDEO_ANISOTROPIC;
}
// draw truform option:
if (gl_pntriangles == YES)
{
myRow += FONT_HEIGHT;
M_Print (FONT_WIDTH, myRow, "ATI Truform Tesselation Level:");
if (gl_truform.value < 0)
{
M_Print ((39 - 3) * FONT_WIDTH, myRow, "Off");
}
else
{
snprintf (myString, 16, "%dx", (int) gl_truform.value);
M_Print ((39 - strlen (myString)) * FONT_WIDTH, myRow, myString);
}
if (gMenuLine == myRow) gMenuItem = VIDEO_TRUFORM;
}
M_Print (4 * FONT_WIDTH + 4, 36 + 23 * FONT_HEIGHT, "Video modes must be set at the");
M_Print (11 * FONT_WIDTH + 4, 36 + 24 * FONT_HEIGHT, "startup dialog!");
M_DrawCharacter (0, gMenuLine, 12 + ((int)(realtime * 4) & 1));
gMenuMaxLine = myRow;
}
//_________________________________________________________________________________________________VID_MenuKey()
void VID_MenuKey (int theKey)
{
switch (theKey)
{
case K_ESCAPE:
S_LocalSound ("misc/menu1.wav");
M_Menu_Options_f ();
break;
case K_UPARROW:
S_LocalSound ("misc/menu1.wav");
gMenuLine -= FONT_HEIGHT;
if (gMenuLine < FIRST_MENU_LINE)
gMenuLine = gMenuMaxLine;
break;
case K_DOWNARROW:
S_LocalSound ("misc/menu1.wav");
gMenuLine += FONT_HEIGHT;
if (gMenuLine > gMenuMaxLine)
gMenuLine = FIRST_MENU_LINE;
break;
case K_LEFTARROW:
S_LocalSound ("misc/menu1.wav");
switch (gMenuItem)
{
case VIDEO_WAIT:
Cvar_SetValue ("vid_wait", (vid_wait.value == 0.0f) ? 1.0f : 0.0f);
break;
case VIDEO_FSAA:
Cvar_SetValue ("gl_fsaa", (gl_fsaa.value <= 1.0f) ? 4.0f : gl_fsaa.value / 2.0f);
break;
case VIDEO_ANISOTROPIC:
Cvar_SetValue ("gl_anisotropic", (gl_anisotropic.value == 0.0f) ? 1.0f : 0.0f);
break;
case VIDEO_TRUFORM:
Cvar_SetValue ("gl_truform", (gl_truform.value <= -1.0f) ? 7.0f : gl_truform.value - 1.0f);
break;
}
break;
case K_RIGHTARROW:
case K_ENTER:
S_LocalSound ("misc/menu1.wav");
switch (gMenuItem)
{
case VIDEO_WAIT:
Cvar_SetValue ("vid_wait", (vid_wait.value == 0.0f) ? 1.0f : 0.0f);
break;
case VIDEO_FSAA:
Cvar_SetValue ("gl_fsaa", (gl_fsaa.value >= 4.0f) ? 1.0f : gl_fsaa.value * 2.0f);
break;
case VIDEO_ANISOTROPIC:
Cvar_SetValue ("gl_anisotropic", (gl_anisotropic.value == 0.0f) ? 1.0f : 0.0f);
break;
case VIDEO_TRUFORM:
Cvar_SetValue ("gl_truform", (gl_truform.value >= 7.0f) ? -1.0f : gl_truform.value + 1.0f);
break;
}
break;
default:
break;
}
}
//___________________________________________________________________________________________GL_GetProcAddress()
void * GL_GetProcAddress (const char *theName, qboolean theSafeMode)
{
NSSymbol mySymbol = NULL;
char * mySymbolName = malloc (strlen (theName) + 2);
if (mySymbolName != NULL)
{
strcpy (mySymbolName + 1, theName);
mySymbolName[0] = '_';
mySymbol = NULL;
if (NSIsSymbolNameDefined (mySymbolName))
mySymbol = NSLookupAndBindSymbol (mySymbolName);
free (mySymbolName);
}
if (theSafeMode == YES && mySymbol == NULL)
{
Sys_Error ("Failed to import a required OpenGL function!\n");
}
return ((mySymbol != NULL) ? NSAddressOfSymbol (mySymbol) : NULL);
}
//_____________________________________________________________________________________________GL_CheckVersion()
Boolean GL_CheckVersion (UInt8 theMajor, UInt8 theMinor)
{
UInt8 myMajor, myMinor;
// just security:
if (gl_version == NULL)
{
return (NO);
}
// get the major version number:
myMajor = atoi (gl_version);
if (myMajor < theMajor)
{
return (NO);
}
// compare minor version numbers only if the majors are equal:
if (myMajor == theMajor)
{
const char * myGLVersion = gl_version;
while (*myGLVersion != 0x00 && *myGLVersion++ != '.');
myMinor = atoi (myGLVersion);
if (myMinor < theMinor)
{
return (NO);
}
}
return (YES);
}
//_____________________________________________________________________________________GL_CheckPalettedTexture()
void GL_CheckPalettedTexture (void)
{
if (strstr (gl_extensions, "GL_EXT_paletted_texture"))
{
gl_palettedtex = YES;
Con_Printf ("Found GL_EXT_paletted_texture...\n");
}
}
//______________________________________________________________________________GL_CheckMultiTextureExtensions()
void GL_CheckMultiTextureExtensions (void)
{
// look for the extension:
if (!strstr (gl_extensions, "GL_ARB_multitexture "))
{
Sys_Error ("\"GL_ARB_multitexture\" not found.\nYour 3D board is not supported by TenebraeQuake.\n");
}
qglActiveTextureARB = GL_GetProcAddress ("glActiveTextureARB", YES);
qglClientActiveTextureARB = GL_GetProcAddress ("glClientActiveTextureARB", YES);
qglMultiTexCoord1fARB = GL_GetProcAddress ("glMultiTexCoord1fARB", YES);
qglMultiTexCoord2fARB = GL_GetProcAddress ("glMultiTexCoord2fARB", YES);
qglMultiTexCoord2fvARB = GL_GetProcAddress ("glMultiTexCoord2fvARB", YES);
qglMultiTexCoord3fARB = GL_GetProcAddress ("glMultiTexCoord3fARB", YES);
qglMultiTexCoord3fvARB = GL_GetProcAddress ("glMultiTexCoord3fvARB", YES);
Con_Printf ("Found GL_ARB_multitexture...\n");
gl_mtexable = YES;
}
//________________________________________________________________________GL_CheckDiffuseBumpMappingExtensions()
void GL_CheckDiffuseBumpMappingExtensions (void)
{
if (!strstr(gl_extensions, "GL_ARB_texture_env_combine"))
{
Sys_Error ("\"GL_ARB_texture_env_combine\" not found.\n"
"Your 3D board is not supported by TenebraeQuake.\n");
}
Con_Printf ("Found GL_ARB_texture_env_combine...\n");
if (!strstr(gl_extensions, "GL_ARB_texture_env_dot3"))
{
Sys_Error ("\"GL_ARB_texture_env_dot3\" not found.\n"
"Your 3D board is not supported by TenebraeQuake.\n");
}
Con_Printf ("Found GL_ARB_texture_env_dot3...\n");
if (!strstr(gl_extensions, "GL_ARB_texture_cube_map"))
{
Sys_Error ("\"GL_ARB_texture_cube_map\" not found.\n"
"Your 3D board is not supported by TenebraeQuake.\n");
}
Con_Printf ("Found GL_ARB_texture_cube_map...\n");
if (GL_CheckVersion (1, 2) == NO &&
!strstr(gl_extensions, "GL_SGI_texture_edge_clamp") &&
!strstr(gl_extensions, "GL_EXT_texture_edge_clamp"))
{
Con_Printf("Warning no edge_clamp extension found...");
}
}
//_______________________________________________________________________GL_CheckSpecularBumpMappingExtensions()
void GL_CheckSpecularBumpMappingExtensions (void)
{
gl_nvcombiner = NO;
if ((strstr(gl_extensions, "GL_NV_register_combiners")) && (!COM_CheckParm ("-forcenonv")))
{
qglCombinerParameterfvNV = GL_GetProcAddress ("glCombinerParameterfvNV", NO);
qglCombinerParameterivNV = GL_GetProcAddress ("glCombinerParameterivNV", NO);
qglCombinerParameterfNV = GL_GetProcAddress ("glCombinerParameterfNV", NO);
qglCombinerParameteriNV = GL_GetProcAddress ("glCombinerParameteriNV", NO);
qglCombinerInputNV = GL_GetProcAddress ("glCombinerInputNV", NO);
qglCombinerOutputNV = GL_GetProcAddress ("glCombinerOutputNV", NO);
qglFinalCombinerInputNV = GL_GetProcAddress ("glFinalCombinerInputNV", NO);
qglGetCombinerInputParameterfvNV = GL_GetProcAddress ("glGetCombinerInputParameterfvNV", NO);
qglGetCombinerInputParameterivNV = GL_GetProcAddress ("glGetCombinerInputParameterivNV", NO);
qglGetCombinerOutputParameterfvNV = GL_GetProcAddress ("glGetCombinerOutputParameterfvNV", NO);
qglGetCombinerOutputParameterivNV = GL_GetProcAddress ("glGetCombinerOutputParameterivNV", NO);
qglGetFinalCombinerInputParameterfvNV = GL_GetProcAddress ("glGetFinalCombinerInputParameterfvNV", NO);
qglGetFinalCombinerInputParameterivNV = GL_GetProcAddress ("glGetFinalCombinerInputParameterivNV", NO);
if (qglCombinerParameterfvNV != NULL &&
qglCombinerParameterivNV != NULL &&
qglCombinerParameterfNV != NULL &&
qglCombinerParameteriNV != NULL &&
qglCombinerInputNV != NULL &&
qglCombinerOutputNV != NULL &&
qglFinalCombinerInputNV != NULL &&
qglGetCombinerInputParameterfvNV != NULL &&
qglGetCombinerInputParameterivNV != NULL &&
qglGetCombinerOutputParameterfvNV != NULL &&
qglGetCombinerOutputParameterivNV != NULL &&
qglGetFinalCombinerInputParameterfvNV != NULL &&
qglGetFinalCombinerInputParameterivNV != NULL)
{
gl_nvcombiner = YES;
Con_Printf ("Found GL_NV_register_combiners...\n");
}
}
}
//__________________________________________________________________________________GL_CheckGeforce3Extensions()
// TODO: Replace GL_NV_vertex_program with GL_ARB_vertex_program since it will probably never be supported under
// MacOS X.
void GL_CheckGeforce3Extensions (void)
{
GLint mySupportedTextureUnits;
glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &mySupportedTextureUnits);
Con_Printf ("%d texture units\n", mySupportedTextureUnits);
gl_geforce3 = NO;
if (strstr (gl_extensions, "GL_NV_vertex_program") && (mySupportedTextureUnits >= 4) &&
(!COM_CheckParm ("-forcegf2")) && (gl_nvcombiner == YES))
{
if (GL_CheckVersion (1, 2) == YES)
{
qglTexImage3DEXT = GL_GetProcAddress ("glTexImage3D", NO);
Con_Printf ("Found OpenGL v1.2 or later. Using \"glTexImage3D ()\"\n");
}
else
{
if (strstr (gl_extensions, "GL_EXT_texture3D"))
{
qglTexImage3DEXT = GL_GetProcAddress ("glTexImage3DEXT", NO);
Con_Printf ("Found GL_EXT_texture3D...\n");
}
}
if (qglTexImage3DEXT != NULL)
{
qglAreProgramsResidentNV = GL_GetProcAddress ("glAreProgramsResidentNV", NO);
qglBindProgramNV = GL_GetProcAddress ("glBindProgramNV", NO);
qglDeleteProgramsNV = GL_GetProcAddress ("glDeleteProgramsNV", NO);
qglExecuteProgramNV = GL_GetProcAddress ("glExecuteProgramNV", NO);
qglGenProgramsNV = GL_GetProcAddress ("glGenProgramsNV", NO);
qglGetProgramParameterdvNV = GL_GetProcAddress ("glGetProgramParameterdvNV", NO);
qglGetProgramParameterfvNV = GL_GetProcAddress ("glGetProgramParameterfvNV", NO);
qglGetProgramivNV = GL_GetProcAddress ("glGetProgramivNV", NO);
qglGetProgramStringNV = GL_GetProcAddress ("glGetProgramStringNV", NO);
qglGetTrackMatrixivNV = GL_GetProcAddress ("glGetTrackMatrixivNV", NO);
qglGetVertexAttribdvNV = GL_GetProcAddress ("glGetVertexAttribdvNV", NO);
qglGetVertexAttribfvNV = GL_GetProcAddress ("glGetVertexAttribfvNV", NO);
qglGetVertexAttribivNV = GL_GetProcAddress ("glGetVertexAttribivNV", NO);
qglGetVertexAttribPointervNV = GL_GetProcAddress ("glGetVertexAttribPointervNV", NO);
qglIsProgramNV = GL_GetProcAddress ("glIsProgramNV", NO);
qglLoadProgramNV = GL_GetProcAddress ("glLoadProgramNV", NO);
qglProgramParameter4dNV = GL_GetProcAddress ("glProgramParameter4dNV", NO);
qglProgramParameter4dvNV = GL_GetProcAddress ("glProgramParameter4dvNV", NO);
qglProgramParameter4fNV = GL_GetProcAddress ("glProgramParameter4fNV", NO);
qglProgramParameter4fvNV = GL_GetProcAddress ("glProgramParameter4fvNV", NO);
qglProgramParameters4dvNV = GL_GetProcAddress ("glProgramParameters4dvNV", NO);
qglProgramParameters4fvNV = GL_GetProcAddress ("glProgramParameters4fvNV", NO);
qglRequestResidentProgramsNV = GL_GetProcAddress ("glRequestResidentProgramsNV", NO);
qglTrackMatrixNV = GL_GetProcAddress ("glTrackMatrixNV", NO);
/* qglBindProgramARB = GL_GetProcAddress ("glBindProgramARB", NO);
qglDeleteProgramsARB = GL_GetProcAddress ("glDeleteProgramsNV", NO);
qglGenProgramsARB = GL_GetProcAddress ("glGenProgramsNV", NO);
qglGetProgramivARB = GL_GetProcAddress ("glGetProgramivNV", NO);
qglGetProgramStringARB = GL_GetProcAddress ("glGetProgramStringNV", NO);
qglGetVertexAttribdvARB = GL_GetProcAddress ("glGetVertexAttribdvNV", NO);
qglGetVertexAttribfvARB = GL_GetProcAddress ("glGetVertexAttribfvNV", NO);
qglGetVertexAttribivARB = GL_GetProcAddress ("glGetVertexAttribivNV", NO);
qglGetVertexAttribPointervARB = GL_GetProcAddress ("glGetVertexAttribPointervNV", NO);
qglIsProgramARB = GL_GetProcAddress ("glIsProgramNV", NO);
qglProgramStringARB = GL_GetProcAddress ("glLoadProgramNV", NO);
if (qglBindProgramARB != NULL &&
qglDeleteProgramsARB != NULL &&
qglGenProgramsARB != NULL &&
qglGetProgramivARB != NULL &&
qglGetVertexAttribdvARB != NULL &&
qglGetVertexAttribfvARB != NULL &&
qglGetVertexAttribivARB != NULL &&
qglGetVertexAttribPointervARB != NULL &&
qglIsProgramARB != NULL &&
qglProgramStringARB != NULL &&
glTrackMatrixNV != NULL)*/
if (qglAreProgramsResidentNV != NULL &&
qglBindProgramNV != NULL &&
qglDeleteProgramsNV != NULL &&
qglExecuteProgramNV != NULL &&
qglGenProgramsNV != NULL &&
qglGetProgramParameterdvNV != NULL &&
qglGetProgramParameterfvNV != NULL &&
qglGetProgramivNV != NULL &&
qglGetProgramStringNV != NULL &&
qglGetTrackMatrixivNV != NULL &&
qglGetVertexAttribdvNV != NULL &&
qglGetVertexAttribfvNV != NULL &&
qglGetVertexAttribivNV != NULL &&
qglGetVertexAttribPointervNV != NULL &&
qglIsProgramNV != NULL &&
qglLoadProgramNV != NULL &&
qglProgramParameter4dNV != NULL &&
qglProgramParameter4dvNV != NULL &&
qglProgramParameter4fNV != NULL &&
qglProgramParameter4fvNV != NULL &&
qglProgramParameters4dvNV != NULL &&
qglProgramParameters4fvNV != NULL &&
qglRequestResidentProgramsNV != NULL &&
qglTrackMatrixNV != NULL)
{
Con_Printf ("Using NVidia Geforce 3 or 4Ti path.\n");
gl_geforce3 = YES;
gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
gl_filter_max = GL_LINEAR;
}
}
}
}
//____________________________________________________________________________________GL_CheckRadeonExtensions()
void GL_CheckRadeonExtensions (void)
{
GLint mySupportedTextureUnits;
Boolean myOpenGL12 = NO,
myExtTexImage3D = NO;
glGetIntegerv (GL_MAX_TEXTURE_UNITS_ARB, &mySupportedTextureUnits);
gl_radeon = NO;
myOpenGL12 = GL_CheckVersion (1, 2);
myExtTexImage3D = strstr (gl_extensions, "GL_EXT_vertex_shader") ? YES : NO;
if ((myOpenGL12 == YES || myExtTexImage3D == YES)
&& (mySupportedTextureUnits >= 4)
&& (!COM_CheckParm ("-forcegeneric"))
&& strstr (gl_extensions, "GL_ATI_fragment_shader")
&& strstr (gl_extensions, "GL_EXT_vertex_shader"))
{
if (myOpenGL12 == YES)
{
qglTexImage3DEXT = GL_GetProcAddress ("glTexImage3D", NO);
Con_Printf ("Found OpenGL v1.2 or later. Using \"glTexImage3D ()\"\n");
}
else
{
if (myExtTexImage3D = YES)
{
qglTexImage3DEXT = GL_GetProcAddress ("glTexImage3DEXT", NO);
Con_Printf ("Found GL_EXT_texture3D...\n");
}
}
if (qglTexImage3DEXT != NULL)
{
if (GL_LookupRadeonSymbols () == YES)
{
gl_filter_min = GL_LINEAR_MIPMAP_LINEAR;
gl_filter_max = GL_LINEAR;
GL_CreateShadersRadeon();
gl_radeon = YES;
Con_Printf("Using ATI Radeon 8500 path.\n");
}
}
}
if (gl_radeon == NO)
{
if (gl_geforce3 == YES)
{
Con_Printf("Using Geforce3 or Geforce4Ti path\n");
}
else
{
if (gl_nvcombiner == YES)
{
Con_Printf("Using Geforce2MX or Geforce4MX path\n");
}
else
{
Con_Printf("Using generic path.\n");
}
}
}
}
//____________________________________________________________________________________GL_CheckVertexArrayRange()
void GL_CheckVertexArrayRange (void)
{
gl_var = NO;
/*
// Don't use it yet. There seems to be something up.
if (strstr(gl_extensions, "GL_APPLE_vertex_array_range"))
{
qglFlushVertexArrayRangeAPPLE = GL_GetProcAddress ("glFlushVertexArrayRangeAPPLE", NO);
qglVertexArrayRangeAPPLE = GL_GetProcAddress ("glVertexArrayRangeAPPLE", NO);
//qglVertexArrayParameteriAPPLE = GL_GetProcAddress ("glVertexArrayParameteriAPPLE", NO);
if (qglFlushVertexArrayRangeAPPLE != NULL &&
qglVertexArrayRangeAPPLE != NULL)
{
AGP_Buffer = malloc (AGP_BUFFER_SIZE);
if (AGP_Buffer != NULL)
{
gl_var = YES;
Con_Printf ("Found GL_APPLE_vertex_array_range...\nAllocated %dkb.\n", AGP_BUFFER_SIZE / 1024);
}
}
}
*/
}
//_______________________________________________________________________________GL_CheckPNTrianglesExtensions()
void GL_CheckPNTrianglesExtensions (void)
{
if (strstr (gl_extensions, "GL_ATIX_pn_triangles"))
{
// symbols present?
if (glPNTrianglesiATIX != NULL)
{
Con_Printf ("Found GL_ATIX_pn_triangles...\n");
gl_pntriangles = YES;
}
else
{
gl_pntriangles = NO;
}
}
else
{
gl_pntriangles = NO;
}
}
//_____________________________________________________________________________GL_CheckARBMultisampleExtension()
Boolean GL_CheckARBMultisampleExtension (CGDirectDisplayID theDisplay)
{
CGLRendererInfoObj myRendererInfo;
CGLError myError;
UInt64 myDisplayMask;
long myCount,
myIndex,
mySampleBuffers,
mySamples,
myMaxSampleBuffers = 0,
myMaxSamples = 0;
// retrieve the renderer info for the selected display:
myDisplayMask = CGDisplayIDToOpenGLDisplayMask (theDisplay);
myError = CGLQueryRendererInfo (myDisplayMask, &myRendererInfo, &myCount);
if (myError == kCGErrorSuccess)
{
// loop through all renderers:
for (myIndex = 0; myIndex < myCount; myIndex++)
{
// check if the current renderer supports sample buffers:
myError = CGLDescribeRenderer (myRendererInfo, myIndex, kCGLRPMaxSampleBuffers, &mySampleBuffers);
if (myError == kCGErrorSuccess && mySampleBuffers > 0)
{
// retrieve the number of samples supported by the current renderer:
myError = CGLDescribeRenderer (myRendererInfo, myIndex, kCGLRPMaxSamples, &mySamples);
if (myError == kCGErrorSuccess && mySamples > myMaxSamples)
{
myMaxSampleBuffers = mySampleBuffers;
myMaxSamples = mySamples;
}
}
}
// get rid of the renderer info:
CGLDestroyRendererInfo (myRendererInfo);
}
// NOTE: we could return the max number of samples at this point, but unfortunately there is a bug
// with the ATI Radeon/PCI drivers: We would return 4 instead of 8. So we assume that the
// max samples are always 8 if we have sample buffers and max samples is greater than 1.
if (myMaxSampleBuffers > 0 && myMaxSamples > 1)
return (YES);
return (NO);
}
//__________________________________________________________________________________GL_CheckSwitchFSAAOnTheFly()
void GL_CheckSwitchFSAAOnTheFly (void)
{
// Changing the FSAA samples is only available for Radeon boards
// [a sample buffer is not required at the pixelformat].
//
// We don't want to support FSAA under 10.1.x because the current driver will crash the WindowServer.
// Thus we check for the Radeon string AND the GL_ARB_multisample extension, which is only available for
// Radeon boards under 10.2 or later.
if (strstr (gl_renderer, "ATI Radeon") && strstr (gl_extensions, "GL_ARB_multisample"))
{
Con_Printf ("Graphics board is capable to switch FSAA samples on the fly...\n");
gl_fsaaavailable = YES;
switch (gARBMultiSamples)
{
case 4:
gFSAALevel = 2.0f;
break;
case 8:
gFSAALevel = 4.0f;
break;
case 0:
default:
gFSAALevel = 1.0f;
break;
}
gl_fsaa.value = gFSAALevel;
return;
}
gl_fsaaavailable = NO;
}
//____________________________________________________________________________GL_CheckTextureFilterAnisotropic()
void GL_CheckTextureFilterAnisotropic (void)
{
if (strstr (gl_extensions, "GL_EXT_texture_filter_anisotropic"))
{
Con_Printf ("Found GL_EXT_texture_filter_anisotropic...\n");
gl_texturefilteranisotropic = YES;
}
else
{
gl_texturefilteranisotropic = NO;
}
}
//_____________________________________________________________________________________________________GL_Init()
void GL_Init (void)
{
// show OpenGL stats at the console:
gl_vendor = glGetString (GL_VENDOR);
Con_Printf ("GL_VENDOR: %s\n", gl_vendor);
gl_renderer = glGetString (GL_RENDERER);
Con_Printf ("GL_RENDERER: %s\n", gl_renderer);
gl_version = glGetString (GL_VERSION);
Con_Printf ("GL_VERSION: %s\n", gl_version);
gl_extensions = glGetString (GL_EXTENSIONS);
Con_Printf ("GL_EXTENSIONS: %s\n", gl_extensions);
// not required for MacOS X, but nevertheless:
if (!strncasecmp ((char*) gl_renderer, "Permedia", 8))
isPermedia = YES;
// check for paletted texture extension (GL_Upload8_EXT ()):
GL_CheckPalettedTexture ();
// check for multitexture extensions:
GL_CheckMultiTextureExtensions ();
// check for extensions required for bump-mapping:
GL_CheckDiffuseBumpMappingExtensions ();
GL_CheckSpecularBumpMappingExtensions ();
GL_CheckGeforce3Extensions ();
GL_CheckRadeonExtensions ();
GL_CheckVertexArrayRange ();
// check for pn_triangles extension:
GL_CheckPNTrianglesExtensions ();
// check if FSAA is available:
GL_CheckSwitchFSAAOnTheFly ();
// check for texture filter anisotropic extension:
GL_CheckTextureFilterAnisotropic ();
// setup OpenGL:
if (gARBMultiSamples > 0)
glEnable (GL_MULTISAMPLE_ARB);
glClearColor (0,0,0,0);
glCullFace (GL_FRONT);
glEnable (GL_TEXTURE_2D);
glEnable (GL_ALPHA_TEST);
glAlphaFunc (GL_GREATER, 0.666f);
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
glShadeModel (GL_FLAT);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
}
//________________________________________________________________________________________GL_RenderInnsideDock()
void GL_RenderInsideDock (void)
{
if ([gWindow isMiniaturized] == YES)
{
UInt8 *myPlanes[5];
UInt32 myStore,
*myTop,
*myBottom,
myRow = vid.width,
mySize = myRow * gDisplayHeight,
j;
if (gMiniBuffer == NULL || gMiniWindow == NULL)
{
return;
}
// get the current OpenGL rendering:
glReadPixels (0, 0, gDisplayWidth, gDisplayHeight, GL_RGBA, GL_UNSIGNED_BYTE, gMiniBuffer);
// mirror the buffer [only vertical]:
myTop = gMiniBuffer;
myBottom = myTop + mySize - myRow;
while (myTop < myBottom)
{
for (j = 0; j < myRow; j++)
{
myStore = myTop[j];
myTop[j] = myBottom[j];
myBottom[j] = myStore;
}
myTop += myRow;
myBottom -= myRow;
}
myPlanes[0] = (UInt8 *) gMiniBuffer;
[gMiniWindow lockFocus];
NSDrawBitmap (NSMakeRect (0, 0, 128, 128), gDisplayWidth, gDisplayHeight, 8, 3, 32,
gDisplayWidth * 4, NO, NO, @"NSDeviceRGBColorSpace",
(const UInt8 * const *) myPlanes);
[gMiniWindow unlockFocus];
// doesn't seem to work:
//[gWindow setMiniwindowImage: gMiniWindow];
// so use the app icon instead:
[NSApp setApplicationIconImage: gMiniWindow];
gIsMinimized = YES;
}
else
{
VID_RestoreOldIcon ();
}
}
//__________________________________________________________________________________________________GL_SetFSAA()
void GL_SetFSAA (UInt32 theFSAALevel)
{
// check the level value:
if (theFSAALevel != 1 && theFSAALevel != 2 && theFSAALevel != 4)
{
Cvar_SetValue (gl_fsaa.name, gFSAALevel);
Con_Printf ("Invalid FSAA level, accepted values are 1, 2 or 4!\n");
return;
}
// check if FSAA is available:
if (gl_fsaaavailable == NO)
{
gFSAALevel = gl_fsaa.value;
if (theFSAALevel != 1)
{
Con_Printf ("FSAA not supported with the current graphics board!\n");
}
return;
}
// set the level:
[gGLContext makeCurrentContext];
if(CGLSetParameter (CGLGetCurrentContext (), ATI_FSAA_LEVEL, &theFSAALevel) == CGDisplayNoErr)
{
gFSAALevel = theFSAALevel;
Con_Printf ("FSAA level set to: %d!\n", theFSAALevel);
}
else
{
Con_Printf ("Error while trying to set the new FSAA Level!\n");
Cvar_SetValue (gl_fsaa.name, gFSAALevel);
}
}
//___________________________________________________________________________________________GL_SetPNTriangles()
void GL_SetPNTriangles (SInt32 thePNTriangleLevel)
{
// check if the pntriangles extension is available:
if (gl_pntriangles == NO)
{
if (thePNTriangleLevel != -1)
{
Con_Printf ("pntriangles not supported with the current graphics board!\n");
}
gPNTriangleLevel = (gl_truform.value > 7.0f) ? 7.0f : gl_truform.value;
if (gPNTriangleLevel < 0.0f) gPNTriangleLevel = -1.0f;
Cvar_SetValue (gl_truform.name, gPNTriangleLevel);
return;
}
if (thePNTriangleLevel >= 0)
{
if (thePNTriangleLevel > 7)
{
thePNTriangleLevel = 7;
Con_Printf ("Clamping to max. pntriangle level 7!\n");
}
// enable pn_triangles. lightning required due to a bug of OpenGL!
glEnable (GL_PN_TRIANGLES_ATIX);
glEnable (GL_LIGHTING);
glLightModelfv (GL_LIGHT_MODEL_AMBIENT, gGLTruformAmbient);
glEnable (GL_COLOR_MATERIAL);
// point mode:
glPNTrianglesiATIX (GL_PN_TRIANGLES_POINT_MODE_ATIX, GL_PN_TRIANGLES_POINT_MODE_LINEAR_ATIX);
// normal mode (no normals used at all by Quake):
glPNTrianglesiATIX (GL_PN_TRIANGLES_NORMAL_MODE_ATIX, GL_PN_TRIANGLES_NORMAL_MODE_LINEAR_ATIX);
// tesselation level:
glPNTrianglesiATIX (GL_PN_TRIANGLES_TESSELATION_LEVEL_ATIX, thePNTriangleLevel);
Con_Printf ("Truform enabled, current tesselation level: %d!\n", thePNTriangleLevel);
}
else
{
thePNTriangleLevel = -1;
glDisable (GL_PN_TRIANGLES_ATIX);
glDisable (GL_LIGHTING);
Con_Printf ("Truform disabled!\n");
}
gPNTriangleLevel = thePNTriangleLevel;
Cvar_SetValue (gl_truform.name, gPNTriangleLevel);
}
//______________________________________________________________________________GL_SetTextureFilterAnisotropic()
void GL_SetTextureFilterAnisotropic (UInt32 theState)
{
// clamp the value to 1 [= enabled]:
gGLAnisotropic = theState ? YES : NO;
Cvar_SetValue (gl_anisotropic.name, gGLAnisotropic);
// check if anisotropic filtering is available:
if (gl_texturefilteranisotropic == NO)
{
gl_texureanisotropylevel = 1.0f;
if (theState != 0)
{
Con_Printf ("Anisotropic tetxure filtering not supported with the current graphics board!\n");
}
return;
}
// enable/disable anisotropic filtering:
if (theState == 0)
{
gl_texureanisotropylevel = 1.0f;
}
else
{
glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gl_texureanisotropylevel);
}
}
//___________________________________________________________________________________________GL_BeginRendering()
void GL_BeginRendering (SInt *x, SInt *y, SInt *width, SInt *height)
{
*x = *y = 0;
*width = gDisplayWidth;
*height = gDisplayHeight;
}
//_____________________________________________________________________________________________GL_EndRendering()
void GL_EndRendering (void)
{
// flush OpenGL buffers:
glFlush ();
CGLFlushDrawable ([gGLContext cglContext]);//CGLGetCurrentContext ());
// check if video_wait changed:
if(vid_wait.value != gVidWait)
{
VID_SetWait ((UInt32) vid_wait.value);
}
// check if anisotropic texture filtering changed:
if (gl_anisotropic.value != gGLAnisotropic)
{
GL_SetTextureFilterAnisotropic ((UInt32) gl_anisotropic.value);
}
// check if vid_fsaa changed:
if (gl_fsaa.value != gFSAALevel)
{
GL_SetFSAA ((UInt32) gl_fsaa.value);
}
// check if truform changed:
if (gl_truform.value != gPNTriangleLevel)
{
GL_SetPNTriangles ((SInt32) gl_truform.value);
}
// specials for windowed/fullscreen mode:
if (gDisplayFullscreen == YES)
{
static float myOldGamma = 0.0f;
// setting the gamma requires fullscreen mode...
if (myOldGamma != v_gamma.value)
{
if (gOriginalGamma != NULL)
{
// set the current gamma:
myOldGamma = 1.0f - v_gamma.value;
if (myOldGamma < 0.0f)
{
myOldGamma = 0.0f;
}
else
{
if (myOldGamma >= 1.0f)
{
myOldGamma = 0.999f;
}
}
CGSetDisplayTransferByFormula (gOriginalGamma[gDisplay].displayID,
myOldGamma,
1.0f,
gOriginalGamma[gDisplay].component[2],
myOldGamma,
1.0f,
gOriginalGamma[gDisplay].component[5],
myOldGamma,
1.0f,
gOriginalGamma[gDisplay].component[8]);
}
else
{
Con_Printf ("Can\'t set the requested gamma value!\n");
}
myOldGamma = v_gamma.value;
}
}
else
{
// if minimized, render inside the Dock!
GL_RenderInsideDock ();
}
}
#pragma mark -
//________________________________________________________________________________iMPLEMENTATION_NSOpenGLContext
@implementation NSOpenGLContext (CGLContextAccess)
//___________________________________________________________________________________________________cglContext:
- (CGLContextObj) cglContext;
{
return (_contextAuxiliary);
}
@end
//______________________________________________________________________________________iMPLEMENTATION_QuakeView
@implementation QuakeView
//_________________________________________________________________________________________acceptsFirstResponder
-(BOOL) acceptsFirstResponder
{
return YES;
}
//________________________________________________________________________________________________windowDidMove:
- (void)windowDidMove: (NSNotification *)note
{
NSRect myRect;
myRect = [gWindow frame];
gWindowPosX = myRect.origin.x + 1.0f;
gWindowPosY = myRect.origin.y + 1.0f;
}
@end
//___________________________________________________________________________________________________________eOF