mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-18 15:08:43 +00:00
2836 lines
85 KiB
C++
2836 lines
85 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// 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 <http://www.gnu.org/licenses/>.
|
|
|
|
#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 <windows.h>
|
|
# include <windowsx.h>
|
|
# include <string>
|
|
# include <GL/gl.h>
|
|
#elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
# define GL_GLEXT_LEGACY
|
|
# include <OpenGL/gl.h>
|
|
# include "mac/glext.h"
|
|
# include "mac/cocoa_adapter.h"
|
|
#elif defined (NL_OS_UNIX)
|
|
# include <GL/gl.h>
|
|
# include <GL/glx.h>
|
|
#endif // NL_OS_UNIX
|
|
|
|
#include <vector>
|
|
|
|
#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 = 0x10;
|
|
|
|
// 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
|
|
|
|
#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)
|
|
|
|
#ifdef NL_OS_WINDOWS
|
|
|
|
_PBuffer = NULL;
|
|
_hWnd = NULL;
|
|
_hRC = NULL;
|
|
_hDC = NULL;
|
|
_NeedToRestaureGammaRamp = false;
|
|
_Interval = 1;
|
|
|
|
#elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
|
|
NL3D::MAC::ctor();
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
cursor = None;
|
|
win = 0;
|
|
dpy = 0;
|
|
|
|
# ifdef XF86VIDMODE
|
|
// zero the old screen mode
|
|
memset(&_OldScreenMode, 0, sizeof(_OldScreenMode));
|
|
|
|
# endif //XF86VIDMODE
|
|
#endif // NL_OS_UNIX
|
|
|
|
_OffScreen = false;
|
|
_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;i<MaxLight;i++)
|
|
_LightDirty[i]= false;
|
|
|
|
_CurrentGlNormalize= false;
|
|
_ForceNormalize= false;
|
|
|
|
_AGPVertexArrayRange= NULL;
|
|
_VRAMVertexArrayRange= NULL;
|
|
_CurrentVertexArrayRange= NULL;
|
|
_CurrentVertexBufferHard= NULL;
|
|
_NVCurrentVARPtr= NULL;
|
|
_NVCurrentVARSize= 0;
|
|
_SupportVBHard= false;
|
|
_SlowUnlockVBHard= false;
|
|
_MaxVerticesByVBHard= 0;
|
|
|
|
_AllocatedTextureMemory= 0;
|
|
|
|
_ForceDXTCCompression= false;
|
|
_ForceTextureResizePower= 0;
|
|
_ForceNativeFragmentPrograms = true;
|
|
|
|
_SumTextureMemoryUsed = false;
|
|
|
|
_NVTextureShaderEnabled = false;
|
|
|
|
// Compute the Flag which say if one texture has been changed in CMaterial.
|
|
_MaterialAllTextureTouchedFlag= 0;
|
|
for(i=0; i < IDRV_MAT_MAXTEXTURES; i++)
|
|
{
|
|
_MaterialAllTextureTouchedFlag|= IDRV_TOUCHED_TEX[i];
|
|
_CurrentTexAddrMode[i] = GL_NONE;
|
|
}
|
|
|
|
_UserTexMatEnabled = 0;
|
|
|
|
// Ligtmap preca.
|
|
_LastVertexSetupIsLightMap= false;
|
|
for(i=0; i < IDRV_MAT_MAXTEXTURES; i++)
|
|
_LightMapUVMap[i]= -1;
|
|
// reserve enough space to never reallocate, nor test for reallocation.
|
|
_LightMapLUT.resize(NL3D_DRV_MAX_LIGHTMAP);
|
|
// must set replace for alpha part.
|
|
_LightMapLastStageEnv.Env.OpAlpha= CMaterial::Replace;
|
|
_LightMapLastStageEnv.Env.SrcArg0Alpha= CMaterial::Texture;
|
|
_LightMapLastStageEnv.Env.OpArg0Alpha= CMaterial::SrcAlpha;
|
|
|
|
_ProjMatDirty = true;
|
|
|
|
std::fill(_StageSupportEMBM, _StageSupportEMBM + IDRV_MAT_MAXTEXTURES, false);
|
|
|
|
ATIWaterShaderHandleNoDiffuseMap = 0;
|
|
ATIWaterShaderHandle = 0;
|
|
ATICloudShaderHandle = 0;
|
|
|
|
_ATIDriverVersion = 0;
|
|
_ATIFogRangeFixed = true;
|
|
|
|
std::fill(ARBWaterShader, ARBWaterShader + 4, 0);
|
|
|
|
/// buildCausticCubeMapTex();
|
|
|
|
_SpecularBatchOn= false;
|
|
|
|
_PolygonSmooth= false;
|
|
|
|
_VBHardProfiling= false;
|
|
_CurVBHardLockCount= 0;
|
|
_NumVBHardProfileFrame= 0;
|
|
_Interval = 1;
|
|
|
|
_TexEnvReplace.setDefault();
|
|
_TexEnvReplace.Env.OpAlpha = CMaterial::Previous;
|
|
_TexEnvReplace.Env.OpRGB = CMaterial::Previous;
|
|
|
|
_WndActive = false;
|
|
//
|
|
_CurrentOcclusionQuery = NULL;
|
|
_SwapBufferCounter = 0;
|
|
|
|
_LightMapDynamicLightEnabled = false;
|
|
_LightMapDynamicLightDirty= false;
|
|
|
|
_CurrentMaterialSupportedShader= CMaterial::Normal;
|
|
|
|
// to avoid any problem if light0 never setted up, and ligthmap rendered
|
|
_UserLight0.setupDirectional(CRGBA::Black, CRGBA::White, CRGBA::White, CVector::K);
|
|
|
|
_TextureTargetCubeFace = 0;
|
|
_TextureTargetUpload = false;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
CDriverGL::~CDriverGL()
|
|
{
|
|
H_AUTO_OGL(CDriverGL_CDriverGLDtor)
|
|
release();
|
|
|
|
#if defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
NL3D::MAC::dtor();
|
|
#endif
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CDriverGL::stretchRect(ITexture * /* srcText */, NLMISC::CRect &/* srcRect */, ITexture * /* destText */, NLMISC::CRect &/* destRect */)
|
|
{
|
|
H_AUTO_OGL(CDriverGL_stretchRect)
|
|
|
|
return false;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CDriverGL::supportBloomEffect() const
|
|
{
|
|
return (isVertexProgramSupported() && supportFrameBufferObject() && supportPackedDepthStencil() && supportTextureRectangle());
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CDriverGL::supportNonPowerOfTwoTextures() const
|
|
{
|
|
return _Extensions.ARBTextureNonPowerOfTwo;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
bool CDriverGL::isTextureRectangle(ITexture * tex) const
|
|
{
|
|
return (supportTextureRectangle() && tex->isBloomTexture() && 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::setupDisplay()
|
|
{
|
|
H_AUTO_OGL(CDriverGL_setupDisplay)
|
|
|
|
// Driver caps.
|
|
//=============
|
|
// Retrieve the extensions for the current context.
|
|
NL3D::registerGlExtensions (_Extensions);
|
|
vector<string> 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<MaxLight;i++)
|
|
_UserLightEnable[i]= false;
|
|
|
|
// init _DriverGLStates
|
|
_DriverGLStates.init(_Extensions.ARBTextureCubeMap, (_Extensions.NVTextureRectangle || _Extensions.EXTTextureRectangle || _Extensions.ARBTextureRectangle), _MaxDriverLight);
|
|
|
|
// Init OpenGL/Driver defaults.
|
|
//=============================
|
|
glViewport(0,0,_WindowWidth,_WindowHeight);
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
glOrtho(0,_WindowWidth,_WindowHeight,0,-1.0f,1.0f);
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glDisable(GL_AUTO_NORMAL);
|
|
glDisable(GL_COLOR_MATERIAL);
|
|
glEnable(GL_DITHER);
|
|
glDisable(GL_FOG);
|
|
glDisable(GL_LINE_SMOOTH);
|
|
glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDisable(GL_NORMALIZE);
|
|
glDisable(GL_COLOR_SUM_EXT);
|
|
|
|
_CurrViewport.init(0.f, 0.f, 1.f, 1.f);
|
|
_CurrScissor.initFullScreen();
|
|
_CurrentGlNormalize= false;
|
|
_ForceNormalize= false;
|
|
// Setup defaults for blend, lighting ...
|
|
_DriverGLStates.forceDefaults(inlGetNumTextStages());
|
|
// Default delta camera pos.
|
|
_PZBCameraPos= CVector::Null;
|
|
|
|
if (_NVTextureShaderEnabled)
|
|
{
|
|
enableNVTextureShader(false);
|
|
}
|
|
|
|
// Be always in EXTSeparateSpecularColor.
|
|
if(_Extensions.EXTSeparateSpecularColor)
|
|
{
|
|
glLightModeli((GLenum)GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);
|
|
}
|
|
|
|
_VertexProgramEnabled= false;
|
|
_LastSetupGLArrayVertexProgram= false;
|
|
|
|
// Init VertexArrayRange according to supported extenstion.
|
|
_SupportVBHard= false;
|
|
_SlowUnlockVBHard= false;
|
|
_MaxVerticesByVBHard= 0;
|
|
// Try with NVidia ext first.
|
|
if(_Extensions.NVVertexArrayRange)
|
|
{
|
|
_AGPVertexArrayRange= new CVertexArrayRangeNVidia(this);
|
|
_VRAMVertexArrayRange= new CVertexArrayRangeNVidia(this);
|
|
_SupportVBHard= true;
|
|
_MaxVerticesByVBHard= _Extensions.NVVertexArrayRangeMaxVertex;
|
|
}
|
|
else if(_Extensions.ATITextureEnvCombine3 && !_Extensions.IsATI9500OrAbove && _Extensions.ATIVertexArrayObject)
|
|
{
|
|
// NB
|
|
// on Radeon 9200 and below : ATI_vertex_array_object is better (no direct access to AGP with ARB_vertex_buffer_object -> 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<inlGetNumTextStages(); stage++)
|
|
{
|
|
// init no texture.
|
|
_CurrentTexture[stage]= NULL;
|
|
_CurrentTextureInfoGL[stage]= NULL;
|
|
// texture are disabled in DriverGLStates.forceDefaults().
|
|
|
|
// init default env.
|
|
CMaterial::CTexEnv env; // envmode init to default.
|
|
env.ConstantColor.set(255,255,255,255);
|
|
forceActivateTexEnvMode(stage, env);
|
|
forceActivateTexEnvColor(stage, env);
|
|
|
|
// Not special TexEnv.
|
|
_CurrentTexEnvSpecial[stage]= TexEnvSpecialDisabled;
|
|
|
|
// set All TexGen by default to identity matrix (prefer use the textureMatrix scheme)
|
|
_DriverGLStates.activeTextureARB(stage);
|
|
GLfloat params[4];
|
|
params[0]=1; params[1]=0; params[2]=0; params[3]=0;
|
|
glTexGenfv(GL_S, GL_OBJECT_PLANE, params);
|
|
glTexGenfv(GL_S, GL_EYE_PLANE, params);
|
|
params[0]=0; params[1]=1; params[2]=0; params[3]=0;
|
|
glTexGenfv(GL_T, GL_OBJECT_PLANE, params);
|
|
glTexGenfv(GL_T, GL_EYE_PLANE, params);
|
|
params[0]=0; params[1]=0; params[2]=1; params[3]=0;
|
|
glTexGenfv(GL_R, GL_OBJECT_PLANE, params);
|
|
glTexGenfv(GL_R, GL_EYE_PLANE, params);
|
|
params[0]=0; params[1]=0; params[2]=0; params[3]=1;
|
|
glTexGenfv(GL_Q, GL_OBJECT_PLANE, params);
|
|
glTexGenfv(GL_Q, GL_EYE_PLANE, params);
|
|
}
|
|
|
|
resetTextureShaders();
|
|
|
|
_PPLExponent = 1.f;
|
|
_PPLightDiffuseColor = NLMISC::CRGBA::White;
|
|
_PPLightSpecularColor = NLMISC::CRGBA::White;
|
|
|
|
// Backward compatibility: default lighting is Light0 default openGL
|
|
// meaning that light direction is always (0,1,0) in eye-space
|
|
// use enableLighting(0....), to get normal behaviour
|
|
_DriverGLStates.enableLight(0, true);
|
|
|
|
_Initialized = true;
|
|
|
|
_ForceDXTCCompression= false;
|
|
_ForceTextureResizePower= 0;
|
|
|
|
// Reset profiling.
|
|
_AllocatedTextureMemory= 0;
|
|
_TextureUsed.clear();
|
|
_PrimitiveProfileIn.reset();
|
|
_PrimitiveProfileOut.reset();
|
|
_NbSetupMaterialCall= 0;
|
|
_NbSetupModelMatrixCall= 0;
|
|
|
|
// check whether per pixel lighting shader is supported
|
|
checkForPerPixelLightingSupport();
|
|
|
|
// if EXTVertexShader is used, bind the standard GL arrays, and allocate constant
|
|
if (!_Extensions.NVVertexProgram && !_Extensions.ARBVertexProgram && _Extensions.EXTVertexShader)
|
|
{
|
|
_EVSPositionHandle = nglBindParameterEXT(GL_CURRENT_VERTEX_EXT);
|
|
_EVSNormalHandle = nglBindParameterEXT(GL_CURRENT_NORMAL);
|
|
_EVSColorHandle = nglBindParameterEXT(GL_CURRENT_COLOR);
|
|
|
|
if (!_EVSPositionHandle || !_EVSNormalHandle || !_EVSColorHandle)
|
|
{
|
|
nlwarning("Unable to bind input parameters for use with EXT_vertex_shader, vertex program support is disabled");
|
|
_Extensions.EXTVertexShader = false;
|
|
}
|
|
else
|
|
{
|
|
// bind texture units
|
|
for(uint k = 0; k < 8; ++k)
|
|
{
|
|
_EVSTexHandle[k] = nglBindTextureUnitParameterEXT(GL_TEXTURE0_ARB + k, GL_CURRENT_TEXTURE_COORDS);
|
|
}
|
|
// Other attributes are managed using variant pointers :
|
|
// Secondary color
|
|
// Fog Coords
|
|
// Skin Weight
|
|
// Skin palette
|
|
// This mean that they must have 4 components
|
|
|
|
// Allocate invariants. One assitionnal variant is needed for fog coordinate if fog bug is not fixed in driver version
|
|
_EVSConstantHandle = nglGenSymbolsEXT(GL_VECTOR_EXT, GL_INVARIANT_EXT, GL_FULL_RANGE_EXT, _EVSNumConstant + (_ATIFogRangeFixed ? 0 : 1));
|
|
|
|
if (_EVSConstantHandle == 0)
|
|
{
|
|
nlwarning("Unable to allocate constants for EXT_vertex_shader, vertex program support is disabled");
|
|
_Extensions.EXTVertexShader = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef NL_OS_WINDOWS
|
|
// Reset the vbl interval
|
|
setSwapVBLInterval(_Interval);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
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;
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
bool CDriverGL::isTextureExist(const ITexture&tex)
|
|
{
|
|
H_AUTO_OGL(CDriverGL_isTextureExist)
|
|
bool result;
|
|
|
|
// Create the shared Name.
|
|
std::string name;
|
|
getTextureShareName (tex, name);
|
|
|
|
{
|
|
CUnfairSynchronized<TTexDrvInfoPtrMap>::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<IVertexBufferHardGL*>::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<CVertexBufferHardGLNVidia*>(*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<IVertexBufferHardGL*>::iterator itVBHard= _VertexBufferHardSet.Set.begin();
|
|
while(itVBHard != _VertexBufferHardSet.Set.end() )
|
|
{
|
|
if((*itVBHard)->VBType == IVertexBufferHardGL::NVidiaVB)
|
|
{
|
|
CVertexBufferHardGLNVidia *vbHardNV= static_cast<CVertexBufferHardGLNVidia*>(*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<NLMISC::CDIEventEmitter *>(_EventEmitter.getEmitter(1))->poll();
|
|
}
|
|
#endif
|
|
|
|
if (!_WndActive)
|
|
{
|
|
if (_AGPVertexArrayRange) _AGPVertexArrayRange->updateLostBuffers();
|
|
if (_VRAMVertexArrayRange) _VRAMVertexArrayRange->updateLostBuffers();
|
|
}
|
|
|
|
#ifdef NL_OS_WINDOWS
|
|
SwapBuffers(_hDC);
|
|
|
|
#elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
NL3D::MAC::swapBuffers();
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
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;stage<inlGetNumTextStages(); stage++)
|
|
{
|
|
// init no texture.
|
|
_CurrentTexture[stage]= NULL;
|
|
_CurrentTextureInfoGL[stage]= NULL;
|
|
// texture are disabled in DriverGLStates.forceDefaults().
|
|
|
|
// init default env.
|
|
CMaterial::CTexEnv env; // envmode init to default.
|
|
env.ConstantColor.set(255,255,255,255);
|
|
forceActivateTexEnvMode(stage, env);
|
|
forceActivateTexEnvColor(stage, env);
|
|
}
|
|
|
|
// Activate the default material.
|
|
//===========================================================
|
|
// Same reasoning as textures :)
|
|
_DriverGLStates.forceDefaults(inlGetNumTextStages());
|
|
if (_NVTextureShaderEnabled)
|
|
{
|
|
glDisable(GL_TEXTURE_SHADER_NV);
|
|
_NVTextureShaderEnabled = false;
|
|
}
|
|
_CurrentMaterial= NULL;
|
|
|
|
// Reset the profiling counter.
|
|
_PrimitiveProfileIn.reset();
|
|
_PrimitiveProfileOut.reset();
|
|
_NbSetupMaterialCall= 0;
|
|
_NbSetupModelMatrixCall= 0;
|
|
|
|
// Reset the texture set
|
|
_TextureUsed.clear();
|
|
|
|
// Reset Profile VBHardLock
|
|
if(_VBHardProfiling)
|
|
{
|
|
_CurVBHardLockCount= 0;
|
|
_NumVBHardProfileFrame++;
|
|
}
|
|
// on ati, if the window is inactive, check all vertex buffer to see which one are lost
|
|
if (_AGPVertexArrayRange) _AGPVertexArrayRange->updateLostBuffers();
|
|
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)
|
|
{
|
|
switchBackToOldMode();
|
|
_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_MAC) && defined(NL_MAC_NATIVE)
|
|
|
|
NL3D::MAC::release();
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
if(_FullScreen)
|
|
{
|
|
switchBackToOldMode();
|
|
|
|
// Ungrab the keyboard (probably not necessary);
|
|
XUnmapWindow(dpy, win);
|
|
XSync(dpy, True);
|
|
XUngrabKeyboard(dpy, CurrentTime);
|
|
}
|
|
|
|
if (ctx)
|
|
{
|
|
glXDestroyContext(dpy, ctx);
|
|
ctx = NULL;
|
|
}
|
|
|
|
XCloseDisplay(dpy);
|
|
dpy = NULL;
|
|
|
|
#endif // NL_OS_UNIX
|
|
|
|
// released
|
|
_Initialized= false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// --------------------------------------------------
|
|
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;
|
|
|
|
#elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
|
|
uint32 clientWidth, clientHeight;
|
|
NL3D::MAC::getWindowSize(clientWidth, clientHeight);
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
|
|
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;
|
|
|
|
#elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
# warning "OpenGL Driver: Missing Mac Implementation"
|
|
// nlwarning("OpenGL Driver: Temporary Mac Implementation");
|
|
|
|
int clientWidth = 1024;
|
|
int clientHeight = 768;
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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<float> &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<float> &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<CTextureDrvInfosGL*>::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;
|
|
}
|
|
|
|
// ***************************************************************************
|
|
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::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<float *>(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<GLint> 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, (GLsizei)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<std::string> &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<std::string> &/* result */)
|
|
{
|
|
// not implemented
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CDriverGL::profileIBAllocation(std::vector<std::string> &/* result */)
|
|
{
|
|
// not implemented
|
|
}
|
|
|
|
// ***************************************************************************
|
|
void CDriverGL::profileVBHardAllocation(std::vector<std::string> &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<IVertexBufferHardGL*>::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);
|
|
}
|
|
#elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE)
|
|
# warning "OpenGL Driver: Missing Mac Implementation"
|
|
nlwarning("OpenGL Driver: Missing Mac Implementation");
|
|
|
|
#elif defined (NL_OS_UNIX)
|
|
// 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<CDriverGLStates &>(_DriverGLStates);
|
|
uint currTexStage = dgs.getActiveTextureARB();
|
|
for(uint k = 0; k < this->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 || _Extensions.EXTTextureRectangle || _Extensions.ARBTextureRectangle);
|
|
}
|
|
|
|
// ***************************************************************************
|
|
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<COcclusionQueryGL *>(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;
|
|
}
|
|
}
|