368604f379
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
3122 lines
88 KiB
C++
Executable File
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
|
|
|
|
|