// NeL - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#include "stdopengl.h"
#include "driver_opengl.h"
#include "driver_opengl_extension.h"
// by default, we disable the windows menu keys (F10, ALT and ALT+SPACE key doesn't freeze or open the menu)
#define NL_DISABLE_MENU
#ifdef NL_OS_WINDOWS
# define WIN32_LEAN_AND_MEAN
# define NOMINMAX
# include
# include
# include
#else // NL_OS_UNIX
# include
#endif // NL_OS_UNIX
#include
#include
#include "nel/3d/viewport.h"
#include "nel/3d/scissor.h"
#include "nel/3d/u_driver.h"
#include "nel/3d/vertex_buffer.h"
#include "nel/3d/light.h"
#include "nel/3d/index_buffer.h"
#include "nel/misc/rect.h"
#include "nel/misc/di_event_emitter.h"
#include "nel/misc/mouse_device.h"
#include "nel/misc/hierarchical_timer.h"
#include "nel/misc/dynloadlib.h"
#include "driver_opengl_vertex_buffer_hard.h"
using namespace std;
using namespace NLMISC;
// ***************************************************************************
// try to allocate 16Mo by default of AGP Ram.
#define NL3D_DRV_VERTEXARRAY_AGP_INIT_SIZE (16384*1024)
// ***************************************************************************
#ifndef NL_STATIC
#ifdef NL_OS_WINDOWS
// dllmain::
BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH)
{
// Yoyo: Vianney change: don't need to call initDebug() anymore.
// initDebug();
}
return true;
}
#endif /* NL_OS_WINDOWS */
class CDriverGLNelLibrary : public INelLibrary {
void onLibraryLoaded(bool firstTime) { }
void onLibraryUnloaded(bool lastTime) { }
};
NLMISC_DECL_PURE_LIB(CDriverGLNelLibrary)
#endif /* #ifndef NL_STATIC */
namespace NL3D
{
CMaterial::CTexEnv CDriverGL::_TexEnvReplace;
#ifdef NL_OS_WINDOWS
uint CDriverGL::_Registered=0;
#endif // NL_OS_WINDOWS
// Version of the driver. Not the interface version!! Increment when implementation of the driver change.
const uint32 CDriverGL::ReleaseVersion = 0xf;
// Number of register to allocate for the EXTVertexShader extension
const uint CDriverGL::_EVSNumConstant = 97;
#ifdef NL_OS_WINDOWS
#ifdef NL_STATIC
# pragma comment(lib, "opengl32")
# pragma comment(lib, "dinput8")
# pragma comment(lib, "dxguid")
IDriver* createGlDriverInstance ()
{
return new CDriverGL;
}
#else
__declspec(dllexport) IDriver* NL3D_createIDriverInstance ()
{
return new CDriverGL;
}
__declspec(dllexport) uint32 NL3D_interfaceVersion ()
{
return IDriver::InterfaceVersion;
}
#endif
static bool GlWndProc(CDriverGL *driver, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
H_AUTO_OGL(GlWndProc)
if(message == WM_SIZE)
{
if (driver != NULL)
{
RECT rect;
GetClientRect (driver->_hWnd, &rect);
// Setup gl viewport
driver->_WindowWidth = rect.right-rect.left;
driver->_WindowHeight = rect.bottom-rect.top;
}
}
else if(message == WM_MOVE)
{
if (driver != NULL)
{
RECT rect;
GetWindowRect (hWnd, &rect);
driver->_WindowX = rect.left;
driver->_WindowY = rect.top;
}
}
else if (message == WM_ACTIVATE)
{
WORD fActive = LOWORD(wParam);
if (fActive == WA_INACTIVE)
{
driver->_WndActive = false;
}
else
{
driver->_WndActive = true;
}
}
bool trapMessage = false;
if (driver->_EventEmitter.getNumEmitters() > 0)
{
CWinEventEmitter *we = NLMISC::safe_cast(driver->_EventEmitter.getEmitter(0));
// Process the message by the emitter
we->setHWnd(hWnd);
trapMessage = we->processMessage (hWnd, message, wParam, lParam);
}
return trapMessage;
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
H_AUTO_OGL(DriverGL_WndProc)
// Get the driver pointer..
CDriverGL *pDriver=(CDriverGL*)GetWindowLongPtr (hWnd, GWLP_USERDATA);
bool trapMessage = false;
if (pDriver != NULL)
{
trapMessage = GlWndProc (pDriver, hWnd, message, wParam, lParam);
}
#ifdef NL_DISABLE_MENU
// disable menu (F10, ALT and ALT+SPACE key doesn't freeze or open the menu)
if(message == WM_SYSCOMMAND && wParam == SC_KEYMENU)
return 0;
#endif // NL_DISABLE_MENU
// disable menu (default ALT-F4 behavior is disabled)
if(message == WM_CLOSE)
{
if(pDriver && pDriver->ExitFunc)
{
pDriver->ExitFunc();
}
else
{
#ifndef NL_DISABLE_MENU
// if we don't disable menu, alt F4 make a direct exit else we discard the message
exit(0);
#endif // NL_DISABLE_MENU
}
return 0;
}
return trapMessage ? 0 : DefWindowProcW(hWnd, message, wParam, lParam);
}
#elif defined (NL_OS_UNIX)
#ifdef NL_STATIC
IDriver* createGlDriverInstance ()
{
return new CDriverGL;
}
#else
extern "C"
{
IDriver* NL3D_createIDriverInstance ()
{
return new CDriverGL;
}
uint32 NL3D_interfaceVersion ()
{
return IDriver::InterfaceVersion;
}
}
#endif
/*
static Bool WndProc(Display *d, XEvent *e, char *arg)
{
nlinfo("3D: glop %d %d", e->type, e->xmap.window);
CDriverGL *pDriver = (CDriverGL*)arg;
if (pDriver != NULL)
{
// Process the message by the emitter
pDriver->_EventEmitter.processMessage();
}
// TODO i'don t know what to return exactly
return (e->type == MapNotify) && (e->xmap.window == (Window) arg);
}
*/
#endif // NL_OS_UNIX
GLenum CDriverGL::NLCubeFaceToGLCubeFace[6] =
{
GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB,
GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB
};
// ***************************************************************************
CDriverGL::CDriverGL()
{
H_AUTO_OGL(CDriverGL_CDriverGL)
_OffScreen = false;
#ifdef NL_OS_WINDOWS
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
_NeedToRestaureGammaRamp = false;
_Interval = 1;
#elif defined (NL_OS_UNIX) // NL_OS_WINDOWS
cursor = None;
#ifdef XF86VIDMODE
// zero the old screen mode
memset(&_OldScreenMode, 0, sizeof(_OldScreenMode));
#endif //XF86VIDMODE
#endif // NL_OS_UNIX
_FullScreen= false;
_CurrentMaterial=NULL;
_Initialized = false;
_FogEnabled= false;
_FogEnd = _FogStart = 0.f;
_CurrentFogColor[0]= 0;
_CurrentFogColor[1]= 0;
_CurrentFogColor[2]= 0;
_CurrentFogColor[3]= 0;
_RenderTargetFBO = false;
_LightSetupDirty= false;
_ModelViewMatrixDirty= false;
_RenderSetupDirty= false;
// All lights default pos.
uint i;
for(i=0;iisBloomTexture() && tex->mipMapOff()
&& (!isPowerOf2(tex->getWidth()) || !isPowerOf2(tex->getHeight())));
}
// ***************************************************************************
bool CDriverGL::activeFrameBufferObject(ITexture * tex)
{
if(supportFrameBufferObject()/* && supportPackedDepthStencil()*/)
{
if(tex)
{
CTextureDrvInfosGL* gltext = (CTextureDrvInfosGL*)(ITextureDrvInfos*)(tex->TextureDrvShare->DrvTexture);
return gltext->activeFrameBufferObject(tex);
}
else
{
nglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
return true;
}
}
return false;
}
// --------------------------------------------------
void CDriverGL::disableHardwareVertexProgram()
{
H_AUTO_OGL(CDriverGL_disableHardwareVertexProgram)
_Extensions.DisableHardwareVertexProgram= true;
}
void CDriverGL::disableHardwareVertexArrayAGP()
{
H_AUTO_OGL(CDriverGL_disableHardwareVertexArrayAGP)
_Extensions.DisableHardwareVertexArrayAGP= true;
}
void CDriverGL::disableHardwareTextureShader()
{
H_AUTO_OGL(CDriverGL_disableHardwareTextureShader)
_Extensions.DisableHardwareTextureShader= true;
}
// --------------------------------------------------
bool CDriverGL::setDisplay(void *wnd, const GfxMode &mode, bool show, bool resizeable) throw(EBadDisplay)
{
H_AUTO_OGL(CDriverGL_setDisplay)
uint width = mode.Width;
uint height = mode.Height;
#ifdef NL_OS_WINDOWS
// Driver caps.
//=============
// Retrieve the WGL extensions before init the driver.
int pf;
_OffScreen = mode.OffScreen;
// Init pointers
_PBuffer = NULL;
_hWnd = NULL;
_WindowWidth = _WindowHeight = _WindowX = _WindowY = 0;
_hRC = NULL;
_hDC = NULL;
// Offscreen mode ?
if (_OffScreen)
{
// Get a hdc
ULONG WndFlags=WS_OVERLAPPEDWINDOW+WS_CLIPCHILDREN+WS_CLIPSIBLINGS;
WndFlags&=~WS_VISIBLE;
RECT WndRect;
WndRect.left=0;
WndRect.top=0;
WndRect.right=width;
WndRect.bottom=height;
AdjustWindowRect(&WndRect,WndFlags,FALSE);
HWND tmpHWND = CreateWindowW(L"NLClass",
L"",
WndFlags,
CW_USEDEFAULT,CW_USEDEFAULT,
WndRect.right,WndRect.bottom,
NULL,
NULL,
GetModuleHandleW(NULL),
NULL);
if (!tmpHWND)
{
nlwarning ("CDriverGL::setDisplay: CreateWindowW failed");
return false;
}
// resize the window
RECT rc;
SetRect (&rc, 0, 0, width, height);
_WindowWidth = width;
_WindowHeight = height;
AdjustWindowRectEx (&rc, GetWindowStyle (_hWnd), GetMenu (_hWnd) != NULL, GetWindowExStyle (_hWnd));
SetWindowPos (_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE );
// Get the
HDC tempHDC = GetDC(tmpHWND);
_Depth=uint8(GetDeviceCaps(tempHDC,BITSPIXEL));
// ---
memset(&_pfd,0,sizeof(_pfd));
_pfd.nSize = sizeof(_pfd);
_pfd.nVersion = 1;
_pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
_pfd.iPixelType = PFD_TYPE_RGBA;
_pfd.cColorBits = (char)_Depth;
// Choose best suited Depth Buffer.
if(_Depth<=16)
{
_pfd.cDepthBits = 16;
}
else
{
_pfd.cDepthBits = 24;
_pfd.cAlphaBits = 8;
}
_pfd.iLayerType = PFD_MAIN_PLANE;
pf=ChoosePixelFormat(tempHDC,&_pfd);
if (!pf)
{
nlwarning ("CDriverGL::setDisplay: ChoosePixelFormat failed");
DestroyWindow (tmpHWND);
return false;
}
if ( !SetPixelFormat(tempHDC,pf,&_pfd) )
{
nlwarning ("CDriverGL::setDisplay: SetPixelFormat failed");
DestroyWindow (tmpHWND);
return false;
}
// Create gl context
HGLRC tempGLRC = wglCreateContext(tempHDC);
if (tempGLRC == NULL)
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglCreateContext failed: 0x%x", error);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
// Make the context current
if (!wglMakeCurrent(tempHDC,tempGLRC))
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglMakeCurrent failed: 0x%x", error);
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
// Register WGL functions
registerWGlExtensions (_Extensions, tempHDC);
HDC hdc = wglGetCurrentDC ();
if (hdc == NULL)
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglGetCurrentDC failed: 0x%x", error);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
// Get ready to query for a suitable pixel format that meets our
// minimum requirements.
int iattributes[2*20];
float fattributes[2*20];
int niattribs = 0;
// Attribute arrays must be "0" terminated - for simplicity, first
// just zero-out the array then fill from left to right.
for ( int a = 0; a < 2*20; a++ )
{
iattributes[a] = 0;
fattributes[a] = 0;
}
// Since we are trying to create a pbuffer, the pixel format we
// request (and subsequently use) must be "buffer capable".
iattributes[2*niattribs ] = WGL_DRAW_TO_PBUFFER_ARB;
iattributes[2*niattribs+1] = true;
niattribs++;
// We require a minimum of 24-bit depth.
iattributes[2*niattribs ] = WGL_DEPTH_BITS_ARB;
iattributes[2*niattribs+1] = 24;
niattribs++;
// We require a minimum of 8-bits for each R, G, B, and A.
iattributes[2*niattribs ] = WGL_RED_BITS_ARB;
iattributes[2*niattribs+1] = 8;
niattribs++;
iattributes[2*niattribs ] = WGL_GREEN_BITS_ARB;
iattributes[2*niattribs+1] = 8;
niattribs++;
iattributes[2*niattribs ] = WGL_BLUE_BITS_ARB;
iattributes[2*niattribs+1] = 8;
niattribs++;
iattributes[2*niattribs ] = WGL_ALPHA_BITS_ARB;
iattributes[2*niattribs+1] = 8;
niattribs++;
// Now obtain a list of pixel formats that meet these minimum
// requirements.
int pformat[20];
unsigned int nformats;
if ( !nwglChoosePixelFormatARB ( hdc, iattributes, fattributes,
20, pformat, &nformats ) )
{
nlwarning ( "pbuffer creation error: Couldn't find a suitable pixel format." );
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
return false;
}
/* After determining a compatible pixel format, the next step is to create a pbuffer of the
chosen format. Fortunately this step is fairly easy, as you merely select one of the formats
returned in the list in step #2 and call the function: */
int iattributes2[1] = {0};
// int iattributes2[] = {WGL_PBUFFER_LARGEST_ARB, 1, 0};
_PBuffer = nwglCreatePbufferARB( hdc, pformat[0], width, height, iattributes2 );
if (_PBuffer == NULL)
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglCreatePbufferARB failed: 0x%x", error);
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
/* After creating a pbuffer, you may use this functions to determine the dimensions of the pbuffer actually created. */
if ( !nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_WIDTH_ARB, (int*)&width ) )
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglQueryPbufferARB failed: 0x%x", error);
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
if ( !nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_HEIGHT_ARB, (int*)&height ) )
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglQueryPbufferARB failed: 0x%x", error);
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
_WindowWidth = width;
_WindowHeight = height;
/* The next step is to create a device context for the newly created pbuffer. To do this,
call the the function: */
_hDC = nwglGetPbufferDCARB( _PBuffer );
if (_hDC == NULL)
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglGetPbufferDCARB failed: 0x%x", error);
nwglDestroyPbufferARB( _PBuffer );
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
/* The final step of pbuffer creation is to create an OpenGL rendering context and
associate it with the handle for the pbuffer's device context created in step #4. This is done as follows */
_hRC = wglCreateContext( _hDC );
if (_hRC == NULL)
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglCreateContext failed: 0x%x", error);
nwglReleasePbufferDCARB( _PBuffer, _hDC );
nwglDestroyPbufferARB( _PBuffer );
wglDeleteContext (tempGLRC);
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
// Get the depth
_Depth = uint8(GetDeviceCaps (_hDC, BITSPIXEL));
// Destroy the temp gl context
if (!wglDeleteContext (tempGLRC))
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglDeleteContext failed: 0x%x", error);
}
// Destroy the temp windows
if (!DestroyWindow (tmpHWND))
nlwarning ("CDriverGL::setDisplay: DestroyWindow failed");
/* After a pbuffer has been successfully created you can use it for off-screen rendering. To do
so, you'll first need to bind the pbuffer, or more precisely, make its GL rendering context
the current context that will interpret all OpenGL commands and state changes. */
if (!wglMakeCurrent(_hDC,_hRC))
{
DWORD error = GetLastError ();
nlwarning ("CDriverGL::setDisplay: wglMakeCurrent failed: 0x%x", error);
wglDeleteContext (_hRC);
nwglReleasePbufferDCARB( _PBuffer, _hDC );
nwglDestroyPbufferARB( _PBuffer );
DestroyWindow (tmpHWND);
_PBuffer = NULL;
_hWnd = NULL;
_hRC = NULL;
_hDC = NULL;
return false;
}
}
else
{
_FullScreen= false;
if (wnd)
{
_hWnd=(HWND)wnd;
_DestroyWindow=false;
}
else
{
ULONG WndFlags;
RECT WndRect;
// Must destroy this window
_DestroyWindow=true;
if(mode.Windowed)
if(resizeable)
WndFlags=WS_OVERLAPPEDWINDOW+WS_CLIPCHILDREN+WS_CLIPSIBLINGS;
else
WndFlags=WS_SYSMENU+WS_DLGFRAME+WS_CLIPCHILDREN+WS_CLIPSIBLINGS;
else
{
WndFlags=WS_POPUP;
_FullScreen= true;
DEVMODE devMode;
_OldScreenMode.dmSize= sizeof(DEVMODE);
_OldScreenMode.dmDriverExtra= 0;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &_OldScreenMode);
_OldScreenMode.dmFields= DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT | DM_DISPLAYFREQUENCY ;
devMode.dmSize= sizeof(DEVMODE);
devMode.dmDriverExtra= 0;
devMode.dmFields= DM_PELSWIDTH | DM_PELSHEIGHT;
devMode.dmPelsWidth= width;
devMode.dmPelsHeight= height;
if(mode.Depth > 0)
{
devMode.dmBitsPerPel= mode.Depth;
devMode.dmFields |= DM_BITSPERPEL;
}
if(mode.Frequency > 0)
{
devMode.dmDisplayFrequency= mode.Frequency;
devMode.dmFields |= DM_DISPLAYFREQUENCY;
}
if (ChangeDisplaySettings(&devMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
return false;
}
WndRect.left=0;
WndRect.top=0;
WndRect.right=width;
WndRect.bottom=height;
AdjustWindowRect(&WndRect,WndFlags,FALSE);
_hWnd = CreateWindowW( L"NLClass",
L"",
WndFlags,
CW_USEDEFAULT,CW_USEDEFAULT,
WndRect.right,WndRect.bottom,
NULL,
NULL,
GetModuleHandleW(NULL),
NULL);
if (_hWnd == NULL)
{
DWORD res = GetLastError();
nlwarning("CreateWindow failed: %u", res);
return false;
}
SetWindowLongPtr (_hWnd, GWLP_USERDATA, (LONG_PTR)this);
// resize the window
RECT rc;
SetRect (&rc, 0, 0, width, height);
AdjustWindowRectEx (&rc, GetWindowStyle (_hWnd), GetMenu (_hWnd) != NULL, GetWindowExStyle (_hWnd));
UINT flags = SWP_NOZORDER | SWP_NOACTIVATE;
if (mode.Windowed)
flags |= SWP_NOMOVE;
SetWindowPos (_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, flags);
if (show || _FullScreen)
showWindow(true);
}
// Init Window Width and Height
RECT clientRect;
GetClientRect (_hWnd, &clientRect);
_WindowWidth = clientRect.right-clientRect.left;
_WindowHeight = clientRect.bottom-clientRect.top;
GetWindowRect (_hWnd, &clientRect);
_WindowX = clientRect.left;
_WindowY = clientRect.top;
_hDC=GetDC(_hWnd);
wglMakeCurrent(_hDC,NULL);
_Depth=uint8(GetDeviceCaps(_hDC,BITSPIXEL));
// ---
memset(&_pfd,0,sizeof(_pfd));
_pfd.nSize = sizeof(_pfd);
_pfd.nVersion = 1;
_pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
_pfd.iPixelType = PFD_TYPE_RGBA;
_pfd.cColorBits = (char)_Depth;
// Choose best suited Depth Buffer.
if(_Depth<=16)
{
_pfd.cDepthBits = 16;
}
else
{
_pfd.cDepthBits = 24;
_pfd.cAlphaBits = 8;
_pfd.cStencilBits = 8;
}
_pfd.iLayerType = PFD_MAIN_PLANE;
pf=ChoosePixelFormat(_hDC,&_pfd);
if (!pf)
{
return false;
}
if ( !SetPixelFormat(_hDC,pf,&_pfd) )
{
return false;
}
_hRC=wglCreateContext(_hDC);
wglMakeCurrent(_hDC,_hRC);
}
/// release old emitter
while (_EventEmitter.getNumEmitters() != 0)
{
_EventEmitter.removeEmitter(_EventEmitter.getEmitter(_EventEmitter.getNumEmitters() - 1));
}
NLMISC::CWinEventEmitter *we = new NLMISC::CWinEventEmitter;
// setup the event emitter, and try to retrieve a direct input interface
_EventEmitter.addEmitter(we, true /*must delete*/); // the main emitter
/// try to get direct input
try
{
NLMISC::CDIEventEmitter *diee = NLMISC::CDIEventEmitter::create(GetModuleHandle(NULL), _hWnd, we);
if (diee)
{
_EventEmitter.addEmitter(diee, true);
}
}
catch(EDirectInput &e)
{
nlinfo(e.what());
}
#elif defined(NL_OS_UNIX) // NL_OS_WINDOWS
static int sAttribList16bpp[] =
{
GLX_RGBA,
GLX_DOUBLEBUFFER,
//GLX_BUFFER_SIZE, 16,
GLX_DEPTH_SIZE, 16,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
None
};
static int sAttribList24bpp[] =
{
GLX_RGBA,
GLX_DOUBLEBUFFER,
//GLX_BUFFER_SIZE, 16,
GLX_DEPTH_SIZE, 24,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
None
};
static int sAttribList32bpp[] =
{
GLX_RGBA,
GLX_DOUBLEBUFFER,
//GLX_BUFFER_SIZE, 32,
GLX_DEPTH_SIZE, 32,
GLX_RED_SIZE, 8,
GLX_GREEN_SIZE, 8,
GLX_BLUE_SIZE, 8,
GLX_ALPHA_SIZE, 8,
None
};
// first try 32bpp and if that fails 24bpp or 16bpp
XVisualInfo *visual_info = glXChooseVisual (dpy, DefaultScreen(dpy), sAttribList32bpp);
if (visual_info == NULL)
visual_info = glXChooseVisual(dpy, DefaultScreen(dpy), sAttribList24bpp);
if (visual_info == NULL)
visual_info = glXChooseVisual(dpy, DefaultScreen(dpy), sAttribList16bpp);
if(visual_info == NULL)
{
nlerror("glXChooseVisual() failed");
}
else
{
nldebug("3D: glXChooseVisual OK");
}
ctx = glXCreateContext (dpy, visual_info, None, GL_TRUE);
if(ctx == NULL)
{
nlerror("glXCreateContext() failed");
}
else
{
nldebug("3D: glXCreateContext() OK");
}
Colormap cmap = XCreateColormap (dpy, RootWindow(dpy, DefaultScreen(dpy)), visual_info->visual, AllocNone);
XSetWindowAttributes attr;
attr.colormap = cmap;
attr.background_pixel = BlackPixel(dpy, DefaultScreen(dpy));
#ifdef XF86VIDMODE
// If we're going to attempt fullscreen, we need to set redirect to True,
// This basically places the window with no borders in the top left
// corner of the screen.
if (mode.Windowed)
{
attr.override_redirect = False;
}
else
{
attr.override_redirect = True;
}
#else
attr.override_redirect = False;
#endif
int attr_flags = CWOverrideRedirect | CWColormap | CWBackPixel;
win = XCreateWindow (dpy, RootWindow(dpy, DefaultScreen(dpy)), 0, 0, width, height, 0, visual_info->depth, InputOutput, visual_info->visual, attr_flags, &attr);
if(!win)
{
nlerror("XCreateWindow() failed");
}
else
{
nldebug("3D: XCreateWindow() OK");
}
XSizeHints size_hints;
size_hints.x = 0;
size_hints.y = 0;
size_hints.width = width;
size_hints.height = height;
size_hints.flags = PSize | PMinSize | PMaxSize;
size_hints.min_width = width;
size_hints.min_height = height;
size_hints.max_width = width;
size_hints.max_height = height;
XTextProperty text_property;
// FIXME char*s are created as const char*, but that doesn't work
// with XStringListToTextProperty()'s char** ...
const char *title="NeL window";
XStringListToTextProperty((char**)&title, 1, &text_property);
XSetWMProperties (dpy, win, &text_property, &text_property, 0, 0, &size_hints, 0, 0);
glXMakeCurrent (dpy, win, ctx);
XMapRaised (dpy, win);
XSelectInput (dpy, win, KeyPressMask|KeyReleaseMask|ButtonPressMask|ButtonReleaseMask|PointerMotionMask);
XMapWindow(dpy, win);
_EventEmitter.init (dpy, win);
// XEvent event;
// XIfEvent(dpy, &event, WaitForNotify, (char *)this);
#ifdef XF86VIDMODE
if (!mode.Windowed)
{
// Set window to the right size, map it to the display, and raise it to the front
XResizeWindow(dpy, win, width, height);
XMapRaised(dpy, win);
XRaiseWindow(dpy, win);
// grab the mouse and keyboard on the fullscreen window
if ((XGrabPointer(dpy, win, True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime) != GrabSuccess) ||
(XGrabKeyboard(dpy, win, True, GrabModeAsync, GrabModeAsync, CurrentTime) != 0) )
{
// Until I work out how to deal with this nicely, it just gives
// an error and exits the prorgam.
nlerror("Unable to grab keyboard and mouse");
}
else
{
// Save the old screen mode and dotclock and viewport
memset(&_OldScreenMode, 0, sizeof(_OldScreenMode));
XF86VidModeGetModeLine(dpy, DefaultScreen(dpy), &_OldDotClock, &_OldScreenMode);
XF86VidModeGetViewPort(dpy, DefaultScreen(dpy), &_OldX, &_OldY);
// Get a list of modes, search for an appropriate one.
XF86VidModeModeInfo **modes;
int nmodes;
if (XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &nmodes, &modes))
{
int mode_index = -1; // Gah, magic numbers all bad.
for (int i = 0; i < nmodes; i++)
{
nldebug("3D: Available mode - %dx%d", modes[i]->hdisplay, modes[i]->vdisplay);
if(modes[i]->hdisplay == width && modes[i]->vdisplay == height)
{
mode_index = i;
break;
}
}
// Switch to the mode
if (mode_index != -1)
{
if(XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), modes[mode_index]))
{
nlinfo("3D: Switching to mode %dx%d", modes[mode_index]->hdisplay, modes[mode_index]->vdisplay);
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), 0, 0);
_FullScreen = true;
}
}
else
{
// This is a problem, since we've nuked the border from
// window in the setup stage, until I work out how
// to get it back (recreate window? seems excessive)
nlerror("Couldn't find an appropriate mode %dx%d", width, height);
}
}
}
}
#endif // XF86VIDMODE
#endif // NL_OS_UNIX
// Driver caps.
//=============
// Retrieve the extensions for the current context.
NL3D::registerGlExtensions (_Extensions);
vector lines;
explode(_Extensions.toString(), string("\n"), lines);
for(uint i = 0; i < lines.size(); i++)
nlinfo("3D: %s", lines[i].c_str());
//
#ifdef NL_OS_WINDOWS
NL3D::registerWGlExtensions (_Extensions, _hDC);
#endif // ifdef NL_OS_WINDOWS
// Check required extensions!!
// ARBMultiTexture is a OpenGL 1.2 required extension.
if(!_Extensions.ARBMultiTexture)
{
nlwarning("Missing Required GL extension: GL_ARB_multitexture. Update your driver");
throw EBadDisplay("Missing Required GL extension: GL_ARB_multitexture. Update your driver");
}
if(!_Extensions.EXTTextureEnvCombine)
{
nlwarning("Missing Important GL extension: GL_EXT_texture_env_combine => All envcombine are setup to GL_MODULATE!!!");
}
// Get num of light for this driver
int numLight;
glGetIntegerv (GL_MAX_LIGHTS, &numLight);
_MaxDriverLight=(uint)numLight;
if (_MaxDriverLight>MaxLight)
_MaxDriverLight=MaxLight;
// All User Light are disabled by Default
uint i;
for(i=0;i slow unlock)
// on Radeon 9500 and above : ARB_vertex_buffer_object is better
if (!_Extensions.ATIMapObjectBuffer)
{
_AGPVertexArrayRange= new CVertexArrayRangeATI(this);
_VRAMVertexArrayRange= new CVertexArrayRangeATI(this);
// BAD ATI extension scheme.
_SlowUnlockVBHard= true;
}
else
{
_AGPVertexArrayRange= new CVertexArrayRangeMapObjectATI(this);
_VRAMVertexArrayRange= new CVertexArrayRangeMapObjectATI(this);
}
_SupportVBHard= true;
// _MaxVerticesByVBHard= 65535; // should always work with recent drivers.
// tmp fix for ati
_MaxVerticesByVBHard= 16777216;
}
// Else, try with ARB ext
else if (_Extensions.ARBVertexBufferObject)
{
_AGPVertexArrayRange= new CVertexArrayRangeARB(this);
_VRAMVertexArrayRange= new CVertexArrayRangeARB(this);
_SupportVBHard= true;
_MaxVerticesByVBHard = ~0; // cant' know the value..
}
// Reset VertexArrayRange.
_CurrentVertexArrayRange= NULL;
_CurrentVertexBufferHard= NULL;
_NVCurrentVARPtr= NULL;
_NVCurrentVARSize= 0;
if(_SupportVBHard)
{
// try to allocate 16Mo by default of AGP Ram.
initVertexBufferHard(NL3D_DRV_VERTEXARRAY_AGP_INIT_SIZE, 0);
// If not success to allocate at least a minimum space in AGP, then disable completely VBHard feature
if( _AGPVertexArrayRange->sizeAllocated()==0 )
{
// reset any allocated VRAM space.
resetVertexArrayRange();
// delete containers
delete _AGPVertexArrayRange;
delete _VRAMVertexArrayRange;
_AGPVertexArrayRange= NULL;
_VRAMVertexArrayRange= NULL;
// disable.
_SupportVBHard= false;
_SlowUnlockVBHard= false;
_MaxVerticesByVBHard= 0;
}
}
// Init embm if present
//===========================================================
initEMBM();
// Init fragment shaders if present
//===========================================================
initFragmentShaders();
// Activate the default texture environnments for all stages.
//===========================================================
for(uint stage=0;stage 0)
{
newDevMode.dmBitsPerPel= mode.Depth;
newDevMode.dmFields |= DM_BITSPERPEL;
}
if(mode.Frequency > 0)
{
newDevMode.dmDisplayFrequency= mode.Frequency;
newDevMode.dmFields |= DM_DISPLAYFREQUENCY;
}
// try to really change the display mode
if (ChangeDisplaySettings(&newDevMode, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
return false;
// mode ok => copy changes
_WindowWidth = mode.Width;
_WindowHeight = mode.Height;
_Depth= mode.Depth;
// bkup user mode
if (!_FullScreen)
_OldScreenMode= oldDevMode;
// if old mode was not fullscreen
if (!_FullScreen)
{
// Under the XP theme desktop, this function call the winproc WM_SIZE and change _WindowWidth and _WindowHeight
sint32 windowWidth = _WindowWidth;
sint32 windowHeight = _WindowHeight;
modifyStyle(_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW+WS_CLIPCHILDREN+WS_CLIPSIBLINGS, WS_POPUP);
_WindowWidth = windowWidth;
_WindowHeight = windowHeight;
}
}
// Resize the window
RECT rc;
SetRect (&rc, 0, 0, _WindowWidth, _WindowHeight);
AdjustWindowRectEx (&rc, GetWindowStyle (_hWnd), false, GetWindowExStyle (_hWnd));
UINT flags = SWP_NOZORDER | SWP_NOACTIVATE;
if (mode.Windowed)
flags |= SWP_NOMOVE;
SetWindowPos (_hWnd, NULL, 0, 0, rc.right - rc.left, rc.bottom - rc.top, flags);
showWindow(true);
// Init Window Width and Height
RECT clientRect;
GetClientRect (_hWnd, &clientRect);
_WindowWidth = clientRect.right-clientRect.left;
_WindowHeight = clientRect.bottom-clientRect.top;
GetWindowRect (_hWnd, &clientRect);
_WindowX = clientRect.left;
_WindowY = clientRect.top;
_FullScreen = !mode.Windowed;
#else
// TODO linux version !!!
#endif
return true;
}
// --------------------------------------------------
bool CDriverGL::getModes(std::vector &modes)
{
H_AUTO_OGL(CDriverGL_getModes)
#ifdef NL_OS_WINDOWS
sint modeIndex = 0;
DEVMODE devMode;
while (EnumDisplaySettings (NULL, modeIndex, &devMode))
{
// Keep only 16 and 32 bits
if ((devMode.dmBitsPerPel == 16 ) || (devMode.dmBitsPerPel == 32))
{
// Add this mode
GfxMode mode;
mode.Width = (uint16)devMode.dmPelsWidth;
mode.Height = (uint16)devMode.dmPelsHeight;
mode.Depth = (uint8)devMode.dmBitsPerPel;
mode.Frequency = devMode.dmDisplayFrequency;
modes.push_back (mode);
}
// Mode index
modeIndex++;
}
#elif defined(NL_OS_MAC)
getMacModes(modes);
#else
# ifdef XF86VIDMODE
int nmodes;
XF86VidModeModeInfo **ms;
Bool ok = XF86VidModeGetAllModeLines(dpy, DefaultScreen(dpy), &nmodes, &ms);
if(ok)
{
nldebug("3D: %d available modes:", nmodes);
for (int j = 0; j < nmodes; j++)
{
// Add this mode
GfxMode mode;
mode.Width = (uint16)ms[j]->hdisplay;
mode.Height = (uint16)ms[j]->vdisplay;
mode.Frequency = 1000 * ms[j]->dotclock / (ms[j]->htotal * ms[j]->vtotal);
nldebug("3D: Mode %d: %dx%d, %d Hz", j, ms[j]->hdisplay,ms[j]->vdisplay, 1000 * ms[j]->dotclock / (ms[j]->htotal * ms[j]->vtotal));
modes.push_back (mode);
}
XFree(ms);
}
else
{
nlwarning("XF86VidModeGetAllModeLines returns 0, cannot get available video mode");
return false;
}
# endif
#endif
return true;
}
// --------------------------------------------------
bool CDriverGL::getCurrentScreenMode(GfxMode &mode)
{
H_AUTO_OGL(CDriverGL_getCurrentScreenMode)
#ifdef NL_OS_WINDOWS
DEVMODE devmode;
devmode.dmSize= sizeof(DEVMODE);
devmode.dmDriverExtra= 0;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &devmode);
mode.Windowed= !_FullScreen;
mode.OffScreen= false;
mode.Depth= (uint8)devmode.dmBitsPerPel;
mode.Frequency= devmode.dmDisplayFrequency,
mode.Width= (uint16)devmode.dmPelsWidth;
mode.Height= (uint16)devmode.dmPelsHeight;
#else
# ifdef XF86VIDMODE
sint pixelClock;
XF86VidModeModeLine xmode;
if (!XF86VidModeGetModeLine(dpy, DefaultScreen(dpy), &pixelClock, &xmode))
{
nlwarning("XF86VidModeGetModeLine returns 0, cannot get current video mode");
return false;
}
mode.Windowed = !_FullScreen;
mode.OffScreen = false;
mode.Depth = (uint) DefaultDepth(dpy, DefaultScreen(dpy));
mode.Frequency = 1000 * pixelClock / (xmode.htotal * xmode.vtotal) ;
mode.Width = xmode.hdisplay;
mode.Height = xmode.vdisplay;
nldebug("Current mode : %dx%d, %d Hz, %dbit", mode.Width, mode.Height, mode.Frequency, mode.Depth);
# endif
#endif
return true;
}
// --------------------------------------------------
void CDriverGL::setWindowTitle(const ucstring &title)
{
#ifdef NL_OS_WINDOWS
SetWindowTextW(_hWnd,(WCHAR*)title.c_str());
#elif defined(NL_OS_UNIX) // NL_OS_WINDOWS
XTextProperty text_property;
char *t = (char*)title.toUtf8().c_str();
XStringListToTextProperty(&t, 1, &text_property);
XSetWMName(dpy, win, &text_property);
#endif // NL_OS_WINDOWS
}
// ***************************************************************************
void CDriverGL::setWindowPos(uint32 x, uint32 y)
{
_WindowX = (sint32)x;
_WindowY = (sint32)y;
#ifdef NL_OS_WINDOWS
SetWindowPos(_hWnd, NULL, _WindowX, _WindowY, 0, 0, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOSIZE);
#elif defined(NL_OS_UNIX) // NL_OS_WINDOWS
XMoveWindow(dpy, win, _WindowX, _WindowY);
#endif // NL_OS_WINDOWS
}
// ***************************************************************************
void CDriverGL::showWindow(bool show)
{
#ifdef NL_OS_WINDOWS
ShowWindow (_hWnd, show ? SW_SHOW:SW_HIDE);
#elif defined(NL_OS_UNIX) // NL_OS_WINDOWS
if (show)
XMapWindow(dpy, win);
else
XUnmapWindow(dpy, win);
#endif // NL_OS_WINDOWS
}
// --------------------------------------------------
void CDriverGL::resetTextureShaders()
{
H_AUTO_OGL(CDriverGL_resetTextureShaders)
if (_Extensions.NVTextureShader)
{
glEnable(GL_TEXTURE_SHADER_NV);
for (uint stage = 0; stage < inlGetNumTextStages(); ++stage)
{
_DriverGLStates.activeTextureARB(stage);
if (stage != 0)
{
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_PREVIOUS_TEXTURE_INPUT_NV, GL_TEXTURE0_ARB + stage - 1);
}
glTexEnvi(GL_TEXTURE_SHADER_NV, GL_SHADER_OPERATION_NV, GL_NONE);
_CurrentTexAddrMode[stage] = GL_NONE;
}
glDisable(GL_TEXTURE_SHADER_NV);
_NVTextureShaderEnabled = false;
}
}
// --------------------------------------------------
emptyProc CDriverGL::getWindowProc()
{
H_AUTO_OGL(CDriverGL_getWindowProc)
#ifdef NL_OS_WINDOWS
return (emptyProc)GlWndProc;
#else // NL_OS_WINDOWS
return NULL;
#endif // NL_OS_WINDOWS
}
// --------------------------------------------------
bool CDriverGL::activate()
{
H_AUTO_OGL(CDriverGL_activate)
#ifdef NL_OS_WINDOWS
HGLRC hglrc=wglGetCurrentContext();
if (hglrc!=_hRC)
{
wglMakeCurrent(_hDC,_hRC);
}
#elif defined (NL_OS_UNIX)
GLXContext nctx=glXGetCurrentContext();
if (nctx != NULL && nctx!=ctx)
{
glXMakeCurrent(dpy, win,ctx);
}
#endif // NL_OS_WINDOWS
return true;
}
// --------------------------------------------------
bool CDriverGL::isTextureExist(const ITexture&tex)
{
H_AUTO_OGL(CDriverGL_isTextureExist)
bool result;
// Create the shared Name.
std::string name;
getTextureShareName (tex, name);
{
CSynchronized::CAccessor access(&_SyncTexDrvInfos);
TTexDrvInfoPtrMap &rTexDrvInfos = access.value();
result = (rTexDrvInfos.find(name) != rTexDrvInfos.end());
}
return result;
}
// --------------------------------------------------
bool CDriverGL::clear2D(CRGBA rgba)
{
H_AUTO_OGL(CDriverGL_clear2D)
glClearColor((float)rgba.R/255.0f,(float)rgba.G/255.0f,(float)rgba.B/255.0f,(float)rgba.A/255.0f);
glClear(GL_COLOR_BUFFER_BIT);
return true;
}
// --------------------------------------------------
bool CDriverGL::clearZBuffer(float zval)
{
H_AUTO_OGL(CDriverGL_clearZBuffer)
glClearDepth(zval);
_DriverGLStates.enableZWrite(true);
glClear(GL_DEPTH_BUFFER_BIT);
return true;
}
// --------------------------------------------------
bool CDriverGL::clearStencilBuffer(float stencilval)
{
H_AUTO_OGL(CDriverGL_clearStencilBuffer)
glClearStencil((int)stencilval);
glClear(GL_STENCIL_BUFFER_BIT);
return true;
}
// --------------------------------------------------
void CDriverGL::setColorMask (bool bRed, bool bGreen, bool bBlue, bool bAlpha)
{
H_AUTO_OGL(CDriverGL_setColorMask )
glColorMask (bRed, bGreen, bBlue, bAlpha);
}
// --------------------------------------------------
bool CDriverGL::swapBuffers()
{
H_AUTO_OGL(CDriverGL_swapBuffers)
++ _SwapBufferCounter;
// Reset texture shaders
//resetTextureShaders();
activeVertexProgram(NULL);
/* Yoyo: must do this (GeForce bug ??) esle weird results if end render with a VBHard.
Setup a std vertex buffer to ensure NVidia synchronisation.
*/
if (_Extensions.NVVertexArrayRange)
{
static CVertexBuffer dummyVB;
static bool dummyVBinit= false;
if(!dummyVBinit)
{
dummyVBinit= true;
// setup a full feature VB (maybe not useful ... :( ).
dummyVB.setVertexFormat(CVertexBuffer::PositionFlag|CVertexBuffer::NormalFlag|
CVertexBuffer::PrimaryColorFlag|CVertexBuffer::SecondaryColorFlag|
CVertexBuffer::TexCoord0Flag|CVertexBuffer::TexCoord1Flag|
CVertexBuffer::TexCoord2Flag|CVertexBuffer::TexCoord3Flag
);
// some vertices.
dummyVB.setNumVertices(10);
}
// activate each frame to close VBHard rendering.
// NVidia: This also force a SetFence on if last VB was a VBHard, "closing" it before swap.
//
activeVertexBuffer(dummyVB);
nlassert(_CurrentVertexBufferHard==NULL);
}
/* PATCH For Possible NVidia Synchronisation.
/*/
// Because of Bug with GeForce, must finishFence() for all VBHard.
/*set::iterator itVBHard= _VertexBufferHardSet.Set.begin();
while(itVBHard != _VertexBufferHardSet.Set.end() )
{
// Need only to do it for NVidia VB ones.
if((*itVBHard)->NVidiaVertexBufferHard)
{
CVertexBufferHardGLNVidia *vbHardNV= static_cast(*itVBHard);
// If needed, "flush" these VB.
vbHardNV->finishFence();
}
itVBHard++;
}*/
/* Need to Do this code only if Synchronisation PATCH before not done!
AS NV_Fence GeForce Implementation says. Test each frame the NVFence, until completion.
NB: finish is not required here. Just test. This is like a "non block synchronisation"
*/
if (_Extensions.NVVertexArrayRange)
{
set::iterator itVBHard= _VertexBufferHardSet.Set.begin();
while(itVBHard != _VertexBufferHardSet.Set.end() )
{
if((*itVBHard)->VBType == IVertexBufferHardGL::NVidiaVB)
{
CVertexBufferHardGLNVidia *vbHardNV= static_cast(*itVBHard);
if(vbHardNV->isFenceSet())
{
// update Fence Cache.
vbHardNV->testFence();
}
}
itVBHard++;
}
}
#ifdef NL_OS_WINDOWS
if (_EventEmitter.getNumEmitters() > 1) // is direct input running ?
{
// flush direct input messages if any
NLMISC::safe_cast(_EventEmitter.getEmitter(1))->poll();
}
#endif
if (!_WndActive)
{
if (_AGPVertexArrayRange) _AGPVertexArrayRange->updateLostBuffers();
if (_VRAMVertexArrayRange) _VRAMVertexArrayRange->updateLostBuffers();
}
#ifdef NL_OS_WINDOWS
SwapBuffers(_hDC);
#else // NL_OS_WINDOWS
glXSwapBuffers(dpy, win);
#endif // NL_OS_WINDOWS
// Activate the default texture environnments for all stages.
//===========================================================
// This is not a requirement, but it ensure a more stable state each frame.
// (well, maybe the good reason is "it hides much more the bugs" :o) ).
for(uint stage=0;stageupdateLostBuffers();
if (_VRAMVertexArrayRange) _VRAMVertexArrayRange->updateLostBuffers();
return true;
}
// --------------------------------------------------
bool CDriverGL::release()
{
H_AUTO_OGL(CDriverGL_release)
// release only if the driver was initialized
if (!_Initialized) return true;
// Call IDriver::release() before, to destroy textures, shaders and VBs...
IDriver::release();
_SwapBufferCounter = 0;
// delete querries
while (!_OcclusionQueryList.empty())
{
deleteOcclusionQuery(_OcclusionQueryList.front());
}
deleteFragmentShaders();
// release caustic cube map
// _CauticCubeMap = NULL;
// Reset VertexArrayRange.
resetVertexArrayRange();
// delete containers
delete _AGPVertexArrayRange;
delete _VRAMVertexArrayRange;
_AGPVertexArrayRange= NULL;
_VRAMVertexArrayRange= NULL;
#ifdef NL_OS_WINDOWS
// Then delete.
// wglMakeCurrent(NULL,NULL);
// Off-screen rendering ?
if (_OffScreen)
{
if (_PBuffer)
{
wglDeleteContext( _hRC );
nwglReleasePbufferDCARB( _PBuffer, _hDC );
nwglDestroyPbufferARB( _PBuffer );
}
}
else
{
if (_hRC)
wglDeleteContext(_hRC);
if (_hWnd&&_hDC)
{
ReleaseDC(_hWnd,_hDC);
if (_DestroyWindow)
DestroyWindow (_hWnd);
}
if(_FullScreen)
{
ChangeDisplaySettings(&_OldScreenMode, 0);
_FullScreen= false;
}
}
_hRC=NULL;
_hDC=NULL;
_hWnd=NULL;
_PBuffer = NULL;
// Restaure monitor color parameters
if (_NeedToRestaureGammaRamp)
{
HDC dc = CreateDC ("DISPLAY", NULL, NULL, NULL);
if (dc)
{
if (!SetDeviceGammaRamp (dc, _GammaRampBackuped))
nlwarning ("(CDriverGL::release): SetDeviceGammaRamp failed");
// Release the DC
ReleaseDC (NULL, dc);
}
else
{
nlwarning ("(CDriverGL::release): can't create DC");
}
}
#elif defined (NL_OS_UNIX)// NL_OS_WINDOWS
#ifdef XF86VIDMODE
if(_FullScreen)
{
XF86VidModeModeInfo info;
nlinfo("3D: Switching back to original mode");
// This is a bit ugly - a quick hack to copy the ModeLine structure
// into the modeInfo structure.
memcpy((XF86VidModeModeLine *)((char *)&info + sizeof(info.dotclock)),&_OldScreenMode, sizeof(XF86VidModeModeLine));
info.dotclock = _OldDotClock;
nlinfo("3D: Switching back mode to %dx%d", info.hdisplay, info.vdisplay);
XF86VidModeSwitchToMode(dpy, DefaultScreen(dpy), &info);
nlinfo("3D: Switching back viewport to %d,%d",_OldX, _OldY);
XF86VidModeSetViewPort(dpy, DefaultScreen(dpy), _OldX, _OldY);
// Ungrab the keyboard (probably not necessary);
XUnmapWindow(dpy, win);
XSync(dpy, True);
XUngrabKeyboard(dpy, CurrentTime);
}
#endif // XF86VIDMODE
if (ctx)
{
glXDestroyContext(dpy, ctx);
ctx = NULL;
}
XCloseDisplay(dpy);
dpy = NULL;
#endif // NL_OS_UNIX
// released
_Initialized= false;
return true;
}
// --------------------------------------------------
IDriver::TMessageBoxId CDriverGL::systemMessageBox (const char* message, const char* title, IDriver::TMessageBoxType type, TMessageBoxIcon icon)
{
H_AUTO_OGL(CDriverGL_systemMessageBox)
#ifdef NL_OS_WINDOWS
switch (::MessageBox (NULL, message, title, ((type==retryCancelType)?MB_RETRYCANCEL:
(type==yesNoCancelType)?MB_YESNOCANCEL:
(type==okCancelType)?MB_OKCANCEL:
(type==abortRetryIgnoreType)?MB_ABORTRETRYIGNORE:
(type==yesNoType)?MB_YESNO|MB_ICONQUESTION:MB_OK)|
((icon==handIcon)?MB_ICONHAND:
(icon==questionIcon)?MB_ICONQUESTION:
(icon==exclamationIcon)?MB_ICONEXCLAMATION:
(icon==asteriskIcon)?MB_ICONASTERISK:
(icon==warningIcon)?MB_ICONWARNING:
(icon==errorIcon)?MB_ICONERROR:
(icon==informationIcon)?MB_ICONINFORMATION:
(icon==stopIcon)?MB_ICONSTOP:0)))
{
case IDOK:
return okId;
case IDCANCEL:
return cancelId;
case IDABORT:
return abortId;
case IDRETRY:
return retryId;
case IDIGNORE:
return ignoreId;
case IDYES:
return yesId;
case IDNO:
return noId;
}
nlstop;
#else // NL_OS_WINDOWS
// Call the console version!
IDriver::systemMessageBox (message, title, type, icon);
#endif // NL_OS_WINDOWS
return okId;
}
// --------------------------------------------------
void CDriverGL::setupViewport (const class CViewport& viewport)
{
H_AUTO_OGL(CDriverGL_setupViewport )
#ifdef NL_OS_WINDOWS
if (_hWnd == NULL) return;
// Setup gl viewport
int clientWidth = _WindowWidth;
int clientHeight = _WindowHeight;
#else // NL_OS_WINDOWS
XWindowAttributes win_attributes;
if (!XGetWindowAttributes(dpy, win, &win_attributes))
throw EBadDisplay("Can't get window attributes.");
// Setup gl viewport
int clientWidth=win_attributes.width;
int clientHeight=win_attributes.height;
#endif // NL_OS_WINDOWS
// Backup the viewport
_CurrViewport = viewport;
// Get viewport
float x;
float y;
float width;
float height;
viewport.getValues (x, y, width, height);
// Render to texture : adjuste the viewport
if (_TextureTarget)
{
float factorX = 1;
float factorY = 1;
if(clientWidth)
factorX = (float)_TextureTarget->getWidth() / (float)clientWidth;
if(clientHeight)
factorY = (float)_TextureTarget->getHeight() / (float)clientHeight;
x *= factorX;
y *= factorY;
width *= factorX;
height *= factorY;
}
// Setup gl viewport
int ix=(int)((float)clientWidth*x+0.5f);
clamp (ix, 0, clientWidth);
int iy=(int)((float)clientHeight*y+0.5f);
clamp (iy, 0, clientHeight);
int iwidth=(int)((float)clientWidth*width+0.5f);
clamp (iwidth, 0, clientWidth-ix);
int iheight=(int)((float)clientHeight*height+0.5f);
clamp (iheight, 0, clientHeight-iy);
glViewport (ix, iy, iwidth, iheight);
}
// --------------------------------------------------
void CDriverGL::getViewport(CViewport &viewport)
{
H_AUTO_OGL(CDriverGL_getViewport)
viewport = _CurrViewport;
}
// --------------------------------------------------
void CDriverGL::setupScissor (const class CScissor& scissor)
{
H_AUTO_OGL(CDriverGL_setupScissor )
#ifdef NL_OS_WINDOWS
if (_hWnd == NULL) return;
// Setup gl viewport
int clientWidth = _WindowWidth;
int clientHeight = _WindowHeight;
#else // NL_OS_WINDOWS
XWindowAttributes win_attributes;
if (!XGetWindowAttributes(dpy, win, &win_attributes))
throw EBadDisplay("Can't get window attributes.");
// Setup gl viewport
int clientWidth=win_attributes.width;
int clientHeight=win_attributes.height;
#endif // NL_OS_WINDOWS
// Backup the scissor
_CurrScissor= scissor;
// Get scissor
float x= scissor.X;
float y= scissor.Y;
float width= scissor.Width;
float height= scissor.Height;
// Render to texture : adjuste the scissor
if (_TextureTarget)
{
float factorX = 1;
float factorY = 1;
if(clientWidth)
factorX = (float) _TextureTarget->getWidth() / (float)clientWidth;
if(clientHeight)
factorY = (float) _TextureTarget->getHeight() / (float)clientHeight;
x *= factorX;
y *= factorY;
width *= factorX;
height *= factorY;
}
// enable or disable Scissor, but AFTER textureTarget adjust
if(x==0 && x==0 && width>=1 && height>=1)
{
glDisable(GL_SCISSOR_TEST);
}
else
{
// Setup gl scissor
int ix0=(int)floor((float)clientWidth * x + 0.5f);
clamp (ix0, 0, clientWidth);
int iy0=(int)floor((float)clientHeight* y + 0.5f);
clamp (iy0, 0, clientHeight);
int ix1=(int)floor((float)clientWidth * (x+width) + 0.5f );
clamp (ix1, 0, clientWidth);
int iy1=(int)floor((float)clientHeight* (y+height) + 0.5f );
clamp (iy1, 0, clientHeight);
int iwidth= ix1 - ix0;
clamp (iwidth, 0, clientWidth);
int iheight= iy1 - iy0;
clamp (iheight, 0, clientHeight);
glScissor (ix0, iy0, iwidth, iheight);
glEnable(GL_SCISSOR_TEST);
}
}
// --------------------------------------------------
void CDriverGL::showCursor(bool b)
{
H_AUTO_OGL(CDriverGL_showCursor)
#ifdef NL_OS_WINDOWS
if (b)
{
while (ShowCursor(b) < 0)
;
}
else
{
while (ShowCursor(b) >= 0)
;
}
#elif defined (NL_OS_UNIX)
if (b)
{
if (cursor != None)
{
XFreeCursor(dpy, cursor);
cursor = None;
}
XUndefineCursor(dpy, win);
}
else
{
if (cursor == None)
{
char bm_no_data[] = { 0,0,0,0, 0,0,0,0 };
Pixmap pixmap_no_data = XCreateBitmapFromData (dpy, win, bm_no_data, 8, 8);
XColor black;
memset(&black, 0, sizeof (XColor));
black.flags = DoRed | DoGreen | DoBlue;
cursor = XCreatePixmapCursor (dpy, pixmap_no_data, pixmap_no_data, &black, &black, 0, 0);
XFreePixmap(dpy, pixmap_no_data);
}
XDefineCursor(dpy, win, cursor);
}
#endif // NL_OS_UNIX
}
// --------------------------------------------------
void CDriverGL::setMousePos(float x, float y)
{
H_AUTO_OGL(CDriverGL_setMousePos)
#ifdef NL_OS_WINDOWS
if (_hWnd)
{
// NeL window coordinate to MSWindows coordinates
POINT pt;
pt.x = (int)((float)(_WindowWidth)*x);
pt.y = (int)((float)(_WindowHeight)*(1.0f-y));
ClientToScreen (_hWnd, &pt);
SetCursorPos(pt.x, pt.y);
}
#elif defined (NL_OS_UNIX)
XWindowAttributes xwa;
XGetWindowAttributes (dpy, win, &xwa);
int x1 = (int)(x * (float) xwa.width);
int y1 = (int)((1.0f - y) * (float) xwa.height);
XWarpPointer (dpy, None, win, None, None, None, None, x1, y1);
#endif // NL_OS_UNIX
}
void CDriverGL::getWindowSize(uint32 &width, uint32 &height)
{
H_AUTO_OGL(CDriverGL_getWindowSize)
#ifdef NL_OS_WINDOWS
// Off-srceen rendering ?
if (_OffScreen)
{
if (_PBuffer)
{
nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_WIDTH_ARB, (int*)&width );
nwglQueryPbufferARB( _PBuffer, WGL_PBUFFER_HEIGHT_ARB, (int*)&height );
}
}
else
{
if (_hWnd)
{
width = (uint32)(_WindowWidth);
height = (uint32)(_WindowHeight);
}
}
#elif defined (NL_OS_UNIX)
XWindowAttributes xwa;
XGetWindowAttributes (dpy, win, &xwa);
width = (uint32) xwa.width;
height = (uint32) xwa.height;
#endif // NL_OS_UNIX
}
void CDriverGL::getWindowPos(uint32 &x, uint32 &y)
{
H_AUTO_OGL(CDriverGL_getWindowPos)
#ifdef NL_OS_WINDOWS
// Off-srceen rendering ?
if (_OffScreen)
{
if (_PBuffer)
{
x = y = 0;
}
}
else
{
if (_hWnd)
{
x = (uint32)(_WindowX);
y = (uint32)(_WindowY);
}
}
#elif defined (NL_OS_UNIX)
x = y = 0;
#endif // NL_OS_UNIX
}
// --------------------------------------------------
bool CDriverGL::isActive()
{
H_AUTO_OGL(CDriverGL_isActive)
#ifdef NL_OS_WINDOWS
return (IsWindow(_hWnd) != 0);
#elif defined (NL_OS_UNIX)
return true;
#endif // NL_OS_UNIX
}
uint8 CDriverGL::getBitPerPixel ()
{
H_AUTO_OGL(CDriverGL_getBitPerPixel )
return _Depth;
}
const char *CDriverGL::getVideocardInformation ()
{
H_AUTO_OGL(CDriverGL_getVideocardInformation)
static char name[1024];
if (!_Initialized) return "OpenGL isn't initialized";
const char *vendor = (const char *) glGetString (GL_VENDOR);
const char *renderer = (const char *) glGetString (GL_RENDERER);
const char *version = (const char *) glGetString (GL_VERSION);
smprintf(name, 1024, "OpenGL / %s / %s / %s", vendor, renderer, version);
return name;
}
void CDriverGL::setCapture (bool b)
{
H_AUTO_OGL(CDriverGL_setCapture )
#ifdef NL_OS_WINDOWS
if (b)
{
RECT client;
GetClientRect (_hWnd, &client);
POINT pt1,pt2;
pt1.x = client.left;
pt1.y = client.top;
ClientToScreen (_hWnd, &pt1);
pt2.x = client.right;
pt2.y = client.bottom;
ClientToScreen (_hWnd, &pt2);
client.bottom = pt2.y;
client.top = pt1.y;
client.left = pt1.x;
client.right = pt2.x;
ClipCursor (&client);
}
else
ClipCursor (NULL);
/*
if (b)
SetCapture (_hWnd);
else
ReleaseCapture ();
*/
#elif defined (NL_OS_UNIX)
if(b) // capture the cursor.
{
XGrabPointer(dpy, win, True, 0, GrabModeAsync, GrabModeAsync, win, None, CurrentTime);
}
else // release the cursor.
{
XUngrabPointer(dpy, CurrentTime);
}
#endif // NL_OS_UNIX
}
bool CDriverGL::clipRect(NLMISC::CRect &rect)
{
H_AUTO_OGL(CDriverGL_clipRect)
// Clip the wanted rectangle with window.
uint32 width, height;
getWindowSize(width, height);
sint32 xr=rect.right() ,yr=rect.bottom();
clamp((sint32&)rect.X, (sint32)0, (sint32)width);
clamp((sint32&)rect.Y, (sint32)0, (sint32)height);
clamp((sint32&)xr, (sint32)rect.X, (sint32)width);
clamp((sint32&)yr, (sint32)rect.Y, (sint32)height);
rect.Width= xr-rect.X;
rect.Height= yr-rect.Y;
return rect.Width>0 && rect.Height>0;
}
void CDriverGL::getBufferPart (CBitmap &bitmap, NLMISC::CRect &rect)
{
H_AUTO_OGL(CDriverGL_getBufferPart )
bitmap.reset();
if(clipRect(rect))
{
bitmap.resize(rect.Width, rect.Height, CBitmap::RGBA);
glReadPixels (rect.X, rect.Y, rect.Width, rect.Height, GL_RGBA, GL_UNSIGNED_BYTE, bitmap.getPixels ().getPtr());
}
}
void CDriverGL::getZBufferPart (std::vector &zbuffer, NLMISC::CRect &rect)
{
H_AUTO_OGL(CDriverGL_getZBufferPart )
zbuffer.clear();
if(clipRect(rect))
{
zbuffer.resize(rect.Width*rect.Height);
glPixelTransferf(GL_DEPTH_SCALE, 1.0f) ;
glPixelTransferf(GL_DEPTH_BIAS, 0.f) ;
glReadPixels (rect.X, rect.Y, rect.Width, rect.Height, GL_DEPTH_COMPONENT , GL_FLOAT, &(zbuffer[0]));
}
}
void CDriverGL::getZBuffer (std::vector &zbuffer)
{
H_AUTO_OGL(CDriverGL_getZBuffer )
CRect rect(0,0);
getWindowSize(rect.Width, rect.Height);
getZBufferPart(zbuffer, rect);
}
void CDriverGL::getBuffer (CBitmap &bitmap)
{
H_AUTO_OGL(CDriverGL_getBuffer )
CRect rect(0,0);
getWindowSize(rect.Width, rect.Height);
getBufferPart(bitmap, rect);
bitmap.flipV();
}
bool CDriverGL::fillBuffer (CBitmap &bitmap)
{
H_AUTO_OGL(CDriverGL_fillBuffer )
CRect rect(0,0);
getWindowSize(rect.Width, rect.Height);
if( rect.Width!=bitmap.getWidth() || rect.Height!=bitmap.getHeight() || bitmap.getPixelFormat()!=CBitmap::RGBA )
return false;
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glDrawPixels (rect.Width, rect.Height, GL_RGBA, GL_UNSIGNED_BYTE, &(bitmap.getPixels()[0]) );
return true;
}
// ***************************************************************************
void CDriverGL::copyFrameBufferToTexture(ITexture *tex,
uint32 level,
uint32 offsetx,
uint32 offsety,
uint32 x,
uint32 y,
uint32 width,
uint32 height,
uint cubeFace /*= 0*/
)
{
H_AUTO_OGL(CDriverGL_copyFrameBufferToTexture)
bool compressed = false;
getGlTextureFormat(*tex, compressed);
nlassert(!compressed);
// first, mark the texture as valid, and make sure there is a corresponding texture in the device memory
setupTexture(*tex);
CTextureDrvInfosGL* gltext = (CTextureDrvInfosGL*)(ITextureDrvInfos*)(tex->TextureDrvShare->DrvTexture);
//if (_RenderTargetFBO)
// gltext->activeFrameBufferObject(NULL);
_DriverGLStates.activeTextureARB(0);
// setup texture mode, after activeTextureARB()
CDriverGLStates::TTextureMode textureMode= CDriverGLStates::Texture2D;
if(gltext->TextureMode == GL_TEXTURE_RECTANGLE_NV)
textureMode = CDriverGLStates::TextureRect;
_DriverGLStates.setTextureMode(textureMode);
if (tex->isTextureCube())
{
glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, gltext->ID);
glCopyTexSubImage2D(NLCubeFaceToGLCubeFace[cubeFace], level, offsetx, offsety, x, y, width, height);
}
else
{
glBindTexture(gltext->TextureMode, gltext->ID);
glCopyTexSubImage2D(gltext->TextureMode, level, offsetx, offsety, x, y, width, height);
}
// disable texturing.
_DriverGLStates.setTextureMode(CDriverGLStates::TextureDisabled);
_CurrentTexture[0] = NULL;
_CurrentTextureInfoGL[0] = NULL;
//if (_RenderTargetFBO)
// gltext->activeFrameBufferObject(tex);
}
void CDriverGL::setPolygonMode (TPolygonMode mode)
{
H_AUTO_OGL(CDriverGL_setPolygonMode )
IDriver::setPolygonMode (mode);
// Set the polygon mode
switch (_PolygonMode)
{
case Filled:
glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
break;
case Line:
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
break;
case Point:
glPolygonMode (GL_FRONT_AND_BACK, GL_POINT);
break;
}
}
bool CDriverGL::fogEnabled()
{
H_AUTO_OGL(CDriverGL_fogEnabled)
return _FogEnabled;
}
void CDriverGL::enableFog(bool enable)
{
H_AUTO_OGL(CDriverGL_enableFog)
_DriverGLStates.enableFog(enable);
_FogEnabled= enable;
}
void CDriverGL::setupFog(float start, float end, CRGBA color)
{
H_AUTO_OGL(CDriverGL_setupFog)
glFogf(GL_FOG_MODE, GL_LINEAR);
glFogf(GL_FOG_START, start);
glFogf(GL_FOG_END, end);
_CurrentFogColor[0]= color.R/255.0f;
_CurrentFogColor[1]= color.G/255.0f;
_CurrentFogColor[2]= color.B/255.0f;
_CurrentFogColor[3]= color.A/255.0f;
glFogfv(GL_FOG_COLOR, _CurrentFogColor);
/** Special : with vertex program, using the extension EXT_vertex_shader, fog is emulated using 1 more constant to scale result to [0, 1]
*/
if (_Extensions.EXTVertexShader && !_Extensions.NVVertexProgram && !_Extensions.ARBVertexProgram)
{
if (!_ATIFogRangeFixed)
{
// last constant is used to store fog informations (fog must be rescaled to [0, 1], because of a driver bug)
if (start != end)
{
setConstant(_EVSNumConstant, 1.f / (start - end), - end / (start - end), 0, 0);
}
else
{
setConstant(_EVSNumConstant, 0.f, 0, 0, 0);
}
}
}
_FogStart = start;
_FogEnd = end;
}
// ***************************************************************************
float CDriverGL::getFogStart() const
{
H_AUTO_OGL(CDriverGL_getFogStart)
return _FogStart;
}
// ***************************************************************************
float CDriverGL::getFogEnd() const
{
H_AUTO_OGL(CDriverGL_getFogEnd)
return _FogEnd;
}
// ***************************************************************************
CRGBA CDriverGL::getFogColor() const
{
H_AUTO_OGL(CDriverGL_getFogColor)
CRGBA ret;
ret.R= (uint8)(_CurrentFogColor[0]*255);
ret.G= (uint8)(_CurrentFogColor[1]*255);
ret.B= (uint8)(_CurrentFogColor[2]*255);
ret.A= (uint8)(_CurrentFogColor[3]*255);
return ret;
}
// ***************************************************************************
void CDriverGL::profileRenderedPrimitives(CPrimitiveProfile &pIn, CPrimitiveProfile &pOut)
{
H_AUTO_OGL(CDriverGL_profileRenderedPrimitives)
pIn= _PrimitiveProfileIn;
pOut= _PrimitiveProfileOut;
}
// ***************************************************************************
uint32 CDriverGL::profileAllocatedTextureMemory()
{
H_AUTO_OGL(CDriverGL_profileAllocatedTextureMemory)
return _AllocatedTextureMemory;
}
// ***************************************************************************
uint32 CDriverGL::profileSetupedMaterials() const
{
H_AUTO_OGL(CDriverGL_profileSetupedMaterials)
return _NbSetupMaterialCall;
}
// ***************************************************************************
uint32 CDriverGL::profileSetupedModelMatrix() const
{
H_AUTO_OGL(CDriverGL_profileSetupedModelMatrix)
return _NbSetupModelMatrixCall;
}
// ***************************************************************************
void CDriverGL::enableUsedTextureMemorySum (bool enable)
{
H_AUTO_OGL(CDriverGL_enableUsedTextureMemorySum )
if (enable)
nlinfo ("3D: PERFORMANCE INFO: enableUsedTextureMemorySum has been set to true in CDriverGL");
_SumTextureMemoryUsed=enable;
}
// ***************************************************************************
uint32 CDriverGL::getUsedTextureMemory() const
{
H_AUTO_OGL(CDriverGL_getUsedTextureMemory)
// Sum memory used
uint32 memory=0;
// For each texture used
set::const_iterator ite=_TextureUsed.begin();
while (ite!=_TextureUsed.end())
{
// Get the gl texture
CTextureDrvInfosGL* gltext;
gltext= (*ite);
// Sum the memory used by this texture
memory+=gltext->TextureMemory;
// Next texture
ite++;
}
// Return the count
return memory;
}
// ***************************************************************************
bool CDriverGL::supportTextureShaders() const
{
H_AUTO_OGL(CDriverGL_supportTextureShaders)
// fully supported by NV_TEXTURE_SHADER
return _Extensions.NVTextureShader;
}
// ***************************************************************************
bool CDriverGL::isWaterShaderSupported() const
{
H_AUTO_OGL(CDriverGL_isWaterShaderSupported);
if(_Extensions.ARBFragmentProgram && ARBWaterShader[0] != 0) return true;
if (!_Extensions.EXTVertexShader && !_Extensions.NVVertexProgram && !_Extensions.ARBVertexProgram) return false; // should support vertex programs
if (!_Extensions.NVTextureShader && !_Extensions.ATIFragmentShader && !_Extensions.ARBFragmentProgram) return false;
return true;
}
// ***************************************************************************
bool CDriverGL::isTextureAddrModeSupported(CMaterial::TTexAddressingMode /* mode */) const
{
H_AUTO_OGL(CDriverGL_isTextureAddrModeSupported)
if (_Extensions.NVTextureShader)
{
// all the given addessing mode are supported with this extension
return true;
}
else
{
return false;
}
}
// ***************************************************************************
void CDriverGL::setMatrix2DForTextureOffsetAddrMode(const uint stage, const float mat[4])
{
H_AUTO_OGL(CDriverGL_setMatrix2DForTextureOffsetAddrMode)
if (!supportTextureShaders()) return;
//nlassert(supportTextureShaders());
nlassert(stage < inlGetNumTextStages() );
_DriverGLStates.activeTextureARB(stage);
glTexEnvfv(GL_TEXTURE_SHADER_NV, GL_OFFSET_TEXTURE_MATRIX_NV, mat);
}
// ***************************************************************************
void CDriverGL::enableNVTextureShader(bool enabled)
{
H_AUTO_OGL(CDriverGL_enableNVTextureShader)
if (enabled != _NVTextureShaderEnabled)
{
if (enabled)
{
glEnable(GL_TEXTURE_SHADER_NV);
}
else
{
glDisable(GL_TEXTURE_SHADER_NV);
}
_NVTextureShaderEnabled = enabled;
}
}
// ***************************************************************************
void CDriverGL::checkForPerPixelLightingSupport()
{
H_AUTO_OGL(CDriverGL_checkForPerPixelLightingSupport)
// we need at least 3 texture stages and cube map support + EnvCombine4 or 3 support
// TODO : support for EnvCombine3
// TODO : support for less than 3 stages
_SupportPerPixelShaderNoSpec = (_Extensions.NVTextureEnvCombine4 || _Extensions.ATITextureEnvCombine3)
&& _Extensions.ARBTextureCubeMap
&& _Extensions.NbTextureStages >= 3
&& (_Extensions.NVVertexProgram || _Extensions.ARBVertexProgram || _Extensions.EXTVertexShader);
_SupportPerPixelShader = (_Extensions.NVTextureEnvCombine4 || _Extensions.ATITextureEnvCombine3)
&& _Extensions.ARBTextureCubeMap
&& _Extensions.NbTextureStages >= 2
&& (_Extensions.NVVertexProgram || _Extensions.ARBVertexProgram || _Extensions.EXTVertexShader);
}
// ***************************************************************************
bool CDriverGL::supportPerPixelLighting(bool specular) const
{
H_AUTO_OGL(CDriverGL_supportPerPixelLighting)
return specular ? _SupportPerPixelShader : _SupportPerPixelShaderNoSpec;
}
// ***************************************************************************
void CDriverGL::setPerPixelLightingLight(CRGBA diffuse, CRGBA specular, float shininess)
{
H_AUTO_OGL(CDriverGL_setPerPixelLightingLight)
_PPLExponent = shininess;
_PPLightDiffuseColor = diffuse;
_PPLightSpecularColor = specular;
}
// ***************************************************************************
NLMISC::IMouseDevice *CDriverGL::enableLowLevelMouse(bool enable, bool exclusive)
{
H_AUTO_OGL(CDriverGL_enableLowLevelMouse)
#ifdef NL_OS_WINDOWS
if (_EventEmitter.getNumEmitters() < 2) return NULL;
NLMISC::CDIEventEmitter *diee = NLMISC::safe_cast(_EventEmitter.getEmitter(1));
if (enable)
{
try
{
NLMISC::IMouseDevice *md = diee->getMouseDevice(exclusive);
return md;
}
catch (EDirectInput &)
{
return NULL;
}
}
else
{
diee->releaseMouse();
return NULL;
}
#else
return NULL;
#endif
}
// ***************************************************************************
NLMISC::IKeyboardDevice *CDriverGL::enableLowLevelKeyboard(bool enable)
{
H_AUTO_OGL(CDriverGL_enableLowLevelKeyboard)
#ifdef NL_OS_WINDOWS
if (_EventEmitter.getNumEmitters() < 2) return NULL;
NLMISC::CDIEventEmitter *diee = NLMISC::safe_cast(_EventEmitter.getEmitter(1));
if (enable)
{
try
{
NLMISC::IKeyboardDevice *md = diee->getKeyboardDevice();
return md;
}
catch (EDirectInput &)
{
return NULL;
}
}
else
{
diee->releaseKeyboard();
return NULL;
}
#else
return NULL;
#endif
}
// ***************************************************************************
NLMISC::IInputDeviceManager *CDriverGL::getLowLevelInputDeviceManager()
{
H_AUTO_OGL(CDriverGL_getLowLevelInputDeviceManager)
#ifdef NL_OS_WINDOWS
if (_EventEmitter.getNumEmitters() < 2) return NULL;
NLMISC::CDIEventEmitter *diee = NLMISC::safe_cast(_EventEmitter.getEmitter(1));
return diee;
#else
return NULL;
#endif
}
// ***************************************************************************
uint CDriverGL::getDoubleClickDelay(bool hardwareMouse)
{
H_AUTO_OGL(CDriverGL_getDoubleClickDelay)
#ifdef NL_OS_WINDOWS
NLMISC::IMouseDevice *md = NULL;
if (_EventEmitter.getNumEmitters() >= 2)
{
NLMISC::CDIEventEmitter *diee = NLMISC::safe_cast(_EventEmitter.getEmitter(1));
if (diee->isMouseCreated())
{
try
{
md = diee->getMouseDevice(hardwareMouse);
}
catch (EDirectInput &)
{
// could not get device ..
}
}
}
if (md)
{
return md->getDoubleClickDelay();
}
// try to read the good value from windows
return ::GetDoubleClickTime();
#else
// TODO for Linux FIXME: FAKE FIX
return 250;
#endif
}
// ***************************************************************************
bool CDriverGL::supportBlendConstantColor() const
{
H_AUTO_OGL(CDriverGL_supportBlendConstantColor)
return _Extensions.EXTBlendColor;
}
// ***************************************************************************
void CDriverGL::setBlendConstantColor(NLMISC::CRGBA col)
{
H_AUTO_OGL(CDriverGL_setBlendConstantColor)
// bkup
_CurrentBlendConstantColor= col;
// update GL
if(!_Extensions.EXTBlendColor)
return;
static const float OO255= 1.0f/255;
nglBlendColorEXT(col.R*OO255, col.G*OO255, col.B*OO255, col.A*OO255);
}
// ***************************************************************************
NLMISC::CRGBA CDriverGL::getBlendConstantColor() const
{
H_AUTO_OGL(CDriverGL_CDriverGL)
return _CurrentBlendConstantColor;
}
// ***************************************************************************
uint CDriverGL::getNbTextureStages() const
{
H_AUTO_OGL(CDriverGL_getNbTextureStages)
return inlGetNumTextStages();
}
// ***************************************************************************
void CDriverGL::refreshProjMatrixFromGL()
{
H_AUTO_OGL(CDriverGL_refreshProjMatrixFromGL)
if (!_ProjMatDirty) return;
float mat[16];
glGetFloatv(GL_PROJECTION_MATRIX, mat);
_GLProjMat.set(mat);
_ProjMatDirty = false;
}
// ***************************************************************************
bool CDriverGL::setMonitorColorProperties (const CMonitorColorProperties &properties)
{
H_AUTO_OGL(CDriverGL_setMonitorColorProperties )
#ifdef NL_OS_WINDOWS
// Get a DC
HDC dc = CreateDC ("DISPLAY", NULL, NULL, NULL);
if (dc)
{
// The ramp
WORD ramp[256*3];
// For each composant
uint c;
for( c=0; c<3; c++ )
{
uint i;
for( i=0; i<256; i++ )
{
// Floating value
float value = (float)i / 256;
// Contrast
value = (float) max (0.0f, (value-0.5f) * (float) pow (3.f, properties.Contrast[c]) + 0.5f );
// Gamma
value = (float) pow (value, (properties.Gamma[c]>0) ? 1 - 3 * properties.Gamma[c] / 4 : 1 - properties.Gamma[c] );
// Luminosity
value = value + properties.Luminosity[c] / 2.f;
ramp[i+(c<<8)] = (WORD)min ((int)65535, max (0, (int)(value * 65535)));
}
}
// Set the ramp
bool result = SetDeviceGammaRamp (dc, ramp) != FALSE;
// Release the DC
ReleaseDC (NULL, dc);
// Returns result
return result;
}
else
{
nlwarning ("(CDriverGL::setMonitorColorProperties): can't create DC");
return false;
}
#else
// TODO for Linux: implement CDriverGL::setMonitorColorProperties
nlwarning ("CDriverGL::setMonitorColorProperties not implemented");
return false;
#endif
}
// ***************************************************************************
bool CDriverGL::supportEMBM() const
{
H_AUTO_OGL(CDriverGL_supportEMBM);
// For now, supported via ATI extension
return _Extensions.ATIEnvMapBumpMap;
}
// ***************************************************************************
bool CDriverGL::isEMBMSupportedAtStage(uint stage) const
{
H_AUTO_OGL(CDriverGL_isEMBMSupportedAtStage)
nlassert(supportEMBM());
nlassert(stage < IDRV_MAT_MAXTEXTURES);
return _StageSupportEMBM[stage];
}
// ***************************************************************************
void CDriverGL::setEMBMMatrix(const uint stage,const float mat[4])
{
H_AUTO_OGL(CDriverGL_setEMBMMatrix)
nlassert(supportEMBM());
nlassert(stage < IDRV_MAT_MAXTEXTURES);
//
if (_Extensions.ATIEnvMapBumpMap)
{
_DriverGLStates.activeTextureARB(stage);
nglTexBumpParameterfvATI(GL_BUMP_ROT_MATRIX_ATI, const_cast(mat));
}
}
// ***************************************************************************
void CDriverGL::initEMBM()
{
H_AUTO_OGL(CDriverGL_initEMBM)
if (supportEMBM())
{
std::fill(_StageSupportEMBM, _StageSupportEMBM + IDRV_MAT_MAXTEXTURES, false);
if (_Extensions.ATIEnvMapBumpMap)
{
// Test which stage support EMBM
GLint numEMBMUnits;
nglGetTexBumpParameterivATI(GL_BUMP_NUM_TEX_UNITS_ATI, &numEMBMUnits);
std::vector EMBMUnits(numEMBMUnits);
// get array of units that supports EMBM
nglGetTexBumpParameterivATI(GL_BUMP_TEX_UNITS_ATI, &EMBMUnits[0]);
numEMBMUnits = std::min(numEMBMUnits, (GLint) _Extensions.NbTextureStages);
EMBMUnits.resize(numEMBMUnits);
uint k;
for(k = 0; k < EMBMUnits.size(); ++k)
{
uint stage = EMBMUnits[k] - GL_TEXTURE0_ARB;
if (stage < (IDRV_MAT_MAXTEXTURES - 1))
{
_StageSupportEMBM[stage] = true;
}
}
// setup each stage to apply the bump map to the next stage (or previous if there's an unit at the last stage)
for(k = 0; k < (uint) _Extensions.NbTextureStages; ++k)
{
if (_StageSupportEMBM[k])
{
// setup each stage so that it apply EMBM on the next stage
_DriverGLStates.activeTextureARB(k);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
if (k != (uint) (_Extensions.NbTextureStages - 1))
{
glTexEnvi(GL_TEXTURE_ENV, GL_BUMP_TARGET_ATI, GL_TEXTURE0_ARB + k + 1);
}
else
{
glTexEnvi(GL_TEXTURE_ENV, GL_BUMP_TARGET_ATI, GL_TEXTURE0_ARB);
}
}
}
_DriverGLStates.activeTextureARB(0);
}
}
}
// ***************************************************************************
/** Water fragment program with extension ARB_fragment_program
*/
static const char *WaterCodeNoDiffuseForARBFragmentProgram =
"!!ARBfp1.0 \n\
OPTION ARB_precision_hint_nicest; \n\
PARAM bump0ScaleBias = program.env[0]; \n\
PARAM bump1ScaleBias = program.env[1]; \n\
ATTRIB bump0TexCoord = fragment.texcoord[0]; \n\
ATTRIB bump1TexCoord = fragment.texcoord[1]; \n\
ATTRIB envMapTexCoord = fragment.texcoord[2]; \n\
OUTPUT oCol = result.color; \n\
TEMP bmValue; \n\
#read bump map 0 \n\
TEX bmValue, bump0TexCoord, texture[0], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump0ScaleBias.xxxx, bump0ScaleBias.yyzz; \n\
ADD bmValue, bmValue, bump1TexCoord; \n\
#read bump map 1 \n\
TEX bmValue, bmValue, texture[1], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump1ScaleBias.xxxx, bump1ScaleBias.yyzz; \n\
#add envmap coord \n\
ADD bmValue, bmValue, envMapTexCoord; \n\
#read envmap \n\
TEX oCol, bmValue, texture[2], 2D; \n\
END ";
static const char *WaterCodeNoDiffuseWithFogForARBFragmentProgram =
"!!ARBfp1.0 \n\
OPTION ARB_precision_hint_nicest; \n\
PARAM bump0ScaleBias = program.env[0]; \n\
PARAM bump1ScaleBias = program.env[1]; \n\
PARAM fogColor = state.fog.color; \n\
PARAM fogFactor = program.env[2]; \n\
ATTRIB bump0TexCoord = fragment.texcoord[0]; \n\
ATTRIB bump1TexCoord = fragment.texcoord[1]; \n\
ATTRIB envMapTexCoord = fragment.texcoord[2]; \n\
ATTRIB fogValue = fragment.fogcoord; \n\
OUTPUT oCol = result.color; \n\
TEMP bmValue; \n\
TEMP envMap; \n\
TEMP tmpFog; \n\
#read bump map 0 \n\
TEX bmValue, bump0TexCoord, texture[0], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump0ScaleBias.xxxx, bump0ScaleBias.yyzz; \n\
ADD bmValue, bmValue, bump1TexCoord; \n\
#read bump map 1 \n\
TEX bmValue, bmValue, texture[1], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump1ScaleBias.xxxx, bump1ScaleBias.yyzz; \n\
#add envmap coord \n\
ADD bmValue, bmValue, envMapTexCoord; \n\
#read envmap \n\
TEX envMap, bmValue, texture[2], 2D; \n\
#compute fog \n\
MAD_SAT tmpFog, fogValue.x, fogFactor.x, fogFactor.y; \n\
LRP oCol, tmpFog.x, envMap, fogColor; \n\
END ";
// **************************************************************************************
/** Water fragment program with extension ARB_fragment_program and a diffuse map applied
*/
static const char *WaterCodeForARBFragmentProgram =
"!!ARBfp1.0 \n\
OPTION ARB_precision_hint_nicest; \n\
PARAM bump0ScaleBias = program.env[0]; \n\
PARAM bump1ScaleBias = program.env[1]; \n\
ATTRIB bump0TexCoord = fragment.texcoord[0]; \n\
ATTRIB bump1TexCoord = fragment.texcoord[1]; \n\
ATTRIB envMapTexCoord = fragment.texcoord[2]; \n\
ATTRIB diffuseTexCoord = fragment.texcoord[3]; \n\
OUTPUT oCol = result.color; \n\
TEMP bmValue; \n\
TEMP diffuse; \n\
TEMP envMap; \n\
#read bump map 0 \n\
TEX bmValue, bump0TexCoord, texture[0], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump0ScaleBias.xxxx, bump0ScaleBias.yyzz; \n\
ADD bmValue, bmValue, bump1TexCoord; \n\
#read bump map 1 \n\
TEX bmValue, bmValue, texture[1], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump1ScaleBias.xxxx, bump1ScaleBias.yyzz; \n\
#add envmap coord \n\
ADD bmValue, bmValue, envMapTexCoord; \n\
#read envmap \n\
TEX envMap, bmValue, texture[2], 2D; \n\
#read diffuse \n\
TEX diffuse, diffuseTexCoord, texture[3], 2D; \n\
#modulate diffuse and envmap to get result \n\
MUL oCol, diffuse, envMap; \n\
END ";
static const char *WaterCodeWithFogForARBFragmentProgram =
"!!ARBfp1.0 \n\
OPTION ARB_precision_hint_nicest; \n\
PARAM bump0ScaleBias = program.env[0]; \n\
PARAM bump1ScaleBias = program.env[1]; \n\
PARAM fogColor = state.fog.color; \n\
PARAM fogFactor = program.env[2]; \n\
ATTRIB bump0TexCoord = fragment.texcoord[0]; \n\
ATTRIB bump1TexCoord = fragment.texcoord[1]; \n\
ATTRIB envMapTexCoord = fragment.texcoord[2]; \n\
ATTRIB diffuseTexCoord = fragment.texcoord[3]; \n\
ATTRIB fogValue = fragment.fogcoord; \n\
OUTPUT oCol = result.color; \n\
TEMP bmValue; \n\
TEMP diffuse; \n\
TEMP envMap; \n\
TEMP tmpFog; \n\
#read bump map 0 \n\
TEX bmValue, bump0TexCoord, texture[0], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump0ScaleBias.xxxx, bump0ScaleBias.yyzz; \n\
ADD bmValue, bmValue, bump1TexCoord; \n\
#read bump map 1 \n\
TEX bmValue, bmValue, texture[1], 2D; \n\
#bias result (include scaling) \n\
MAD bmValue, bmValue, bump1ScaleBias.xxxx, bump1ScaleBias.yyzz; \n\
#add envmap coord \n\
ADD bmValue, bmValue, envMapTexCoord; \n\
TEX envMap, bmValue, texture[2], 2D; \n\
TEX diffuse, diffuseTexCoord, texture[3], 2D; \n\
MAD_SAT tmpFog, fogValue.x, fogFactor.x, fogFactor.y; \n\
#modulate diffuse and envmap to get result \n\
MUL diffuse, diffuse, envMap; \n\
LRP oCol, tmpFog.x, diffuse, fogColor; \n\
END ";
// ***************************************************************************
/** Load a ARB_fragment_program_code, and ensure it is loaded natively
*/
uint loadARBFragmentProgramStringNative(const char *prog, bool forceNativePrograms)
{
H_AUTO_OGL(loadARBFragmentProgramStringNative);
if (!prog)
{
nlwarning("The param 'prog' is null, cannot load");
return 0;
}
GLuint progID;
nglGenProgramsARB(1, &progID);
if (!progID)
{
nlwarning("glGenProgramsARB returns a progID NULL");
return 0;
}
nglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, progID);
GLint errorPos, isNative;
nglProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, strlen(prog), prog);
nglBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, 0);
glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &errorPos);
nglGetProgramivARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_UNDER_NATIVE_LIMITS_ARB, &isNative);
if (errorPos == -1) {
if (!isNative && forceNativePrograms) {
nlwarning("Fragment program isn't supported natively; purging program");
nglDeleteProgramsARB(1, &progID);
return 0;
}
return progID;
}
else {
nlwarning("init fragment program failed: errorPos: %d isNative: %d", errorPos, isNative);
}
return 0;
}
// ***************************************************************************
/** R200 Fragment Shader :
* Send fragment shader to fetch a perturbed envmap from the addition of 2 bumpmap
* The result is in R2 after the 2nd pass
*/
static void fetchPerturbedEnvMapR200()
{
H_AUTO_OGL(CDriverGL_fetchPerturbedEnvMapR200)
////////////
// PASS 1 //
////////////
nglSampleMapATI(GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI); // sample bump map 0
nglSampleMapATI(GL_REG_1_ATI, GL_TEXTURE1_ARB, GL_SWIZZLE_STR_ATI); // sample bump map 1
nglPassTexCoordATI(GL_REG_2_ATI, GL_TEXTURE2_ARB, GL_SWIZZLE_STR_ATI); // get texcoord for envmap
nglColorFragmentOp3ATI(GL_MAD_ATI, GL_REG_2_ATI, GL_NONE, GL_NONE, GL_REG_0_ATI, GL_NONE, GL_BIAS_BIT_ATI|GL_2X_BIT_ATI, GL_CON_0_ATI, GL_NONE, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE); // scale bumpmap 1 & add envmap coords
nglColorFragmentOp3ATI(GL_MAD_ATI, GL_REG_2_ATI, GL_NONE, GL_NONE, GL_REG_1_ATI, GL_NONE, GL_BIAS_BIT_ATI|GL_2X_BIT_ATI, GL_CON_1_ATI, GL_NONE, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE); // scale bumpmap 2 & add to bump map 1
////////////
// PASS 2 //
////////////
nglSampleMapATI(GL_REG_2_ATI, GL_REG_2_ATI, GL_SWIZZLE_STR_ATI); // fetch envmap at perturbed texcoords
}
// ***************************************************************************
void CDriverGL::forceNativeFragmentPrograms(bool nativeOnly)
{
_ForceNativeFragmentPrograms = nativeOnly;
}
void CDriverGL::initFragmentShaders()
{
H_AUTO_OGL(CDriverGL_initFragmentShaders)
///////////////////
// WATER SHADERS //
///////////////////
// the ARB_fragment_program is prioritary over other extensions when present
if (_Extensions.ARBFragmentProgram)
{
nlinfo("WATER: Try ARB_fragment_program");
ARBWaterShader[0] = loadARBFragmentProgramStringNative(WaterCodeNoDiffuseForARBFragmentProgram, _ForceNativeFragmentPrograms);
ARBWaterShader[1] = loadARBFragmentProgramStringNative(WaterCodeNoDiffuseWithFogForARBFragmentProgram, _ForceNativeFragmentPrograms);
ARBWaterShader[2] = loadARBFragmentProgramStringNative(WaterCodeForARBFragmentProgram, _ForceNativeFragmentPrograms);
ARBWaterShader[3] = loadARBFragmentProgramStringNative(WaterCodeWithFogForARBFragmentProgram, _ForceNativeFragmentPrograms);
bool ok = true;
for(uint k = 0; k < 4; ++k)
{
if (!ARBWaterShader[k])
{
ok = false;
deleteARBFragmentPrograms();
nlwarning("WATER: fragment %d is not loaded, not using ARB_fragment_program at all", k);
break;
}
}
if (ok)
{
nlinfo("WATER: ARB_fragment_program OK, Use it");
return;
}
}
if (_Extensions.ATIFragmentShader)
{
nlinfo("WATER: Try ATI_fragment_program");
///////////
// WATER //
///////////
ATIWaterShaderHandleNoDiffuseMap = nglGenFragmentShadersATI(1);
ATIWaterShaderHandle = nglGenFragmentShadersATI(1);
if (!ATIWaterShaderHandle || !ATIWaterShaderHandleNoDiffuseMap)
{
ATIWaterShaderHandleNoDiffuseMap = ATIWaterShaderHandle = 0;
nlwarning("Couldn't generate water shader using ATI_fragment_shader !");
}
else
{
glGetError();
// Water shader for R200 : we just add the 2 bump map contributions (du, dv). We then use this contribution to perturbate the envmap
nglBindFragmentShaderATI(ATIWaterShaderHandleNoDiffuseMap);
nglBeginFragmentShaderATI();
//
fetchPerturbedEnvMapR200();
nglColorFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE);
nglAlphaFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE);
//
nglEndFragmentShaderATI();
GLenum error = glGetError();
nlassert(error == GL_NONE);
// The same but with a diffuse map added
nglBindFragmentShaderATI(ATIWaterShaderHandle);
nglBeginFragmentShaderATI();
//
fetchPerturbedEnvMapR200();
nglSampleMapATI(GL_REG_3_ATI, GL_TEXTURE3_ARB, GL_SWIZZLE_STR_ATI); // fetch envmap at perturbed texcoords
nglColorFragmentOp2ATI(GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_3_ATI, GL_NONE, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE); // scale bumpmap 1 & add envmap coords
nglAlphaFragmentOp2ATI(GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_REG_3_ATI, GL_NONE, GL_NONE, GL_REG_2_ATI, GL_NONE, GL_NONE);
nglEndFragmentShaderATI();
error = glGetError();
nlassert(error == GL_NONE);
nglBindFragmentShaderATI(0);
}
////////////
// CLOUDS //
////////////
ATICloudShaderHandle = nglGenFragmentShadersATI(1);
if (!ATICloudShaderHandle)
{
nlwarning("Couldn't generate cloud shader using ATI_fragment_shader !");
}
else
{
glGetError();
nglBindFragmentShaderATI(ATICloudShaderHandle);
nglBeginFragmentShaderATI();
//
nglSampleMapATI(GL_REG_0_ATI, GL_TEXTURE0_ARB, GL_SWIZZLE_STR_ATI); // sample texture 0
nglSampleMapATI(GL_REG_1_ATI, GL_TEXTURE1_ARB, GL_SWIZZLE_STR_ATI); // sample texture 1
// lerp between tex 0 & tex 1 using diffuse alpha
nglAlphaFragmentOp3ATI(GL_LERP_ATI, GL_REG_0_ATI, GL_NONE, GL_PRIMARY_COLOR_ARB, GL_NONE, GL_NONE, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_REG_1_ATI, GL_NONE, GL_NONE);
//nglAlphaFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_REG_0_ATI, GL_NONE, GL_NONE);
// output 0 as RGB
//nglColorFragmentOp1ATI(GL_MOV_ATI, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_ZERO, GL_NONE, GL_NONE);
// output alpha multiplied by constant 0
nglAlphaFragmentOp2ATI(GL_MUL_ATI, GL_REG_0_ATI, GL_NONE, GL_REG_0_ATI, GL_NONE, GL_NONE, GL_CON_0_ATI, GL_NONE, GL_NONE);
nglEndFragmentShaderATI();
GLenum error = glGetError();
nlassert(error == GL_NONE);
nglBindFragmentShaderATI(0);
}
}
// if none of the previous programs worked, fallback on NV_texture_shader, or (todo) simpler shader
}
// ***************************************************************************
void CDriverGL::deleteARBFragmentPrograms()
{
H_AUTO_OGL(CDriverGL_deleteARBFragmentPrograms)
for(uint k = 0; k < 4; ++k)
{
if (ARBWaterShader[k])
{
GLuint progId = (GLuint) ARBWaterShader[k];
nglDeleteProgramsARB(1, &progId);
ARBWaterShader[k] = 0;
}
}
}
// ***************************************************************************
void CDriverGL::deleteFragmentShaders()
{
H_AUTO_OGL(CDriverGL_deleteFragmentShaders)
deleteARBFragmentPrograms();
if (ATIWaterShaderHandleNoDiffuseMap)
{
nglDeleteFragmentShaderATI((GLuint) ATIWaterShaderHandleNoDiffuseMap);
ATIWaterShaderHandleNoDiffuseMap = 0;
}
if (ATIWaterShaderHandle)
{
nglDeleteFragmentShaderATI((GLuint) ATIWaterShaderHandle);
ATIWaterShaderHandle = 0;
}
if (ATICloudShaderHandle)
{
nglDeleteFragmentShaderATI((GLuint) ATICloudShaderHandle);
ATICloudShaderHandle = 0;
}
}
// ***************************************************************************
void CDriverGL::finish()
{
H_AUTO_OGL(CDriverGL_finish)
glFinish();
}
// ***************************************************************************
void CDriverGL::flush()
{
H_AUTO_OGL(CDriverGL_flush)
glFlush();
}
// ***************************************************************************
void CDriverGL::setSwapVBLInterval(uint interval)
{
H_AUTO_OGL(CDriverGL_setSwapVBLInterval)
#ifdef NL_OS_WINDOWS
_Interval = interval;
if(_Extensions.WGLEXTSwapControl && _Initialized)
{
nwglSwapIntervalEXT(_Interval);
}
#endif
}
// ***************************************************************************
uint CDriverGL::getSwapVBLInterval()
{
H_AUTO_OGL(CDriverGL_getSwapVBLInterval)
#ifdef NL_OS_WINDOWS
if(_Extensions.WGLEXTSwapControl)
{
return _Interval;
}
else
return 1;
#else
return 1;
#endif
}
// ***************************************************************************
void CDriverGL::enablePolygonSmoothing(bool smooth)
{
H_AUTO_OGL(CDriverGL_enablePolygonSmoothing)
if(smooth)
glEnable(GL_POLYGON_SMOOTH);
else
glDisable(GL_POLYGON_SMOOTH);
_PolygonSmooth= smooth;
}
// ***************************************************************************
bool CDriverGL::isPolygonSmoothingEnabled() const
{
H_AUTO_OGL(CDriverGL_isPolygonSmoothingEnabled)
return _PolygonSmooth;
}
// ***************************************************************************
void CDriverGL::startProfileVBHardLock()
{
if(_VBHardProfiling)
return;
// start
_VBHardProfiles.clear();
_VBHardProfiles.reserve(50);
_VBHardProfiling= true;
_CurVBHardLockCount= 0;
_NumVBHardProfileFrame= 0;
}
// ***************************************************************************
void CDriverGL::endProfileVBHardLock(vector &result)
{
if(!_VBHardProfiling)
return;
// Fill infos.
result.clear();
result.resize(_VBHardProfiles.size() + 1);
float total= 0;
for(uint i=0;i<_VBHardProfiles.size();i++)
{
const uint tmpSize= 256;
char tmp[tmpSize];
CVBHardProfile &vbProf= _VBHardProfiles[i];
const char *vbName;
if(vbProf.VBHard && !vbProf.VBHard->getName().empty())
{
vbName= vbProf.VBHard->getName().c_str();
}
else
{
vbName= "????";
}
// Display in ms.
float timeLock= (float)CTime::ticksToSecond(vbProf.AccumTime)*1000 / max(_NumVBHardProfileFrame,1U);
smprintf(tmp, tmpSize, "%16s%c: %2.3f ms", vbName, vbProf.Change?'*':' ', timeLock );
total+= timeLock;
result[i]= tmp;
}
result[_VBHardProfiles.size()]= toString("Total: %2.3f", total);
// clear.
_VBHardProfiling= false;
contReset(_VBHardProfiles);
}
// ***************************************************************************
void CDriverGL::appendVBHardLockProfile(NLMISC::TTicks time, CVertexBuffer *vb)
{
// must allocate a new place?
if(_CurVBHardLockCount>=_VBHardProfiles.size())
{
_VBHardProfiles.resize(_VBHardProfiles.size()+1);
// set the original VBHard
_VBHardProfiles[_CurVBHardLockCount].VBHard= vb;
}
// Accumulate.
_VBHardProfiles[_CurVBHardLockCount].AccumTime+= time;
// if change of VBHard for this chrono place
if(_VBHardProfiles[_CurVBHardLockCount].VBHard != vb)
{
// flag, and set new
_VBHardProfiles[_CurVBHardLockCount].VBHard= vb;
_VBHardProfiles[_CurVBHardLockCount].Change= true;
}
// next!
_CurVBHardLockCount++;
}
// ***************************************************************************
void CDriverGL::startProfileIBLock()
{
// not implemented
}
// ***************************************************************************
void CDriverGL::endProfileIBLock(std::vector &/* result */)
{
// not implemented
}
// ***************************************************************************
void CDriverGL::profileIBAllocation(std::vector &/* result */)
{
// not implemented
}
// ***************************************************************************
void CDriverGL::profileVBHardAllocation(std::vector &result)
{
result.clear();
result.reserve(1000);
result.push_back(toString("Memory Allocated: %4d Ko in AGP / %4d Ko in VRAM",
getAvailableVertexAGPMemory()/1000, getAvailableVertexVRAMMemory()/1000 ));
result.push_back(toString("Num VBHard: %d", _VertexBufferHardSet.Set.size()));
uint totalMemUsed= 0;
set::iterator it;
for(it= _VertexBufferHardSet.Set.begin(); it!=_VertexBufferHardSet.Set.end(); it++)
{
IVertexBufferHardGL *vbHard= *it;
if(vbHard)
{
uint vSize= vbHard->VB->getVertexSize();
uint numVerts= vbHard->VB->getNumVertices();
totalMemUsed+= vSize*numVerts;
}
}
result.push_back(toString("Mem Used: %4d Ko", totalMemUsed/1000) );
for(it= _VertexBufferHardSet.Set.begin(); it!=_VertexBufferHardSet.Set.end(); it++)
{
IVertexBufferHardGL *vbHard= *it;
if(vbHard)
{
uint vSize= vbHard->VB->getVertexSize();
uint numVerts= vbHard->VB->getNumVertices();
result.push_back(toString(" %16s: %4d ko (format: %d / numVerts: %d)",
vbHard->VB->getName().c_str(), vSize*numVerts/1000, vSize, numVerts ));
}
}
}
// ***************************************************************************
bool CDriverGL::supportCloudRenderSinglePass() const
{
H_AUTO_OGL(CDriverGL_supportCloudRenderSinglePass)
//return _Extensions.NVTextureEnvCombine4 || (_Extensions.ATIXTextureEnvRoute && _Extensions.EXTTextureEnvCombine);
// there are slowdown for now with ati fragment shader... don't know why
return _Extensions.NVTextureEnvCombine4 || _Extensions.ATIFragmentShader;
}
// ***************************************************************************
void CDriverGL::retrieveATIDriverVersion()
{
H_AUTO_OGL(CDriverGL_retrieveATIDriverVersion)
_ATIDriverVersion = 0;
// we may need this driver version to fix flaws of previous ati drivers version (fog issue with V.P)
#ifdef NL_OS_WINDOWS
// get from the registry
HKEY parentKey;
// open key about current video card
LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E968-E325-11CE-BFC1-08002BE10318}", 0, KEY_READ, &parentKey);
if (result == ERROR_SUCCESS)
{
// find last config
DWORD keyIndex = 0;
uint latestConfigVersion = 0;
char subKeyName[256];
char latestSubKeyName[256] = "";
DWORD nameBufferSize = sizeof(subKeyName) / sizeof(subKeyName[0]);
FILETIME lastWriteTime;
bool configFound = false;
for(;;)
{
nameBufferSize = sizeof(subKeyName) / sizeof(subKeyName[0]);
result = RegEnumKeyEx(parentKey, keyIndex, subKeyName, &nameBufferSize, NULL, NULL, NULL, &lastWriteTime);
if (result == ERROR_NO_MORE_ITEMS) break;
if (result == ERROR_SUCCESS)
{
// see if the name is numerical.
bool isNumerical = true;
for(uint k = 0; k < nameBufferSize; ++k)
{
if (!isdigit(subKeyName[k]))
{
isNumerical = false;
break;
}
}
if (isNumerical)
{
uint configVersion;
fromString((const char*)subKeyName, configVersion);
if (configVersion >= latestConfigVersion)
{
configFound = true;
latestConfigVersion = configVersion;
strcpy(latestSubKeyName, subKeyName);
}
}
++ keyIndex;
}
else
{
RegCloseKey(parentKey);
return;
}
}
if (configFound)
{
HKEY subKey;
result = RegOpenKeyEx(parentKey, latestSubKeyName, 0, KEY_READ, &subKey);
if (result == ERROR_SUCCESS)
{
// see if it is a radeon card
DWORD valueType;
char driverDesc[256];
DWORD driverDescBufSize = sizeof(driverDesc) / sizeof(driverDesc[0]);
result = RegQueryValueEx(subKey, "DriverDesc", NULL, &valueType, (unsigned char *) driverDesc, &driverDescBufSize);
if (result == ERROR_SUCCESS && valueType == REG_SZ)
{
toLower(driverDesc);
if (strstr(driverDesc, "radeon")) // is it a radeon card ?
{
char driverVersion[256];
DWORD driverVersionBufSize = sizeof(driverVersion) / sizeof(driverVersion[0]);
result = RegQueryValueEx(subKey, "DriverVersion", NULL, &valueType, (unsigned char *) driverVersion, &driverVersionBufSize);
if (result == ERROR_SUCCESS && valueType == REG_SZ)
{
int subVersionNumber[4];
if (sscanf(driverVersion, "%d.%d.%d.%d", &subVersionNumber[0], &subVersionNumber[1], &subVersionNumber[2], &subVersionNumber[3]) == 4)
{
_ATIDriverVersion = (uint) subVersionNumber[3];
/** see if fog range for V.P is bad in that driver version (is so, do a fix during vertex program conversion to EXT_vertex_shader
* In earlier versions of the driver, fog coordinates had to be output in the [0, 1] range
* From the 6.14.10.6343 driver, fog output must be in world units
*/
if (_ATIDriverVersion < 6343)
{
_ATIFogRangeFixed = false;
}
}
}
}
}
}
RegCloseKey(subKey);
}
RegCloseKey(parentKey);
}
#else
// TODO for Linux: implement retrieveATIDriverVersion... assuming versions under linux are probably different
#endif
}
// ***************************************************************************
bool CDriverGL::supportMADOperator() const
{
H_AUTO_OGL(CDriverGL_supportMADOperator)
return _Extensions.NVTextureEnvCombine4 || _Extensions.ATITextureEnvCombine3;
}
// ***************************************************************************
uint CDriverGL::getNumAdapter() const
{
H_AUTO_OGL(CDriverGL_getNumAdapter)
return 1;
}
// ***************************************************************************
bool CDriverGL::getAdapter(uint adapter, CAdapter &desc) const
{
H_AUTO_OGL(CDriverGL_getAdapter)
if (adapter == 0)
{
desc.DeviceName = (const char *) glGetString (GL_RENDERER);
desc.Driver = (const char *) glGetString (GL_VERSION);
desc.Vendor= (const char *) glGetString (GL_VENDOR);
desc.Description = "Default openGL adapter";
desc.DeviceId = 0;
desc.DriverVersion = 0;
desc.Revision = 0;
desc.SubSysId = 0;
desc.VendorId = 0;
return true;
}
return false;
}
// ***************************************************************************
bool CDriverGL::setAdapter(uint adapter)
{
H_AUTO_OGL(CDriverGL_setAdapter)
return adapter == 0;
}
// ***************************************************************************
CVertexBuffer::TVertexColorType CDriverGL::getVertexColorFormat() const
{
H_AUTO_OGL(CDriverGL_CDriverGL)
return CVertexBuffer::TRGBA;
}
// ***************************************************************************
bool CDriverGL::activeShader(CShader * /* shd */)
{
H_AUTO_OGL(CDriverGL_activeShader)
return false;
}
// ***************************************************************************
void CDriverGL::startBench (bool wantStandardDeviation, bool quick, bool reset)
{
CHTimer::startBench (wantStandardDeviation, quick, reset);
}
// ***************************************************************************
void CDriverGL::endBench ()
{
CHTimer::endBench ();
}
// ***************************************************************************
void CDriverGL::displayBench (class NLMISC::CLog *log)
{
// diplay
CHTimer::displayHierarchicalByExecutionPathSorted(log, CHTimer::TotalTime, true, 48, 2);
CHTimer::displayHierarchical(log, true, 48, 2);
CHTimer::displayByExecutionPath(log, CHTimer::TotalTime);
CHTimer::display(log, CHTimer::TotalTime);
CHTimer::display(log, CHTimer::TotalTimeWithoutSons);
}
#ifdef NL_DEBUG
void CDriverGL::dumpMappedBuffers()
{
_AGPVertexArrayRange->dumpMappedBuffers();
}
#endif
// ***************************************************************************
void CDriverGL::checkTextureOn() const
{
H_AUTO_OGL(CDriverGL_checkTextureOn)
// tmp for debug
CDriverGLStates &dgs = const_cast(_DriverGLStates);
uint currTexStage = dgs.getActiveTextureARB();
for(uint k = 0; k < getNbTextureStages(); ++k)
{
dgs.activeTextureARB(k);
GLboolean flag2D;
GLboolean flagCM;
GLboolean flagTR;
glGetBooleanv(GL_TEXTURE_2D, &flag2D);
glGetBooleanv(GL_TEXTURE_CUBE_MAP_ARB, &flagCM);
glGetBooleanv(GL_TEXTURE_RECTANGLE_NV, &flagTR);
switch(dgs.getTextureMode())
{
case CDriverGLStates::TextureDisabled:
nlassert(!flag2D);
nlassert(!flagCM);
break;
case CDriverGLStates::Texture2D:
nlassert(flag2D);
nlassert(!flagCM);
break;
case CDriverGLStates::TextureRect:
nlassert(flagTR);
nlassert(!flagCM);
break;
case CDriverGLStates::TextureCubeMap:
nlassert(!flag2D);
nlassert(flagCM);
break;
default:
break;
}
}
dgs.activeTextureARB(currTexStage);
}
// ***************************************************************************
bool CDriverGL::supportOcclusionQuery() const
{
H_AUTO_OGL(CDriverGL_supportOcclusionQuery)
return _Extensions.NVOcclusionQuery;
}
// ***************************************************************************
bool CDriverGL::supportTextureRectangle() const
{
H_AUTO_OGL(CDriverGL_supportTextureRectangle)
return _Extensions.NVTextureRectangle;
}
// ***************************************************************************
bool CDriverGL::supportPackedDepthStencil() const
{
H_AUTO_OGL(CDriverGL_supportPackedDepthStencil)
return _Extensions.PackedDepthStencil;
}
// ***************************************************************************
bool CDriverGL::supportFrameBufferObject() const
{
H_AUTO_OGL(CDriverGL_supportFrameBufferObject)
return _Extensions.FrameBufferObject;
}
// ***************************************************************************
IOcclusionQuery *CDriverGL::createOcclusionQuery()
{
H_AUTO_OGL(CDriverGL_createOcclusionQuery)
nlassert(_Extensions.NVOcclusionQuery);
GLuint id;
nglGenOcclusionQueriesNV(1, &id);
if (id == 0) return NULL;
COcclusionQueryGL *oqgl = new COcclusionQueryGL;
oqgl->Driver = this;
oqgl->ID = id;
oqgl->OcclusionType = IOcclusionQuery::NotAvailable;
_OcclusionQueryList.push_front(oqgl);
oqgl->Iterator = _OcclusionQueryList.begin();
oqgl->VisibleCount = 0;
return oqgl;
}
// ***************************************************************************
void CDriverGL::deleteOcclusionQuery(IOcclusionQuery *oq)
{
H_AUTO_OGL(CDriverGL_deleteOcclusionQuery)
if (!oq) return;
COcclusionQueryGL *oqgl = NLMISC::safe_cast(oq);
nlassert((CDriverGL *) oqgl->Driver == this); // should come from the same driver
oqgl->Driver = NULL;
nlassert(oqgl->ID != 0);
GLuint id = oqgl->ID;
nglDeleteOcclusionQueriesNV(1, &id);
_OcclusionQueryList.erase(oqgl->Iterator);
if (oqgl == _CurrentOcclusionQuery)
{
_CurrentOcclusionQuery = NULL;
}
delete oqgl;
}
// ***************************************************************************
void COcclusionQueryGL::begin()
{
H_AUTO_OGL(COcclusionQueryGL_begin)
nlassert(Driver);
nlassert(Driver->_CurrentOcclusionQuery == NULL); // only one query at a time
nlassert(ID);
nglBeginOcclusionQueryNV(ID);
Driver->_CurrentOcclusionQuery = this;
OcclusionType = NotAvailable;
VisibleCount = 0;
}
// ***************************************************************************
void COcclusionQueryGL::end()
{
H_AUTO_OGL(COcclusionQueryGL_end)
nlassert(Driver);
nlassert(Driver->_CurrentOcclusionQuery == this); // only one query at a time
nlassert(ID);
nglEndOcclusionQueryNV();
Driver->_CurrentOcclusionQuery = NULL;
}
// ***************************************************************************
IOcclusionQuery::TOcclusionType COcclusionQueryGL::getOcclusionType()
{
H_AUTO_OGL(COcclusionQueryGL_getOcclusionType)
nlassert(Driver);
nlassert(ID);
nlassert(Driver->_CurrentOcclusionQuery != this); // can't query result between a begin/end pair!
if (OcclusionType == NotAvailable)
{
GLuint result;
// retrieve result
nglGetOcclusionQueryuivNV(ID, GL_PIXEL_COUNT_AVAILABLE_NV, &result);
if (result != GL_FALSE)
{
nglGetOcclusionQueryuivNV(ID, GL_PIXEL_COUNT_NV, &result);
OcclusionType = result != 0 ? NotOccluded : Occluded;
VisibleCount = (uint) result;
// Note : we could return the exact number of pixels that passed the z-test, but this value is not supported by all implementation (Direct3D ...)
}
}
return OcclusionType;
}
// ***************************************************************************
uint COcclusionQueryGL::getVisibleCount()
{
H_AUTO_OGL(COcclusionQueryGL_getVisibleCount)
nlassert(Driver);
nlassert(ID);
nlassert(Driver->_CurrentOcclusionQuery != this); // can't query result between a begin/end pair!
if (getOcclusionType() == NotAvailable) return 0;
return VisibleCount;
}
// ***************************************************************************
void CDriverGL::setDepthRange(float znear, float zfar)
{
H_AUTO_OGL(CDriverGL_setDepthRange)
_DriverGLStates.setDepthRange(znear, zfar);
}
// ***************************************************************************
void CDriverGL::getDepthRange(float &znear, float &zfar) const
{
H_AUTO_OGL(CDriverGL_getDepthRange)
_DriverGLStates.getDepthRange(znear, zfar);
}
// ***************************************************************************
void CDriverGL::setCullMode(TCullMode cullMode)
{
H_AUTO_OGL(CDriverGL_setCullMode)
_DriverGLStates.setCullMode((CDriverGLStates::TCullMode) cullMode);
}
// ***************************************************************************
CDriverGL::TCullMode CDriverGL::getCullMode() const
{
H_AUTO_OGL(CDriverGL_CDriverGL)
return (CDriverGL::TCullMode) _DriverGLStates.getCullMode();
}
// ***************************************************************************
void CDriverGL::enableStencilTest(bool enable)
{
H_AUTO_OGL(CDriverGL_CDriverGL)
_DriverGLStates.enableStencilTest(enable);
}
// ***************************************************************************
bool CDriverGL::isStencilTestEnabled() const
{
H_AUTO_OGL(CDriverGL_CDriverGL)
return _DriverGLStates.isStencilTestEnabled();
}
// ***************************************************************************
void CDriverGL::stencilFunc(TStencilFunc stencilFunc, int ref, uint mask)
{
H_AUTO_OGL(CDriverGL_CDriverGL)
GLenum glstencilFunc;
switch(stencilFunc)
{
case IDriver::never: glstencilFunc=GL_NEVER; break;
case IDriver::less: glstencilFunc=GL_LESS; break;
case IDriver::lessequal: glstencilFunc=GL_LEQUAL; break;
case IDriver::equal: glstencilFunc=GL_EQUAL; break;
case IDriver::notequal: glstencilFunc=GL_NOTEQUAL; break;
case IDriver::greaterequal: glstencilFunc=GL_GEQUAL; break;
case IDriver::greater: glstencilFunc=GL_GREATER; break;
case IDriver::always: glstencilFunc=GL_ALWAYS; break;
default: nlstop;
}
_DriverGLStates.stencilFunc(glstencilFunc, (GLint)ref, (GLuint)mask);
}
// ***************************************************************************
void CDriverGL::stencilOp(TStencilOp fail, TStencilOp zfail, TStencilOp zpass)
{
H_AUTO_OGL(CDriverGL_CDriverGL)
GLenum glFail, glZFail, glZPass;
switch(fail)
{
case IDriver::keep: glFail=GL_KEEP; break;
case IDriver::zero: glFail=GL_ZERO; break;
case IDriver::replace: glFail=GL_REPLACE; break;
case IDriver::incr: glFail=GL_INCR; break;
case IDriver::decr: glFail=GL_DECR; break;
case IDriver::invert: glFail=GL_INVERT; break;
default: nlstop;
}
switch(zfail)
{
case IDriver::keep: glZFail=GL_KEEP; break;
case IDriver::zero: glZFail=GL_ZERO; break;
case IDriver::replace: glZFail=GL_REPLACE; break;
case IDriver::incr: glZFail=GL_INCR; break;
case IDriver::decr: glZFail=GL_DECR; break;
case IDriver::invert: glZFail=GL_INVERT; break;
default: nlstop;
}
switch(zpass)
{
case IDriver::keep: glZPass=GL_KEEP; break;
case IDriver::zero: glZPass=GL_ZERO; break;
case IDriver::replace: glZPass=GL_REPLACE; break;
case IDriver::incr: glZPass=GL_INCR; break;
case IDriver::decr: glZPass=GL_DECR; break;
case IDriver::invert: glZPass=GL_INVERT; break;
default: nlstop;
}
_DriverGLStates.stencilOp(glFail, glZFail, glZPass);
}
// ***************************************************************************
void CDriverGL::stencilMask(uint mask)
{
H_AUTO_OGL(CDriverGL_CDriverGL)
_DriverGLStates.stencilMask((GLuint)mask);
}
// ***************************************************************************
void CDriverGL::getNumPerStageConstant(uint &lightedMaterial, uint &unlightedMaterial) const
{
lightedMaterial = inlGetNumTextStages();
unlightedMaterial = inlGetNumTextStages();
}
// ***************************************************************************
void CDriverGL::beginDialogMode()
{
}
// ***************************************************************************
void CDriverGL::endDialogMode()
{
}
} // NL3D
void displayGLError(GLenum error)
{
switch(error)
{
case GL_NO_ERROR: nlwarning("GL_NO_ERROR"); break;
case GL_INVALID_ENUM: nlwarning("GL_INVALID_ENUM"); break;
case GL_INVALID_VALUE: nlwarning("GL_INVALID_VALUE"); break;
case GL_INVALID_OPERATION: nlwarning("GL_INVALID_OPERATION"); break;
case GL_STACK_OVERFLOW: nlwarning("GL_STACK_OVERFLOW"); break;
case GL_STACK_UNDERFLOW: nlwarning("GL_STACK_UNDERFLOW"); break;
case GL_OUT_OF_MEMORY: nlwarning("GL_OUT_OF_MEMORY"); break;
default:
nlwarning("GL_ERROR");
break;
}
}