// 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 # include #elif defined(NL_OS_MAC) && defined(NL_MAC_NATIVE) # define GL_GLEXT_LEGACY # include # include "mac/glext.h" # include "mac/cocoa_adapter.h" #elif defined (NL_OS_UNIX) # include # include #endif // NL_OS_UNIX #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 = 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;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::setupDisplay() { H_AUTO_OGL(CDriverGL_setupDisplay) // 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::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); #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;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) { 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) # warning "OpenGL Driver: Missing Mac Implementation" nlwarning("OpenGL Driver: Missing Mac Implementation"); #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 &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; } // *************************************************************************** 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(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, (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 &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); } #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(_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(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; } }