stk-code_catmod/irrlicht_1.5_update/source/Irrlicht/COpenGLDriver.cpp
hikerstk 368604f379 Added speedometer.
NOTE: to use the speedometer, a patched version of irrlicht is needed. The replacement
files are included here, we hope that the necessary functions will be included in the
next irrlicht release (STK will still compile without a patched irrlicht version,
but not display the speedometer).


git-svn-id: svn+ssh://svn.code.sf.net/p/supertuxkart/code/main/branches/irrlicht@3781 178a84e3-b1eb-0310-8ba1-8eac791a3b58
2009-07-27 11:56:09 +00:00

3122 lines
88 KiB
C++
Executable File

// Copyright (C) 2002-2008 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h
#include "COpenGLDriver.h"
// needed here also because of the create methods' parameters
#include "CNullDriver.h"
#ifdef _IRR_COMPILE_WITH_OPENGL_
#include "COpenGLTexture.h"
#include "COpenGLMaterialRenderer.h"
#include "COpenGLShaderMaterialRenderer.h"
#include "COpenGLSLMaterialRenderer.h"
#include "COpenGLNormalMapRenderer.h"
#include "COpenGLParallaxMapRenderer.h"
#include "CImage.h"
#include "os.h"
#ifdef _IRR_USE_SDL_DEVICE_
#include <SDL/SDL.h>
#endif
namespace irr
{
namespace video
{
// -----------------------------------------------------------------------
// WINDOWS CONSTRUCTOR
// -----------------------------------------------------------------------
#ifdef _IRR_USE_WINDOWS_DEVICE_
//! Windows constructor and init code
COpenGLDriver::COpenGLDriver(const irr::SIrrlichtCreationParameters& params,
io::IFileSystem* io)
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
CurrentRenderMode(ERM_NONE), ResetRenderStates(true), Transformation3DChanged(true),
AntiAlias(params.AntiAlias), RenderTargetTexture(0), LastSetLight(-1),
CurrentRendertargetSize(0,0),
HDc(0), Window(static_cast<HWND>(params.WindowId)), HRc(0)
{
#ifdef _DEBUG
setDebugName("COpenGLDriver");
#endif
}
//! inits the open gl driver
bool COpenGLDriver::initDriver(irr::SIrrlichtCreationParameters params)
{
// Set up ixel format descriptor with desired parameters
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
1, // Version Number
PFD_DRAW_TO_WINDOW | // Format Must Support Window
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
PFD_DOUBLEBUFFER, // Must Support Double Buffering
PFD_TYPE_RGBA, // Request An RGBA Format
params.Bits, // Select Our Color Depth
0, 0, 0, 0, 0, 0, // Color Bits Ignored
0, // No Alpha Buffer
0, // Shift Bit Ignored
0, // No Accumulation Buffer
0, 0, 0, 0, // Accumulation Bits Ignored
24, // Z-Buffer (Depth Buffer)
params.Stencilbuffer ? 1 : 0, // Stencil Buffer Depth
0, // No Auxiliary Buffer
PFD_MAIN_PLANE, // Main Drawing Layer
0, // Reserved
0, 0, 0 // Layer Masks Ignored
};
GLuint PixelFormat;
if (AntiAlias)
{
// Create a window to test antialiasing support
const c8* ClassName = "GLCIrrDeviceWin32";
HINSTANCE lhInstance = GetModuleHandle(0);
// Register Class
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)DefWindowProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = lhInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = ClassName;
wcex.hIconSm = 0;
wcex.hIcon = 0;
RegisterClassEx(&wcex);
RECT clientSize;
clientSize.top = 0;
clientSize.left = 0;
clientSize.right = params.WindowSize.Width;
clientSize.bottom = params.WindowSize.Height;
DWORD style = WS_POPUP;
if (!params.Fullscreen)
style = WS_SYSMENU | WS_BORDER | WS_CAPTION | WS_CLIPCHILDREN | WS_CLIPSIBLINGS;
AdjustWindowRect(&clientSize, style, FALSE);
const s32 realWidth = clientSize.right - clientSize.left;
const s32 realHeight = clientSize.bottom - clientSize.top;
const s32 windowLeft = (GetSystemMetrics(SM_CXSCREEN) - realWidth) / 2;
const s32 windowTop = (GetSystemMetrics(SM_CYSCREEN) - realHeight) / 2;
HWND temporary_wnd=CreateWindow(ClassName, "", style, windowLeft, windowTop,
realWidth, realHeight, NULL, NULL, lhInstance, NULL);
if(!temporary_wnd)
{
os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
return false;
}
HDc = GetDC(temporary_wnd);
for (u32 i=0; i<5; ++i)
{
if (i == 1)
{
if (params.Stencilbuffer)
{
os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
params.Stencilbuffer = false;
pfd.cStencilBits = 0;
}
else
continue;
}
else
if (i == 2)
{
pfd.cDepthBits = 24;
}
if (i == 3)
{
if (params.Bits!=16)
pfd.cDepthBits = 16;
else
continue;
}
else
if (i == 4)
{
os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
return false;
}
// choose pixelformat
PixelFormat = ChoosePixelFormat(HDc, &pfd);
if (PixelFormat)
break;
}
SetPixelFormat(HDc, PixelFormat, &pfd);
HRc=wglCreateContext(HDc);
if(!HRc)
{
os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
return false;
}
if(!wglMakeCurrent(HDc, HRc))
{
os::Printer::log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
wglDeleteContext(HRc);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
return false;
}
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
if(wglChoosePixelFormat_ARB)
{
// This value determines the number of samples used for antialiasing
// valid numbers are 2, 4, 8. My experience is that 8 does not
// show a big improvement over 4, but 4 shows a big improvement over
// 2.
const s32 numSamples = 4;
f32 fAttributes[] =
{
0.0, 0.0
};
s32 iAttributes[] =
{
WGL_DRAW_TO_WINDOW_ARB,GL_TRUE,
WGL_SUPPORT_OPENGL_ARB,GL_TRUE,
WGL_ACCELERATION_ARB,WGL_FULL_ACCELERATION_ARB,
WGL_COLOR_BITS_ARB,(params.Bits==32) ? 24 : 15,
WGL_ALPHA_BITS_ARB,(params.Bits==32) ? 8 : 1,
WGL_DEPTH_BITS_ARB,params.ZBufferBits,
WGL_STENCIL_BITS_ARB,(params.Stencilbuffer) ? 1 : 0,
WGL_DOUBLE_BUFFER_ARB,GL_TRUE,
WGL_SAMPLE_BUFFERS_ARB,GL_TRUE,
WGL_SAMPLES_ARB,numSamples,
0,0
};
s32 rv=0;
// Try to get an acceptable pixel format
while(rv==0 && iAttributes[19]>1)
{
s32 pixelFormat=0;
u32 numFormats=0;
const s32 valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
if(valid && numFormats>0)
rv = pixelFormat;
else
iAttributes[19] >>= 1;
}
if(rv)
PixelFormat=rv;
}
wglMakeCurrent(HDc, NULL);
wglDeleteContext(HRc);
ReleaseDC(temporary_wnd, HDc);
DestroyWindow(temporary_wnd);
}
// get hdc
HDc=GetDC(Window);
if (!HDc)
{
os::Printer::log("Cannot create a GL device context.", ELL_ERROR);
return false;
}
// search for pixel format the simple way
if (!AntiAlias)
{
for (u32 i=0; i<5; ++i)
{
if (i == 1)
{
if (params.Stencilbuffer)
{
os::Printer::log("Cannot create a GL device with stencil buffer, disabling stencil shadows.", ELL_WARNING);
params.Stencilbuffer = false;
pfd.cStencilBits = 0;
}
else
continue;
}
else
if (i == 2)
{
pfd.cDepthBits = 24;
}
if (i == 3)
{
if (params.Bits!=16)
pfd.cDepthBits = 16;
else
continue;
}
else
if (i == 4)
{
os::Printer::log("Cannot create a GL device context", "No suitable format.", ELL_ERROR);
return false;
}
// choose pixelformat
PixelFormat = ChoosePixelFormat(HDc, &pfd);
if (PixelFormat)
break;
}
}
// set pixel format
if(!SetPixelFormat(HDc, PixelFormat, &pfd))
{
os::Printer::log("Cannot set the pixel format.", ELL_ERROR);
return false;
}
// create rendering context
HRc=wglCreateContext(HDc);
if (!HRc)
{
os::Printer::log("Cannot create a GL rendering context.", ELL_ERROR);
return false;
}
// activate rendering context
if(!wglMakeCurrent(HDc, HRc))
{
os::Printer::log("Cannot activate GL rendering context", ELL_ERROR);
wglDeleteContext(HRc);
return false;
}
int pf = GetPixelFormat(HDc);
DescribePixelFormat(HDc, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);
if (pfd.cAlphaBits != 0)
{
if (pfd.cRedBits == 8)
ColorFormat = ECF_A8R8G8B8;
else
ColorFormat = ECF_A1R5G5B5;
}
else
{
if (pfd.cRedBits == 8)
ColorFormat = ECF_R8G8B8;
else
ColorFormat = ECF_R5G6B5;
}
genericDriverInit(params.WindowSize, params.Stencilbuffer);
// set vsync
if (wglSwapIntervalEXT)
wglSwapIntervalEXT(params.Vsync ? 1 : 0);
// set exposed data
ExposedData.OpenGLWin32.HDc = HDc;
ExposedData.OpenGLWin32.HRc = HRc;
ExposedData.OpenGLWin32.HWnd = Window;
return true;
}
#endif //IRR_USE_WINDOWS_DEVICE_
// -----------------------------------------------------------------------
// MacOSX CONSTRUCTOR
// -----------------------------------------------------------------------
#ifdef _IRR_USE_OSX_DEVICE_
//! Windows constructor and init code
COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params,
io::IFileSystem* io, CIrrDeviceMacOSX *device)
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
CurrentRenderMode(ERM_NONE), ResetRenderStates(true), Transformation3DChanged(true),
AntiAlias(params.AntiAlias), RenderTargetTexture(0), LastSetLight(-1),
CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8), _device(device)
{
#ifdef _DEBUG
setDebugName("COpenGLDriver");
#endif
genericDriverInit(params.WindowSize, params.Stencilbuffer);
}
#endif
// -----------------------------------------------------------------------
// LINUX CONSTRUCTOR
// -----------------------------------------------------------------------
#ifdef _IRR_USE_LINUX_DEVICE_
//! Linux constructor and init code
COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params,
io::IFileSystem* io)
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
CurrentRenderMode(ERM_NONE), ResetRenderStates(true),
Transformation3DChanged(true), AntiAlias(params.AntiAlias),
RenderTargetTexture(0), LastSetLight(-1), CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8)
{
#ifdef _DEBUG
setDebugName("COpenGLDriver");
#endif
ExposedData.OpenGLLinux.X11Context = glXGetCurrentContext();
ExposedData.OpenGLLinux.X11Display = glXGetCurrentDisplay();
ExposedData.OpenGLLinux.X11Window = (unsigned long)params.WindowId;
Drawable = glXGetCurrentDrawable();
genericDriverInit(params.WindowSize, params.Stencilbuffer);
// set vsync
#ifdef GLX_SGI_swap_control
#ifdef _IRR_OPENGL_USE_EXTPOINTER_
if (params.Vsync && glxSwapIntervalSGI)
glxSwapIntervalSGI(1);
#else
if (params.Vsync)
glXSwapIntervalSGI(1);
#endif
#endif
}
#endif // _IRR_USE_LINUX_DEVICE_
// -----------------------------------------------------------------------
// SDL CONSTRUCTOR
// -----------------------------------------------------------------------
#ifdef _IRR_USE_SDL_DEVICE_
//! SDL constructor and init code
COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params,
io::IFileSystem* io)
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
CurrentRenderMode(ERM_NONE), ResetRenderStates(true),
Transformation3DChanged(true), AntiAlias(params.AntiAlias),
RenderTargetTexture(0), LastSetLight(-1), CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8)
{
#ifdef _DEBUG
setDebugName("COpenGLDriver");
#endif
genericDriverInit(params.WindowSize, params.Stencilbuffer);
}
#endif // _IRR_USE_SDL_DEVICE_
//! destructor
COpenGLDriver::~COpenGLDriver()
{
deleteMaterialRenders();
// I get a blue screen on my laptop, when I do not delete the
// textures manually before releasing the dc. Oh how I love this.
deleteAllTextures();
#ifdef _IRR_USE_WINDOWS_DEVICE_
if (HRc)
{
if (!wglMakeCurrent(0, 0))
os::Printer::log("Release of dc and rc failed.", ELL_WARNING);
if (!wglDeleteContext(HRc))
os::Printer::log("Release of rendering context failed.", ELL_WARNING);
}
if (HDc)
ReleaseDC(Window, HDc);
#endif
}
// -----------------------------------------------------------------------
// METHODS
// -----------------------------------------------------------------------
bool COpenGLDriver::genericDriverInit(const core::dimension2d<s32>& screenSize, bool stencilBuffer)
{
Name=L"OpenGL ";
Name.append(glGetString(GL_VERSION));
s32 pos=Name.findNext(L' ', 7);
if (pos != -1)
Name=Name.subString(0, pos);
printVersion();
// print renderer information
const GLubyte* renderer = glGetString(GL_RENDERER);
const GLubyte* vendor = glGetString(GL_VENDOR);
if (renderer && vendor)
{
os::Printer::log(reinterpret_cast<const c8*>(renderer), reinterpret_cast<const c8*>(vendor), ELL_INFORMATION);
vendorName = reinterpret_cast<const c8*>(vendor);
}
u32 i;
for (i=0; i<MATERIAL_MAX_TEXTURES; ++i)
CurrentTexture[i]=0;
// load extensions
initExtensions(stencilBuffer);
if (queryFeature(EVDF_ARB_GLSL))
{
char buf[32];
const u32 maj = ShaderLanguageVersion/100;
snprintf(buf, 32, "%u.%u", maj, ShaderLanguageVersion-maj*100);
os::Printer::log("GLSL version", buf, ELL_INFORMATION);
}
else
os::Printer::log("GLSL not available.", ELL_INFORMATION);
glPixelStorei(GL_PACK_ALIGNMENT, 1);
// Reset The Current Viewport
glViewport(0, 0, screenSize.Width, screenSize.Height);
// This needs an SMaterial flag to enable/disable later on, but should become default sometimes
// glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
// glEnable(GL_COLOR_MATERIAL);
setAmbientLight(SColorf(0.0f,0.0f,0.0f,0.0f));
#ifdef GL_EXT_separate_specular_color
if (FeatureAvailable[IRR_EXT_separate_specular_color])
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
#endif
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
// This is a fast replacement for NORMALIZE_NORMALS
// if ((Version>101) || FeatureAvailable[IRR_EXT_rescale_normal])
// glEnable(GL_RESCALE_NORMAL_EXT);
glClearDepth(1.0);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glDepthFunc(GL_LEQUAL);
glFrontFace( GL_CW );
if (AntiAlias)
{
if (MultiSamplingExtension)
glEnable(GL_MULTISAMPLE_ARB);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable(GL_LINE_SMOOTH);
}
// currently disabled, because often in software, and thus very slow
// glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
// glEnable(GL_POINT_SMOOTH);
UserClipPlane.reallocate(MaxUserClipPlanes);
UserClipPlaneEnabled.reallocate(MaxUserClipPlanes);
for (i=0; i<MaxUserClipPlanes; ++i)
{
UserClipPlane.push_back(core::plane3df());
UserClipPlaneEnabled.push_back(false);
}
// create material renderers
createMaterialRenderers();
// set the renderstates
setRenderStates3DMode();
glAlphaFunc(GL_GREATER, 0.f);
// set fog mode
setFog(FogColor, LinearFog, FogStart, FogEnd, FogDensity, PixelFog, RangeFog);
// create matrix for flipping textures
TextureFlipMatrix.buildTextureTransform(0.0f, core::vector2df(0,0), core::vector2df(0,1.0f), core::vector2df(1.0f,-1.0f));
return true;
}
void COpenGLDriver::createMaterialRenderers()
{
// create OpenGL material renderers
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_SOLID(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_SOLID_2_LAYER(this));
// add the same renderer for all lightmap types
COpenGLMaterialRenderer_LIGHTMAP* lmr = new COpenGLMaterialRenderer_LIGHTMAP(this);
addMaterialRenderer(lmr); // for EMT_LIGHTMAP:
addMaterialRenderer(lmr); // for EMT_LIGHTMAP_ADD:
addMaterialRenderer(lmr); // for EMT_LIGHTMAP_M2:
addMaterialRenderer(lmr); // for EMT_LIGHTMAP_M4:
addMaterialRenderer(lmr); // for EMT_LIGHTMAP_LIGHTING:
addMaterialRenderer(lmr); // for EMT_LIGHTMAP_LIGHTING_M2:
addMaterialRenderer(lmr); // for EMT_LIGHTMAP_LIGHTING_M4:
lmr->drop();
// add remaining material renderer
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_DETAIL_MAP(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_SPHERE_MAP(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_REFLECTION_2_LAYER(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_TRANSPARENT_ADD_COLOR(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_TRANSPARENT_ALPHA_CHANNEL_REF(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_TRANSPARENT_VERTEX_ALPHA(this));
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_TRANSPARENT_REFLECTION_2_LAYER(this));
// add normal map renderers
s32 tmp = 0;
video::IMaterialRenderer* renderer = 0;
renderer = new COpenGLNormalMapRenderer(this, tmp, MaterialRenderers[EMT_SOLID].Renderer);
renderer->drop();
renderer = new COpenGLNormalMapRenderer(this, tmp, MaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].Renderer);
renderer->drop();
renderer = new COpenGLNormalMapRenderer(this, tmp, MaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].Renderer);
renderer->drop();
// add parallax map renderers
renderer = new COpenGLParallaxMapRenderer(this, tmp, MaterialRenderers[EMT_SOLID].Renderer);
renderer->drop();
renderer = new COpenGLParallaxMapRenderer(this, tmp, MaterialRenderers[EMT_TRANSPARENT_ADD_COLOR].Renderer);
renderer->drop();
renderer = new COpenGLParallaxMapRenderer(this, tmp, MaterialRenderers[EMT_TRANSPARENT_VERTEX_ALPHA].Renderer);
renderer->drop();
// add basic 1 texture blending
addAndDropMaterialRenderer(new COpenGLMaterialRenderer_ONETEXTURE_BLEND(this));
}
//! presents the rendered scene on the screen, returns false if failed
bool COpenGLDriver::endScene()
{
CNullDriver::endScene();
glFlush();
#ifdef _IRR_USE_WINDOWS_DEVICE_
return SwapBuffers(HDc) == TRUE;
#elif defined(_IRR_USE_LINUX_DEVICE_)
glXSwapBuffers((Display*)ExposedData.OpenGLLinux.X11Display, Drawable);
return true;
#elif defined(_IRR_USE_OSX_DEVICE_)
_device->flush();
return true;
#elif defined(_IRR_USE_SDL_DEVICE_)
SDL_GL_SwapBuffers();
return true;
#else
return false;
#endif
}
//! clears the zbuffer
bool COpenGLDriver::beginScene(bool backBuffer, bool zBuffer, SColor color,
void* windowId, core::rect<s32>* sourceRect)
{
CNullDriver::beginScene(backBuffer, zBuffer, color, windowId, sourceRect);
GLbitfield mask = 0;
if (backBuffer)
{
const f32 inv = 1.0f / 255.0f;
glClearColor(color.getRed() * inv, color.getGreen() * inv,
color.getBlue() * inv, color.getAlpha() * inv);
mask |= GL_COLOR_BUFFER_BIT;
}
if (zBuffer)
{
glDepthMask(GL_TRUE);
LastMaterial.ZWriteEnable=true;
mask |= GL_DEPTH_BUFFER_BIT;
}
glClear(mask);
return true;
}
//! Returns the transformation set by setTransform
const core::matrix4& COpenGLDriver::getTransform(E_TRANSFORMATION_STATE state) const
{
return Matrices[state];
}
//! sets transformation
void COpenGLDriver::setTransform(E_TRANSFORMATION_STATE state, const core::matrix4& mat)
{
Matrices[state] = mat;
Transformation3DChanged = true;
switch(state)
{
case ETS_VIEW:
case ETS_WORLD:
{
// OpenGL only has a model matrix, view and world is not existent. so lets fake these two.
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf((Matrices[ETS_VIEW] * Matrices[ETS_WORLD]).pointer());
// we have to update the clip planes to the latest view matrix
for (u32 i=0; i<MaxUserClipPlanes; ++i)
if (UserClipPlaneEnabled[i])
uploadClipPlane(i);
}
break;
case ETS_PROJECTION:
{
GLfloat glmat[16];
createGLMatrix(glmat, mat);
// flip z to compensate OpenGLs right-hand coordinate system
glmat[12] *= -1.0f;
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(glmat);
}
break;
case ETS_TEXTURE_0:
case ETS_TEXTURE_1:
case ETS_TEXTURE_2:
case ETS_TEXTURE_3:
{
const u32 i = state - ETS_TEXTURE_0;
const bool isRTT = Material.getTexture(i) && Material.getTexture(i)->isRenderTarget();
if (MultiTextureExtension)
extGlActiveTexture(GL_TEXTURE0_ARB + i);
glMatrixMode(GL_TEXTURE);
if (mat.isIdentity() && !isRTT)
glLoadIdentity();
else
{
GLfloat glmat[16];
if (isRTT)
createGLTextureMatrix(glmat, mat * TextureFlipMatrix);
else
createGLTextureMatrix(glmat, mat);
glLoadMatrixf(glmat);
}
break;
}
default:
break;
}
}
bool COpenGLDriver::updateVertexHardwareBuffer(SHWBufferLink_opengl *HWBuffer)
{
if (!HWBuffer)
return false;
if (!FeatureAvailable[IRR_ARB_vertex_buffer_object])
return false;
#if defined(GL_ARB_vertex_buffer_object)
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void* vertices=mb->getVertices();
const u32 vertexCount=mb->getVertexCount();
const E_VERTEX_TYPE vType=mb->getVertexType();
const u32 vertexSize = getVertexPitchFromType(vType);
//buffer vertex data, and convert colours...
core::array<c8> buffer(vertexSize * vertexCount);
memcpy(buffer.pointer(), vertices, vertexSize * vertexCount);
// in order to convert the colors into opengl format (RGBA)
switch (vType)
{
case EVT_STANDARD:
{
S3DVertex* pb = reinterpret_cast<S3DVertex*>(buffer.pointer());
const S3DVertex* po = static_cast<const S3DVertex*>(vertices);
for (u32 i=0; i<vertexCount; i++)
{
po[i].Color.toOpenGLColor((u8*)&(pb[i].Color.color));
}
}
break;
case EVT_2TCOORDS:
{
S3DVertex2TCoords* pb = reinterpret_cast<S3DVertex2TCoords*>(buffer.pointer());
const S3DVertex2TCoords* po = static_cast<const S3DVertex2TCoords*>(vertices);
for (u32 i=0; i<vertexCount; i++)
{
po[i].Color.toOpenGLColor((u8*)&(pb[i].Color.color));
}
}
break;
case EVT_TANGENTS:
{
S3DVertexTangents* pb = reinterpret_cast<S3DVertexTangents*>(buffer.pointer());
const S3DVertexTangents* po = static_cast<const S3DVertexTangents*>(vertices);
for (u32 i=0; i<vertexCount; i++)
{
po[i].Color.toOpenGLColor((u8*)&(pb[i].Color.color));
}
}
break;
default:
{
return false;
}
}
//get or create buffer
bool newBuffer=false;
if (!HWBuffer->vbo_verticesID)
{
extGlGenBuffers(1, &HWBuffer->vbo_verticesID);
if (!HWBuffer->vbo_verticesID) return false;
newBuffer=true;
}
else if (HWBuffer->vbo_verticesSize < vertexCount*vertexSize)
{
newBuffer=true;
}
extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID );
//copy data to graphics card
glGetError(); // clear error storage
if (!newBuffer)
extGlBufferSubData(GL_ARRAY_BUFFER, 0, vertexCount * vertexSize, buffer.const_pointer());
else
{
HWBuffer->vbo_verticesSize = vertexCount*vertexSize;
if (HWBuffer->Mapped_Vertex==scene::EHM_STATIC)
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_STATIC_DRAW);
else if (HWBuffer->Mapped_Vertex==scene::EHM_DYNAMIC)
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_DYNAMIC_DRAW);
else //scene::EHM_STREAM
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, buffer.const_pointer(), GL_STREAM_DRAW);
}
extGlBindBuffer(GL_ARRAY_BUFFER, 0);
return (glGetError() == GL_NO_ERROR);
#else
return false;
#endif
}
bool COpenGLDriver::updateIndexHardwareBuffer(SHWBufferLink_opengl *HWBuffer)
{
if (!HWBuffer)
return false;
if(!FeatureAvailable[IRR_ARB_vertex_buffer_object])
return false;
#if defined(GL_ARB_vertex_buffer_object)
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void* indices=mb->getIndices();
u32 indexCount= mb->getIndexCount();
GLenum indexSize;
switch (mb->getIndexType())
{
case (EIT_16BIT):
{
indexSize=sizeof(u16);
break;
}
case (EIT_32BIT):
{
indexSize=sizeof(u32);
break;
}
default:
{
return false;
}
}
//get or create buffer
bool newBuffer=false;
if (!HWBuffer->vbo_indicesID)
{
extGlGenBuffers(1, &HWBuffer->vbo_indicesID);
if (!HWBuffer->vbo_indicesID) return false;
newBuffer=true;
}
else if (HWBuffer->vbo_indicesSize < indexCount*indexSize)
{
newBuffer=true;
}
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID);
//copy data to graphics card
glGetError(); // clear error storage
if (!newBuffer)
extGlBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, indexCount * indexSize, indices);
else
{
HWBuffer->vbo_indicesSize = indexCount*indexSize;
if (HWBuffer->Mapped_Index==scene::EHM_STATIC)
extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STATIC_DRAW);
else if (HWBuffer->Mapped_Index==scene::EHM_DYNAMIC)
extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_DYNAMIC_DRAW);
else //scene::EHM_STREAM
extGlBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * indexSize, indices, GL_STREAM_DRAW);
}
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
return (glGetError() == GL_NO_ERROR);
#else
return false;
#endif
}
//! updates hardware buffer if needed
bool COpenGLDriver::updateHardwareBuffer(SHWBufferLink *HWBuffer)
{
if (!HWBuffer)
return false;
if (HWBuffer->Mapped_Vertex!=scene::EHM_NEVER)
{
if (HWBuffer->ChangedID_Vertex != HWBuffer->MeshBuffer->getChangedID_Vertex()
|| !((SHWBufferLink_opengl*)HWBuffer)->vbo_verticesID)
{
HWBuffer->ChangedID_Vertex = HWBuffer->MeshBuffer->getChangedID_Vertex();
if (!updateVertexHardwareBuffer((SHWBufferLink_opengl*)HWBuffer))
return false;
}
}
if (HWBuffer->Mapped_Index!=scene::EHM_NEVER)
{
if (HWBuffer->ChangedID_Index != HWBuffer->MeshBuffer->getChangedID_Index()
|| !((SHWBufferLink_opengl*)HWBuffer)->vbo_indicesID)
{
HWBuffer->ChangedID_Index = HWBuffer->MeshBuffer->getChangedID_Index();
if (!updateIndexHardwareBuffer((SHWBufferLink_opengl*)HWBuffer))
return false;
}
}
return true;
}
//! Create hardware buffer from meshbuffer
COpenGLDriver::SHWBufferLink *COpenGLDriver::createHardwareBuffer(const scene::IMeshBuffer* mb)
{
#if defined(GL_ARB_vertex_buffer_object)
if (!mb || (mb->getHardwareMappingHint_Index()==scene::EHM_NEVER && mb->getHardwareMappingHint_Vertex()==scene::EHM_NEVER))
return 0;
SHWBufferLink_opengl *HWBuffer=new SHWBufferLink_opengl(mb);
//add to map
HWBufferMap.insert(HWBuffer->MeshBuffer, HWBuffer);
HWBuffer->ChangedID_Vertex=HWBuffer->MeshBuffer->getChangedID_Vertex();
HWBuffer->ChangedID_Index=HWBuffer->MeshBuffer->getChangedID_Index();
HWBuffer->Mapped_Vertex=mb->getHardwareMappingHint_Vertex();
HWBuffer->Mapped_Index=mb->getHardwareMappingHint_Index();
HWBuffer->LastUsed=0;
HWBuffer->vbo_verticesID=0;
HWBuffer->vbo_indicesID=0;
HWBuffer->vbo_verticesSize=0;
HWBuffer->vbo_indicesSize=0;
if (!updateHardwareBuffer(HWBuffer))
{
deleteHardwareBuffer(HWBuffer);
return 0;
}
return HWBuffer;
#else
return 0;
#endif
}
void COpenGLDriver::deleteHardwareBuffer(SHWBufferLink *_HWBuffer)
{
if (!_HWBuffer) return;
#if defined(GL_ARB_vertex_buffer_object)
SHWBufferLink_opengl *HWBuffer=(SHWBufferLink_opengl*)_HWBuffer;
if (HWBuffer->vbo_verticesID)
{
extGlDeleteBuffers(1, &HWBuffer->vbo_verticesID);
HWBuffer->vbo_verticesID=0;
}
if (HWBuffer->vbo_indicesID)
{
extGlDeleteBuffers(1, &HWBuffer->vbo_indicesID);
HWBuffer->vbo_indicesID=0;
}
#endif
CNullDriver::deleteHardwareBuffer(_HWBuffer);
}
//! Draw hardware buffer
void COpenGLDriver::drawHardwareBuffer(SHWBufferLink *_HWBuffer)
{
if (!_HWBuffer)
return;
SHWBufferLink_opengl *HWBuffer=(SHWBufferLink_opengl*)_HWBuffer;
updateHardwareBuffer(HWBuffer); //check if update is needed
HWBuffer->LastUsed=0;//reset count
#if defined(GL_ARB_vertex_buffer_object)
const scene::IMeshBuffer* mb = HWBuffer->MeshBuffer;
const void *vertices=mb->getVertices();
const void *indexList=mb->getIndices();
if (HWBuffer->Mapped_Vertex!=scene::EHM_NEVER)
{
extGlBindBuffer(GL_ARRAY_BUFFER, HWBuffer->vbo_verticesID);
vertices=0;
}
if (HWBuffer->Mapped_Index!=scene::EHM_NEVER)
{
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, HWBuffer->vbo_indicesID);
indexList=0;
}
drawVertexPrimitiveList(vertices, mb->getVertexCount(), indexList, mb->getIndexCount()/3, mb->getVertexType(), scene::EPT_TRIANGLES, mb->getIndexType());
if (HWBuffer->Mapped_Vertex!=scene::EHM_NEVER)
extGlBindBuffer(GL_ARRAY_BUFFER, 0);
if (HWBuffer->Mapped_Index!=scene::EHM_NEVER)
extGlBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
#endif
}
// small helper function to create vertex buffer object adress offsets
static inline u8* buffer_offset(const long offset)
{
return ((u8*)0 + offset);
}
//! draws a vertex primitive list
void COpenGLDriver::drawVertexPrimitiveList(const void* vertices, u32 vertexCount,
const void* indexList, u32 primitiveCount,
E_VERTEX_TYPE vType, scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
{
if (!primitiveCount || !vertexCount)
return;
if (!checkPrimitiveCount(primitiveCount))
return;
CNullDriver::drawVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType);
if (vertices)
{
// convert colors to gl color format.
vertexCount *= 4; //reused as color component count
ColorBuffer.set_used(vertexCount);
u32 i;
switch (vType)
{
case EVT_STANDARD:
{
const S3DVertex* p = static_cast<const S3DVertex*>(vertices);
for ( i=0; i<vertexCount; i+=4)
{
p->Color.toOpenGLColor(&ColorBuffer[i]);
++p;
}
}
break;
case EVT_2TCOORDS:
{
const S3DVertex2TCoords* p = static_cast<const S3DVertex2TCoords*>(vertices);
for ( i=0; i<vertexCount; i+=4)
{
p->Color.toOpenGLColor(&ColorBuffer[i]);
++p;
}
}
break;
case EVT_TANGENTS:
{
const S3DVertexTangents* p = static_cast<const S3DVertexTangents*>(vertices);
for ( i=0; i<vertexCount; i+=4)
{
p->Color.toOpenGLColor(&ColorBuffer[i]);
++p;
}
}
break;
}
}
// draw everything
setRenderStates3DMode();
if (MultiTextureExtension)
extGlClientActiveTexture(GL_TEXTURE0_ARB);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);
if ((pType!=scene::EPT_POINTS) && (pType!=scene::EPT_POINT_SPRITES))
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if ((pType!=scene::EPT_POINTS) && (pType!=scene::EPT_POINT_SPRITES))
glEnableClientState(GL_NORMAL_ARRAY);
if (vertices)
glColorPointer(4, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]);
switch (vType)
{
case EVT_STANDARD:
if (vertices)
{
glNormalPointer(GL_FLOAT, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].Normal);
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].TCoords);
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].Pos);
}
else
{
glNormalPointer(GL_FLOAT, sizeof(S3DVertex), buffer_offset(12));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24));
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28));
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex), 0);
}
if (MultiTextureExtension && CurrentTexture[1])
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (vertices)
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].TCoords);
else
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28));
}
break;
case EVT_2TCOORDS:
if (vertices)
{
glNormalPointer(GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].Normal);
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].TCoords);
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].Pos);
}
else
{
glNormalPointer(GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(12));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24));
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(28));
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(0));
}
if (MultiTextureExtension)
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (vertices)
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].TCoords2);
else
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(36));
}
break;
case EVT_TANGENTS:
if (vertices)
{
glNormalPointer(GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Normal);
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].TCoords);
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Pos);
}
else
{
glNormalPointer(GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(12));
glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24));
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(28));
glVertexPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(0));
}
if (MultiTextureExtension)
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
if (vertices)
glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Tangent);
else
glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(36));
extGlClientActiveTexture(GL_TEXTURE2_ARB);
glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
if (vertices)
glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Binormal);
else
glTexCoordPointer(3, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(48));
}
break;
}
GLenum indexSize=0;
switch (iType)
{
case (EIT_16BIT):
{
indexSize=GL_UNSIGNED_SHORT;
break;
}
case (EIT_32BIT):
{
indexSize=GL_UNSIGNED_INT;
break;
}
}
switch (pType)
{
case scene::EPT_POINTS:
case scene::EPT_POINT_SPRITES:
{
#ifdef GL_ARB_point_sprite
if (pType==scene::EPT_POINT_SPRITES && FeatureAvailable[IRR_ARB_point_sprite])
glEnable(GL_POINT_SPRITE_ARB);
#endif
float quadratic[] = {0.0f, 0.0f, 10.01f};
extGlPointParameterfv(GL_POINT_DISTANCE_ATTENUATION_ARB, quadratic);
float maxParticleSize=1.0f;
glGetFloatv(GL_POINT_SIZE_MAX_ARB, &maxParticleSize);
// maxParticleSize=maxParticleSize<Material.Thickness?maxParticleSize:Material.Thickness;
// extGlPointParameterf(GL_POINT_SIZE_MAX_ARB,maxParticleSize);
// extGlPointParameterf(GL_POINT_SIZE_MIN_ARB,Material.Thickness);
extGlPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 60.0f);
glPointSize(Material.Thickness);
#ifdef GL_ARB_point_sprite
if (pType==scene::EPT_POINT_SPRITES && FeatureAvailable[IRR_ARB_point_sprite])
glTexEnvf(GL_POINT_SPRITE_ARB,GL_COORD_REPLACE, GL_TRUE);
#endif
glDrawArrays(GL_POINTS, 0, primitiveCount);
#ifdef GL_ARB_point_sprite
if (pType==scene::EPT_POINT_SPRITES && FeatureAvailable[IRR_ARB_point_sprite])
{
glDisable(GL_POINT_SPRITE_ARB);
glTexEnvf(GL_POINT_SPRITE_ARB,GL_COORD_REPLACE, GL_FALSE);
}
#endif
}
break;
case scene::EPT_LINE_STRIP:
glDrawElements(GL_LINE_STRIP, primitiveCount+1, indexSize, indexList);
break;
case scene::EPT_LINE_LOOP:
glDrawElements(GL_LINE_LOOP, primitiveCount, indexSize, indexList);
break;
case scene::EPT_LINES:
glDrawElements(GL_LINES, primitiveCount*2, indexSize, indexList);
break;
case scene::EPT_TRIANGLE_STRIP:
glDrawElements(GL_TRIANGLE_STRIP, primitiveCount+2, indexSize, indexList);
break;
case scene::EPT_TRIANGLE_FAN:
glDrawElements(GL_TRIANGLE_FAN, primitiveCount+2, indexSize, indexList);
break;
case scene::EPT_TRIANGLES:
glDrawElements(GL_TRIANGLES, primitiveCount*3, indexSize, indexList);
break;
case scene::EPT_QUAD_STRIP:
glDrawElements(GL_QUAD_STRIP, primitiveCount*2+2, indexSize, indexList);
break;
case scene::EPT_QUADS:
glDrawElements(GL_QUADS, primitiveCount*4, indexSize, indexList);
break;
case scene::EPT_POLYGON:
glDrawElements(GL_POLYGON, primitiveCount, indexSize, indexList);
break;
}
if (MultiTextureExtension)
{
if (vType==EVT_TANGENTS)
{
extGlClientActiveTexture(GL_TEXTURE2_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
if ((vType!=EVT_STANDARD) || CurrentTexture[1])
{
extGlClientActiveTexture(GL_TEXTURE1_ARB);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
extGlClientActiveTexture(GL_TEXTURE0_ARB);
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
void COpenGLDriver::draw2DPolygon(const core::array<core::vector2df> &vertices,
const core::array<video::SColor> *colors,
const video::ITexture *texture,
bool useAlphaChannelOfTexture,
const core::array<core::vector2di> *coordinates)
{
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture && texture->isRenderTarget();
core::array<core::vector2df> tex_coordinates;
if(texture)
{
const core::dimension2d<s32>& ss = texture->getOriginalSize();
const f32 height = static_cast<f32>(ss.Height);
const f32 invW = 1.f / static_cast<f32>(ss.Width);
const f32 invH = 1.f / static_cast<f32>(ss.Height);
for(u32 i=0; i<(*coordinates).size(); i++)
{
core::vector2df t( (*coordinates)[i].X * invW,
(isRTT?height-(*coordinates)[i].Y:(*coordinates)[i].Y)*invH);
tex_coordinates.push_back(t);
}
} // if texture
disableTextures(1);
if(texture)
setTexture(0, texture);
bool doAlpha = colors!=NULL;
setRenderStates2DMode(doAlpha, texture!=NULL, useAlphaChannelOfTexture);
// Some obvious optimisation.
if(vertices.size()==3)
glBegin(GL_TRIANGLES);
else if(vertices.size()==4)
glBegin(GL_QUADS);
else
glBegin(GL_POLYGON);
for (u32 i=0; i<vertices.size(); i++)
{
if(colors)
{
const video::SColor &c = i<colors->size() ? (*colors)[i] : (*colors)[0];
glColor4ub(c.getRed(), c.getGreen(), c.getBlue(), c.getAlpha());
}
if(texture)
glTexCoord2f(tex_coordinates[i].X, tex_coordinates[i].Y);
glVertex2f(GLfloat(vertices[i].X), GLfloat(vertices[i].Y));
}
glEnd();
} // draw2DPolygon
//! draws a 2d image, using a color and the alpha channel of the texture if
//! desired. The image is drawn at pos, clipped against clipRect (if != 0).
//! Only the subtexture defined by sourceRect is used.
void COpenGLDriver::draw2DImage(const video::ITexture* texture,
const core::position2d<s32>& pos,
const core::rect<s32>& sourceRect,
const core::rect<s32>* clipRect, SColor color,
bool useAlphaChannelOfTexture)
{
if (!texture)
return;
if (!sourceRect.isValid())
return;
core::position2d<s32> targetPos(pos);
core::position2d<s32> sourcePos(sourceRect.UpperLeftCorner);
core::dimension2d<s32> sourceSize(sourceRect.getSize());
if (clipRect)
{
if (targetPos.X < clipRect->UpperLeftCorner.X)
{
sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X;
if (sourceSize.Width <= 0)
return;
sourcePos.X -= targetPos.X - clipRect->UpperLeftCorner.X;
targetPos.X = clipRect->UpperLeftCorner.X;
}
if (targetPos.X + sourceSize.Width > clipRect->LowerRightCorner.X)
{
sourceSize.Width -= (targetPos.X + sourceSize.Width) - clipRect->LowerRightCorner.X;
if (sourceSize.Width <= 0)
return;
}
if (targetPos.Y < clipRect->UpperLeftCorner.Y)
{
sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y;
if (sourceSize.Height <= 0)
return;
sourcePos.Y -= targetPos.Y - clipRect->UpperLeftCorner.Y;
targetPos.Y = clipRect->UpperLeftCorner.Y;
}
if (targetPos.Y + sourceSize.Height > clipRect->LowerRightCorner.Y)
{
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - clipRect->LowerRightCorner.Y;
if (sourceSize.Height <= 0)
return;
}
}
// clip these coordinates
if (targetPos.X<0)
{
sourceSize.Width += targetPos.X;
if (sourceSize.Width <= 0)
return;
sourcePos.X -= targetPos.X;
targetPos.X = 0;
}
const core::dimension2d<s32>& renderTargetSize = getCurrentRenderTargetSize();
if (targetPos.X + sourceSize.Width > renderTargetSize.Width)
{
sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width;
if (sourceSize.Width <= 0)
return;
}
if (targetPos.Y<0)
{
sourceSize.Height += targetPos.Y;
if (sourceSize.Height <= 0)
return;
sourcePos.Y -= targetPos.Y;
targetPos.Y = 0;
}
if (targetPos.Y + sourceSize.Height > renderTargetSize.Height)
{
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height;
if (sourceSize.Height <= 0)
return;
}
// ok, we've clipped everything.
// now draw it.
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture->isRenderTarget();
const core::dimension2d<s32>& ss = texture->getOriginalSize();
const f32 invW = 1.f / static_cast<f32>(ss.Width);
const f32 invH = 1.f / static_cast<f32>(ss.Height);
const core::rect<f32> tcoords(
sourcePos.X * invW,
(isRTT?(sourcePos.Y + sourceSize.Height):sourcePos.Y) * invH,
(sourcePos.X + sourceSize.Width) * invW,
(isRTT?sourcePos.Y:(sourcePos.Y + sourceSize.Height)) * invH);
const core::rect<s32> poss(targetPos, sourceSize);
disableTextures(1);
if (!setTexture(0, texture))
return;
setRenderStates2DMode(color.getAlpha()<255, true, useAlphaChannelOfTexture);
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
glBegin(GL_QUADS);
glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.UpperLeftCorner.Y));
glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.UpperLeftCorner.Y));
glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.LowerRightCorner.Y));
glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.LowerRightCorner.Y));
glEnd();
}
//! The same, but with a four element array of colors, one for each vertex
void COpenGLDriver::draw2DImage(const video::ITexture* texture, const core::rect<s32>& destRect,
const core::rect<s32>& sourceRect, const core::rect<s32>* clipRect,
const video::SColor* const colors, bool useAlphaChannelOfTexture)
{
if (!texture)
return;
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture->isRenderTarget();
const core::dimension2d<s32>& ss = texture->getOriginalSize();
const f32 invW = 1.f / static_cast<f32>(ss.Width);
const f32 invH = 1.f / static_cast<f32>(ss.Height);
const core::rect<f32> tcoords(
sourceRect.UpperLeftCorner.X * invW,
(isRTT?sourceRect.LowerRightCorner.Y:sourceRect.UpperLeftCorner.Y) * invH,
sourceRect.LowerRightCorner.X * invW,
(isRTT?sourceRect.UpperLeftCorner.Y:sourceRect.LowerRightCorner.Y) *invH);
const video::SColor temp[4] =
{
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF,
0xFFFFFFFF
};
const video::SColor* const useColor = colors ? colors : temp;
disableTextures(1);
setTexture(0, texture);
setRenderStates2DMode(useColor[0].getAlpha()<255 || useColor[1].getAlpha()<255 ||
useColor[2].getAlpha()<255 || useColor[3].getAlpha()<255,
true, useAlphaChannelOfTexture);
if (clipRect)
{
if (!clipRect->isValid())
return;
glEnable(GL_SCISSOR_TEST);
const core::dimension2d<s32>& renderTargetSize = getCurrentRenderTargetSize();
glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height-clipRect->LowerRightCorner.Y,
clipRect->getWidth(), clipRect->getHeight());
}
glBegin(GL_QUADS);
glColor4ub(useColor[0].getRed(), useColor[0].getGreen(), useColor[0].getBlue(), useColor[0].getAlpha());
glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
glVertex2f(GLfloat(destRect.UpperLeftCorner.X), GLfloat(destRect.UpperLeftCorner.Y));
glColor4ub(useColor[3].getRed(), useColor[3].getGreen(), useColor[3].getBlue(), useColor[3].getAlpha());
glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
glVertex2f(GLfloat(destRect.LowerRightCorner.X), GLfloat(destRect.UpperLeftCorner.Y));
glColor4ub(useColor[2].getRed(), useColor[2].getGreen(), useColor[2].getBlue(), useColor[2].getAlpha());
glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
glVertex2f(GLfloat(destRect.LowerRightCorner.X), GLfloat(destRect.LowerRightCorner.Y));
glColor4ub(useColor[1].getRed(), useColor[1].getGreen(), useColor[1].getBlue(), useColor[1].getAlpha());
glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
glVertex2f(GLfloat(destRect.UpperLeftCorner.X), GLfloat(destRect.LowerRightCorner.Y));
glEnd();
if (clipRect)
glDisable(GL_SCISSOR_TEST);
}
//! draws a set of 2d images, using a color and the alpha channel of the
//! texture if desired. The images are drawn beginning at pos and concatenated
//! in one line. All drawings are clipped against clipRect (if != 0).
//! The subtextures are defined by the array of sourceRects and are chosen
//! by the indices given.
void COpenGLDriver::draw2DImage(const video::ITexture* texture,
const core::position2d<s32>& pos,
const core::array<core::rect<s32> >& sourceRects,
const core::array<s32>& indices,
const core::rect<s32>* clipRect, SColor color,
bool useAlphaChannelOfTexture)
{
if (!texture)
return;
disableTextures(1);
if (!setTexture(0, texture))
return;
setRenderStates2DMode(color.getAlpha()<255, true, useAlphaChannelOfTexture);
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
if (clipRect)
{
if (!clipRect->isValid())
return;
glEnable(GL_SCISSOR_TEST);
const core::dimension2d<s32>& renderTargetSize = getCurrentRenderTargetSize();
glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height-clipRect->LowerRightCorner.Y,
clipRect->getWidth(),clipRect->getHeight());
}
const core::dimension2d<s32>& ss = texture->getOriginalSize();
core::position2d<s32> targetPos(pos);
// texcoords need to be flipped horizontally for RTTs
const bool isRTT = texture->isRenderTarget();
const f32 invW = 1.f / static_cast<f32>(ss.Width);
const f32 invH = 1.f / static_cast<f32>(ss.Height);
for (u32 i=0; i<indices.size(); ++i)
{
const s32 currentIndex = indices[i];
if (!sourceRects[currentIndex].isValid())
break;
const core::rect<f32> tcoords(
sourceRects[currentIndex].UpperLeftCorner.X * invW,
(isRTT?sourceRects[currentIndex].LowerRightCorner.Y:sourceRects[currentIndex].UpperLeftCorner.Y) * invH,
sourceRects[currentIndex].LowerRightCorner.X * invW,
(isRTT?sourceRects[currentIndex].UpperLeftCorner.Y:sourceRects[currentIndex].LowerRightCorner.Y) * invH);
const core::rect<s32> poss(targetPos, sourceRects[currentIndex].getSize());
glBegin(GL_QUADS);
glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.UpperLeftCorner.Y);
glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.UpperLeftCorner.Y));
glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.UpperLeftCorner.Y);
glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.UpperLeftCorner.Y));
glTexCoord2f(tcoords.LowerRightCorner.X, tcoords.LowerRightCorner.Y);
glVertex2f(GLfloat(poss.LowerRightCorner.X), GLfloat(poss.LowerRightCorner.Y));
glTexCoord2f(tcoords.UpperLeftCorner.X, tcoords.LowerRightCorner.Y);
glVertex2f(GLfloat(poss.UpperLeftCorner.X), GLfloat(poss.LowerRightCorner.Y));
glEnd();
targetPos.X += sourceRects[currentIndex].getWidth();
}
if (clipRect)
glDisable(GL_SCISSOR_TEST);
}
//! draw a 2d rectangle
void COpenGLDriver::draw2DRectangle(SColor color, const core::rect<s32>& position,
const core::rect<s32>* clip)
{
disableTextures();
setRenderStates2DMode(color.getAlpha() < 255, false, false);
core::rect<s32> pos = position;
if (clip)
pos.clipAgainst(*clip);
if (!pos.isValid())
return;
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
glRectf(GLfloat(pos.UpperLeftCorner.X), GLfloat(pos.UpperLeftCorner.Y),
GLfloat(pos.LowerRightCorner.X), GLfloat(pos.LowerRightCorner.Y));
}
//! draw an 2d rectangle
void COpenGLDriver::draw2DRectangle(const core::rect<s32>& position,
SColor colorLeftUp, SColor colorRightUp, SColor colorLeftDown, SColor colorRightDown,
const core::rect<s32>* clip)
{
core::rect<s32> pos = position;
if (clip)
pos.clipAgainst(*clip);
if (!pos.isValid())
return;
disableTextures();
setRenderStates2DMode(colorLeftUp.getAlpha() < 255 ||
colorRightUp.getAlpha() < 255 ||
colorLeftDown.getAlpha() < 255 ||
colorRightDown.getAlpha() < 255, false, false);
glBegin(GL_QUADS);
glColor4ub(colorLeftUp.getRed(), colorLeftUp.getGreen(),
colorLeftUp.getBlue(), colorLeftUp.getAlpha());
glVertex2f(GLfloat(pos.UpperLeftCorner.X), GLfloat(pos.UpperLeftCorner.Y));
glColor4ub(colorRightUp.getRed(), colorRightUp.getGreen(),
colorRightUp.getBlue(), colorRightUp.getAlpha());
glVertex2f(GLfloat(pos.LowerRightCorner.X), GLfloat(pos.UpperLeftCorner.Y));
glColor4ub(colorRightDown.getRed(), colorRightDown.getGreen(),
colorRightDown.getBlue(), colorRightDown.getAlpha());
glVertex2f(GLfloat(pos.LowerRightCorner.X), GLfloat(pos.LowerRightCorner.Y));
glColor4ub(colorLeftDown.getRed(), colorLeftDown.getGreen(),
colorLeftDown.getBlue(), colorLeftDown.getAlpha());
glVertex2f(GLfloat(pos.UpperLeftCorner.X), GLfloat(pos.LowerRightCorner.Y));
glEnd();
}
//! Draws a 2d line.
void COpenGLDriver::draw2DLine(const core::position2d<s32>& start,
const core::position2d<s32>& end,
SColor color)
{
disableTextures();
setRenderStates2DMode(color.getAlpha() < 255, false, false);
glBegin(GL_LINES);
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
glVertex2f(GLfloat(start.X), GLfloat(start.Y));
glVertex2f(GLfloat(end.X), GLfloat(end.Y));
glEnd();
}
//! Draws a pixel
void COpenGLDriver::drawPixel(u32 x, u32 y, const SColor &color)
{
const core::dimension2d<s32>& renderTargetSize = getCurrentRenderTargetSize();
if(x > (u32)renderTargetSize.Width || y > (u32)renderTargetSize.Height)
return;
disableTextures();
setRenderStates2DMode(color.getAlpha() < 255, false, false);
glBegin(GL_POINTS);
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
glVertex2i(x, y);
glEnd();
}
bool COpenGLDriver::setTexture(u32 stage, const video::ITexture* texture)
{
if (stage >= MaxTextureUnits)
return false;
if (CurrentTexture[stage]==texture)
return true;
if (MultiTextureExtension)
extGlActiveTexture(GL_TEXTURE0_ARB + stage);
CurrentTexture[stage]=texture;
if (!texture)
{
glDisable(GL_TEXTURE_2D);
return true;
}
else
{
if (texture->getDriverType() != EDT_OPENGL)
{
glDisable(GL_TEXTURE_2D);
os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR);
return false;
}
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,
static_cast<const COpenGLTexture*>(texture)->getOpenGLTextureName());
}
return true;
}
//! disables all textures beginning with the optional fromStage parameter. Otherwise all texture stages are disabled.
//! Returns whether disabling was successful or not.
bool COpenGLDriver::disableTextures(u32 fromStage)
{
bool result=true;
for (u32 i=fromStage; i<MaxTextureUnits; ++i)
result &= setTexture(i, 0);
return result;
}
//! creates a matrix in supplied GLfloat array to pass to OpenGL
inline void COpenGLDriver::createGLMatrix(GLfloat gl_matrix[16], const core::matrix4& m)
{
memcpy(gl_matrix, m.pointer(), 16 * sizeof(f32));
}
//! creates a opengltexturematrix from a D3D style texture matrix
inline void COpenGLDriver::createGLTextureMatrix(GLfloat *o, const core::matrix4& m)
{
o[0] = m[0];
o[1] = m[1];
o[2] = 0.f;
o[3] = 0.f;
o[4] = m[4];
o[5] = m[5];
o[6] = 0.f;
o[7] = 0.f;
o[8] = 0.f;
o[9] = 0.f;
o[10] = 1.f;
o[11] = 0.f;
o[12] = m[8];
o[13] = m[9];
o[14] = 0.f;
o[15] = 1.f;
}
//! returns a device dependent texture from a software surface (IImage)
video::ITexture* COpenGLDriver::createDeviceDependentTexture(IImage* surface, const char* name)
{
return new COpenGLTexture(surface, name, this);
}
//! Sets a material. All 3d drawing functions draw geometry now
//! using this material.
//! \param material: Material to be used from now on.
void COpenGLDriver::setMaterial(const SMaterial& material)
{
Material = material;
for (s32 i = MaxTextureUnits-1; i>= 0; --i)
{
setTransform ((E_TRANSFORMATION_STATE) ( ETS_TEXTURE_0 + i ),
material.getTextureMatrix(i));
}
}
//! prints error if an error happened.
bool COpenGLDriver::testGLError()
{
#ifdef _DEBUG
GLenum g = glGetError();
switch(g)
{
case GL_NO_ERROR:
return false;
case GL_INVALID_ENUM:
os::Printer::log("GL_INVALID_ENUM", ELL_ERROR); break;
case GL_INVALID_VALUE:
os::Printer::log("GL_INVALID_VALUE", ELL_ERROR); break;
case GL_INVALID_OPERATION:
os::Printer::log("GL_INVALID_OPERATION", ELL_ERROR); break;
case GL_STACK_OVERFLOW:
os::Printer::log("GL_STACK_OVERFLOW", ELL_ERROR); break;
case GL_STACK_UNDERFLOW:
os::Printer::log("GL_STACK_UNDERFLOW", ELL_ERROR); break;
case GL_OUT_OF_MEMORY:
os::Printer::log("GL_OUT_OF_MEMORY", ELL_ERROR); break;
case GL_TABLE_TOO_LARGE:
os::Printer::log("GL_TABLE_TOO_LARGE", ELL_ERROR); break;
#if defined(GL_EXT_framebuffer_object)
case GL_INVALID_FRAMEBUFFER_OPERATION_EXT:
os::Printer::log("GL_INVALID_FRAMEBUFFER_OPERATION", ELL_ERROR); break;
#endif
};
return true;
#else
return false;
#endif
}
//! sets the needed renderstates
void COpenGLDriver::setRenderStates3DMode()
{
if (CurrentRenderMode != ERM_3D)
{
// Reset Texture Stages
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_COLOR);
// switch back the matrices
glMatrixMode(GL_MODELVIEW);
glLoadMatrixf((Matrices[ETS_VIEW] * Matrices[ETS_WORLD]).pointer());
GLfloat glmat[16];
createGLMatrix(glmat, Matrices[ETS_PROJECTION]);
glmat[12] *= -1.0f;
glMatrixMode(GL_PROJECTION);
glLoadMatrixf(glmat);
ResetRenderStates = true;
}
if ( ResetRenderStates || LastMaterial != Material)
{
// unset old material
if (LastMaterial.MaterialType != Material.MaterialType &&
static_cast<u32>(LastMaterial.MaterialType) < MaterialRenderers.size())
MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial();
// set new material.
if (static_cast<u32>(Material.MaterialType) < MaterialRenderers.size())
MaterialRenderers[Material.MaterialType].Renderer->OnSetMaterial(
Material, LastMaterial, ResetRenderStates, this);
LastMaterial = Material;
ResetRenderStates = false;
}
if (static_cast<u32>(Material.MaterialType) < MaterialRenderers.size())
MaterialRenderers[Material.MaterialType].Renderer->OnRender(this, video::EVT_STANDARD);
CurrentRenderMode = ERM_3D;
}
void COpenGLDriver::setWrapMode(const SMaterial& material)
{
// texture address mode
// Has to be checked always because it depends on the textures
for (u32 u=0; u<MaxTextureUnits; ++u)
{
if (MultiTextureExtension)
extGlActiveTexture(GL_TEXTURE0_ARB + u);
else if (u>0)
break; // stop loop
GLint mode=GL_REPEAT;
switch (material.TextureLayer[u].TextureWrap)
{
case ETC_REPEAT:
mode=GL_REPEAT;
break;
case ETC_CLAMP:
mode=GL_CLAMP;
break;
case ETC_CLAMP_TO_EDGE:
#ifdef GL_VERSION_1_2
if (Version>101)
mode=GL_CLAMP_TO_EDGE;
else
#endif
#ifdef GL_SGIS_texture_edge_clamp
if (FeatureAvailable[IRR_SGIS_texture_edge_clamp])
mode=GL_CLAMP_TO_EDGE_SGIS;
else
#endif
// fallback
mode=GL_CLAMP;
break;
case ETC_CLAMP_TO_BORDER:
#ifdef GL_VERSION_1_3
if (Version>102)
mode=GL_CLAMP_TO_BORDER;
else
#endif
#ifdef GL_ARB_texture_border_clamp
if (FeatureAvailable[IRR_ARB_texture_border_clamp])
mode=GL_CLAMP_TO_BORDER_ARB;
else
#endif
#ifdef GL_SGIS_texture_border_clamp
if (FeatureAvailable[IRR_SGIS_texture_border_clamp])
mode=GL_CLAMP_TO_BORDER_SGIS;
else
#endif
// fallback
mode=GL_CLAMP;
break;
case ETC_MIRROR:
#ifdef GL_VERSION_1_4
if (Version>103)
mode=GL_MIRRORED_REPEAT;
else
#endif
#ifdef GL_ARB_texture_border_clamp
if (FeatureAvailable[IRR_ARB_texture_mirrored_repeat])
mode=GL_MIRRORED_REPEAT_ARB;
else
#endif
#ifdef GL_IBM_texture_mirrored_repeat
if (FeatureAvailable[IRR_IBM_texture_mirrored_repeat])
mode=GL_MIRRORED_REPEAT_IBM;
else
#endif
mode=GL_REPEAT;
break;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, mode);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, mode);
}
}
//! Can be called by an IMaterialRenderer to make its work easier.
void COpenGLDriver::setBasicRenderStates(const SMaterial& material, const SMaterial& lastmaterial,
bool resetAllRenderStates)
{
if (resetAllRenderStates ||
lastmaterial.AmbientColor != material.AmbientColor ||
lastmaterial.DiffuseColor != material.DiffuseColor ||
lastmaterial.SpecularColor != material.SpecularColor ||
lastmaterial.EmissiveColor != material.EmissiveColor ||
lastmaterial.Shininess != material.Shininess)
{
GLfloat color[4];
const f32 inv = 1.0f / 255.0f;
color[0] = material.AmbientColor.getRed() * inv;
color[1] = material.AmbientColor.getGreen() * inv;
color[2] = material.AmbientColor.getBlue() * inv;
color[3] = material.AmbientColor.getAlpha() * inv;
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
color[0] = material.DiffuseColor.getRed() * inv;
color[1] = material.DiffuseColor.getGreen() * inv;
color[2] = material.DiffuseColor.getBlue() * inv;
color[3] = material.DiffuseColor.getAlpha() * inv;
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
// disable Specular colors if no shininess is set
if (material.Shininess != 0.0f)
{
#ifdef GL_EXT_separate_specular_color
if (FeatureAvailable[IRR_EXT_separate_specular_color])
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
#endif
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.Shininess);
color[0] = material.SpecularColor.getRed() * inv;
color[1] = material.SpecularColor.getGreen() * inv;
color[2] = material.SpecularColor.getBlue() * inv;
color[3] = material.SpecularColor.getAlpha() * inv;
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color);
}
#ifdef GL_EXT_separate_specular_color
else
if (FeatureAvailable[IRR_EXT_separate_specular_color])
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR);
#endif
color[0] = material.EmissiveColor.getRed() * inv;
color[1] = material.EmissiveColor.getGreen() * inv;
color[2] = material.EmissiveColor.getBlue() * inv;
color[3] = material.EmissiveColor.getAlpha() * inv;
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color);
}
// Texture filter
// Has to be checked always because it depends on the textures
// Filtering has to be set for each texture layer
for (u32 i=0; i<MaxTextureUnits; ++i)
{
if (MultiTextureExtension)
extGlActiveTexture(GL_TEXTURE0_ARB + i);
else if (i>0)
break;
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
(material.TextureLayer[i].BilinearFilter || material.TextureLayer[i].TrilinearFilter) ? GL_LINEAR : GL_NEAREST);
if (material.getTexture(i) && material.getTexture(i)->hasMipMaps())
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
material.TextureLayer[i].TrilinearFilter ? GL_LINEAR_MIPMAP_LINEAR :
material.TextureLayer[i].BilinearFilter ? GL_LINEAR_MIPMAP_NEAREST :
GL_NEAREST_MIPMAP_NEAREST );
else
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
(material.TextureLayer[i].BilinearFilter || material.TextureLayer[i].TrilinearFilter) ? GL_LINEAR : GL_NEAREST);
#ifdef GL_EXT_texture_filter_anisotropic
if (AnisotropyExtension)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
material.TextureLayer[i].AnisotropicFilter ? MaxAnisotropy : 1.0f );
#endif
}
// fillmode
if (resetAllRenderStates || (lastmaterial.Wireframe != material.Wireframe) || (lastmaterial.PointCloud != material.PointCloud))
glPolygonMode(GL_FRONT_AND_BACK, material.Wireframe ? GL_LINE : material.PointCloud? GL_POINT : GL_FILL);
// shademode
if (resetAllRenderStates || (lastmaterial.GouraudShading != material.GouraudShading))
{
if (material.GouraudShading)
glShadeModel(GL_SMOOTH);
else
glShadeModel(GL_FLAT);
}
// lighting
if (resetAllRenderStates || (lastmaterial.Lighting != material.Lighting))
{
if (material.Lighting)
glEnable(GL_LIGHTING);
else
glDisable(GL_LIGHTING);
}
// zbuffer
if (resetAllRenderStates || lastmaterial.ZBuffer != material.ZBuffer)
{
switch (material.ZBuffer)
{
case 0:
glDisable(GL_DEPTH_TEST);
break;
case 1:
glEnable(GL_DEPTH_TEST);
glDepthFunc ( GL_LEQUAL );
break;
case 2:
glEnable(GL_DEPTH_TEST);
glDepthFunc ( GL_EQUAL );
break;
}
}
// zwrite
// if (resetAllRenderStates || lastmaterial.ZWriteEnable != material.ZWriteEnable)
{
if (material.ZWriteEnable && (AllowZWriteOnTransparent || !material.isTransparent()))
{
glDepthMask(GL_TRUE);
}
else
glDepthMask(GL_FALSE);
}
// back face culling
if (resetAllRenderStates || (lastmaterial.FrontfaceCulling != material.FrontfaceCulling) || (lastmaterial.BackfaceCulling != material.BackfaceCulling))
{
if ((material.FrontfaceCulling) && (material.BackfaceCulling))
{
glCullFace(GL_FRONT_AND_BACK);
glEnable(GL_CULL_FACE);
}
else
if (material.BackfaceCulling)
{
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
}
else
if (material.FrontfaceCulling)
{
glCullFace(GL_FRONT);
glEnable(GL_CULL_FACE);
}
else
glDisable(GL_CULL_FACE);
}
// fog
if (resetAllRenderStates || lastmaterial.FogEnable != material.FogEnable)
{
if (material.FogEnable)
glEnable(GL_FOG);
else
glDisable(GL_FOG);
}
// normalization
if (resetAllRenderStates || lastmaterial.NormalizeNormals != material.NormalizeNormals)
{
if (material.NormalizeNormals)
glEnable(GL_NORMALIZE);
else
glDisable(GL_NORMALIZE);
}
// thickness
if (resetAllRenderStates || lastmaterial.Thickness != material.Thickness)
{
glPointSize(material.Thickness);
glLineWidth(material.Thickness);
}
setWrapMode(material);
// be sure to leave in texture stage 0
if (MultiTextureExtension)
extGlActiveTexture(GL_TEXTURE0_ARB);
}
//! sets the needed renderstates
void COpenGLDriver::setRenderStates2DMode(bool alpha, bool texture, bool alphaChannel)
{
if (CurrentRenderMode != ERM_2D || Transformation3DChanged)
{
// unset last 3d material
if (CurrentRenderMode == ERM_3D)
{
if (static_cast<u32>(LastMaterial.MaterialType) < MaterialRenderers.size())
MaterialRenderers[LastMaterial.MaterialType].Renderer->OnUnsetMaterial();
SMaterial mat;
mat.ZBuffer=0;
mat.Lighting=false;
mat.TextureLayer[0].BilinearFilter=false;
setBasicRenderStates(mat, mat, true);
LastMaterial = mat;
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
glMatrixMode(GL_PROJECTION);
const core::dimension2d<s32>& renderTargetSize = getCurrentRenderTargetSize();
core::matrix4 m;
m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-renderTargetSize.Height), -1.0, 1.0);
m.setTranslation(core::vector3df(-1,1,0));
glLoadMatrixf(m.pointer());
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(0.375, 0.375, 0.0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
Transformation3DChanged = false;
}
if (alphaChannel || alpha)
{
glEnable(GL_BLEND);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.f);
}
else
{
glDisable(GL_BLEND);
glDisable(GL_ALPHA_TEST);
}
if (texture)
{
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
if (alphaChannel)
{
// if alpha and alpha texture just modulate, otherwise use only the alpha channel
if (alpha)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
else
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_TEXTURE);
// rgb always modulates
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
}
}
else
{
if (alpha)
{
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_EXT, GL_REPLACE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_EXT, GL_PRIMARY_COLOR_EXT);
// rgb always modulates
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE);
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT);
}
else
{
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
}
}
}
CurrentRenderMode = ERM_2D;
}
//! \return Returns the name of the video driver.
const wchar_t* COpenGLDriver::getName() const
{
return Name.c_str();
}
//! deletes all dynamic lights there are
void COpenGLDriver::deleteAllDynamicLights()
{
for (s32 i=0; i<LastSetLight+1; ++i)
glDisable(GL_LIGHT0 + i);
LastSetLight = -1;
CNullDriver::deleteAllDynamicLights();
}
//! adds a dynamic light
void COpenGLDriver::addDynamicLight(const SLight& light)
{
if (LastSetLight == MaxLights-1)
return;
setTransform(ETS_WORLD, core::matrix4());
++LastSetLight;
CNullDriver::addDynamicLight(light);
s32 lidx = GL_LIGHT0 + LastSetLight;
GLfloat data[4];
switch (light.Type)
{
case video::ELT_SPOT:
data[0] = light.Direction.X;
data[1] = light.Direction.Y;
data[2] = light.Direction.Z;
data[3] = 0.0f;
glLightfv(lidx, GL_SPOT_DIRECTION, data);
// set position
data[0] = light.Position.X;
data[1] = light.Position.Y;
data[2] = light.Position.Z;
data[3] = 1.0f; // 1.0f for positional light
glLightfv(lidx, GL_POSITION, data);
glLightf(lidx, GL_SPOT_EXPONENT, light.Falloff);
glLightf(lidx, GL_SPOT_CUTOFF, light.OuterCone);
break;
case video::ELT_POINT:
// set position
data[0] = light.Position.X;
data[1] = light.Position.Y;
data[2] = light.Position.Z;
data[3] = 1.0f; // 1.0f for positional light
glLightfv(lidx, GL_POSITION, data);
glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);
glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);
break;
case video::ELT_DIRECTIONAL:
// set direction
data[0] = -light.Direction.X;
data[1] = -light.Direction.Y;
data[2] = -light.Direction.Z;
data[3] = 0.0f; // 0.0f for directional light
glLightfv(lidx, GL_POSITION, data);
glLightf(lidx, GL_SPOT_EXPONENT, 0.0f);
glLightf(lidx, GL_SPOT_CUTOFF, 180.0f);
break;
}
// set diffuse color
data[0] = light.DiffuseColor.r;
data[1] = light.DiffuseColor.g;
data[2] = light.DiffuseColor.b;
data[3] = light.DiffuseColor.a;
glLightfv(lidx, GL_DIFFUSE, data);
// set specular color
data[0] = light.SpecularColor.r;
data[1] = light.SpecularColor.g;
data[2] = light.SpecularColor.b;
data[3] = light.SpecularColor.a;
glLightfv(lidx, GL_SPECULAR, data);
// set ambient color
data[0] = light.AmbientColor.r;
data[1] = light.AmbientColor.g;
data[2] = light.AmbientColor.b;
data[3] = light.AmbientColor.a;
glLightfv(lidx, GL_AMBIENT, data);
// 1.0f / (constant + linear * d + quadratic*(d*d);
// set attenuation
glLightf(lidx, GL_CONSTANT_ATTENUATION, light.Attenuation.X);
glLightf(lidx, GL_LINEAR_ATTENUATION, light.Attenuation.Y);
glLightf(lidx, GL_QUADRATIC_ATTENUATION, light.Attenuation.Z);
glEnable(lidx);
}
//! returns the maximal amount of dynamic lights the device can handle
u32 COpenGLDriver::getMaximalDynamicLightAmount() const
{
return MaxLights;
}
//! Sets the dynamic ambient light color. The default color is
//! (0,0,0,0) which means it is dark.
//! \param color: New color of the ambient light.
void COpenGLDriver::setAmbientLight(const SColorf& color)
{
GLfloat data[4] = {color.r, color.g, color.b, color.a};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, data);
}
// this code was sent in by Oliver Klems, thank you! (I modified the glViewport
// method just a bit.
void COpenGLDriver::setViewPort(const core::rect<s32>& area)
{
core::rect<s32> vp = area;
core::rect<s32> rendert(0,0, getCurrentRenderTargetSize().Width, getCurrentRenderTargetSize().Height);
vp.clipAgainst(rendert);
if (vp.getHeight()>0 && vp.getWidth()>0)
glViewport(vp.UpperLeftCorner.X,
getCurrentRenderTargetSize().Height - vp.UpperLeftCorner.Y - vp.getHeight(),
vp.getWidth(), vp.getHeight());
ViewPort = vp;
}
//! Draws a shadow volume into the stencil buffer. To draw a stencil shadow, do
//! this: First, draw all geometry. Then use this method, to draw the shadow
//! volume. Next use IVideoDriver::drawStencilShadow() to visualize the shadow.
void COpenGLDriver::drawStencilShadowVolume(const core::vector3df* triangles, s32 count, bool zfail)
{
if (!StencilBuffer || !count)
return;
// unset last 3d material
if (CurrentRenderMode == ERM_3D &&
static_cast<u32>(Material.MaterialType) < MaterialRenderers.size())
{
MaterialRenderers[Material.MaterialType].Renderer->OnUnsetMaterial();
ResetRenderStates = true;
}
// store current OpenGL state
glPushAttrib(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT |
GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT);
glDisable(GL_LIGHTING);
glDisable(GL_FOG);
glDepthFunc(GL_LEQUAL);
glDepthMask(GL_FALSE); // no depth buffer writing
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); // no color buffer drawing
glEnable(GL_STENCIL_TEST);
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(0.0f, 1.0f);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(3,GL_FLOAT,sizeof(core::vector3df),&triangles[0]);
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);
// The first parts are not correctly working, yet.
#if 0
#ifdef GL_EXT_stencil_two_side
if (FeatureAvailable[IRR_EXT_stencil_two_side])
{
glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT);
#ifdef GL_NV_depth_clamp
if (FeatureAvailable[IRR_NV_depth_clamp])
glEnable(GL_DEPTH_CLAMP_NV);
#endif
glDisable(GL_CULL_FACE);
if (!zfail)
{
// ZPASS Method
extGlActiveStencilFace(GL_BACK);
if (FeatureAvailable[IRR_EXT_stencil_wrap])
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR_WRAP_EXT);
else
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);
extGlActiveStencilFace(GL_FRONT);
if (FeatureAvailable[IRR_EXT_stencil_wrap])
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR_WRAP_EXT);
else
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);
glDrawArrays(GL_TRIANGLES,0,count);
}
else
{
// ZFAIL Method
extGlActiveStencilFace(GL_BACK);
if (FeatureAvailable[IRR_EXT_stencil_wrap])
glStencilOp(GL_KEEP, GL_INCR_WRAP_EXT, GL_KEEP);
else
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);
extGlActiveStencilFace(GL_FRONT);
if (FeatureAvailable[IRR_EXT_stencil_wrap])
glStencilOp(GL_KEEP, GL_DECR_WRAP_EXT, GL_KEEP);
else
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
glStencilMask(~0);
glStencilFunc(GL_ALWAYS, 0, ~0);
glDrawArrays(GL_TRIANGLES,0,count);
}
}
else
#endif
if (FeatureAvailable[IRR_ATI_separate_stencil])
{
glDisable(GL_CULL_FACE);
if (!zfail)
{
// ZPASS Method
extGlStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, GL_DECR);
extGlStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, GL_INCR);
extGlStencilFuncSeparate(GL_FRONT_AND_BACK, GL_ALWAYS, 0, ~0);
glStencilMask(~0);
glDrawArrays(GL_TRIANGLES,0,count);
}
else
{
// ZFAIL Method
extGlStencilOpSeparate(GL_BACK, GL_KEEP, GL_INCR, GL_KEEP);
extGlStencilOpSeparate(GL_FRONT, GL_KEEP, GL_DECR, GL_KEEP);
extGlStencilFuncSeparate(GL_FRONT_AND_BACK, GL_ALWAYS, 0, ~0);
glDrawArrays(GL_TRIANGLES,0,count);
}
}
else
#endif
{
glEnable(GL_CULL_FACE);
if (!zfail)
{
// ZPASS Method
glCullFace(GL_BACK);
glStencilOp(GL_KEEP, GL_KEEP, GL_INCR);
glDrawArrays(GL_TRIANGLES,0,count);
glCullFace(GL_FRONT);
glStencilOp(GL_KEEP, GL_KEEP, GL_DECR);
glDrawArrays(GL_TRIANGLES,0,count);
}
else
{
// ZFAIL Method
glStencilOp(GL_KEEP, GL_INCR, GL_KEEP);
glCullFace(GL_FRONT);
glDrawArrays(GL_TRIANGLES,0,count);
glStencilOp(GL_KEEP, GL_DECR, GL_KEEP);
glCullFace(GL_BACK);
glDrawArrays(GL_TRIANGLES,0,count);
}
}
glDisableClientState(GL_VERTEX_ARRAY); //not stored on stack
glPopAttrib();
}
void COpenGLDriver::drawStencilShadow(bool clearStencilBuffer, video::SColor leftUpEdge,
video::SColor rightUpEdge, video::SColor leftDownEdge, video::SColor rightDownEdge)
{
if (!StencilBuffer)
return;
disableTextures();
// store attributes
glPushAttrib( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT | GL_STENCIL_BUFFER_BIT );
glDisable( GL_LIGHTING );
glDisable(GL_FOG);
glDepthMask(GL_FALSE);
glShadeModel( GL_FLAT );
glColorMask( GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE );
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable( GL_STENCIL_TEST );
glStencilFunc(GL_NOTEQUAL, 0, ~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
// draw a shadow rectangle covering the entire screen using stencil buffer
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glBegin(GL_QUADS);
glColor4ub (leftDownEdge.getRed(), leftDownEdge.getGreen(), leftDownEdge.getBlue(), leftDownEdge.getAlpha() );
glVertex3f(-1.1f,-1.1f,0.9f);
glColor4ub (leftUpEdge.getRed(), leftUpEdge.getGreen(), leftUpEdge.getBlue(), leftUpEdge.getAlpha() );
glVertex3f(-1.1f, 1.1f,0.9f);
glColor4ub (rightUpEdge.getRed(), rightUpEdge.getGreen(), rightUpEdge.getBlue(), rightUpEdge.getAlpha() );
glVertex3f( 1.1f, 1.1f,0.9f);
glColor4ub (rightDownEdge.getRed(), rightDownEdge.getGreen(), rightDownEdge.getBlue(), rightDownEdge.getAlpha() );
glVertex3f( 1.1f,-1.1f,0.9f);
glEnd();
if (clearStencilBuffer)
glClear(GL_STENCIL_BUFFER_BIT);
// restore settings
glPopMatrix();
glPopAttrib();
}
//! Sets the fog mode.
void COpenGLDriver::setFog(SColor c, bool linearFog, f32 start,
f32 end, f32 density, bool pixelFog, bool rangeFog)
{
CNullDriver::setFog(c, linearFog, start, end, density, pixelFog, rangeFog);
glFogf(GL_FOG_MODE, GLfloat(linearFog ? GL_LINEAR : GL_EXP));
#ifdef GL_EXT_fog_coord
if (FeatureAvailable[IRR_EXT_fog_coord])
glFogi(GL_FOG_COORDINATE_SOURCE, GL_FRAGMENT_DEPTH);
#endif
if(linearFog)
{
glFogf(GL_FOG_START, start);
glFogf(GL_FOG_END, end);
}
else
glFogf(GL_FOG_DENSITY, density);
if (pixelFog)
glHint(GL_FOG_HINT, GL_NICEST);
else
glHint(GL_FOG_HINT, GL_FASTEST);
SColorf color(c);
GLfloat data[4] = {color.r, color.g, color.b, color.a};
glFogfv(GL_FOG_COLOR, data);
}
//! Draws a 3d line.
void COpenGLDriver::draw3DLine(const core::vector3df& start,
const core::vector3df& end, SColor color)
{
setRenderStates3DMode();
glBegin(GL_LINES);
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
glVertex3f(start.X, start.Y, start.Z);
glVertex3f(end.X, end.Y, end.Z);
glEnd();
}
//! Only used by the internal engine. Used to notify the driver that
//! the window was resized.
void COpenGLDriver::OnResize(const core::dimension2d<s32>& size)
{
CNullDriver::OnResize(size);
glViewport(0, 0, size.Width, size.Height);
Transformation3DChanged = true;
}
//! Returns type of video driver
E_DRIVER_TYPE COpenGLDriver::getDriverType() const
{
return EDT_OPENGL;
}
//! returns color format
ECOLOR_FORMAT COpenGLDriver::getColorFormat() const
{
return ColorFormat;
}
//! Sets a vertex shader constant.
void COpenGLDriver::setVertexShaderConstant(const f32* data, s32 startRegister, s32 constantAmount)
{
#ifdef GL_ARB_vertex_program
for (s32 i=0; i<constantAmount; ++i)
extGlProgramLocalParameter4fv(GL_VERTEX_PROGRAM_ARB, startRegister+i, &data[i*4]);
#endif
}
//! Sets a pixel shader constant.
void COpenGLDriver::setPixelShaderConstant(const f32* data, s32 startRegister, s32 constantAmount)
{
#ifdef GL_ARB_fragment_program
for (s32 i=0; i<constantAmount; ++i)
extGlProgramLocalParameter4fv(GL_FRAGMENT_PROGRAM_ARB, startRegister+i, &data[i*4]);
#endif
}
//! Sets a constant for the vertex shader based on a name.
bool COpenGLDriver::setVertexShaderConstant(const c8* name, const f32* floats, int count)
{
//pass this along, as in GLSL the same routine is used for both vertex and fragment shaders
return setPixelShaderConstant(name, floats, count);
}
//! Sets a constant for the pixel shader based on a name.
bool COpenGLDriver::setPixelShaderConstant(const c8* name, const f32* floats, int count)
{
os::Printer::log("Error: Please call services->setPixelShaderConstant(), not VideoDriver->setPixelShaderConstant().");
return false;
}
//! Adds a new material renderer to the VideoDriver, using pixel and/or
//! vertex shaders to render geometry.
s32 COpenGLDriver::addShaderMaterial(const c8* vertexShaderProgram,
const c8* pixelShaderProgram,
IShaderConstantSetCallBack* callback,
E_MATERIAL_TYPE baseMaterial, s32 userData)
{
s32 nr = -1;
COpenGLShaderMaterialRenderer* r = new COpenGLShaderMaterialRenderer(
this, nr, vertexShaderProgram, pixelShaderProgram,
callback, getMaterialRenderer(baseMaterial), userData);
r->drop();
return nr;
}
//! Adds a new material renderer to the VideoDriver, using GLSL to render geometry.
s32 COpenGLDriver::addHighLevelShaderMaterial(
const c8* vertexShaderProgram,
const c8* vertexShaderEntryPointName,
E_VERTEX_SHADER_TYPE vsCompileTarget,
const c8* pixelShaderProgram,
const c8* pixelShaderEntryPointName,
E_PIXEL_SHADER_TYPE psCompileTarget,
IShaderConstantSetCallBack* callback,
E_MATERIAL_TYPE baseMaterial,
s32 userData)
{
s32 nr = -1;
COpenGLSLMaterialRenderer* r = new COpenGLSLMaterialRenderer(
this, nr, vertexShaderProgram, vertexShaderEntryPointName,
vsCompileTarget, pixelShaderProgram, pixelShaderEntryPointName, psCompileTarget,
callback,getMaterialRenderer(baseMaterial), userData);
r->drop();
return nr;
}
//! Returns a pointer to the IVideoDriver interface. (Implementation for
//! IMaterialRendererServices)
IVideoDriver* COpenGLDriver::getVideoDriver()
{
return this;
}
//! Returns pointer to the IGPUProgrammingServices interface.
IGPUProgrammingServices* COpenGLDriver::getGPUProgrammingServices()
{
return this;
}
ITexture* COpenGLDriver::addRenderTargetTexture(const core::dimension2d<s32>& size, const c8* name)
{
//disable mip-mapping
bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
video::ITexture* rtt = 0;
if (name==0)
name="rt";
#if defined(GL_EXT_framebuffer_object)
// if driver supports FrameBufferObjects, use them
if (queryFeature(EVDF_FRAMEBUFFER_OBJECT))
{
rtt = new COpenGLFBOTexture(size, name, this);
if (rtt)
{
addTexture(rtt);
ITexture* tex = createDepthTexture(rtt);
if (tex)
{
static_cast<video::COpenGLFBODepthTexture*>(tex)->attach(rtt);
tex->drop();
}
rtt->drop();
}
}
else
#endif
{
// the simple texture is only possible for size <= screensize
// we try to find an optimal size with the original constraints
core::dimension2di destSize(core::min_(size.Width,ScreenSize.Width), core::min_(size.Height,ScreenSize.Height));
destSize = destSize.getOptimalSize((size==size.getOptimalSize()), false, false);
rtt = addTexture(destSize, name, ECF_A8R8G8B8);
if (rtt)
{
static_cast<video::COpenGLTexture*>(rtt)->setIsRenderTarget(true);
}
}
//restore mip-mapping
setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, generateMipLevels);
return rtt;
}
//! Returns the maximum amount of primitives (mostly vertices) which
//! the device is able to render with one drawIndexedTriangleList
//! call.
u32 COpenGLDriver::getMaximalPrimitiveCount() const
{
return 0x7fffffff;
}
//! set or reset render target
bool COpenGLDriver::setRenderTarget(video::ITexture* texture, bool clearBackBuffer,
bool clearZBuffer, SColor color)
{
// check for right driver type
if (texture && texture->getDriverType() != EDT_OPENGL)
{
os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR);
return false;
}
// check if we should set the previous RT back
setTexture(0, 0);
ResetRenderStates=true;
if (RenderTargetTexture!=0)
{
RenderTargetTexture->unbindRTT();
}
if (texture)
{
// we want to set a new target. so do this.
glViewport(0, 0, texture->getSize().Width, texture->getSize().Height);
RenderTargetTexture = static_cast<COpenGLTexture*>(texture);
RenderTargetTexture->bindRTT();
CurrentRendertargetSize = texture->getSize();
}
else
{
glViewport(0,0,ScreenSize.Width,ScreenSize.Height);
RenderTargetTexture = 0;
CurrentRendertargetSize = core::dimension2d<s32>(0,0);
}
GLbitfield mask = 0;
if (clearBackBuffer)
{
const f32 inv = 1.0f / 255.0f;
glClearColor(color.getRed() * inv, color.getGreen() * inv,
color.getBlue() * inv, color.getAlpha() * inv);
mask |= GL_COLOR_BUFFER_BIT;
}
if (clearZBuffer)
{
glDepthMask(GL_TRUE);
LastMaterial.ZWriteEnable=true;
mask |= GL_DEPTH_BUFFER_BIT;
}
glClear(mask);
return true;
}
// returns the current size of the screen or rendertarget
const core::dimension2d<s32>& COpenGLDriver::getCurrentRenderTargetSize() const
{
if ( CurrentRendertargetSize.Width == 0 )
return ScreenSize;
else
return CurrentRendertargetSize;
}
//! Clears the ZBuffer.
void COpenGLDriver::clearZBuffer()
{
GLboolean enabled = GL_TRUE;
glGetBooleanv(GL_DEPTH_WRITEMASK, &enabled);
glDepthMask(GL_TRUE);
glClear(GL_DEPTH_BUFFER_BIT);
glDepthMask(enabled);
}
//! Returns an image created from the last rendered frame.
IImage* COpenGLDriver::createScreenShot()
{
IImage* newImage = new CImage(ECF_R8G8B8, ScreenSize);
u8* pixels = static_cast<u8*>(newImage->lock());
if (!pixels)
{
newImage->drop();
return 0;
}
// allows to read pixels in top-to-bottom order
#ifdef GL_MESA_pack_invert
if (FeatureAvailable[IRR_MESA_pack_invert])
glPixelStorei(GL_PACK_INVERT_MESA, GL_TRUE);
#endif
// We want to read the front buffer to get the latest render finished.
glReadBuffer(GL_FRONT);
glReadPixels(0, 0, ScreenSize.Width, ScreenSize.Height, GL_RGB, GL_UNSIGNED_BYTE, pixels);
glReadBuffer(GL_BACK);
#ifdef GL_MESA_pack_invert
if (FeatureAvailable[IRR_MESA_pack_invert])
glPixelStorei(GL_PACK_INVERT_MESA, GL_FALSE);
else
#endif
{
// opengl images are horizontally flipped, so we have to fix that here.
const s32 pitch=newImage->getPitch();
u8* p2 = pixels + (ScreenSize.Height - 1) * pitch;
u8* tmpBuffer = new u8[pitch];
for (s32 i=0; i < ScreenSize.Height; i += 2)
{
memcpy(tmpBuffer, pixels, pitch);
memcpy(pixels, p2, pitch);
memcpy(p2, tmpBuffer, pitch);
pixels += pitch;
p2 -= pitch;
}
delete [] tmpBuffer;
}
newImage->unlock();
if (testGLError())
{
newImage->drop();
return 0;
}
return newImage;
}
//! get depth texture for the given render target texture
ITexture* COpenGLDriver::createDepthTexture(ITexture* texture, bool shared)
{
if ((texture->getDriverType() != EDT_OPENGL) || (!texture->isRenderTarget()))
return 0;
COpenGLTexture* tex = static_cast<COpenGLTexture*>(texture);
if (!tex->isFrameBufferObject())
return 0;
if (shared)
{
for (u32 i=0; i<DepthTextures.size(); ++i)
{
if (DepthTextures[i]->getSize()==texture->getSize())
{
DepthTextures[i]->grab();
return DepthTextures[i];
}
}
DepthTextures.push_back(new COpenGLFBODepthTexture(texture->getSize(), "depth1", this));
return DepthTextures.getLast();
}
return (new COpenGLFBODepthTexture(texture->getSize(), "depth1", this));
}
void COpenGLDriver::removeDepthTexture(ITexture* texture)
{
for (u32 i=0; i<DepthTextures.size(); ++i)
{
if (texture==DepthTextures[i])
{
DepthTextures.erase(i);
return;
}
}
}
//! Set/unset a clipping plane.
bool COpenGLDriver::setClipPlane(u32 index, const core::plane3df& plane, bool enable)
{
if (index >= MaxUserClipPlanes)
return false;
UserClipPlane[index]=plane;
enableClipPlane(index, enable);
return true;
}
void COpenGLDriver::uploadClipPlane(u32 index)
{
// opengl needs an array of doubles for the plane equation
double clip_plane[4];
clip_plane[0] = UserClipPlane[index].Normal.X;
clip_plane[1] = UserClipPlane[index].Normal.Y;
clip_plane[2] = UserClipPlane[index].Normal.Z;
clip_plane[3] = UserClipPlane[index].D;
glClipPlane(GL_CLIP_PLANE0 + index, clip_plane);
}
//! Enable/disable a clipping plane.
void COpenGLDriver::enableClipPlane(u32 index, bool enable)
{
if (index >= MaxUserClipPlanes)
return;
if (enable)
{
if (!UserClipPlaneEnabled[index])
{
uploadClipPlane(index);
glEnable(GL_CLIP_PLANE0 + index);
}
}
else
glDisable(GL_CLIP_PLANE0 + index);
UserClipPlaneEnabled[index]=enable;
}
} // end namespace
} // end namespace
#endif // _IRR_COMPILE_WITH_OPENGL_
namespace irr
{
namespace video
{
// -----------------------------------
// WINDOWS VERSION
// -----------------------------------
#ifdef _IRR_USE_WINDOWS_DEVICE_
IVideoDriver* createOpenGLDriver(const irr::SIrrlichtCreationParameters& params,
io::IFileSystem* io)
{
#ifdef _IRR_COMPILE_WITH_OPENGL_
COpenGLDriver* ogl = new COpenGLDriver(params, io);
if (!ogl->initDriver(params))
{
ogl->drop();
ogl = 0;
}
return ogl;
#else
return 0;
#endif // _IRR_COMPILE_WITH_OPENGL_
}
#endif // _IRR_USE_WINDOWS_DEVICE_
// -----------------------------------
// MACOSX VERSION
// -----------------------------------
#if defined(_IRR_USE_OSX_DEVICE_)
IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
io::IFileSystem* io, CIrrDeviceMacOSX *device)
{
#ifdef _IRR_COMPILE_WITH_OPENGL_
return new COpenGLDriver(params, io, device);
#else
return 0;
#endif // _IRR_COMPILE_WITH_OPENGL_
}
#endif // _IRR_USE_OSX_DEVICE_
// -----------------------------------
// X11/SDL VERSION
// -----------------------------------
#if defined(_IRR_USE_LINUX_DEVICE_) || defined(_IRR_USE_SDL_DEVICE_)
IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
io::IFileSystem* io)
{
#ifdef _IRR_COMPILE_WITH_OPENGL_
return new COpenGLDriver(params, io);
#else
return 0;
#endif // _IRR_COMPILE_WITH_OPENGL_
}
#endif // _IRR_USE_LINUX_DEVICE_
} // end namespace
} // end namespace