e00995d099
It would be nice to simplify it a bit, i.e. decide if OpenGL context should be created on device side or driver side, use single constructor in GLES driver etc... But I'm not really sure how it will look like after Benau's space partitioning work, so some refactoring postponed till later.
5035 lines
146 KiB
C++
5035 lines
146 KiB
C++
// Copyright (C) 2002-2012 Nikolaus Gebhardt
|
|
// This file is part of the "Irrlicht Engine".
|
|
// For conditions of distribution and use, see copyright notice in irrlicht.h
|
|
|
|
|
|
extern bool GLContextDebugBit;
|
|
|
|
#include "COpenGLDriver.h"
|
|
// needed here also because of the create methods' parameters
|
|
#include "CNullDriver.h"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
|
|
#include "COpenGLMaterialRenderer.h"
|
|
#include "COpenGLShaderMaterialRenderer.h"
|
|
#include "COpenGLSLMaterialRenderer.h"
|
|
#include "COpenGLNormalMapRenderer.h"
|
|
#include "COpenGLParallaxMapRenderer.h"
|
|
#include "os.h"
|
|
|
|
#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_
|
|
#include <SDL/SDL.h>
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OSX_DEVICE_
|
|
#include "MacOSX/CIrrDeviceMacOSX.h"
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_WAYLAND_DEVICE_
|
|
#include "CIrrDeviceWayland.h"
|
|
#include "CContextEGL.h"
|
|
#endif
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
bool useCoreContext;
|
|
// -----------------------------------------------------------------------
|
|
// WINDOWS CONSTRUCTOR
|
|
// -----------------------------------------------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
//! Windows constructor and init code
|
|
COpenGLDriver::COpenGLDriver(const irr::SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceWin32* device)
|
|
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
|
|
CurrentRenderMode(ERM_NONE), ResetRenderStates(true), Transformation3DChanged(true),
|
|
AntiAlias(params.AntiAlias), RenderTargetTexture(0),
|
|
CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8),
|
|
CurrentTarget(ERT_FRAME_BUFFER), Params(params),
|
|
HDc(0), Window(static_cast<HWND>(params.WindowId)), Win32Device(device),
|
|
DeviceType(EIDT_WIN32)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COpenGLDriver");
|
|
#endif
|
|
}
|
|
|
|
|
|
bool COpenGLDriver::changeRenderContext(const SExposedVideoData& videoData, CIrrDeviceWin32* device)
|
|
{
|
|
if (videoData.OpenGLWin32.HWnd && videoData.OpenGLWin32.HDc && videoData.OpenGLWin32.HRc)
|
|
{
|
|
if (!wglMakeCurrent((HDC)videoData.OpenGLWin32.HDc, (HGLRC)videoData.OpenGLWin32.HRc))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
HDc = (HDC)videoData.OpenGLWin32.HDc;
|
|
}
|
|
}
|
|
// set back to main context
|
|
else if (HDc != ExposedData.OpenGLWin32.HDc)
|
|
{
|
|
if (!wglMakeCurrent((HDC)ExposedData.OpenGLWin32.HDc, (HGLRC)ExposedData.OpenGLWin32.HRc))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
HDc = (HDC)ExposedData.OpenGLWin32.HDc;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs_ARB;
|
|
|
|
static HGLRC getMeAGLContext(HDC HDc, bool force_legacy_context)
|
|
{
|
|
if (!force_legacy_context)
|
|
{
|
|
useCoreContext = true;
|
|
HGLRC hrc = 0;
|
|
int ctx44debug[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
int ctx44[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
hrc = wglCreateContextAttribs_ARB(HDc, 0, GLContextDebugBit ? ctx44debug : ctx44);
|
|
if (hrc)
|
|
return hrc;
|
|
|
|
int ctx40debug[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
int ctx40[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 0,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
hrc = wglCreateContextAttribs_ARB(HDc, 0, GLContextDebugBit ? ctx40debug : ctx40);
|
|
if (hrc)
|
|
return hrc;
|
|
|
|
int ctx33debug[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
int ctx33[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
hrc = wglCreateContextAttribs_ARB(HDc, 0, GLContextDebugBit ? ctx33debug : ctx33);
|
|
if (hrc)
|
|
return hrc;
|
|
|
|
int ctx31debug[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_DEBUG_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
int ctx31[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
hrc = wglCreateContextAttribs_ARB(HDc, 0, GLContextDebugBit ? ctx31debug : ctx31);
|
|
if (hrc)
|
|
return hrc;
|
|
} // if (!force_legacy_context)
|
|
|
|
useCoreContext = false;
|
|
int legacyctx[] =
|
|
{
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 2,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 1,
|
|
0
|
|
};
|
|
HGLRC hrc = wglCreateContextAttribs_ARB(HDc, 0, legacyctx);
|
|
if (hrc)
|
|
return hrc;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//! inits the open gl driver
|
|
bool COpenGLDriver::initDriver(CIrrDeviceWin32* device)
|
|
{
|
|
// Create a window to test antialiasing support
|
|
const fschar_t* ClassName = __TEXT("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, __TEXT(""), style, windowLeft,
|
|
windowTop, realWidth, realHeight, NULL, NULL, lhInstance, NULL);
|
|
|
|
if (!temporary_wnd)
|
|
{
|
|
os::Printer::log("Cannot create a temporary window.", ELL_ERROR);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
HDc = GetDC(temporary_wnd);
|
|
|
|
// Set up pixel 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
|
|
(Params.Doublebuffer?PFD_DOUBLEBUFFER:0) | // Must Support Double Buffering
|
|
(Params.Stereobuffer?PFD_STEREO:0), // Must Support Stereo Buffer
|
|
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
|
|
Params.ZBufferBits, // Z-Buffer (Depth Buffer)
|
|
BYTE(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;
|
|
|
|
for (u32 i=0; i<6; ++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;
|
|
}
|
|
else
|
|
if (i == 3)
|
|
{
|
|
if (Params.Bits!=16)
|
|
pfd.cDepthBits = 16;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 4)
|
|
{
|
|
// try single buffer
|
|
if (Params.Doublebuffer)
|
|
pfd.dwFlags &= ~PFD_DOUBLEBUFFER;
|
|
else
|
|
continue;
|
|
}
|
|
else
|
|
if (i == 5)
|
|
{
|
|
os::Printer::log("Cannot create a GL device context", "No suitable format for temporary window.", ELL_ERROR);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
// choose pixelformat
|
|
PixelFormat = ChoosePixelFormat(HDc, &pfd);
|
|
if (PixelFormat)
|
|
break;
|
|
}
|
|
|
|
SetPixelFormat(HDc, PixelFormat, &pfd);
|
|
HGLRC hrc=wglCreateContext(HDc);
|
|
if (!hrc)
|
|
{
|
|
os::Printer::log("Cannot create a temporary GL rendering context.", ELL_ERROR);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
SExposedVideoData data;
|
|
data.OpenGLWin32.HDc = HDc;
|
|
data.OpenGLWin32.HRc = hrc;
|
|
data.OpenGLWin32.HWnd = temporary_wnd;
|
|
|
|
|
|
if (!changeRenderContext(data, device))
|
|
{
|
|
os::Printer::log("Cannot activate a temporary GL rendering context.", ELL_ERROR);
|
|
wglDeleteContext(hrc);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
return false;
|
|
}
|
|
|
|
core::stringc wglExtensions;
|
|
#ifdef WGL_ARB_extensions_string
|
|
PFNWGLGETEXTENSIONSSTRINGARBPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
|
|
if (irrGetExtensionsString)
|
|
wglExtensions = irrGetExtensionsString(HDc);
|
|
#elif defined(WGL_EXT_extensions_string)
|
|
PFNWGLGETEXTENSIONSSTRINGEXTPROC irrGetExtensionsString = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
|
|
if (irrGetExtensionsString)
|
|
wglExtensions = irrGetExtensionsString(HDc);
|
|
#endif
|
|
const bool pixel_format_supported = (wglExtensions.find("WGL_ARB_pixel_format") != -1);
|
|
const bool multi_sample_supported = ((wglExtensions.find("WGL_ARB_multisample") != -1) ||
|
|
(wglExtensions.find("WGL_EXT_multisample") != -1) || (wglExtensions.find("WGL_3DFX_multisample") != -1) );
|
|
#ifdef _DEBUG
|
|
os::Printer::log("WGL_extensions", wglExtensions);
|
|
#endif
|
|
|
|
#ifdef WGL_ARB_pixel_format
|
|
PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormat_ARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
|
|
if (pixel_format_supported && wglChoosePixelFormat_ARB)
|
|
{
|
|
// This value determines the number of samples used for antialiasing
|
|
// My experience is that 8 does not show a big
|
|
// improvement over 4, but 4 shows a big improvement
|
|
// over 2.
|
|
|
|
if(AntiAlias > 32)
|
|
AntiAlias = 32;
|
|
|
|
f32 fAttributes[] = {0.0, 0.0};
|
|
s32 iAttributes[] =
|
|
{
|
|
WGL_DRAW_TO_WINDOW_ARB,1,
|
|
WGL_SUPPORT_OPENGL_ARB,1,
|
|
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, // 10,11
|
|
WGL_STENCIL_BITS_ARB,Params.Stencilbuffer ? 1 : 0,
|
|
WGL_DOUBLE_BUFFER_ARB,Params.Doublebuffer ? 1 : 0,
|
|
WGL_STEREO_ARB,Params.Stereobuffer ? 1 : 0,
|
|
WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
|
|
#ifdef WGL_ARB_multisample
|
|
WGL_SAMPLES_ARB,AntiAlias, // 20,21
|
|
WGL_SAMPLE_BUFFERS_ARB, 1,
|
|
#elif defined(WGL_EXT_multisample)
|
|
WGL_SAMPLES_EXT,AntiAlias, // 20,21
|
|
WGL_SAMPLE_BUFFERS_EXT, 1,
|
|
#elif defined(WGL_3DFX_multisample)
|
|
WGL_SAMPLES_3DFX,AntiAlias, // 20,21
|
|
WGL_SAMPLE_BUFFERS_3DFX, 1,
|
|
#endif
|
|
#ifdef WGL_ARB_framebuffer_sRGB
|
|
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB, Params.HandleSRGB ? 1:0,
|
|
#elif defined(WGL_EXT_framebuffer_sRGB)
|
|
WGL_FRAMEBUFFER_SRGB_CAPABLE_EXT, Params.HandleSRGB ? 1:0,
|
|
#endif
|
|
// WGL_DEPTH_FLOAT_EXT, 1,
|
|
0,0,0,0
|
|
};
|
|
int iAttrSize = sizeof(iAttributes)/sizeof(int);
|
|
const bool framebuffer_srgb_supported = ((wglExtensions.find("WGL_ARB_framebuffer_sRGB") != -1) ||
|
|
(wglExtensions.find("WGL_EXT_framebuffer_sRGB") != -1));
|
|
if (!framebuffer_srgb_supported)
|
|
{
|
|
memmove(&iAttributes[24],&iAttributes[26],sizeof(int)*(iAttrSize-26));
|
|
iAttrSize -= 2;
|
|
}
|
|
if (!multi_sample_supported)
|
|
{
|
|
memmove(&iAttributes[20],&iAttributes[24],sizeof(int)*(iAttrSize-24));
|
|
iAttrSize -= 4;
|
|
}
|
|
|
|
s32 rv=0;
|
|
// Try to get an acceptable pixel format
|
|
do
|
|
{
|
|
int pixelFormat=0;
|
|
UINT numFormats=0;
|
|
const BOOL valid = wglChoosePixelFormat_ARB(HDc,iAttributes,fAttributes,1,&pixelFormat,&numFormats);
|
|
|
|
if (valid && numFormats)
|
|
rv = pixelFormat;
|
|
else
|
|
iAttributes[21] -= 1;
|
|
}
|
|
while(rv==0 && iAttributes[21]>1);
|
|
if (rv)
|
|
{
|
|
PixelFormat=rv;
|
|
AntiAlias=iAttributes[21];
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
AntiAlias=0;
|
|
#ifdef WGL_ARB_create_context
|
|
wglCreateContextAttribs_ARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC)wglGetProcAddress("wglCreateContextAttribsARB");
|
|
#endif
|
|
wglMakeCurrent(HDc, NULL);
|
|
wglDeleteContext(hrc);
|
|
ReleaseDC(temporary_wnd, HDc);
|
|
DestroyWindow(temporary_wnd);
|
|
UnregisterClass(ClassName, lhInstance);
|
|
|
|
// 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 (PixelFormat==0 || (!SetPixelFormat(HDc, PixelFormat, &pfd)))
|
|
{
|
|
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;
|
|
}
|
|
os::Printer::log("Pixel Format", core::stringc(PixelFormat).c_str(), ELL_DEBUG);
|
|
|
|
// create rendering context
|
|
#ifdef WGL_ARB_create_context
|
|
if (wglCreateContextAttribs_ARB)
|
|
{
|
|
hrc = getMeAGLContext(HDc, Params.ForceLegacyDevice);
|
|
}
|
|
else
|
|
#endif
|
|
hrc=wglCreateContext(HDc);
|
|
|
|
if (!hrc)
|
|
{
|
|
os::Printer::log("Cannot create a GL rendering context.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// set exposed data
|
|
ExposedData.OpenGLWin32.HDc = HDc;
|
|
ExposedData.OpenGLWin32.HRc = hrc;
|
|
ExposedData.OpenGLWin32.HWnd = Window;
|
|
|
|
// activate rendering context
|
|
|
|
if (!changeRenderContext(ExposedData, device))
|
|
{
|
|
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();
|
|
|
|
extGlSwapInterval(Params.Vsync ? 1 : 0);
|
|
return true;
|
|
}
|
|
|
|
#endif // _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
|
|
// -----------------------------------------------------------------------
|
|
// MacOSX CONSTRUCTOR
|
|
// -----------------------------------------------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_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),
|
|
CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8),
|
|
CurrentTarget(ERT_FRAME_BUFFER), Params(params),
|
|
OSXDevice(device), DeviceType(EIDT_OSX)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COpenGLDriver");
|
|
#endif
|
|
|
|
genericDriverInit();
|
|
}
|
|
|
|
#endif
|
|
|
|
// -----------------------------------------------------------------------
|
|
// LINUX CONSTRUCTOR
|
|
// -----------------------------------------------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
|
|
//! Linux constructor and init code
|
|
COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceLinux* device)
|
|
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
|
|
CurrentRenderMode(ERM_NONE), ResetRenderStates(true),
|
|
Transformation3DChanged(true), AntiAlias(params.AntiAlias),
|
|
RenderTargetTexture(0), CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8),
|
|
CurrentTarget(ERT_FRAME_BUFFER), Params(params),
|
|
X11Device(device), DeviceType(EIDT_X11)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COpenGLDriver");
|
|
#endif
|
|
}
|
|
|
|
|
|
bool COpenGLDriver::changeRenderContext(const SExposedVideoData& videoData, CIrrDeviceLinux* device)
|
|
{
|
|
if (videoData.OpenGLLinux.X11Window)
|
|
{
|
|
if (videoData.OpenGLLinux.X11Display && videoData.OpenGLLinux.X11Context)
|
|
{
|
|
if (!glXMakeCurrent((Display*)videoData.OpenGLLinux.X11Display, videoData.OpenGLLinux.X11Window, (GLXContext)videoData.OpenGLLinux.X11Context))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Drawable = videoData.OpenGLLinux.X11Window;
|
|
X11Display = (Display*)videoData.OpenGLLinux.X11Display;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// in case we only got a window ID, try with the existing values for display and context
|
|
if (!glXMakeCurrent((Display*)ExposedData.OpenGLLinux.X11Display, videoData.OpenGLLinux.X11Window, (GLXContext)ExposedData.OpenGLLinux.X11Context))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Drawable = videoData.OpenGLLinux.X11Window;
|
|
X11Display = (Display*)ExposedData.OpenGLLinux.X11Display;
|
|
}
|
|
}
|
|
}
|
|
// set back to main context
|
|
else if (X11Display != ExposedData.OpenGLLinux.X11Display)
|
|
{
|
|
if (!glXMakeCurrent((Display*)ExposedData.OpenGLLinux.X11Display, ExposedData.OpenGLLinux.X11Window, (GLXContext)ExposedData.OpenGLLinux.X11Context))
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Drawable = ExposedData.OpenGLLinux.X11Window;
|
|
X11Display = (Display*)ExposedData.OpenGLLinux.X11Display;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
//! inits the open gl driver
|
|
bool COpenGLDriver::initDriver(CIrrDeviceLinux* device)
|
|
{
|
|
ExposedData.OpenGLLinux.X11Context = glXGetCurrentContext();
|
|
ExposedData.OpenGLLinux.X11Display = glXGetCurrentDisplay();
|
|
ExposedData.OpenGLLinux.X11Window = (unsigned long)Params.WindowId;
|
|
Drawable = glXGetCurrentDrawable();
|
|
X11Display = (Display*)ExposedData.OpenGLLinux.X11Display;
|
|
|
|
genericDriverInit();
|
|
|
|
// set vsync
|
|
extGlSwapInterval(Params.Vsync ? 1 : 0);
|
|
return true;
|
|
}
|
|
|
|
#endif // _IRR_COMPILE_WITH_X11_DEVICE_
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Wayland CONSTRUCTOR
|
|
// -----------------------------------------------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_WAYLAND_DEVICE_
|
|
//! Linux constructor and init code
|
|
COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceWayland* device)
|
|
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
|
|
CurrentRenderMode(ERM_NONE), ResetRenderStates(true),
|
|
Transformation3DChanged(true), AntiAlias(params.AntiAlias),
|
|
RenderTargetTexture(0), CurrentRendertargetSize(0, 0), ColorFormat(ECF_R8G8B8),
|
|
CurrentTarget(ERT_FRAME_BUFFER), Params(params),
|
|
wl_device(device), DeviceType(EIDT_WAYLAND)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COpenGLDriver");
|
|
#endif
|
|
}
|
|
|
|
|
|
bool COpenGLDriver::changeRenderContext(const SExposedVideoData& videoData,
|
|
CIrrDeviceWayland* device)
|
|
{
|
|
if (!device->getEGLContext()->makeCurrent())
|
|
{
|
|
os::Printer::log("Render Context switch failed.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//! inits the open gl driver
|
|
bool COpenGLDriver::initDriver(CIrrDeviceWayland* device)
|
|
{
|
|
genericDriverInit();
|
|
|
|
return true;
|
|
}
|
|
|
|
#endif // _IRR_COMPILE_WITH_WAYLAND_DEVICE
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
// SDL CONSTRUCTOR
|
|
// -----------------------------------------------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_
|
|
//! SDL constructor and init code
|
|
COpenGLDriver::COpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceSDL* device)
|
|
: CNullDriver(io, params.WindowSize), COpenGLExtensionHandler(),
|
|
CurrentRenderMode(ERM_NONE), ResetRenderStates(true),
|
|
Transformation3DChanged(true), AntiAlias(params.AntiAlias),
|
|
RenderTargetTexture(0), CurrentRendertargetSize(0,0), ColorFormat(ECF_R8G8B8),
|
|
CurrentTarget(ERT_FRAME_BUFFER), Params(params),
|
|
SDLDevice(device), DeviceType(EIDT_SDL)
|
|
{
|
|
#ifdef _DEBUG
|
|
setDebugName("COpenGLDriver");
|
|
#endif
|
|
|
|
genericDriverInit();
|
|
}
|
|
|
|
#endif // _IRR_COMPILE_WITH_SDL_DEVICE_
|
|
|
|
|
|
//! destructor
|
|
COpenGLDriver::~COpenGLDriver()
|
|
{
|
|
RequestedLights.clear();
|
|
|
|
deleteMaterialRenders();
|
|
|
|
CurrentTexture.clear();
|
|
// 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();
|
|
removeAllOcclusionQueries();
|
|
removeAllHardwareBuffers();
|
|
|
|
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
if (DeviceType == EIDT_WIN32)
|
|
{
|
|
|
|
if (ExposedData.OpenGLWin32.HRc)
|
|
{
|
|
if (!wglMakeCurrent(HDc, 0))
|
|
os::Printer::log("Release of dc and rc failed.", ELL_WARNING);
|
|
|
|
if (!wglDeleteContext((HGLRC)ExposedData.OpenGLWin32.HRc))
|
|
os::Printer::log("Release of rendering context failed.", ELL_WARNING);
|
|
}
|
|
|
|
if (HDc)
|
|
ReleaseDC(Window, HDc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// METHODS
|
|
// -----------------------------------------------------------------------
|
|
|
|
bool COpenGLDriver::genericDriverInit()
|
|
{
|
|
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;
|
|
CurrentTexture.clear();
|
|
// load extensions
|
|
initExtensions(Params.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);
|
|
DriverAttributes->setAttribute("MaxTextures", MaxTextureUnits);
|
|
DriverAttributes->setAttribute("MaxSupportedTextures", MaxSupportedTextures);
|
|
DriverAttributes->setAttribute("MaxLights", MaxLights);
|
|
DriverAttributes->setAttribute("MaxAnisotropy", MaxAnisotropy);
|
|
DriverAttributes->setAttribute("MaxUserClipPlanes", MaxUserClipPlanes);
|
|
DriverAttributes->setAttribute("MaxAuxBuffers", MaxAuxBuffers);
|
|
DriverAttributes->setAttribute("MaxMultipleRenderTargets", MaxMultipleRenderTargets);
|
|
DriverAttributes->setAttribute("MaxIndices", (s32)MaxIndices);
|
|
DriverAttributes->setAttribute("MaxTextureSize", (s32)MaxTextureSize);
|
|
DriverAttributes->setAttribute("MaxGeometryVerticesOut", (s32)MaxGeometryVerticesOut);
|
|
DriverAttributes->setAttribute("MaxTextureLODBias", MaxTextureLODBias);
|
|
DriverAttributes->setAttribute("Version", Version);
|
|
DriverAttributes->setAttribute("ShaderLanguageVersion", ShaderLanguageVersion);
|
|
DriverAttributes->setAttribute("AntiAlias", AntiAlias);
|
|
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 1);
|
|
|
|
// Reset The Current Viewport
|
|
glViewport(0, 0, Params.WindowSize.Width, Params.WindowSize.Height);
|
|
|
|
UserClipPlanes.reallocate(MaxUserClipPlanes);
|
|
for (i=0; i<MaxUserClipPlanes; ++i)
|
|
UserClipPlanes.push_back(SUserClipPlane());
|
|
|
|
for (i=0; i<ETS_COUNT; ++i)
|
|
setTransform(static_cast<E_TRANSFORMATION_STATE>(i), core::IdentityMatrix);
|
|
|
|
setAmbientLight(SColorf(0.0f,0.0f,0.0f,0.0f));
|
|
#ifdef GL_EXT_separate_specular_color
|
|
if (FeatureAvailable[IRR_EXT_separate_specular_color] && !useCoreContext)
|
|
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
|
|
#endif
|
|
if (!useCoreContext)
|
|
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, 1);
|
|
|
|
Params.HandleSRGB &= ((FeatureAvailable[IRR_ARB_framebuffer_sRGB] || FeatureAvailable[IRR_EXT_framebuffer_sRGB]) &&
|
|
FeatureAvailable[IRR_EXT_texture_sRGB]);
|
|
|
|
glDisable(GL_FRAMEBUFFER_SRGB);
|
|
//#if defined(GL_ARB_framebuffer_sRGB)
|
|
// if (Params.HandleSRGB)
|
|
// glEnable(GL_FRAMEBUFFER_SRGB);
|
|
//#elif defined(GL_EXT_framebuffer_sRGB)
|
|
// if (Params.HandleSRGB)
|
|
// glEnable(GL_FRAMEBUFFER_SRGB_EXT);
|
|
//#endif
|
|
|
|
// This is a fast replacement for NORMALIZE_NORMALS
|
|
// if ((Version>101) || FeatureAvailable[IRR_EXT_rescale_normal])
|
|
// glEnable(GL_RESCALE_NORMAL_EXT);
|
|
|
|
glClearDepth(1.0);
|
|
if (!useCoreContext)
|
|
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
|
|
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
|
|
glHint(GL_POINT_SMOOTH_HINT, GL_FASTEST);
|
|
glDepthFunc(GL_LEQUAL);
|
|
glFrontFace(GL_CW);
|
|
// adjust flat coloring scheme to DirectX version
|
|
#if defined(GL_ARB_provoking_vertex) || defined(GL_EXT_provoking_vertex)
|
|
extGlProvokingVertex(GL_FIRST_VERTEX_CONVENTION_EXT);
|
|
#endif
|
|
|
|
// create material renderers
|
|
createMaterialRenderers();
|
|
|
|
// set the renderstates
|
|
setRenderStates3DMode();
|
|
|
|
if (!useCoreContext)
|
|
glAlphaFunc(GL_GREATER, 0.f);
|
|
|
|
// set fog mode
|
|
setFog(FogColor, FogType, 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));
|
|
|
|
// We need to reset once more at the beginning of the first rendering.
|
|
// This fixes problems with intermediate changes to the material during texture load.
|
|
ResetRenderStates = true;
|
|
|
|
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;
|
|
if (!useCoreContext)
|
|
{
|
|
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_COMPILE_WITH_WINDOWS_DEVICE_
|
|
if (DeviceType == EIDT_WIN32)
|
|
return SwapBuffers(HDc) == TRUE;
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
|
|
if (DeviceType == EIDT_X11)
|
|
{
|
|
glXSwapBuffers(X11Display, Drawable);
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_WAYLAND_DEVICE_
|
|
if (DeviceType == EIDT_WAYLAND)
|
|
{
|
|
wl_device->swapBuffers();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_OSX_DEVICE_
|
|
if (DeviceType == EIDT_OSX)
|
|
{
|
|
OSXDevice->flush();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_
|
|
if (DeviceType == EIDT_SDL)
|
|
{
|
|
SDL_GL_SwapBuffers();
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
// todo: console device present
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//! clears the zbuffer and color buffer
|
|
void COpenGLDriver::clearBuffers(bool backBuffer, bool zBuffer, bool stencilBuffer, SColor color)
|
|
{
|
|
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;
|
|
}
|
|
|
|
if (stencilBuffer)
|
|
mask |= GL_STENCIL_BUFFER_BIT;
|
|
|
|
if (mask)
|
|
glClear(mask);
|
|
}
|
|
|
|
|
|
//! init call for rendering start
|
|
bool COpenGLDriver::beginScene(bool backBuffer, bool zBuffer, SColor color,
|
|
const SExposedVideoData& videoData, core::rect<s32>* sourceRect)
|
|
{
|
|
CNullDriver::beginScene(backBuffer, zBuffer, color, videoData, sourceRect);
|
|
|
|
switch (DeviceType)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
case EIDT_WIN32:
|
|
changeRenderContext(videoData, Win32Device);
|
|
break;
|
|
#endif
|
|
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
|
|
case EIDT_X11:
|
|
changeRenderContext(videoData, X11Device);
|
|
break;
|
|
#endif
|
|
#ifdef _IRR_COMPILE_WITH_WAYLAND_DEVICE_
|
|
case EIDT_WAYLAND:
|
|
changeRenderContext(videoData, wl_device);
|
|
break;
|
|
#endif
|
|
default:
|
|
changeRenderContext(videoData, (void*)0);
|
|
break;
|
|
}
|
|
|
|
#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_)
|
|
if (DeviceType == EIDT_SDL)
|
|
{
|
|
// todo: SDL sets glFrontFace(GL_CCW) after driver creation,
|
|
// it would be better if this was fixed elsewhere.
|
|
glFrontFace(GL_CW);
|
|
}
|
|
#endif
|
|
|
|
clearBuffers(backBuffer, zBuffer, false, color);
|
|
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.
|
|
if (!useCoreContext)
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
// first load the viewing transformation for user clip planes
|
|
if (!useCoreContext)
|
|
glLoadMatrixf((Matrices[ETS_VIEW]).pointer());
|
|
|
|
// we have to update the clip planes to the latest view matrix
|
|
for (u32 i=0; i<MaxUserClipPlanes; ++i)
|
|
{
|
|
if (UserClipPlanes[i].Enabled)
|
|
uploadClipPlane(i);
|
|
}
|
|
|
|
// now the real model-view matrix
|
|
if (!useCoreContext)
|
|
glMultMatrixf(Matrices[ETS_WORLD].pointer());
|
|
}
|
|
break;
|
|
case ETS_PROJECTION:
|
|
{
|
|
if (!useCoreContext)
|
|
glMatrixMode(GL_PROJECTION);
|
|
if (!useCoreContext)
|
|
glLoadMatrixf(mat.pointer());
|
|
}
|
|
break;
|
|
case ETS_COUNT:
|
|
return;
|
|
default:
|
|
{
|
|
const u32 i = state - ETS_TEXTURE_0;
|
|
if (i >= MATERIAL_MAX_TEXTURES)
|
|
break;
|
|
|
|
const bool isRTT = Material.getTexture(i) && Material.getTexture(i)->isRenderTarget();
|
|
|
|
if (MultiTextureExtension)
|
|
extGlActiveTexture(GL_TEXTURE0_ARB + i);
|
|
|
|
if (!useCoreContext)
|
|
glMatrixMode(GL_TEXTURE);
|
|
if (!isRTT && mat.isIdentity() && !useCoreContext)
|
|
glLoadIdentity();
|
|
else
|
|
{
|
|
GLfloat glmat[16];
|
|
if (isRTT && CurrentTarget == ERT_FRAME_BUFFER)
|
|
getGLTextureMatrix(glmat, mat * TextureFlipMatrix);
|
|
else
|
|
getGLTextureMatrix(glmat, mat);
|
|
if (!useCoreContext)
|
|
glLoadMatrixf(glmat);
|
|
}
|
|
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);
|
|
|
|
const c8* vbuf = static_cast<const c8*>(vertices);
|
|
core::array<c8> buffer;
|
|
if (!FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra])
|
|
{
|
|
//buffer vertex data, and convert colors...
|
|
buffer.set_used(vertexSize * vertexCount);
|
|
memcpy(buffer.pointer(), vertices, vertexSize * vertexCount);
|
|
vbuf = buffer.const_pointer();
|
|
|
|
// 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));
|
|
}
|
|
}
|
|
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));
|
|
}
|
|
}
|
|
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));
|
|
}
|
|
}
|
|
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, vbuf);
|
|
else
|
|
{
|
|
HWBuffer->vbo_verticesSize = vertexCount*vertexSize;
|
|
|
|
if (HWBuffer->Mapped_Vertex==scene::EHM_STATIC)
|
|
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, vbuf, GL_STATIC_DRAW);
|
|
else if (HWBuffer->Mapped_Vertex==scene::EHM_DYNAMIC)
|
|
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, vbuf, GL_DYNAMIC_DRAW);
|
|
else //scene::EHM_STREAM
|
|
extGlBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize, vbuf, 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;
|
|
|
|
updateHardwareBuffer(_HWBuffer); //check if update is needed
|
|
_HWBuffer->LastUsed=0; //reset count
|
|
|
|
#if defined(GL_ARB_vertex_buffer_object)
|
|
SHWBufferLink_opengl *HWBuffer=(SHWBufferLink_opengl*)_HWBuffer;
|
|
|
|
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, indiceToPrimitiveCount(mb->getPrimitiveType(), mb->getIndexCount()), mb->getVertexType(), mb->getPrimitiveType(), 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
|
|
}
|
|
|
|
|
|
//! Create occlusion query.
|
|
/** Use node for identification and mesh for occlusion test. */
|
|
void COpenGLDriver::addOcclusionQuery(scene::ISceneNode* node,
|
|
const scene::IMesh* mesh)
|
|
{
|
|
if (!queryFeature(EVDF_OCCLUSION_QUERY))
|
|
return;
|
|
|
|
CNullDriver::addOcclusionQuery(node, mesh);
|
|
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
|
|
if ((index != -1) && (OcclusionQueries[index].UID == 0))
|
|
extGlGenQueries(1, reinterpret_cast<GLuint*>(&OcclusionQueries[index].UID));
|
|
}
|
|
|
|
|
|
//! Remove occlusion query.
|
|
void COpenGLDriver::removeOcclusionQuery(scene::ISceneNode* node)
|
|
{
|
|
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
|
|
if (index != -1)
|
|
{
|
|
if (OcclusionQueries[index].UID != 0)
|
|
extGlDeleteQueries(1, reinterpret_cast<GLuint*>(&OcclusionQueries[index].UID));
|
|
CNullDriver::removeOcclusionQuery(node);
|
|
}
|
|
}
|
|
|
|
|
|
//! Run occlusion query. Draws mesh stored in query.
|
|
/** If the mesh shall not be rendered visible, use
|
|
overrideMaterial to disable the color and depth buffer. */
|
|
void COpenGLDriver::runOcclusionQuery(scene::ISceneNode* node, bool visible)
|
|
{
|
|
if (!node)
|
|
return;
|
|
|
|
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
|
|
if (index != -1)
|
|
{
|
|
if (OcclusionQueries[index].UID)
|
|
extGlBeginQuery(
|
|
#ifdef GL_ARB_occlusion_query
|
|
GL_SAMPLES_PASSED_ARB,
|
|
#else
|
|
0,
|
|
#endif
|
|
OcclusionQueries[index].UID);
|
|
CNullDriver::runOcclusionQuery(node,visible);
|
|
if (OcclusionQueries[index].UID)
|
|
extGlEndQuery(
|
|
#ifdef GL_ARB_occlusion_query
|
|
GL_SAMPLES_PASSED_ARB);
|
|
#else
|
|
0);
|
|
#endif
|
|
testGLError();
|
|
}
|
|
}
|
|
|
|
|
|
//! Update occlusion query. Retrieves results from GPU.
|
|
/** If the query shall not block, set the flag to false.
|
|
Update might not occur in this case, though */
|
|
void COpenGLDriver::updateOcclusionQuery(scene::ISceneNode* node, bool block)
|
|
{
|
|
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
|
|
if (index != -1)
|
|
{
|
|
// not yet started
|
|
if (OcclusionQueries[index].Run==u32(~0))
|
|
return;
|
|
GLint available = block?GL_TRUE:GL_FALSE;
|
|
if (!block)
|
|
extGlGetQueryObjectiv(OcclusionQueries[index].UID,
|
|
#ifdef GL_ARB_occlusion_query
|
|
GL_QUERY_RESULT_AVAILABLE_ARB,
|
|
#elif defined(GL_NV_occlusion_query)
|
|
GL_PIXEL_COUNT_AVAILABLE_NV,
|
|
#else
|
|
0,
|
|
#endif
|
|
&available);
|
|
testGLError();
|
|
if (available==GL_TRUE)
|
|
{
|
|
extGlGetQueryObjectiv(OcclusionQueries[index].UID,
|
|
#ifdef GL_ARB_occlusion_query
|
|
GL_QUERY_RESULT_ARB,
|
|
#elif defined(GL_NV_occlusion_query)
|
|
GL_PIXEL_COUNT_NV,
|
|
#else
|
|
0,
|
|
#endif
|
|
&available);
|
|
if (queryFeature(EVDF_OCCLUSION_QUERY))
|
|
OcclusionQueries[index].Result = available;
|
|
}
|
|
testGLError();
|
|
}
|
|
}
|
|
|
|
|
|
//! Return query result.
|
|
/** Return value is the number of visible pixels/fragments.
|
|
The value is a safe approximation, i.e. can be larger than the
|
|
actual value of pixels. */
|
|
u32 COpenGLDriver::getOcclusionQueryResult(scene::ISceneNode* node) const
|
|
{
|
|
const s32 index = OcclusionQueries.linear_search(SOccQuery(node));
|
|
if (index != -1)
|
|
return OcclusionQueries[index].Result;
|
|
else
|
|
return ~0;
|
|
}
|
|
|
|
|
|
// 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 && !FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra])
|
|
getColorBuffer(vertices, vertexCount, vType);
|
|
|
|
// 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);
|
|
|
|
//due to missing defines in OSX headers, we have to be more specific with this check
|
|
//#if defined(GL_ARB_vertex_array_bgra) || defined(GL_EXT_vertex_array_bgra)
|
|
#ifdef GL_BGRA
|
|
const GLint colorSize=(FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra])?GL_BGRA:4;
|
|
#else
|
|
const GLint colorSize=4;
|
|
#endif
|
|
if (vertices)
|
|
{
|
|
if (FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra])
|
|
{
|
|
switch (vType)
|
|
{
|
|
case EVT_STANDARD:
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].Color);
|
|
break;
|
|
case EVT_2TCOORDS:
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].Color);
|
|
break;
|
|
case EVT_TANGENTS:
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Color);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// avoid passing broken pointer to OpenGL
|
|
_IRR_DEBUG_BREAK_IF(ColorBuffer.size()==0);
|
|
glColorPointer(colorSize, 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(colorSize, 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(colorSize, 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(colorSize, 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;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
renderArray(indexList, primitiveCount, pType, iType);
|
|
|
|
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::getColorBuffer(const void* vertices, u32 vertexCount, E_VERTEX_TYPE vType)
|
|
{
|
|
// 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;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
void COpenGLDriver::renderArray(const void* indexList, u32 primitiveCount,
|
|
scene::E_PRIMITIVE_TYPE pType, E_INDEX_TYPE iType)
|
|
{
|
|
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
|
|
|
|
// prepare size and attenuation (where supported)
|
|
GLfloat particleSize=Material.Thickness;
|
|
// if (AntiAlias)
|
|
// particleSize=core::clamp(particleSize, DimSmoothedPoint[0], DimSmoothedPoint[1]);
|
|
// else
|
|
particleSize=core::clamp(particleSize, DimAliasedPoint[0], DimAliasedPoint[1]);
|
|
#if defined(GL_VERSION_1_4) || defined(GL_ARB_point_parameters) || defined(GL_EXT_point_parameters) || defined(GL_SGIS_point_parameters)
|
|
const float att[] = {1.0f, 1.0f, 0.0f};
|
|
#if defined(GL_VERSION_1_4)
|
|
extGlPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, att);
|
|
// extGlPointParameterf(GL_POINT_SIZE_MIN,1.f);
|
|
extGlPointParameterf(GL_POINT_SIZE_MAX, particleSize);
|
|
extGlPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE, 1.0f);
|
|
#elif defined(GL_ARB_point_parameters)
|
|
extGlPointParameterfv(GL_POINT_DISTANCE_ATTENUATION_ARB, att);
|
|
// extGlPointParameterf(GL_POINT_SIZE_MIN_ARB,1.f);
|
|
extGlPointParameterf(GL_POINT_SIZE_MAX_ARB, particleSize);
|
|
extGlPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0f);
|
|
#elif defined(GL_EXT_point_parameters)
|
|
extGlPointParameterfv(GL_DISTANCE_ATTENUATION_EXT, att);
|
|
// extGlPointParameterf(GL_POINT_SIZE_MIN_EXT,1.f);
|
|
extGlPointParameterf(GL_POINT_SIZE_MAX_EXT, particleSize);
|
|
extGlPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE_EXT, 1.0f);
|
|
#elif defined(GL_SGIS_point_parameters)
|
|
extGlPointParameterfv(GL_DISTANCE_ATTENUATION_SGIS, att);
|
|
// extGlPointParameterf(GL_POINT_SIZE_MIN_SGIS,1.f);
|
|
extGlPointParameterf(GL_POINT_SIZE_MAX_SGIS, particleSize);
|
|
extGlPointParameterf(GL_POINT_FADE_THRESHOLD_SIZE_SGIS, 1.0f);
|
|
#endif
|
|
#endif
|
|
glPointSize(particleSize);
|
|
|
|
#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;
|
|
}
|
|
}
|
|
|
|
|
|
//! draws a vertex primitive list in 2d
|
|
void COpenGLDriver::draw2DVertexPrimitiveList(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 (useCoreContext)
|
|
return;
|
|
|
|
if (!checkPrimitiveCount(primitiveCount))
|
|
return;
|
|
|
|
CNullDriver::draw2DVertexPrimitiveList(vertices, vertexCount, indexList, primitiveCount, vType, pType, iType);
|
|
|
|
if (vertices && !FeatureAvailable[IRR_ARB_vertex_array_bgra] && !FeatureAvailable[IRR_EXT_vertex_array_bgra])
|
|
getColorBuffer(vertices, vertexCount, vType);
|
|
|
|
// draw everything
|
|
this->setActiveTexture(0, Material.getTexture(0));
|
|
if (Material.MaterialType==EMT_ONETEXTURE_BLEND)
|
|
{
|
|
E_BLEND_FACTOR srcFact;
|
|
E_BLEND_FACTOR dstFact;
|
|
E_MODULATE_FUNC modulo;
|
|
u32 alphaSource;
|
|
unpack_textureBlendFunc ( srcFact, dstFact, modulo, alphaSource, Material.MaterialTypeParam);
|
|
setRenderStates2DMode(alphaSource&video::EAS_VERTEX_COLOR, (Material.getTexture(0) != 0), (alphaSource&video::EAS_TEXTURE) != 0);
|
|
}
|
|
else
|
|
setRenderStates2DMode(Material.MaterialType==EMT_TRANSPARENT_VERTEX_ALPHA, (Material.getTexture(0) != 0), Material.MaterialType==EMT_TRANSPARENT_ALPHA_CHANNEL);
|
|
|
|
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);
|
|
|
|
//due to missing defines in OSX headers, we have to be more specific with this check
|
|
//#if defined(GL_ARB_vertex_array_bgra) || defined(GL_EXT_vertex_array_bgra)
|
|
#ifdef GL_BGRA
|
|
const GLint colorSize=(FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra])?GL_BGRA:4;
|
|
#else
|
|
const GLint colorSize=4;
|
|
#endif
|
|
if (vertices)
|
|
{
|
|
if (FeatureAvailable[IRR_ARB_vertex_array_bgra] || FeatureAvailable[IRR_EXT_vertex_array_bgra])
|
|
{
|
|
switch (vType)
|
|
{
|
|
case EVT_STANDARD:
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].Color);
|
|
break;
|
|
case EVT_2TCOORDS:
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].Color);
|
|
break;
|
|
case EVT_TANGENTS:
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Color);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// avoid passing broken pointer to OpenGL
|
|
_IRR_DEBUG_BREAK_IF(ColorBuffer.size()==0);
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, 0, &ColorBuffer[0]);
|
|
}
|
|
}
|
|
|
|
switch (vType)
|
|
{
|
|
case EVT_STANDARD:
|
|
if (vertices)
|
|
{
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].TCoords);
|
|
glVertexPointer(2, GL_FLOAT, sizeof(S3DVertex), &(static_cast<const S3DVertex*>(vertices))[0].Pos);
|
|
}
|
|
else
|
|
{
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex), buffer_offset(24));
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex), buffer_offset(28));
|
|
glVertexPointer(2, 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)
|
|
{
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].TCoords);
|
|
glVertexPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), &(static_cast<const S3DVertex2TCoords*>(vertices))[0].Pos);
|
|
}
|
|
else
|
|
{
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertex2TCoords), buffer_offset(24));
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertex2TCoords), buffer_offset(28));
|
|
glVertexPointer(2, 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)
|
|
{
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].TCoords);
|
|
glVertexPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), &(static_cast<const S3DVertexTangents*>(vertices))[0].Pos);
|
|
}
|
|
else
|
|
{
|
|
glColorPointer(colorSize, GL_UNSIGNED_BYTE, sizeof(S3DVertexTangents), buffer_offset(24));
|
|
glTexCoordPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(28));
|
|
glVertexPointer(2, GL_FLOAT, sizeof(S3DVertexTangents), buffer_offset(0));
|
|
}
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
renderArray(indexList, primitiveCount, pType, iType);
|
|
|
|
if (MultiTextureExtension)
|
|
{
|
|
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_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
|
|
//! draws a set of 2d images, using a color and the alpha channel of the
|
|
//! texture if desired.
|
|
void COpenGLDriver::draw2DImageBatch(const video::ITexture* texture,
|
|
const core::array<core::position2d<s32> >& positions,
|
|
const core::array<core::rect<s32> >& sourceRects,
|
|
const core::rect<s32>* clipRect,
|
|
SColor color,
|
|
bool useAlphaChannelOfTexture)
|
|
{
|
|
if (!texture)
|
|
return;
|
|
|
|
const u32 drawCount = core::min_<u32>(positions.size(), sourceRects.size());
|
|
|
|
const core::dimension2d<u32>& ss = texture->getSize();
|
|
const f32 invW = 1.f / static_cast<f32>(ss.Width);
|
|
const f32 invH = 1.f / static_cast<f32>(ss.Height);
|
|
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
|
|
|
|
disableTextures(1);
|
|
if (!setActiveTexture(0, texture))
|
|
return;
|
|
setRenderStates2DMode(color.getAlpha()<255, true, useAlphaChannelOfTexture);
|
|
|
|
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
|
glBegin(GL_QUADS);
|
|
|
|
for (u32 i=0; i<drawCount; ++i)
|
|
{
|
|
if (!sourceRects[i].isValid())
|
|
continue;
|
|
|
|
core::position2d<s32> targetPos(positions[i]);
|
|
core::position2d<s32> sourcePos(sourceRects[i].UpperLeftCorner);
|
|
// This needs to be signed as it may go negative.
|
|
core::dimension2d<s32> sourceSize(sourceRects[i].getSize());
|
|
if (clipRect)
|
|
{
|
|
if (targetPos.X < clipRect->UpperLeftCorner.X)
|
|
{
|
|
sourceSize.Width += targetPos.X - clipRect->UpperLeftCorner.X;
|
|
if (sourceSize.Width <= 0)
|
|
continue;
|
|
|
|
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)
|
|
continue;
|
|
}
|
|
|
|
if (targetPos.Y < clipRect->UpperLeftCorner.Y)
|
|
{
|
|
sourceSize.Height += targetPos.Y - clipRect->UpperLeftCorner.Y;
|
|
if (sourceSize.Height <= 0)
|
|
continue;
|
|
|
|
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)
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// clip these coordinates
|
|
|
|
if (targetPos.X<0)
|
|
{
|
|
sourceSize.Width += targetPos.X;
|
|
if (sourceSize.Width <= 0)
|
|
continue;
|
|
|
|
sourcePos.X -= targetPos.X;
|
|
targetPos.X = 0;
|
|
}
|
|
|
|
if (targetPos.X + sourceSize.Width > (s32)renderTargetSize.Width)
|
|
{
|
|
sourceSize.Width -= (targetPos.X + sourceSize.Width) - renderTargetSize.Width;
|
|
if (sourceSize.Width <= 0)
|
|
continue;
|
|
}
|
|
|
|
if (targetPos.Y<0)
|
|
{
|
|
sourceSize.Height += targetPos.Y;
|
|
if (sourceSize.Height <= 0)
|
|
continue;
|
|
|
|
sourcePos.Y -= targetPos.Y;
|
|
targetPos.Y = 0;
|
|
}
|
|
|
|
if (targetPos.Y + sourceSize.Height > (s32)renderTargetSize.Height)
|
|
{
|
|
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height;
|
|
if (sourceSize.Height <= 0)
|
|
continue;
|
|
}
|
|
|
|
// ok, we've clipped everything.
|
|
// now draw it.
|
|
|
|
const core::rect<f32> tcoords(
|
|
sourcePos.X * invW,
|
|
sourcePos.Y * invH,
|
|
(sourcePos.X + sourceSize.Width) * invW,
|
|
(sourcePos.Y + sourceSize.Height) * invH);
|
|
|
|
const core::rect<s32> poss(targetPos, sourceSize);
|
|
|
|
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();
|
|
}
|
|
|
|
|
|
//! 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);
|
|
// This needs to be signed as it may go negative.
|
|
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<u32>& renderTargetSize = getCurrentRenderTargetSize();
|
|
|
|
if (targetPos.X + sourceSize.Width > (s32)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 > (s32)renderTargetSize.Height)
|
|
{
|
|
sourceSize.Height -= (targetPos.Y + sourceSize.Height) - renderTargetSize.Height;
|
|
if (sourceSize.Height <= 0)
|
|
return;
|
|
}
|
|
|
|
// ok, we've clipped everything.
|
|
// now draw it.
|
|
|
|
const core::dimension2d<u32>& ss = texture->getSize();
|
|
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,
|
|
sourcePos.Y * invH,
|
|
(sourcePos.X + sourceSize.Width) * invW,
|
|
(sourcePos.Y + sourceSize.Height) * invH);
|
|
|
|
const core::rect<s32> poss(targetPos, sourceSize);
|
|
|
|
disableTextures(1);
|
|
if (!setActiveTexture(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;
|
|
|
|
const core::dimension2d<u32>& ss = texture->getSize();
|
|
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,
|
|
sourceRect.UpperLeftCorner.Y * invH,
|
|
sourceRect.LowerRightCorner.X * invW,
|
|
sourceRect.LowerRightCorner.Y *invH);
|
|
|
|
const video::SColor temp[4] =
|
|
{
|
|
0xFFFFFFFF,
|
|
0xFFFFFFFF,
|
|
0xFFFFFFFF,
|
|
0xFFFFFFFF
|
|
};
|
|
|
|
const video::SColor* const useColor = colors ? colors : temp;
|
|
|
|
disableTextures(1);
|
|
setActiveTexture(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<u32>& 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 (!setActiveTexture(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<u32>& renderTargetSize = getCurrentRenderTargetSize();
|
|
glScissor(clipRect->UpperLeftCorner.X, renderTargetSize.Height-clipRect->LowerRightCorner.Y,
|
|
clipRect->getWidth(),clipRect->getHeight());
|
|
}
|
|
|
|
const core::dimension2d<u32>& ss = texture->getSize();
|
|
core::position2d<s32> targetPos(pos);
|
|
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,
|
|
sourceRects[currentIndex].UpperLeftCorner.Y * invH,
|
|
sourceRects[currentIndex].LowerRightCorner.X * invW,
|
|
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)
|
|
{
|
|
if (start==end)
|
|
drawPixel(start.X, start.Y, color);
|
|
else
|
|
{
|
|
disableTextures();
|
|
setRenderStates2DMode(color.getAlpha() < 255, false, false);
|
|
|
|
glBegin(GL_LINES);
|
|
glColor4ub(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
|
|
GLfloat x=(GLfloat)start.X;
|
|
GLfloat y=(GLfloat)start.Y;
|
|
if (x>end.X)
|
|
x += 0.5f;
|
|
if (y>end.Y)
|
|
y += 0.5f;
|
|
glVertex2f(GLfloat(x), GLfloat(y));
|
|
x=(GLfloat)end.X;
|
|
y=(GLfloat)end.Y;
|
|
if (x>start.X)
|
|
x += 0.5f;
|
|
if (y>start.Y)
|
|
y += 0.5f;
|
|
glVertex2f(GLfloat(x), GLfloat(y));
|
|
glEnd();
|
|
}
|
|
}
|
|
|
|
//! Draws a pixel
|
|
void COpenGLDriver::drawPixel(u32 x, u32 y, const SColor &color)
|
|
{
|
|
const core::dimension2d<u32>& 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::setActiveTexture(u32 stage, const video::ITexture* texture)
|
|
{
|
|
if (stage >= MaxSupportedTextures)
|
|
return false;
|
|
|
|
if (CurrentTexture[stage]==texture)
|
|
return true;
|
|
|
|
if (MultiTextureExtension)
|
|
extGlActiveTexture(GL_TEXTURE0_ARB + stage);
|
|
|
|
CurrentTexture.set(stage,texture);
|
|
|
|
if (!texture)
|
|
{
|
|
if (!useCoreContext)
|
|
glDisable(GL_TEXTURE_2D);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (texture->getDriverType() != EDT_OPENGL)
|
|
{
|
|
if (!useCoreContext)
|
|
glDisable(GL_TEXTURE_2D);
|
|
CurrentTexture.set(stage, 0);
|
|
os::Printer::log("Fatal Error: Tried to set a texture not owned by this driver.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (!useCoreContext)
|
|
glEnable(GL_TEXTURE_2D);
|
|
glBindTexture(GL_TEXTURE_2D, 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<MaxSupportedTextures; ++i)
|
|
result &= setActiveTexture(i, 0);
|
|
return result;
|
|
}
|
|
|
|
|
|
//! creates a matrix in supplied GLfloat array to pass to OpenGL
|
|
inline void COpenGLDriver::getGLMatrix(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::getGLTextureMatrix(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 io::path& name, void* mipmapData)
|
|
{
|
|
return new COpenGLTexture(surface, name, mipmapData, this);
|
|
}
|
|
|
|
|
|
//! Sets a material. All 3d drawing functions draw geometry now using this material.
|
|
void COpenGLDriver::setMaterial(const SMaterial& material)
|
|
{
|
|
Material = material;
|
|
OverrideMaterial.apply(Material);
|
|
|
|
for (s32 i = MaxTextureUnits-1; i>= 0; --i)
|
|
{
|
|
setActiveTexture(i, material.getTexture(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
|
|
};
|
|
// _IRR_DEBUG_BREAK_IF(true);
|
|
return true;
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
|
|
//! sets the needed renderstates
|
|
void COpenGLDriver::setRenderStates3DMode()
|
|
{
|
|
if (CurrentRenderMode != ERM_3D)
|
|
{
|
|
// Reset Texture Stages
|
|
glDisable(GL_BLEND);
|
|
if (!useCoreContext)
|
|
glDisable(GL_ALPHA_TEST);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
// switch back the matrices
|
|
if (!useCoreContext)
|
|
glMatrixMode(GL_MODELVIEW);
|
|
if (!useCoreContext)
|
|
glLoadMatrixf((Matrices[ETS_VIEW] * Matrices[ETS_WORLD]).pointer());
|
|
|
|
if (!useCoreContext)
|
|
glMatrixMode(GL_PROJECTION);
|
|
if (!useCoreContext)
|
|
glLoadMatrixf(Matrices[ETS_PROJECTION].pointer());
|
|
|
|
ResetRenderStates = true;
|
|
#ifdef GL_EXT_clip_volume_hint
|
|
// if (FeatureAvailable[IRR_EXT_clip_volume_hint])
|
|
// glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_NICEST);
|
|
#endif
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
//! Get native wrap mode value
|
|
GLint COpenGLDriver::getTextureWrapMode(const u8 clamp)
|
|
{
|
|
GLint mode=GL_REPEAT;
|
|
switch (clamp)
|
|
{
|
|
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;
|
|
case ETC_MIRROR_CLAMP:
|
|
#ifdef GL_EXT_texture_mirror_clamp
|
|
if (FeatureAvailable[IRR_EXT_texture_mirror_clamp])
|
|
mode=GL_MIRROR_CLAMP_EXT;
|
|
else
|
|
#endif
|
|
#if defined(GL_ATI_texture_mirror_once)
|
|
if (FeatureAvailable[IRR_ATI_texture_mirror_once])
|
|
mode=GL_MIRROR_CLAMP_ATI;
|
|
else
|
|
#endif
|
|
mode=GL_CLAMP;
|
|
break;
|
|
case ETC_MIRROR_CLAMP_TO_EDGE:
|
|
#ifdef GL_EXT_texture_mirror_clamp
|
|
if (FeatureAvailable[IRR_EXT_texture_mirror_clamp])
|
|
mode=GL_MIRROR_CLAMP_TO_EDGE_EXT;
|
|
else
|
|
#endif
|
|
#if defined(GL_ATI_texture_mirror_once)
|
|
if (FeatureAvailable[IRR_ATI_texture_mirror_once])
|
|
mode=GL_MIRROR_CLAMP_TO_EDGE_ATI;
|
|
else
|
|
#endif
|
|
mode=GL_CLAMP;
|
|
break;
|
|
case ETC_MIRROR_CLAMP_TO_BORDER:
|
|
#ifdef GL_EXT_texture_mirror_clamp
|
|
if (FeatureAvailable[IRR_EXT_texture_mirror_clamp])
|
|
mode=GL_MIRROR_CLAMP_TO_BORDER_EXT;
|
|
else
|
|
#endif
|
|
mode=GL_CLAMP;
|
|
break;
|
|
}
|
|
return mode;
|
|
}
|
|
|
|
|
|
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 (!CurrentTexture[u])
|
|
continue;
|
|
if (MultiTextureExtension)
|
|
extGlActiveTexture(GL_TEXTURE0_ARB + u);
|
|
else if (u>0)
|
|
break; // stop loop
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, getTextureWrapMode(material.TextureLayer[u].TextureWrapU));
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, getTextureWrapMode(material.TextureLayer[u].TextureWrapV));
|
|
}
|
|
}
|
|
|
|
|
|
//! 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.ColorMaterial != material.ColorMaterial)
|
|
{
|
|
switch (material.ColorMaterial)
|
|
{
|
|
case ECM_NONE:
|
|
glDisable(GL_COLOR_MATERIAL);
|
|
break;
|
|
case ECM_DIFFUSE:
|
|
if (!useCoreContext)
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);
|
|
break;
|
|
case ECM_AMBIENT:
|
|
if (!useCoreContext)
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT);
|
|
break;
|
|
case ECM_EMISSIVE:
|
|
if (!useCoreContext)
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_EMISSION);
|
|
break;
|
|
case ECM_SPECULAR:
|
|
if (!useCoreContext)
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR);
|
|
break;
|
|
case ECM_DIFFUSE_AND_AMBIENT:
|
|
if (!useCoreContext)
|
|
glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
|
|
break;
|
|
}
|
|
if (material.ColorMaterial != ECM_NONE && !useCoreContext)
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
}
|
|
|
|
if (resetAllRenderStates ||
|
|
lastmaterial.AmbientColor != material.AmbientColor ||
|
|
lastmaterial.DiffuseColor != material.DiffuseColor ||
|
|
lastmaterial.EmissiveColor != material.EmissiveColor ||
|
|
lastmaterial.ColorMaterial != material.ColorMaterial)
|
|
{
|
|
GLfloat color[4];
|
|
|
|
const f32 inv = 1.0f / 255.0f;
|
|
|
|
if ((material.ColorMaterial != video::ECM_AMBIENT) &&
|
|
(material.ColorMaterial != video::ECM_DIFFUSE_AND_AMBIENT))
|
|
{
|
|
color[0] = material.AmbientColor.getRed() * inv;
|
|
color[1] = material.AmbientColor.getGreen() * inv;
|
|
color[2] = material.AmbientColor.getBlue() * inv;
|
|
color[3] = material.AmbientColor.getAlpha() * inv;
|
|
if (!useCoreContext)
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color);
|
|
}
|
|
|
|
if ((material.ColorMaterial != video::ECM_DIFFUSE) &&
|
|
(material.ColorMaterial != video::ECM_DIFFUSE_AND_AMBIENT))
|
|
{
|
|
color[0] = material.DiffuseColor.getRed() * inv;
|
|
color[1] = material.DiffuseColor.getGreen() * inv;
|
|
color[2] = material.DiffuseColor.getBlue() * inv;
|
|
color[3] = material.DiffuseColor.getAlpha() * inv;
|
|
if (!useCoreContext)
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color);
|
|
}
|
|
|
|
if (material.ColorMaterial != video::ECM_EMISSIVE)
|
|
{
|
|
color[0] = material.EmissiveColor.getRed() * inv;
|
|
color[1] = material.EmissiveColor.getGreen() * inv;
|
|
color[2] = material.EmissiveColor.getBlue() * inv;
|
|
color[3] = material.EmissiveColor.getAlpha() * inv;
|
|
if (!useCoreContext)
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color);
|
|
}
|
|
}
|
|
|
|
if (resetAllRenderStates ||
|
|
lastmaterial.SpecularColor != material.SpecularColor ||
|
|
lastmaterial.Shininess != material.Shininess ||
|
|
lastmaterial.ColorMaterial != material.ColorMaterial)
|
|
{
|
|
GLfloat color[4]={0.f,0.f,0.f,1.f};
|
|
const f32 inv = 1.0f / 255.0f;
|
|
|
|
if (!useCoreContext)
|
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.Shininess);
|
|
// disable Specular colors if no shininess is set
|
|
if ((material.Shininess != 0.0f) &&
|
|
(material.ColorMaterial != video::ECM_SPECULAR))
|
|
{
|
|
#ifdef GL_EXT_separate_specular_color
|
|
if (FeatureAvailable[IRR_EXT_separate_specular_color] && !useCoreContext)
|
|
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);
|
|
#endif
|
|
color[0] = material.SpecularColor.getRed() * inv;
|
|
color[1] = material.SpecularColor.getGreen() * inv;
|
|
color[2] = material.SpecularColor.getBlue() * inv;
|
|
color[3] = material.SpecularColor.getAlpha() * inv;
|
|
}
|
|
#ifdef GL_EXT_separate_specular_color
|
|
else if (FeatureAvailable[IRR_EXT_separate_specular_color] && !useCoreContext)
|
|
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR);
|
|
#endif
|
|
if (!useCoreContext)
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, 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 (!CurrentTexture[i])
|
|
continue;
|
|
if (MultiTextureExtension)
|
|
extGlActiveTexture(GL_TEXTURE0_ARB + i);
|
|
else if (i>0)
|
|
break;
|
|
|
|
#ifdef GL_EXT_texture_lod_bias
|
|
if (FeatureAvailable[IRR_EXT_texture_lod_bias])
|
|
{
|
|
if (material.TextureLayer[i].LODBias)
|
|
{
|
|
const float tmp = core::clamp(material.TextureLayer[i].LODBias * 0.125f, -MaxTextureLODBias, MaxTextureLODBias);
|
|
glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, tmp);
|
|
}
|
|
else
|
|
glTexEnvf(GL_TEXTURE_FILTER_CONTROL_EXT, GL_TEXTURE_LOD_BIAS_EXT, 0.f);
|
|
}
|
|
#endif
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
|
|
(material.TextureLayer[i].BilinearFilter || material.TextureLayer[i].TrilinearFilter) ? GL_LINEAR : GL_NEAREST);
|
|
|
|
if (material.UseMipMaps && CurrentTexture[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 (FeatureAvailable[IRR_EXT_texture_filter_anisotropic])
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
|
|
material.TextureLayer[i].AnisotropicFilter>1 ? core::min_(MaxAnisotropy, material.TextureLayer[i].AnisotropicFilter) : 1);
|
|
#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 && !useCoreContext)
|
|
glShadeModel(GL_SMOOTH);
|
|
else if (!useCoreContext)
|
|
glShadeModel(GL_FLAT);
|
|
}
|
|
|
|
// lighting
|
|
if (resetAllRenderStates || (lastmaterial.Lighting != material.Lighting))
|
|
{
|
|
if (material.Lighting && !useCoreContext)
|
|
glEnable(GL_LIGHTING);
|
|
else if (!useCoreContext)
|
|
glDisable(GL_LIGHTING);
|
|
}
|
|
|
|
// zbuffer
|
|
if (resetAllRenderStates || lastmaterial.ZBuffer != material.ZBuffer)
|
|
{
|
|
switch (material.ZBuffer)
|
|
{
|
|
case ECFN_NEVER:
|
|
glDisable(GL_DEPTH_TEST);
|
|
break;
|
|
case ECFN_LESSEQUAL:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_LEQUAL);
|
|
break;
|
|
case ECFN_EQUAL:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_EQUAL);
|
|
break;
|
|
case ECFN_LESS:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_LESS);
|
|
break;
|
|
case ECFN_NOTEQUAL:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_NOTEQUAL);
|
|
break;
|
|
case ECFN_GREATEREQUAL:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_GEQUAL);
|
|
break;
|
|
case ECFN_GREATER:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_GREATER);
|
|
break;
|
|
case ECFN_ALWAYS:
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthFunc(GL_ALWAYS);
|
|
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 && !useCoreContext)
|
|
glEnable(GL_FOG);
|
|
else if (!useCoreContext)
|
|
glDisable(GL_FOG);
|
|
}
|
|
|
|
// normalization
|
|
if (resetAllRenderStates || lastmaterial.NormalizeNormals != material.NormalizeNormals)
|
|
{
|
|
if (material.NormalizeNormals && !useCoreContext)
|
|
glEnable(GL_NORMALIZE);
|
|
else if (!useCoreContext)
|
|
glDisable(GL_NORMALIZE);
|
|
}
|
|
|
|
// Color Mask
|
|
if (resetAllRenderStates || lastmaterial.ColorMask != material.ColorMask)
|
|
{
|
|
glColorMask(
|
|
(material.ColorMask & ECP_RED)?GL_TRUE:GL_FALSE,
|
|
(material.ColorMask & ECP_GREEN)?GL_TRUE:GL_FALSE,
|
|
(material.ColorMask & ECP_BLUE)?GL_TRUE:GL_FALSE,
|
|
(material.ColorMask & ECP_ALPHA)?GL_TRUE:GL_FALSE);
|
|
}
|
|
|
|
if (queryFeature(EVDF_BLEND_OPERATIONS) &&
|
|
(resetAllRenderStates|| lastmaterial.BlendOperation != material.BlendOperation))
|
|
{
|
|
if (material.BlendOperation==EBO_NONE)
|
|
glDisable(GL_BLEND);
|
|
else
|
|
{
|
|
glEnable(GL_BLEND);
|
|
#if defined(GL_EXT_blend_subtract) || defined(GL_EXT_blend_minmax) || defined(GL_EXT_blend_logic_op) || defined(GL_VERSION_1_2)
|
|
switch (material.BlendOperation)
|
|
{
|
|
case EBO_SUBTRACT:
|
|
#if defined(GL_EXT_blend_subtract)
|
|
if (FeatureAvailable[IRR_EXT_blend_subtract] || (Version>=120))
|
|
extGlBlendEquation(GL_FUNC_SUBTRACT_EXT);
|
|
#elif defined(GL_VERSION_1_2)
|
|
if (Version>=120)
|
|
extGlBlendEquation(GL_FUNC_SUBTRACT);
|
|
#endif
|
|
break;
|
|
case EBO_REVSUBTRACT:
|
|
#if defined(GL_EXT_blend_subtract)
|
|
if (FeatureAvailable[IRR_EXT_blend_subtract] || (Version>=120))
|
|
extGlBlendEquation(GL_FUNC_REVERSE_SUBTRACT_EXT);
|
|
#elif defined(GL_VERSION_1_2)
|
|
if (Version>=120)
|
|
extGlBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
|
|
#endif
|
|
break;
|
|
case EBO_MIN:
|
|
#if defined(GL_EXT_blend_minmax)
|
|
if (FeatureAvailable[IRR_EXT_blend_minmax] || (Version>=120))
|
|
extGlBlendEquation(GL_MIN_EXT);
|
|
#elif defined(GL_VERSION_1_2)
|
|
if (Version>=120)
|
|
extGlBlendEquation(GL_MIN);
|
|
#endif
|
|
break;
|
|
case EBO_MAX:
|
|
#if defined(GL_EXT_blend_minmax)
|
|
if (FeatureAvailable[IRR_EXT_blend_minmax] || (Version>=120))
|
|
extGlBlendEquation(GL_MAX_EXT);
|
|
#elif defined(GL_VERSION_1_2)
|
|
if (Version>=120)
|
|
extGlBlendEquation(GL_MAX);
|
|
#endif
|
|
break;
|
|
case EBO_MIN_FACTOR:
|
|
#if defined(GL_AMD_blend_minmax_factor)
|
|
if (FeatureAvailable[IRR_AMD_blend_minmax_factor])
|
|
extGlBlendEquation(GL_FACTOR_MIN_AMD);
|
|
#endif
|
|
// fallback in case of missing extension
|
|
#if defined(GL_VERSION_1_2)
|
|
#if defined(GL_AMD_blend_minmax_factor)
|
|
else
|
|
#endif
|
|
if (Version>=120)
|
|
extGlBlendEquation(GL_MIN);
|
|
#endif
|
|
break;
|
|
case EBO_MAX_FACTOR:
|
|
#if defined(GL_AMD_blend_minmax_factor)
|
|
if (FeatureAvailable[IRR_AMD_blend_minmax_factor])
|
|
extGlBlendEquation(GL_FACTOR_MAX_AMD);
|
|
#endif
|
|
// fallback in case of missing extension
|
|
#if defined(GL_VERSION_1_2)
|
|
#if defined(GL_AMD_blend_minmax_factor)
|
|
else
|
|
#endif
|
|
if (Version>=120)
|
|
extGlBlendEquation(GL_MAX);
|
|
#endif
|
|
break;
|
|
case EBO_MIN_ALPHA:
|
|
#if defined(GL_SGIX_blend_alpha_minmax)
|
|
if (FeatureAvailable[IRR_SGIX_blend_alpha_minmax])
|
|
extGlBlendEquation(GL_ALPHA_MIN_SGIX);
|
|
// fallback in case of missing extension
|
|
else
|
|
if (FeatureAvailable[IRR_EXT_blend_minmax])
|
|
extGlBlendEquation(GL_MIN_EXT);
|
|
#endif
|
|
break;
|
|
case EBO_MAX_ALPHA:
|
|
#if defined(GL_SGIX_blend_alpha_minmax)
|
|
if (FeatureAvailable[IRR_SGIX_blend_alpha_minmax])
|
|
extGlBlendEquation(GL_ALPHA_MAX_SGIX);
|
|
// fallback in case of missing extension
|
|
else
|
|
if (FeatureAvailable[IRR_EXT_blend_minmax])
|
|
extGlBlendEquation(GL_MAX_EXT);
|
|
#endif
|
|
break;
|
|
default:
|
|
#if defined(GL_EXT_blend_subtract) || defined(GL_EXT_blend_minmax) || defined(GL_EXT_blend_logic_op)
|
|
extGlBlendEquation(GL_FUNC_ADD_EXT);
|
|
#elif defined(GL_VERSION_1_2)
|
|
extGlBlendEquation(GL_FUNC_ADD);
|
|
#endif
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Polygon Offset
|
|
if (queryFeature(EVDF_POLYGON_OFFSET) && (resetAllRenderStates ||
|
|
lastmaterial.PolygonOffsetDirection != material.PolygonOffsetDirection ||
|
|
lastmaterial.PolygonOffsetFactor != material.PolygonOffsetFactor))
|
|
{
|
|
glDisable(lastmaterial.Wireframe?GL_POLYGON_OFFSET_LINE:lastmaterial.PointCloud?GL_POLYGON_OFFSET_POINT:GL_POLYGON_OFFSET_FILL);
|
|
if (material.PolygonOffsetFactor)
|
|
{
|
|
glDisable(material.Wireframe?GL_POLYGON_OFFSET_LINE:material.PointCloud?GL_POLYGON_OFFSET_POINT:GL_POLYGON_OFFSET_FILL);
|
|
glEnable(material.Wireframe?GL_POLYGON_OFFSET_LINE:material.PointCloud?GL_POLYGON_OFFSET_POINT:GL_POLYGON_OFFSET_FILL);
|
|
}
|
|
if (material.PolygonOffsetDirection==EPO_BACK)
|
|
glPolygonOffset(1.0f, (GLfloat)material.PolygonOffsetFactor);
|
|
else
|
|
glPolygonOffset(-1.0f, (GLfloat)-material.PolygonOffsetFactor);
|
|
}
|
|
|
|
// thickness
|
|
if (resetAllRenderStates || lastmaterial.Thickness != material.Thickness)
|
|
{
|
|
if (AntiAlias)
|
|
{
|
|
// glPointSize(core::clamp(static_cast<GLfloat>(material.Thickness), DimSmoothedPoint[0], DimSmoothedPoint[1]));
|
|
// we don't use point smoothing
|
|
glPointSize(core::clamp(static_cast<GLfloat>(material.Thickness), DimAliasedPoint[0], DimAliasedPoint[1]));
|
|
glLineWidth(core::clamp(static_cast<GLfloat>(material.Thickness), DimSmoothedLine[0], DimSmoothedLine[1]));
|
|
}
|
|
else
|
|
{
|
|
glPointSize(core::clamp(static_cast<GLfloat>(material.Thickness), DimAliasedPoint[0], DimAliasedPoint[1]));
|
|
glLineWidth(core::clamp(static_cast<GLfloat>(material.Thickness), DimAliasedLine[0], DimAliasedLine[1]));
|
|
}
|
|
}
|
|
|
|
// Anti aliasing
|
|
if (resetAllRenderStates || lastmaterial.AntiAliasing != material.AntiAliasing)
|
|
{
|
|
if (FeatureAvailable[IRR_ARB_multisample])
|
|
{
|
|
if (material.AntiAliasing & EAAM_ALPHA_TO_COVERAGE)
|
|
glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
|
|
else if (lastmaterial.AntiAliasing & EAAM_ALPHA_TO_COVERAGE)
|
|
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB);
|
|
|
|
if ((AntiAlias >= 2) && (material.AntiAliasing & (EAAM_SIMPLE|EAAM_QUALITY)))
|
|
{
|
|
glEnable(GL_MULTISAMPLE_ARB);
|
|
#ifdef GL_NV_multisample_filter_hint
|
|
if (FeatureAvailable[IRR_NV_multisample_filter_hint])
|
|
{
|
|
if ((material.AntiAliasing & EAAM_QUALITY) == EAAM_QUALITY)
|
|
glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
|
|
else
|
|
glHint(GL_MULTISAMPLE_FILTER_HINT_NV, GL_NICEST);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
glDisable(GL_MULTISAMPLE_ARB);
|
|
}
|
|
if ((material.AntiAliasing & EAAM_LINE_SMOOTH) != (lastmaterial.AntiAliasing & EAAM_LINE_SMOOTH))
|
|
{
|
|
if (material.AntiAliasing & EAAM_LINE_SMOOTH)
|
|
glEnable(GL_LINE_SMOOTH);
|
|
else if (lastmaterial.AntiAliasing & EAAM_LINE_SMOOTH)
|
|
glDisable(GL_LINE_SMOOTH);
|
|
}
|
|
if ((material.AntiAliasing & EAAM_POINT_SMOOTH) != (lastmaterial.AntiAliasing & EAAM_POINT_SMOOTH))
|
|
{
|
|
if (material.AntiAliasing & EAAM_POINT_SMOOTH)
|
|
// often in software, and thus very slow
|
|
glEnable(GL_POINT_SMOOTH);
|
|
else if (lastmaterial.AntiAliasing & EAAM_POINT_SMOOTH)
|
|
glDisable(GL_POINT_SMOOTH);
|
|
}
|
|
}
|
|
|
|
setWrapMode(material);
|
|
|
|
// be sure to leave in texture stage 0
|
|
if (MultiTextureExtension)
|
|
extGlActiveTexture(GL_TEXTURE0_ARB);
|
|
}
|
|
|
|
|
|
//! Enable the 2d override material
|
|
void COpenGLDriver::enableMaterial2D(bool enable)
|
|
{
|
|
if (!enable)
|
|
CurrentRenderMode = ERM_NONE;
|
|
CNullDriver::enableMaterial2D(enable);
|
|
}
|
|
|
|
|
|
//! 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();
|
|
}
|
|
if (Transformation3DChanged)
|
|
{
|
|
glMatrixMode(GL_PROJECTION);
|
|
|
|
const core::dimension2d<u32>& renderTargetSize = getCurrentRenderTargetSize();
|
|
core::matrix4 m(core::matrix4::EM4CONST_NOTHING);
|
|
m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0f, 1.0f);
|
|
m.setTranslation(core::vector3df(-1,1,0));
|
|
glLoadMatrixf(m.pointer());
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
glTranslatef(0.375f, 0.375f, 0.0f);
|
|
|
|
// Make sure we set first texture matrix
|
|
if (MultiTextureExtension)
|
|
extGlActiveTexture(GL_TEXTURE0_ARB);
|
|
|
|
Transformation3DChanged = false;
|
|
}
|
|
if (!OverrideMaterial2DEnabled)
|
|
{
|
|
setBasicRenderStates(InitMaterial2D, LastMaterial, true);
|
|
LastMaterial = InitMaterial2D;
|
|
}
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
#ifdef GL_EXT_clip_volume_hint
|
|
// if (FeatureAvailable[IRR_EXT_clip_volume_hint])
|
|
// glHint(GL_CLIP_VOLUME_CLIPPING_HINT_EXT, GL_FASTEST);
|
|
#endif
|
|
|
|
}
|
|
if (OverrideMaterial2DEnabled)
|
|
{
|
|
OverrideMaterial2D.Lighting=false;
|
|
setBasicRenderStates(OverrideMaterial2D, LastMaterial, false);
|
|
LastMaterial = OverrideMaterial2D;
|
|
}
|
|
|
|
// no alphaChannel without texture
|
|
alphaChannel &= texture;
|
|
|
|
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)
|
|
{
|
|
if (!OverrideMaterial2DEnabled)
|
|
{
|
|
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);
|
|
}
|
|
Material.setTexture(0, const_cast<video::ITexture*>(CurrentTexture[0]));
|
|
setTransform(ETS_TEXTURE_0, core::IdentityMatrix);
|
|
// Due to the transformation change, the previous line would call a reset each frame
|
|
// but we can safely reset the variable as it was false before
|
|
Transformation3DChanged=false;
|
|
|
|
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
|
|
{
|
|
#if defined(GL_ARB_texture_env_combine) || defined(GL_EXT_texture_env_combine)
|
|
if (FeatureAvailable[IRR_ARB_texture_env_combine]||FeatureAvailable[IRR_EXT_texture_env_combine])
|
|
{
|
|
#ifdef GL_ARB_texture_env_combine
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE);
|
|
// rgb always modulates
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
|
|
#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);
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (alpha)
|
|
{
|
|
#if defined(GL_ARB_texture_env_combine) || defined(GL_EXT_texture_env_combine)
|
|
if (FeatureAvailable[IRR_ARB_texture_env_combine]||FeatureAvailable[IRR_EXT_texture_env_combine])
|
|
{
|
|
#ifdef GL_ARB_texture_env_combine
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PRIMARY_COLOR_ARB);
|
|
// rgb always modulates
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_MODULATE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE);
|
|
glTexEnvf(GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PRIMARY_COLOR_ARB);
|
|
#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_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);
|
|
#endif
|
|
}
|
|
else
|
|
#endif
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
}
|
|
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()
|
|
{
|
|
if (!useCoreContext)
|
|
{
|
|
for (s32 i=0; i<MaxLights; ++i)
|
|
glDisable(GL_LIGHT0 + i);
|
|
}
|
|
|
|
RequestedLights.clear();
|
|
|
|
CNullDriver::deleteAllDynamicLights();
|
|
}
|
|
|
|
|
|
//! adds a dynamic light
|
|
s32 COpenGLDriver::addDynamicLight(const SLight& light)
|
|
{
|
|
CNullDriver::addDynamicLight(light);
|
|
|
|
RequestedLights.push_back(RequestedLight(light));
|
|
|
|
u32 newLightIndex = RequestedLights.size() - 1;
|
|
|
|
// Try and assign a hardware light just now, but don't worry if I can't
|
|
assignHardwareLight(newLightIndex);
|
|
|
|
return (s32)newLightIndex;
|
|
}
|
|
|
|
|
|
void COpenGLDriver::assignHardwareLight(u32 lightIndex)
|
|
{
|
|
setTransform(ETS_WORLD, core::matrix4());
|
|
|
|
s32 lidx;
|
|
for (lidx=GL_LIGHT0; lidx < GL_LIGHT0 + MaxLights; ++lidx)
|
|
{
|
|
if(!glIsEnabled(lidx))
|
|
{
|
|
RequestedLights[lightIndex].HardwareLightIndex = lidx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(lidx == GL_LIGHT0 + MaxLights) // There's no room for it just now
|
|
return;
|
|
|
|
GLfloat data[4];
|
|
const SLight & light = RequestedLights[lightIndex].LightData;
|
|
|
|
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;
|
|
default:
|
|
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);
|
|
}
|
|
|
|
|
|
//! Turns a dynamic light on or off
|
|
//! \param lightIndex: the index returned by addDynamicLight
|
|
//! \param turnOn: true to turn the light on, false to turn it off
|
|
void COpenGLDriver::turnLightOn(s32 lightIndex, bool turnOn)
|
|
{
|
|
if(lightIndex < 0 || lightIndex >= (s32)RequestedLights.size())
|
|
return;
|
|
|
|
RequestedLight & requestedLight = RequestedLights[lightIndex];
|
|
|
|
requestedLight.DesireToBeOn = turnOn;
|
|
|
|
if(turnOn)
|
|
{
|
|
if(-1 == requestedLight.HardwareLightIndex)
|
|
assignHardwareLight(lightIndex);
|
|
}
|
|
else
|
|
{
|
|
if(-1 != requestedLight.HardwareLightIndex)
|
|
{
|
|
// It's currently assigned, so free up the hardware light
|
|
glDisable(requestedLight.HardwareLightIndex);
|
|
requestedLight.HardwareLightIndex = -1;
|
|
|
|
// Now let the first light that's waiting on a free hardware light grab it
|
|
for(u32 requested = 0; requested < RequestedLights.size(); ++requested)
|
|
if(RequestedLights[requested].DesireToBeOn
|
|
&&
|
|
-1 == RequestedLights[requested].HardwareLightIndex)
|
|
{
|
|
assignHardwareLight(requested);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//! 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};
|
|
if (!useCoreContext)
|
|
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)
|
|
{
|
|
if (area == ViewPort)
|
|
return;
|
|
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::array<core::vector3df>& triangles, bool zfail, u32 debugDataVisible)
|
|
{
|
|
const u32 count=triangles.size();
|
|
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_LESS);
|
|
glDepthMask(GL_FALSE); // no depth buffer writing
|
|
if (debugDataVisible & scene::EDS_MESH_WIRE_OVERLAY)
|
|
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
if (!(debugDataVisible & (scene::EDS_SKELETON|scene::EDS_MESH_WIRE_OVERLAY)))
|
|
{
|
|
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // no color buffer drawing
|
|
glEnable(GL_STENCIL_TEST);
|
|
}
|
|
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
glVertexPointer(3,GL_FLOAT,sizeof(core::vector3df),triangles.const_pointer());
|
|
glStencilMask(~0);
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
GLenum incr = GL_INCR;
|
|
GLenum decr = GL_DECR;
|
|
#ifdef GL_EXT_stencil_wrap
|
|
if (FeatureAvailable[IRR_EXT_stencil_wrap])
|
|
{
|
|
incr = GL_INCR_WRAP_EXT;
|
|
decr = GL_DECR_WRAP_EXT;
|
|
}
|
|
#endif
|
|
#ifdef GL_NV_depth_clamp
|
|
if (FeatureAvailable[IRR_NV_depth_clamp])
|
|
glEnable(GL_DEPTH_CLAMP_NV);
|
|
#endif
|
|
|
|
// 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);
|
|
glDisable(GL_CULL_FACE);
|
|
if (zfail)
|
|
{
|
|
extGlActiveStencilFace(GL_BACK);
|
|
glStencilOp(GL_KEEP, incr, GL_KEEP);
|
|
glStencilMask(~0);
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
extGlActiveStencilFace(GL_FRONT);
|
|
glStencilOp(GL_KEEP, decr, GL_KEEP);
|
|
}
|
|
else // zpass
|
|
{
|
|
extGlActiveStencilFace(GL_BACK);
|
|
glStencilOp(GL_KEEP, GL_KEEP, decr);
|
|
glStencilMask(~0);
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
|
|
extGlActiveStencilFace(GL_FRONT);
|
|
glStencilOp(GL_KEEP, GL_KEEP, incr);
|
|
}
|
|
glStencilMask(~0);
|
|
glStencilFunc(GL_ALWAYS, 0, ~0);
|
|
glDrawArrays(GL_TRIANGLES,0,count);
|
|
glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT);
|
|
}
|
|
else
|
|
#endif
|
|
if (FeatureAvailable[IRR_ATI_separate_stencil])
|
|
{
|
|
glDisable(GL_CULL_FACE);
|
|
if (zfail)
|
|
{
|
|
extGlStencilOpSeparate(GL_BACK, GL_KEEP, incr, GL_KEEP);
|
|
extGlStencilOpSeparate(GL_FRONT, GL_KEEP, decr, GL_KEEP);
|
|
}
|
|
else // zpass
|
|
{
|
|
extGlStencilOpSeparate(GL_BACK, GL_KEEP, GL_KEEP, decr);
|
|
extGlStencilOpSeparate(GL_FRONT, GL_KEEP, GL_KEEP, incr);
|
|
}
|
|
extGlStencilFuncSeparate(GL_ALWAYS, GL_ALWAYS, 0, ~0);
|
|
glStencilMask(~0);
|
|
glDrawArrays(GL_TRIANGLES,0,count);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
glEnable(GL_CULL_FACE);
|
|
if (zfail)
|
|
{
|
|
glCullFace(GL_FRONT);
|
|
glStencilOp(GL_KEEP, incr, GL_KEEP);
|
|
glDrawArrays(GL_TRIANGLES,0,count);
|
|
|
|
glCullFace(GL_BACK);
|
|
glStencilOp(GL_KEEP, decr, GL_KEEP);
|
|
glDrawArrays(GL_TRIANGLES,0,count);
|
|
}
|
|
else // zpass
|
|
{
|
|
glCullFace(GL_BACK);
|
|
glStencilOp(GL_KEEP, GL_KEEP, incr);
|
|
glDrawArrays(GL_TRIANGLES,0,count);
|
|
|
|
glCullFace(GL_FRONT);
|
|
glStencilOp(GL_KEEP, GL_KEEP, decr);
|
|
glDrawArrays(GL_TRIANGLES,0,count);
|
|
}
|
|
}
|
|
#ifdef GL_NV_depth_clamp
|
|
if (FeatureAvailable[IRR_NV_depth_clamp])
|
|
glDisable(GL_DEPTH_CLAMP_NV);
|
|
#endif
|
|
|
|
glDisable(GL_POLYGON_OFFSET_FILL);
|
|
glDisableClientState(GL_VERTEX_ARRAY); //not stored on stack
|
|
glPopAttrib();
|
|
}
|
|
|
|
//! Fills the stencil shadow with color. After the shadow volume has been drawn
|
|
//! into the stencil buffer using IVideoDriver::drawStencilShadowVolume(), use this
|
|
//! to draw the color of the shadow.
|
|
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 | GL_LIGHTING_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();
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
glLoadIdentity();
|
|
|
|
glBegin(GL_QUADS);
|
|
|
|
glColor4ub(leftDownEdge.getRed(), leftDownEdge.getGreen(), leftDownEdge.getBlue(), leftDownEdge.getAlpha());
|
|
glVertex3f(-1.f,-1.f,-0.9f);
|
|
|
|
glColor4ub(leftUpEdge.getRed(), leftUpEdge.getGreen(), leftUpEdge.getBlue(), leftUpEdge.getAlpha());
|
|
glVertex3f(-1.f, 1.f,-0.9f);
|
|
|
|
glColor4ub(rightUpEdge.getRed(), rightUpEdge.getGreen(), rightUpEdge.getBlue(), rightUpEdge.getAlpha());
|
|
glVertex3f(1.f, 1.f,-0.9f);
|
|
|
|
glColor4ub(rightDownEdge.getRed(), rightDownEdge.getGreen(), rightDownEdge.getBlue(), rightDownEdge.getAlpha());
|
|
glVertex3f(1.f,-1.f,-0.9f);
|
|
|
|
glEnd();
|
|
|
|
clearBuffers(false, false, clearStencilBuffer, 0x0);
|
|
|
|
// restore settings
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glPopMatrix();
|
|
glPopAttrib();
|
|
}
|
|
|
|
|
|
//! Sets the fog mode.
|
|
void COpenGLDriver::setFog(SColor c, E_FOG_TYPE fogType, f32 start,
|
|
f32 end, f32 density, bool pixelFog, bool rangeFog)
|
|
{
|
|
CNullDriver::setFog(c, fogType, start, end, density, pixelFog, rangeFog);
|
|
|
|
if (!useCoreContext)
|
|
glFogf(GL_FOG_MODE, GLfloat((fogType==EFT_FOG_LINEAR)? GL_LINEAR : (fogType==EFT_FOG_EXP)?GL_EXP:GL_EXP2));
|
|
|
|
#ifdef GL_EXT_fog_coord
|
|
if (FeatureAvailable[IRR_EXT_fog_coord] && !useCoreContext)
|
|
glFogi(GL_FOG_COORDINATE_SOURCE, GL_FRAGMENT_DEPTH);
|
|
#endif
|
|
#ifdef GL_NV_fog_distance
|
|
if (FeatureAvailable[IRR_NV_fog_distance])
|
|
{
|
|
if (rangeFog && !useCoreContext)
|
|
glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_RADIAL_NV);
|
|
else if (!useCoreContext)
|
|
glFogi(GL_FOG_DISTANCE_MODE_NV, GL_EYE_PLANE_ABSOLUTE_NV);
|
|
}
|
|
#endif
|
|
|
|
if (fogType==EFT_FOG_LINEAR)
|
|
{
|
|
if (!useCoreContext)
|
|
glFogf(GL_FOG_START, start);
|
|
if (!useCoreContext)
|
|
glFogf(GL_FOG_END, end);
|
|
}
|
|
else if (!useCoreContext)
|
|
glFogf(GL_FOG_DENSITY, density);
|
|
|
|
if (pixelFog && !useCoreContext)
|
|
glHint(GL_FOG_HINT, GL_NICEST);
|
|
else if (!useCoreContext)
|
|
glHint(GL_FOG_HINT, GL_FASTEST);
|
|
|
|
SColorf color(c);
|
|
GLfloat data[4] = {color.r, color.g, color.b, color.a};
|
|
if (!useCoreContext)
|
|
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();
|
|
}
|
|
|
|
|
|
//! Removes a texture from the texture cache and deletes it, freeing lot of memory.
|
|
void COpenGLDriver::removeTexture(ITexture* texture)
|
|
{
|
|
if (!texture)
|
|
return;
|
|
|
|
CNullDriver::removeTexture(texture);
|
|
// Remove this texture from CurrentTexture as well
|
|
CurrentTexture.remove(texture);
|
|
}
|
|
|
|
|
|
//! Only used by the internal engine. Used to notify the driver that
|
|
//! the window was resized.
|
|
void COpenGLDriver::OnResize(const core::dimension2d<u32>& 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);
|
|
}
|
|
|
|
//! Bool interface for the above.
|
|
bool COpenGLDriver::setVertexShaderConstant(const c8* name, const bool* bools, int count)
|
|
{
|
|
return setPixelShaderConstant(name, bools, count);
|
|
}
|
|
|
|
//! Int interface for the above.
|
|
bool COpenGLDriver::setVertexShaderConstant(const c8* name, const s32* ints, int count)
|
|
{
|
|
return setPixelShaderConstant(name, ints, 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;
|
|
}
|
|
|
|
//! Bool interface for the above.
|
|
bool COpenGLDriver::setPixelShaderConstant(const c8* name, const bool* bools, int count)
|
|
{
|
|
os::Printer::log("Error: Please call services->setPixelShaderConstant(), not VideoDriver->setPixelShaderConstant().");
|
|
return false;
|
|
}
|
|
|
|
//! Int interface for the above.
|
|
bool COpenGLDriver::setPixelShaderConstant(const c8* name, const s32* ints, 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,
|
|
const c8* geometryShaderProgram,
|
|
const c8* geometryShaderEntryPointName,
|
|
E_GEOMETRY_SHADER_TYPE gsCompileTarget,
|
|
scene::E_PRIMITIVE_TYPE inType,
|
|
scene::E_PRIMITIVE_TYPE outType,
|
|
u32 verticesOut,
|
|
IShaderConstantSetCallBack* callback,
|
|
E_MATERIAL_TYPE baseMaterial,
|
|
s32 userData, E_GPU_SHADING_LANGUAGE shadingLang)
|
|
{
|
|
s32 nr = -1;
|
|
|
|
{
|
|
COpenGLSLMaterialRenderer* r = new COpenGLSLMaterialRenderer(
|
|
this, nr,
|
|
vertexShaderProgram, vertexShaderEntryPointName, vsCompileTarget,
|
|
pixelShaderProgram, pixelShaderEntryPointName, psCompileTarget,
|
|
geometryShaderProgram, geometryShaderEntryPointName, gsCompileTarget,
|
|
inType, outType, verticesOut,
|
|
callback,getMaterialRenderer(baseMaterial), userData);
|
|
|
|
r->drop();
|
|
}
|
|
|
|
return nr;
|
|
}
|
|
|
|
|
|
//! Returns a pointer to the IVideoDriver interface. (Implementation for
|
|
//! IMaterialRendererServices)
|
|
IVideoDriver* COpenGLDriver::getVideoDriver()
|
|
{
|
|
return this;
|
|
}
|
|
|
|
|
|
ITexture* COpenGLDriver::addRenderTargetTexture(const core::dimension2d<u32>& size,
|
|
const io::path& name,
|
|
const ECOLOR_FORMAT format,
|
|
const bool useStencil)
|
|
{
|
|
//disable mip-mapping
|
|
bool generateMipLevels = getTextureCreationFlag(ETCF_CREATE_MIP_MAPS);
|
|
setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
|
|
|
|
video::ITexture* rtt = 0;
|
|
#if defined(GL_EXT_framebuffer_object)
|
|
// if driver supports FrameBufferObjects, use them
|
|
if (queryFeature(EVDF_FRAMEBUFFER_OBJECT))
|
|
{
|
|
rtt = new COpenGLFBOTexture(size, name, this, format);
|
|
if (rtt)
|
|
{
|
|
bool success = false;
|
|
addTexture(rtt);
|
|
ITexture* tex = createDepthTexture(rtt, useStencil);
|
|
if (tex)
|
|
{
|
|
success = static_cast<video::COpenGLFBODepthTexture*>(tex)->attach(rtt);
|
|
if ( !success )
|
|
{
|
|
removeDepthTexture(tex);
|
|
}
|
|
tex->drop();
|
|
}
|
|
rtt->drop();
|
|
if (!success)
|
|
{
|
|
removeTexture(rtt);
|
|
rtt=0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// the simple texture is only possible for size <= screensize
|
|
// we try to find an optimal size with the original constraints
|
|
core::dimension2du 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::E_RENDER_TARGET target, bool clearTarget,
|
|
bool clearZBuffer, SColor color)
|
|
{
|
|
if (target != CurrentTarget)
|
|
setRenderTarget(0, false, false, 0x0);
|
|
|
|
if (ERT_RENDER_TEXTURE == target)
|
|
{
|
|
os::Printer::log("For render textures call setRenderTarget with the actual texture as first parameter.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
if (ERT_MULTI_RENDER_TEXTURES == target)
|
|
{
|
|
os::Printer::log("For multiple render textures call setRenderTarget with the texture array as first parameter.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if (Params.Stereobuffer && (ERT_STEREO_RIGHT_BUFFER == target))
|
|
{
|
|
if (Params.Doublebuffer)
|
|
glDrawBuffer(GL_BACK_RIGHT);
|
|
else
|
|
glDrawBuffer(GL_FRONT_RIGHT);
|
|
}
|
|
else if (Params.Stereobuffer && ERT_STEREO_BOTH_BUFFERS == target)
|
|
{
|
|
if (Params.Doublebuffer)
|
|
glDrawBuffer(GL_BACK);
|
|
else
|
|
glDrawBuffer(GL_FRONT);
|
|
}
|
|
else if ((target >= ERT_AUX_BUFFER0) && (target-ERT_AUX_BUFFER0 < MaxAuxBuffers))
|
|
{
|
|
glDrawBuffer(GL_AUX0+target-ERT_AUX_BUFFER0);
|
|
}
|
|
else
|
|
{
|
|
if (Params.Doublebuffer)
|
|
glDrawBuffer(GL_BACK_LEFT);
|
|
else
|
|
glDrawBuffer(GL_FRONT_LEFT);
|
|
// exit with false, but also with working color buffer
|
|
if (target != ERT_FRAME_BUFFER)
|
|
return false;
|
|
}
|
|
CurrentTarget=target;
|
|
clearBuffers(clearTarget, clearZBuffer, false, color);
|
|
return true;
|
|
}
|
|
|
|
|
|
//! 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;
|
|
}
|
|
|
|
#if defined(GL_EXT_framebuffer_object)
|
|
if (CurrentTarget==ERT_MULTI_RENDER_TEXTURES)
|
|
{
|
|
for (u32 i=0; i<MRTargets.size(); ++i)
|
|
{
|
|
if (MRTargets[i].TargetType==ERT_RENDER_TEXTURE)
|
|
{
|
|
for (++i; i<MRTargets.size(); ++i)
|
|
if (MRTargets[i].TargetType==ERT_RENDER_TEXTURE)
|
|
extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT+i, GL_TEXTURE_2D, 0, 0);
|
|
}
|
|
}
|
|
MRTargets.clear();
|
|
}
|
|
#endif
|
|
|
|
// check if we should set the previous RT back
|
|
if ((RenderTargetTexture != texture) ||
|
|
(CurrentTarget==ERT_MULTI_RENDER_TEXTURES))
|
|
{
|
|
setActiveTexture(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);
|
|
// calls glDrawBuffer as well
|
|
RenderTargetTexture->bindRTT();
|
|
CurrentRendertargetSize = texture->getSize();
|
|
CurrentTarget=ERT_RENDER_TEXTURE;
|
|
}
|
|
else
|
|
{
|
|
glViewport(0,0,ScreenSize.Width,ScreenSize.Height);
|
|
RenderTargetTexture = 0;
|
|
CurrentRendertargetSize = core::dimension2d<u32>(0,0);
|
|
CurrentTarget=ERT_FRAME_BUFFER;
|
|
glDrawBuffer(Params.Doublebuffer?GL_BACK_LEFT:GL_FRONT_LEFT);
|
|
}
|
|
// we need to update the matrices due to the rendersize change.
|
|
Transformation3DChanged=true;
|
|
}
|
|
|
|
clearBuffers(clearBackBuffer, clearZBuffer, false, color);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//! Sets multiple render targets
|
|
bool COpenGLDriver::setRenderTarget(const core::array<video::IRenderTarget>& targets,
|
|
bool clearBackBuffer, bool clearZBuffer, SColor color)
|
|
{
|
|
// if simply disabling the MRT via array call
|
|
if (targets.size()==0)
|
|
return setRenderTarget(0, clearBackBuffer, clearZBuffer, color);
|
|
// if disabling old MRT, but enabling new one as well
|
|
if ((MRTargets.size()!=0) && (targets != MRTargets))
|
|
setRenderTarget(0, clearBackBuffer, clearZBuffer, color);
|
|
// if no change, simply clear buffers
|
|
else if (targets == MRTargets)
|
|
{
|
|
clearBuffers(clearBackBuffer, clearZBuffer, false, color);
|
|
return true;
|
|
}
|
|
|
|
// copy to storage for correct disabling
|
|
MRTargets=targets;
|
|
|
|
u32 maxMultipleRTTs = core::min_(static_cast<u32>(MaxMultipleRenderTargets), targets.size());
|
|
|
|
// determine common size
|
|
core::dimension2du rttSize = CurrentRendertargetSize;
|
|
if (targets[0].TargetType==ERT_RENDER_TEXTURE)
|
|
{
|
|
if (!targets[0].RenderTexture)
|
|
{
|
|
os::Printer::log("Missing render texture for MRT.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
rttSize=targets[0].RenderTexture->getSize();
|
|
}
|
|
|
|
for (u32 i = 0; i < maxMultipleRTTs; ++i)
|
|
{
|
|
// check for right driver type
|
|
if (targets[i].TargetType==ERT_RENDER_TEXTURE)
|
|
{
|
|
if (!targets[i].RenderTexture)
|
|
{
|
|
maxMultipleRTTs=i;
|
|
os::Printer::log("Missing render texture for MRT.", ELL_WARNING);
|
|
break;
|
|
}
|
|
if (targets[i].RenderTexture->getDriverType() != EDT_OPENGL)
|
|
{
|
|
maxMultipleRTTs=i;
|
|
os::Printer::log("Tried to set a texture not owned by this driver.", ELL_WARNING);
|
|
break;
|
|
}
|
|
|
|
// check for valid render target
|
|
if (!targets[i].RenderTexture->isRenderTarget() || !static_cast<COpenGLTexture*>(targets[i].RenderTexture)->isFrameBufferObject())
|
|
{
|
|
maxMultipleRTTs=i;
|
|
os::Printer::log("Tried to set a non FBO-RTT as render target.", ELL_WARNING);
|
|
break;
|
|
}
|
|
|
|
// check for valid size
|
|
if (rttSize != targets[i].RenderTexture->getSize())
|
|
{
|
|
maxMultipleRTTs=i;
|
|
os::Printer::log("Render target texture has wrong size.", ELL_WARNING);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (maxMultipleRTTs==0)
|
|
{
|
|
os::Printer::log("No valid MRTs.", ELL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
// init FBO, if any
|
|
for (u32 i=0; i<maxMultipleRTTs; ++i)
|
|
{
|
|
if (targets[i].TargetType==ERT_RENDER_TEXTURE)
|
|
{
|
|
setRenderTarget(targets[i].RenderTexture, false, false, 0x0);
|
|
break; // bind only first RTT
|
|
}
|
|
}
|
|
// init other main buffer, if necessary
|
|
if (targets[0].TargetType!=ERT_RENDER_TEXTURE)
|
|
setRenderTarget(targets[0].TargetType, false, false, 0x0);
|
|
|
|
// attach other textures and store buffers into array
|
|
if (maxMultipleRTTs > 1)
|
|
{
|
|
CurrentTarget=ERT_MULTI_RENDER_TEXTURES;
|
|
core::array<GLenum> MRTs;
|
|
MRTs.set_used(maxMultipleRTTs);
|
|
for(u32 i = 0; i < maxMultipleRTTs; i++)
|
|
{
|
|
if (FeatureAvailable[IRR_EXT_draw_buffers2])
|
|
{
|
|
extGlColorMaskIndexed(i,
|
|
(targets[i].ColorMask & ECP_RED)?GL_TRUE:GL_FALSE,
|
|
(targets[i].ColorMask & ECP_GREEN)?GL_TRUE:GL_FALSE,
|
|
(targets[i].ColorMask & ECP_BLUE)?GL_TRUE:GL_FALSE,
|
|
(targets[i].ColorMask & ECP_ALPHA)?GL_TRUE:GL_FALSE);
|
|
if (targets[i].BlendOp==EBO_NONE)
|
|
extGlDisableIndexed(GL_BLEND, i);
|
|
else
|
|
extGlEnableIndexed(GL_BLEND, i);
|
|
}
|
|
#if defined(GL_AMD_draw_buffers_blend) || defined(GL_ARB_draw_buffers_blend)
|
|
if (FeatureAvailable[IRR_AMD_draw_buffers_blend] || FeatureAvailable[IRR_ARB_draw_buffers_blend])
|
|
{
|
|
extGlBlendFuncIndexed(i, getGLBlend(targets[i].BlendFuncSrc), getGLBlend(targets[i].BlendFuncDst));
|
|
switch(targets[i].BlendOp)
|
|
{
|
|
case EBO_SUBTRACT:
|
|
extGlBlendEquationIndexed(i, GL_FUNC_SUBTRACT);
|
|
break;
|
|
case EBO_REVSUBTRACT:
|
|
extGlBlendEquationIndexed(i, GL_FUNC_REVERSE_SUBTRACT);
|
|
break;
|
|
case EBO_MIN:
|
|
extGlBlendEquationIndexed(i, GL_MIN);
|
|
break;
|
|
case EBO_MAX:
|
|
extGlBlendEquationIndexed(i, GL_MAX);
|
|
break;
|
|
case EBO_MIN_FACTOR:
|
|
case EBO_MIN_ALPHA:
|
|
#if defined(GL_AMD_blend_minmax_factor)
|
|
if (FeatureAvailable[IRR_AMD_blend_minmax_factor])
|
|
extGlBlendEquationIndexed(i, GL_FACTOR_MIN_AMD);
|
|
// fallback in case of missing extension
|
|
else
|
|
#endif
|
|
extGlBlendEquation(GL_MIN);
|
|
break;
|
|
case EBO_MAX_FACTOR:
|
|
case EBO_MAX_ALPHA:
|
|
#if defined(GL_AMD_blend_minmax_factor)
|
|
if (FeatureAvailable[IRR_AMD_blend_minmax_factor])
|
|
extGlBlendEquationIndexed(i, GL_FACTOR_MAX_AMD);
|
|
// fallback in case of missing extension
|
|
else
|
|
#endif
|
|
extGlBlendEquation(GL_MAX);
|
|
break;
|
|
default:
|
|
extGlBlendEquationIndexed(i, GL_FUNC_ADD);
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
if (targets[i].TargetType==ERT_RENDER_TEXTURE)
|
|
{
|
|
GLenum attachment = GL_NONE;
|
|
#ifdef GL_EXT_framebuffer_object
|
|
// attach texture to FrameBuffer Object on Color [i]
|
|
attachment = GL_COLOR_ATTACHMENT0_EXT+i;
|
|
if ((i != 0) && (targets[i].RenderTexture != RenderTargetTexture))
|
|
extGlFramebufferTexture2D(GL_FRAMEBUFFER_EXT, attachment, GL_TEXTURE_2D, static_cast<COpenGLTexture*>(targets[i].RenderTexture)->getOpenGLTextureName(), 0);
|
|
#endif
|
|
MRTs[i]=attachment;
|
|
}
|
|
else
|
|
{
|
|
switch(targets[i].TargetType)
|
|
{
|
|
case ERT_FRAME_BUFFER:
|
|
MRTs[i]=GL_BACK_LEFT;
|
|
break;
|
|
case ERT_STEREO_BOTH_BUFFERS:
|
|
MRTs[i]=GL_BACK;
|
|
break;
|
|
case ERT_STEREO_RIGHT_BUFFER:
|
|
MRTs[i]=GL_BACK_RIGHT;
|
|
break;
|
|
case ERT_STEREO_LEFT_BUFFER:
|
|
MRTs[i]=GL_BACK_LEFT;
|
|
break;
|
|
default:
|
|
MRTs[i]=GL_AUX0+(targets[i].TargetType-ERT_AUX_BUFFER0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
extGlDrawBuffers(maxMultipleRTTs, MRTs.const_pointer());
|
|
}
|
|
|
|
clearBuffers(clearBackBuffer, clearZBuffer, false, color);
|
|
return true;
|
|
}
|
|
|
|
|
|
// returns the current size of the screen or rendertarget
|
|
const core::dimension2d<u32>& COpenGLDriver::getCurrentRenderTargetSize() const
|
|
{
|
|
if (CurrentRendertargetSize.Width == 0)
|
|
return ScreenSize;
|
|
else
|
|
return CurrentRendertargetSize;
|
|
}
|
|
|
|
|
|
//! Clears the ZBuffer.
|
|
void COpenGLDriver::clearZBuffer()
|
|
{
|
|
clearBuffers(false, true, false, 0x0);
|
|
}
|
|
|
|
|
|
//! Returns an image created from the last rendered frame.
|
|
IImage* COpenGLDriver::createScreenShot(video::ECOLOR_FORMAT format, video::E_RENDER_TARGET target)
|
|
{
|
|
if (target==video::ERT_MULTI_RENDER_TEXTURES || target==video::ERT_RENDER_TEXTURE || target==video::ERT_STEREO_BOTH_BUFFERS)
|
|
return 0;
|
|
|
|
if (format==video::ECF_UNKNOWN)
|
|
format=getColorFormat();
|
|
GLenum fmt;
|
|
GLenum type;
|
|
switch (format)
|
|
{
|
|
case ECF_A1R5G5B5:
|
|
fmt = GL_BGRA;
|
|
type = GL_UNSIGNED_SHORT_1_5_5_5_REV;
|
|
break;
|
|
case ECF_R5G6B5:
|
|
fmt = GL_RGB;
|
|
type = GL_UNSIGNED_SHORT_5_6_5;
|
|
break;
|
|
case ECF_R8G8B8:
|
|
fmt = GL_RGB;
|
|
type = GL_UNSIGNED_BYTE;
|
|
break;
|
|
case ECF_A8R8G8B8:
|
|
fmt = GL_BGRA;
|
|
if (Version > 101)
|
|
type = GL_UNSIGNED_INT_8_8_8_8_REV;
|
|
else
|
|
type = GL_UNSIGNED_BYTE;
|
|
break;
|
|
case ECF_R8G8:
|
|
// GL_ARB_texture_rg is considered always available in headers. No ifdefs.
|
|
fmt = GL_RG;
|
|
type = GL_UNSIGNED_BYTE;
|
|
break;
|
|
case ECF_R16G16:
|
|
fmt = GL_RG;
|
|
type = GL_UNSIGNED_SHORT;
|
|
break;
|
|
case ECF_R8:
|
|
fmt = GL_RED;
|
|
type = GL_UNSIGNED_BYTE;
|
|
break;
|
|
case ECF_R16:
|
|
fmt = GL_RED;
|
|
type = GL_UNSIGNED_SHORT;
|
|
break;
|
|
case ECF_R16F:
|
|
if (FeatureAvailable[IRR_ARB_texture_rg])
|
|
fmt = GL_RED;
|
|
else
|
|
fmt = GL_LUMINANCE;
|
|
#ifdef GL_ARB_half_float_pixel
|
|
if (FeatureAvailable[IRR_ARB_half_float_pixel])
|
|
type = GL_HALF_FLOAT_ARB;
|
|
else
|
|
#endif
|
|
{
|
|
type = GL_FLOAT;
|
|
format = ECF_R32F;
|
|
}
|
|
break;
|
|
case ECF_G16R16F:
|
|
#ifdef GL_ARB_texture_rg
|
|
if (FeatureAvailable[IRR_ARB_texture_rg])
|
|
fmt = GL_RG;
|
|
else
|
|
#endif
|
|
fmt = GL_LUMINANCE_ALPHA;
|
|
#ifdef GL_ARB_half_float_pixel
|
|
if (FeatureAvailable[IRR_ARB_half_float_pixel])
|
|
type = GL_HALF_FLOAT_ARB;
|
|
else
|
|
#endif
|
|
{
|
|
type = GL_FLOAT;
|
|
format = ECF_G32R32F;
|
|
}
|
|
break;
|
|
case ECF_A16B16G16R16F:
|
|
fmt = GL_BGRA;
|
|
#ifdef GL_ARB_half_float_pixel
|
|
if (FeatureAvailable[IRR_ARB_half_float_pixel])
|
|
type = GL_HALF_FLOAT_ARB;
|
|
else
|
|
#endif
|
|
{
|
|
type = GL_FLOAT;
|
|
format = ECF_A32B32G32R32F;
|
|
}
|
|
break;
|
|
case ECF_R32F:
|
|
if (FeatureAvailable[IRR_ARB_texture_rg])
|
|
fmt = GL_RED;
|
|
else
|
|
fmt = GL_LUMINANCE;
|
|
type = GL_FLOAT;
|
|
break;
|
|
case ECF_G32R32F:
|
|
#ifdef GL_ARB_texture_rg
|
|
if (FeatureAvailable[IRR_ARB_texture_rg])
|
|
fmt = GL_RG;
|
|
else
|
|
#endif
|
|
fmt = GL_LUMINANCE_ALPHA;
|
|
type = GL_FLOAT;
|
|
break;
|
|
case ECF_A32B32G32R32F:
|
|
fmt = GL_BGRA;
|
|
type = GL_FLOAT;
|
|
break;
|
|
default:
|
|
fmt = GL_BGRA;
|
|
type = GL_UNSIGNED_BYTE;
|
|
break;
|
|
}
|
|
IImage* newImage = createImage(format, ScreenSize);
|
|
|
|
u8* pixels = 0;
|
|
if (newImage)
|
|
pixels = static_cast<u8*>(newImage->lock());
|
|
if (pixels)
|
|
{
|
|
GLenum tgt=GL_BACK;
|
|
switch (target)
|
|
{
|
|
case video::ERT_FRAME_BUFFER:
|
|
break;
|
|
case video::ERT_STEREO_LEFT_BUFFER:
|
|
tgt=GL_FRONT_LEFT;
|
|
break;
|
|
case video::ERT_STEREO_RIGHT_BUFFER:
|
|
tgt=GL_FRONT_RIGHT;
|
|
break;
|
|
default:
|
|
tgt=GL_AUX0+(target-video::ERT_AUX_BUFFER0);
|
|
break;
|
|
}
|
|
glReadBuffer(tgt);
|
|
glReadPixels(0, 0, ScreenSize.Width, ScreenSize.Height, fmt, type, pixels);
|
|
testGLError();
|
|
glReadBuffer(GL_BACK);
|
|
}
|
|
|
|
if (pixels)
|
|
{
|
|
// 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 (u32 i=0; i < ScreenSize.Height; i += 2)
|
|
{
|
|
memcpy(tmpBuffer, pixels, pitch);
|
|
// for (u32 j=0; j<pitch; ++j)
|
|
// {
|
|
// pixels[j]=(u8)(p2[j]*255.f);
|
|
// }
|
|
memcpy(pixels, p2, pitch);
|
|
// for (u32 j=0; j<pitch; ++j)
|
|
// {
|
|
// p2[j]=(u8)(tmpBuffer[j]*255.f);
|
|
// }
|
|
memcpy(p2, tmpBuffer, pitch);
|
|
pixels += pitch;
|
|
p2 -= pitch;
|
|
}
|
|
delete [] tmpBuffer;
|
|
}
|
|
|
|
if (newImage)
|
|
{
|
|
newImage->unlock();
|
|
if (testGLError() || !pixels)
|
|
{
|
|
newImage->drop();
|
|
return 0;
|
|
}
|
|
}
|
|
return newImage;
|
|
}
|
|
|
|
|
|
//! get depth texture for the given render target texture
|
|
ITexture* COpenGLDriver::createDepthTexture(ITexture* texture, const bool useStencil, const 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() &&
|
|
useStencil == DepthTextures[i]->hasStencil())
|
|
{
|
|
DepthTextures[i]->grab();
|
|
return DepthTextures[i];
|
|
}
|
|
}
|
|
DepthTextures.push_back(new COpenGLFBODepthTexture(texture->getSize(), "depth1", this, useStencil));
|
|
return DepthTextures.getLast();
|
|
}
|
|
return (new COpenGLFBODepthTexture(texture->getSize(), "depth1", this, useStencil));
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
UserClipPlanes[index].Plane=plane;
|
|
enableClipPlane(index, enable);
|
|
return true;
|
|
}
|
|
|
|
|
|
void COpenGLDriver::uploadClipPlane(u32 index)
|
|
{
|
|
// opengl needs an array of doubles for the plane equation
|
|
GLdouble clip_plane[4];
|
|
clip_plane[0] = UserClipPlanes[index].Plane.Normal.X;
|
|
clip_plane[1] = UserClipPlanes[index].Plane.Normal.Y;
|
|
clip_plane[2] = UserClipPlanes[index].Plane.Normal.Z;
|
|
clip_plane[3] = UserClipPlanes[index].Plane.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 (!UserClipPlanes[index].Enabled)
|
|
{
|
|
uploadClipPlane(index);
|
|
glEnable(GL_CLIP_PLANE0 + index);
|
|
}
|
|
}
|
|
else
|
|
glDisable(GL_CLIP_PLANE0 + index);
|
|
|
|
UserClipPlanes[index].Enabled=enable;
|
|
}
|
|
|
|
|
|
core::dimension2du COpenGLDriver::getMaxTextureSize() const
|
|
{
|
|
return core::dimension2du(MaxTextureSize, MaxTextureSize);
|
|
}
|
|
|
|
|
|
//! Convert E_PRIMITIVE_TYPE to OpenGL equivalent
|
|
GLenum COpenGLDriver::primitiveTypeToGL(scene::E_PRIMITIVE_TYPE type) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case scene::EPT_POINTS:
|
|
return GL_POINTS;
|
|
case scene::EPT_LINE_STRIP:
|
|
return GL_LINE_STRIP;
|
|
case scene::EPT_LINE_LOOP:
|
|
return GL_LINE_LOOP;
|
|
case scene::EPT_LINES:
|
|
return GL_LINES;
|
|
case scene::EPT_TRIANGLE_STRIP:
|
|
return GL_TRIANGLE_STRIP;
|
|
case scene::EPT_TRIANGLE_FAN:
|
|
return GL_TRIANGLE_FAN;
|
|
case scene::EPT_TRIANGLES:
|
|
return GL_TRIANGLES;
|
|
case scene::EPT_QUAD_STRIP:
|
|
return GL_QUAD_STRIP;
|
|
case scene::EPT_QUADS:
|
|
return GL_QUADS;
|
|
case scene::EPT_POLYGON:
|
|
return GL_POLYGON;
|
|
case scene::EPT_POINT_SPRITES:
|
|
#ifdef GL_ARB_point_sprite
|
|
return GL_POINT_SPRITE_ARB;
|
|
#else
|
|
return GL_POINTS;
|
|
#endif
|
|
}
|
|
return GL_TRIANGLES;
|
|
}
|
|
|
|
|
|
GLenum COpenGLDriver::getGLBlend(E_BLEND_FACTOR factor) const
|
|
{
|
|
GLenum r = 0;
|
|
switch (factor)
|
|
{
|
|
case EBF_ZERO: r = GL_ZERO; break;
|
|
case EBF_ONE: r = GL_ONE; break;
|
|
case EBF_DST_COLOR: r = GL_DST_COLOR; break;
|
|
case EBF_ONE_MINUS_DST_COLOR: r = GL_ONE_MINUS_DST_COLOR; break;
|
|
case EBF_SRC_COLOR: r = GL_SRC_COLOR; break;
|
|
case EBF_ONE_MINUS_SRC_COLOR: r = GL_ONE_MINUS_SRC_COLOR; break;
|
|
case EBF_SRC_ALPHA: r = GL_SRC_ALPHA; break;
|
|
case EBF_ONE_MINUS_SRC_ALPHA: r = GL_ONE_MINUS_SRC_ALPHA; break;
|
|
case EBF_DST_ALPHA: r = GL_DST_ALPHA; break;
|
|
case EBF_ONE_MINUS_DST_ALPHA: r = GL_ONE_MINUS_DST_ALPHA; break;
|
|
case EBF_SRC_ALPHA_SATURATE: r = GL_SRC_ALPHA_SATURATE; break;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
GLenum COpenGLDriver::getZBufferBits() const
|
|
{
|
|
GLenum bits = 0;
|
|
switch (Params.ZBufferBits)
|
|
{
|
|
case 16:
|
|
bits = GL_DEPTH_COMPONENT16;
|
|
break;
|
|
case 24:
|
|
bits = GL_DEPTH_COMPONENT24;
|
|
break;
|
|
case 32:
|
|
bits = GL_DEPTH_COMPONENT32;
|
|
break;
|
|
default:
|
|
bits = GL_DEPTH_COMPONENT;
|
|
break;
|
|
}
|
|
return bits;
|
|
}
|
|
|
|
|
|
} // end namespace
|
|
} // end namespace
|
|
|
|
#endif // _IRR_COMPILE_WITH_OPENGL_
|
|
|
|
namespace irr
|
|
{
|
|
namespace video
|
|
{
|
|
|
|
|
|
// -----------------------------------
|
|
// WINDOWS VERSION
|
|
// -----------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceWin32* device)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
COpenGLDriver* ogl = new COpenGLDriver(params, io, device);
|
|
if (!ogl->initDriver(device))
|
|
{
|
|
ogl->drop();
|
|
ogl = 0;
|
|
}
|
|
return ogl;
|
|
#else
|
|
return 0;
|
|
#endif // _IRR_COMPILE_WITH_OPENGL_
|
|
}
|
|
#endif // _IRR_COMPILE_WITH_WINDOWS_DEVICE_
|
|
|
|
// -----------------------------------
|
|
// MACOSX VERSION
|
|
// -----------------------------------
|
|
#if defined(_IRR_COMPILE_WITH_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_COMPILE_WITH_OSX_DEVICE_
|
|
|
|
// -----------------------------------
|
|
// X11 VERSION
|
|
// -----------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_X11_DEVICE_
|
|
IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceLinux* device)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
COpenGLDriver* ogl = new COpenGLDriver(params, io, device);
|
|
if (!ogl->initDriver(device))
|
|
{
|
|
ogl->drop();
|
|
ogl = 0;
|
|
}
|
|
return ogl;
|
|
#else
|
|
return 0;
|
|
#endif // _IRR_COMPILE_WITH_OPENGL_
|
|
}
|
|
#endif // _IRR_COMPILE_WITH_X11_DEVICE_
|
|
|
|
|
|
// -----------------------------------
|
|
// Wayland VERSION
|
|
// -----------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_WAYLAND_DEVICE_
|
|
IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceWayland* device)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
COpenGLDriver* ogl = new COpenGLDriver(params, io, device);
|
|
if (!ogl->initDriver(device))
|
|
{
|
|
ogl->drop();
|
|
ogl = 0;
|
|
}
|
|
return ogl;
|
|
#else
|
|
return 0;
|
|
#endif // _IRR_COMPILE_WITH_OPENGL_
|
|
}
|
|
#endif // _IRR_COMPILE_WITH_WAYLAND_DEVICE
|
|
|
|
|
|
// -----------------------------------
|
|
// SDL VERSION
|
|
// -----------------------------------
|
|
#ifdef _IRR_COMPILE_WITH_SDL_DEVICE_
|
|
IVideoDriver* createOpenGLDriver(const SIrrlichtCreationParameters& params,
|
|
io::IFileSystem* io, CIrrDeviceSDL* device)
|
|
{
|
|
#ifdef _IRR_COMPILE_WITH_OPENGL_
|
|
return new COpenGLDriver(params, io, device);
|
|
#else
|
|
return 0;
|
|
#endif // _IRR_COMPILE_WITH_OPENGL_
|
|
}
|
|
#endif // _IRR_COMPILE_WITH_SDL_DEVICE_
|
|
|
|
} // end namespace
|
|
} // end namespace
|
|
|
|
|