forked from LeenkxTeam/LNXSDK
1074 lines
28 KiB
C++
1074 lines
28 KiB
C++
#include "OpenGL.h"
|
|
#include "VertexBufferImpl.h"
|
|
#include "ogl.h"
|
|
#include <Kore/Log.h>
|
|
#include <Kore/Math/Core.h>
|
|
#include <Kore/System.h>
|
|
#include <cassert>
|
|
#include <cstdio>
|
|
|
|
#ifdef KINC_IOS
|
|
#include <OpenGLES/ES1/glext.h>
|
|
#endif
|
|
|
|
#ifdef KINC_WINDOWS
|
|
#include <GL/wglew.h>
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#define NOMINMAX
|
|
#include <Windows.h>
|
|
|
|
#pragma comment(lib, "opengl32.lib")
|
|
#pragma comment(lib, "glu32.lib")
|
|
#endif
|
|
|
|
using namespace Kore;
|
|
|
|
namespace Kore {
|
|
#if !defined(KINC_IOS) && !defined(KINC_ANDROID)
|
|
extern bool programUsesTessellation;
|
|
#endif
|
|
}
|
|
|
|
namespace {
|
|
#ifdef KINC_WINDOWS
|
|
HINSTANCE instance = 0;
|
|
HDC deviceContexts[10] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
|
|
HGLRC glContexts[10] = {nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr};
|
|
#endif
|
|
|
|
Graphics3::TextureFilter minFilters[10][32];
|
|
Graphics3::MipmapFilter mipFilters[10][32];
|
|
int originalFramebuffer[10] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
|
|
uint arrayId[10];
|
|
|
|
int _width;
|
|
int _height;
|
|
int _renderTargetWidth;
|
|
int _renderTargetHeight;
|
|
bool renderToBackbuffer;
|
|
|
|
bool depthTest = false;
|
|
bool depthMask = false;
|
|
|
|
struct wvpTransform_t {
|
|
mat4 projection;
|
|
mat4 view;
|
|
mat4 world;
|
|
} g_wvpTransform;
|
|
|
|
#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18
|
|
void *glesDrawBuffers;
|
|
#endif
|
|
}
|
|
|
|
void Graphics3::destroy(int windowId) {
|
|
#ifdef KINC_WINDOWS
|
|
if (glContexts[windowId]) {
|
|
if (!wglMakeCurrent(nullptr, nullptr)) {
|
|
// MessageBox(NULL,"Release Of DC And RC Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
if (!wglDeleteContext(glContexts[windowId])) {
|
|
// MessageBox(NULL,"Release Rendering Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
|
|
}
|
|
glContexts[windowId] = nullptr;
|
|
}
|
|
|
|
HWND windowHandle = (HWND)System::windowHandle(windowId);
|
|
|
|
// TODO (DK) shouldn't 'deviceContexts[windowId] = nullptr;' be moved out of here?
|
|
if (deviceContexts[windowId] && !ReleaseDC(windowHandle, deviceContexts[windowId])) {
|
|
// MessageBox(NULL,"Release Device Context Failed.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
|
|
deviceContexts[windowId] = nullptr;
|
|
}
|
|
#endif
|
|
|
|
System::destroyWindow(windowId);
|
|
}
|
|
|
|
#undef CreateWindow
|
|
|
|
#ifdef KINC_WINDOWS
|
|
namespace Kore {
|
|
namespace System {
|
|
extern int currentDeviceId;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef KINC_WINDOWS
|
|
void Graphics3::setup() {}
|
|
#endif
|
|
|
|
void Graphics3::init(int windowId, int depthBufferBits, int stencilBufferBits, bool vsync) {
|
|
#ifdef KINC_WINDOWS
|
|
HWND windowHandle = (HWND)System::windowHandle(windowId);
|
|
|
|
#ifndef VR_RIFT
|
|
// TODO (DK) use provided settings for depth/stencil buffer
|
|
|
|
PIXELFORMATDESCRIPTOR pfd = // pfd Tells Windows How We Want Things To Be
|
|
{
|
|
sizeof(PIXELFORMATDESCRIPTOR), // Size Of This Pixel Format Descriptor
|
|
1, // Version Number
|
|
PFD_DRAW_TO_WINDOW | // Format Must Support Window
|
|
PFD_SUPPORT_OPENGL | // Format Must Support OpenGL
|
|
PFD_DOUBLEBUFFER, // Must Support Double Buffering
|
|
PFD_TYPE_RGBA, // Request An RGBA Format
|
|
32, // 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
|
|
static_cast<BYTE>(depthBufferBits), // 16Bit Z-Buffer (Depth Buffer)
|
|
static_cast<BYTE>(stencilBufferBits), // 8Bit Stencil Buffer
|
|
0, // No Auxiliary Buffer
|
|
PFD_MAIN_PLANE, // Main Drawing Layer
|
|
0, // Reserved
|
|
0,
|
|
0,
|
|
0 // Layer Masks Ignored
|
|
};
|
|
|
|
deviceContexts[windowId] = GetDC(windowHandle);
|
|
GLuint pixelFormat = ChoosePixelFormat(deviceContexts[windowId], &pfd);
|
|
SetPixelFormat(deviceContexts[windowId], pixelFormat, &pfd);
|
|
HGLRC tempGlContext = wglCreateContext(deviceContexts[windowId]);
|
|
wglMakeCurrent(deviceContexts[windowId], tempGlContext);
|
|
Kore::System::currentDeviceId = windowId;
|
|
|
|
// TODO (DK) make a Graphics3::setup() (called from System::setup()) and call it there only once?
|
|
if (windowId == 0) {
|
|
glewInit();
|
|
}
|
|
|
|
#if 0
|
|
if (wglewIsSupported("WGL_ARB_create_context") == 1) {
|
|
int attributes[] = {
|
|
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
|
|
WGL_CONTEXT_MINOR_VERSION_ARB, 2,
|
|
WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
|
|
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
|
|
0
|
|
};
|
|
|
|
glContexts[windowId] = wglCreateContextAttribsARB(deviceContexts[windowId], glContexts[0], attributes);
|
|
glCheckErrors();
|
|
wglMakeCurrent(nullptr, nullptr);
|
|
wglDeleteContext(tempGlContext);
|
|
wglMakeCurrent(deviceContexts[windowId], glContexts[windowId]);
|
|
glCheckErrors();
|
|
}
|
|
else
|
|
#endif
|
|
{ glContexts[windowId] = tempGlContext; }
|
|
|
|
ShowWindow(windowHandle, SW_SHOW);
|
|
SetForegroundWindow(windowHandle); // Slightly Higher Priority
|
|
SetFocus(windowHandle); // Sets Keyboard Focus To The Window
|
|
#else
|
|
deviceContexts[windowId] = GetDC(windowHandle);
|
|
glContexts[windowId] = wglGetCurrentContext();
|
|
glewInit();
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef VR_RIFT
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
setRenderState(DepthTest, false);
|
|
glViewport(0, 0, System::windowWidth(windowId), System::windowHeight(windowId));
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &originalFramebuffer[windowId]);
|
|
|
|
for (int i = 0; i < 32; ++i) {
|
|
minFilters[windowId][i] = LinearFilter;
|
|
mipFilters[windowId][i] = NoMipFilter;
|
|
}
|
|
#endif
|
|
|
|
#ifdef KINC_WINDOWS
|
|
if (windowId == 0) {
|
|
// TODO (DK) check if we actually want vsync
|
|
if (wglSwapIntervalEXT != nullptr)
|
|
wglSwapIntervalEXT(1);
|
|
}
|
|
#endif
|
|
|
|
#if defined(KINC_IOS)
|
|
glGenVertexArraysOES(1, &arrayId[windowId]);
|
|
#elif defined(KINC_MACOS)
|
|
glGenVertexArraysAPPLE(1, &arrayId[windowId]);
|
|
#elif !defined(KINC_ANDROID) && !defined(KINC_EMSCRIPTEN) && !defined(KINC_RASPBERRY_PI)
|
|
glGenVertexArrays(1, &arrayId[windowId]);
|
|
#endif
|
|
glCheckErrors();
|
|
|
|
_width = System::windowWidth(0);
|
|
_height = System::windowHeight(0);
|
|
_renderTargetWidth = _width;
|
|
_renderTargetHeight = _height;
|
|
renderToBackbuffer = true;
|
|
|
|
#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18
|
|
glesDrawBuffers = (void *)eglGetProcAddress("glDrawBuffers");
|
|
#endif
|
|
}
|
|
|
|
void Graphics3::changeResolution(int width, int height) {
|
|
_width = width;
|
|
_height = height;
|
|
if (renderToBackbuffer) {
|
|
_renderTargetWidth = _width;
|
|
_renderTargetHeight = _height;
|
|
}
|
|
}
|
|
|
|
// TODO (DK) should return displays refreshrate?
|
|
unsigned Graphics3::refreshRate() {
|
|
return 60;
|
|
}
|
|
|
|
// TODO (DK) should check the extension and return wheter it's enabled (wglSwapIntervalEXT(1)) or not?
|
|
bool Graphics3::vsynced() {
|
|
return true;
|
|
}
|
|
|
|
static void invertMatrixEntry(mat4 &m, int row, int col) {
|
|
m.Set(row, col, -m.get(row, col));
|
|
}
|
|
|
|
// Invert matrix Z-axis (for view matrices)
|
|
static void invertMatrixZ(mat4 &m) {
|
|
invertMatrixEntry(m, 2, 0);
|
|
invertMatrixEntry(m, 2, 1);
|
|
invertMatrixEntry(m, 2, 2);
|
|
invertMatrixEntry(m, 2, 3);
|
|
}
|
|
|
|
/*
|
|
Converts the specified left-handed projection matrix
|
|
into a right-handed projection matrix (required for OpenGL).
|
|
*/
|
|
static void convertToRightHandedProjection(mat4 &m) {
|
|
// Invert Z-axis of projection matrix (along 3rd column, i.e. zero-based-index 2)
|
|
invertMatrixEntry(m, 0, 2);
|
|
invertMatrixEntry(m, 1, 2);
|
|
invertMatrixEntry(m, 2, 2);
|
|
invertMatrixEntry(m, 3, 2);
|
|
}
|
|
|
|
static void uploadModelViewMatrix() {
|
|
// Compute: modelViewMatrix = viewMatrix * worldMatrix
|
|
mat4 modelViewMatrix = g_wvpTransform.view;
|
|
modelViewMatrix *= g_wvpTransform.world;
|
|
|
|
// Update GL model-view matrix
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadMatrixf(modelViewMatrix.data);
|
|
}
|
|
|
|
static void uploadProjectionMatrix() {
|
|
// Update GL projection matrix
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadMatrixf(g_wvpTransform.projection.data);
|
|
}
|
|
|
|
void Graphics3::setFogColor(const Graphics1::Color &color) {
|
|
glFogfv(GL_FOG_COLOR, &(color.R));
|
|
}
|
|
|
|
void Graphics3::setViewMatrix(const mat4 &value) {
|
|
// Convert view matrix from left-handed to right-handed coordinate system
|
|
g_wvpTransform.view = value;
|
|
#ifndef G3_DISABLE_AUTO_PROJECTION
|
|
invertMatrixZ(g_wvpTransform.view);
|
|
#endif
|
|
uploadModelViewMatrix();
|
|
}
|
|
|
|
void Graphics3::setWorldMatrix(const mat4 &value) {
|
|
g_wvpTransform.world = value;
|
|
uploadModelViewMatrix();
|
|
}
|
|
|
|
void Graphics3::setProjectionMatrix(const mat4 &value) {
|
|
g_wvpTransform.projection = value;
|
|
#ifndef G3_DISABLE_AUTO_PROJECTION
|
|
convertToRightHandedProjection(g_wvpTransform.projection);
|
|
#endif
|
|
uploadProjectionMatrix();
|
|
}
|
|
|
|
void Graphics3::drawIndexedVertices() {
|
|
drawIndexedVertices(0, IndexBufferImpl::current->count());
|
|
}
|
|
|
|
void Graphics3::drawIndexedVertices(int start, int count) {
|
|
#ifdef KINC_OPENGL_ES
|
|
#if defined(KINC_ANDROID) || defined(KINC_RASPBERRY_PI)
|
|
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_SHORT, (void *)(start * sizeof(GL_UNSIGNED_SHORT)));
|
|
#else
|
|
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, (void *)(start * sizeof(GL_UNSIGNED_INT)));
|
|
#endif
|
|
glCheckErrors();
|
|
#else
|
|
{
|
|
glDrawElements(GL_TRIANGLES, count, GL_UNSIGNED_INT, (void *)(start * sizeof(GL_UNSIGNED_INT)));
|
|
glCheckErrors();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Graphics3::swapBuffers(int contextId) {
|
|
#ifdef KINC_WINDOWS
|
|
::SwapBuffers(deviceContexts[contextId]);
|
|
#else
|
|
System::swapBuffers(contextId);
|
|
#endif
|
|
}
|
|
|
|
#ifdef KINC_IOS
|
|
void beginGL();
|
|
#endif
|
|
|
|
#if defined(KINC_WINDOWS)
|
|
void Graphics3::makeCurrent(int contextId) {
|
|
wglMakeCurrent(deviceContexts[contextId], glContexts[contextId]);
|
|
}
|
|
#endif
|
|
|
|
void Graphics3::begin(int contextId) {
|
|
if (System::currentDevice() != -1) {
|
|
if (System::currentDevice() != contextId) {
|
|
log(Warning, "begin: wrong glContext is active");
|
|
}
|
|
else {
|
|
//**log(Warning, "begin: a glContext is still active");
|
|
}
|
|
|
|
// return; // TODO (DK) return here?
|
|
}
|
|
|
|
// System::setCurrentDevice(contextId);
|
|
System::makeCurrent(contextId);
|
|
|
|
glViewport(0, 0, _width, _height);
|
|
|
|
#ifdef KINC_IOS
|
|
beginGL();
|
|
#endif
|
|
|
|
#ifdef KINC_ANDROID
|
|
// if rendered to a texture, strange things happen if the backbuffer is not cleared
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
#endif
|
|
}
|
|
|
|
void Graphics3::viewport(int x, int y, int width, int height) {
|
|
glViewport(x, _renderTargetHeight - y - height, width, height);
|
|
}
|
|
|
|
void Graphics3::scissor(int x, int y, int width, int height) {
|
|
glEnable(GL_SCISSOR_TEST);
|
|
glScissor(x, _renderTargetHeight - y - height, width, height);
|
|
}
|
|
|
|
void Graphics3::disableScissor() {
|
|
glDisable(GL_SCISSOR_TEST);
|
|
}
|
|
|
|
namespace {
|
|
GLenum convert(Graphics3::StencilAction action) {
|
|
switch (action) {
|
|
case Graphics3::Decrement:
|
|
return GL_DECR;
|
|
case Graphics3::DecrementWrap:
|
|
return GL_DECR_WRAP;
|
|
case Graphics3::Increment:
|
|
return GL_INCR;
|
|
case Graphics3::IncrementWrap:
|
|
return GL_INCR_WRAP;
|
|
case Graphics3::Invert:
|
|
return GL_INVERT;
|
|
case Graphics3::Keep:
|
|
return GL_KEEP;
|
|
case Graphics3::Replace:
|
|
return GL_REPLACE;
|
|
case Graphics3::Zero:
|
|
return GL_ZERO;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void Graphics3::setStencilParameters(ZCompareMode compareMode, StencilAction bothPass, StencilAction depthFail, StencilAction stencilFail, int referenceValue,
|
|
int readMask, int writeMask) {
|
|
if (compareMode == ZCompareAlways && bothPass == Keep && depthFail == Keep && stencilFail == Keep) {
|
|
glDisable(GL_STENCIL_TEST);
|
|
}
|
|
else {
|
|
glEnable(GL_STENCIL_TEST);
|
|
int stencilFunc = 0;
|
|
switch (compareMode) {
|
|
case ZCompareAlways:
|
|
stencilFunc = GL_ALWAYS;
|
|
break;
|
|
case ZCompareEqual:
|
|
stencilFunc = GL_EQUAL;
|
|
break;
|
|
case ZCompareGreater:
|
|
stencilFunc = GL_GREATER;
|
|
break;
|
|
case ZCompareGreaterEqual:
|
|
stencilFunc = GL_GEQUAL;
|
|
break;
|
|
case ZCompareLess:
|
|
stencilFunc = GL_LESS;
|
|
break;
|
|
case ZCompareLessEqual:
|
|
stencilFunc = GL_LEQUAL;
|
|
break;
|
|
case ZCompareNever:
|
|
stencilFunc = GL_NEVER;
|
|
break;
|
|
case ZCompareNotEqual:
|
|
stencilFunc = GL_NOTEQUAL;
|
|
break;
|
|
}
|
|
glStencilMask(writeMask);
|
|
glStencilOp(convert(stencilFail), convert(depthFail), convert(bothPass));
|
|
glStencilFunc(stencilFunc, referenceValue, readMask);
|
|
}
|
|
}
|
|
|
|
/*void glCheckErrors() {
|
|
if (System::currentDevice() == -1) {
|
|
log(Warning, "no OpenGL device context is set");
|
|
return;
|
|
}
|
|
|
|
//#ifdef _DEBUG
|
|
GLenum code = glGetError();
|
|
while (code != GL_NO_ERROR) {
|
|
//std::printf("GLError: %s\n", glewGetErrorString(code));
|
|
switch (code) {
|
|
case GL_INVALID_VALUE:
|
|
log(Warning, "OpenGL: Invalid value");
|
|
break;
|
|
case GL_INVALID_OPERATION:
|
|
log(Warning, "OpenGL: Invalid operation");
|
|
break;
|
|
default:
|
|
log(Warning, "OpenGL: Error code %i", code);
|
|
break;
|
|
}
|
|
code = glGetError();
|
|
}
|
|
//#endif
|
|
}*/
|
|
|
|
#ifdef KINC_WINDOWS
|
|
void Graphics3::clearCurrent() {
|
|
wglMakeCurrent(nullptr, nullptr);
|
|
}
|
|
#endif
|
|
|
|
// TODO (DK) this never gets called on some targets, needs investigation?
|
|
void Graphics3::end(int windowId) {
|
|
// glClearColor(1.0f, 1.0f, 0.0f, 1.0f);
|
|
// glClear(GL_COLOR_BUFFER_BIT);
|
|
glCheckErrors();
|
|
|
|
if (System::currentDevice() == -1) {
|
|
log(Warning, "end: a glContext wasn't active");
|
|
}
|
|
|
|
if (System::currentDevice() != windowId) {
|
|
log(Warning, "end: wrong glContext is active");
|
|
}
|
|
|
|
System::clearCurrent();
|
|
}
|
|
|
|
void Graphics3::clear(uint flags, uint color, float depth, int stencil) {
|
|
glClearColor(((color & 0x00ff0000) >> 16) / 255.0f, ((color & 0x0000ff00) >> 8) / 255.0f, (color & 0x000000ff) / 255.0f, (color & 0xff000000) / 255.0f);
|
|
glCheckErrors();
|
|
if (flags & ClearDepthFlag) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_TRUE);
|
|
glCheckErrors();
|
|
}
|
|
#ifdef KINC_OPENGL_ES
|
|
glClearDepthf(depth);
|
|
#else
|
|
glClearDepth(depth);
|
|
#endif
|
|
glCheckErrors();
|
|
glStencilMask(0xff);
|
|
glCheckErrors();
|
|
glClearStencil(stencil);
|
|
glCheckErrors();
|
|
GLbitfield oglflags = ((flags & ClearColorFlag) ? GL_COLOR_BUFFER_BIT : 0) | ((flags & ClearDepthFlag) ? GL_DEPTH_BUFFER_BIT : 0) |
|
|
((flags & ClearStencilFlag) ? GL_STENCIL_BUFFER_BIT : 0);
|
|
glClear(oglflags);
|
|
glCheckErrors();
|
|
if (depthTest) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
}
|
|
else {
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
glCheckErrors();
|
|
if (depthMask) {
|
|
glDepthMask(GL_TRUE);
|
|
}
|
|
else {
|
|
glDepthMask(GL_FALSE);
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
void Graphics3::setColorMask(bool red, bool green, bool blue, bool alpha) {
|
|
glColorMask(red, green, blue, alpha);
|
|
}
|
|
|
|
void Graphics3::setMaterialState(MaterialState state, const vec4 &value) {
|
|
switch (state) {
|
|
case AmbientColor:
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, value.values);
|
|
break;
|
|
case DiffuseColor:
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, value.values);
|
|
break;
|
|
case SpecularColor:
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, value.values);
|
|
break;
|
|
case EmissionColor:
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, value.values);
|
|
break;
|
|
case SolidColor:
|
|
glColor4fv(value.values);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Graphics3::setMaterialState(MaterialState state, float value) {
|
|
switch (state) {
|
|
case ShininessExponent:
|
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, max(0.0f, min(value, 180.0f)));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Graphics3::setTextureMapping(TextureUnit texunit, TextureMapping mapping, bool on) {
|
|
glActiveTexture(GL_TEXTURE0 + texunit.unit);
|
|
switch (mapping) {
|
|
case Texture1D:
|
|
// Enable/disabled 1D texture mapping for active texture layer
|
|
if (on)
|
|
glEnable(GL_TEXTURE_1D);
|
|
else
|
|
glDisable(GL_TEXTURE_1D);
|
|
break;
|
|
case Texture2D:
|
|
// Enable/disabled 2D texture mapping for active texture layer
|
|
if (on)
|
|
glEnable(GL_TEXTURE_2D);
|
|
else
|
|
glDisable(GL_TEXTURE_2D);
|
|
break;
|
|
case Texture3D:
|
|
// Enable/disabled 3D texture mapping for active texture layer
|
|
if (on)
|
|
glEnable(GL_TEXTURE_3D);
|
|
else
|
|
glDisable(GL_TEXTURE_3D);
|
|
break;
|
|
case TextureCubeMap:
|
|
// Enable/disabled cube texture mapping for active texture layer
|
|
if (on)
|
|
glEnable(GL_TEXTURE_CUBE_MAP);
|
|
else
|
|
glDisable(GL_TEXTURE_CUBE_MAP);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static GLenum texCoordToGLenum(Graphics3::TextureCoordinate texcoord) {
|
|
switch (texcoord) {
|
|
case Graphics3::TexCoordX:
|
|
return GL_S;
|
|
case Graphics3::TexCoordY:
|
|
return GL_T;
|
|
case Graphics3::TexCoordZ:
|
|
return GL_R;
|
|
case Graphics3::TexCoordW:
|
|
return GL_Q;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static GLenum texGenCoordToGLenum(Graphics3::TextureCoordinate texcoord) {
|
|
switch (texcoord) {
|
|
case Graphics3::TexCoordX:
|
|
return GL_TEXTURE_GEN_S;
|
|
case Graphics3::TexCoordY:
|
|
return GL_TEXTURE_GEN_T;
|
|
case Graphics3::TexCoordZ:
|
|
return GL_TEXTURE_GEN_R;
|
|
case Graphics3::TexCoordW:
|
|
return GL_TEXTURE_GEN_Q;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void Graphics3::setTexCoordGeneration(TextureUnit texunit, TextureCoordinate texcoord, TexCoordGeneration generation) {
|
|
glActiveTexture(GL_TEXTURE0 + texunit.unit);
|
|
switch (generation) {
|
|
case TexGenDisabled:
|
|
// Disable texture coordinate generation for 'texcoord'
|
|
glDisable(texGenCoordToGLenum(texcoord));
|
|
break;
|
|
case TexGenObjectLinear:
|
|
// Enable and configure texture coordinate generation for 'texcoord' with 'generation' mode
|
|
glEnable(texGenCoordToGLenum(texcoord));
|
|
glTexGeni(texCoordToGLenum(texcoord), GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
|
|
break;
|
|
case TexGenViewLinear:
|
|
// Enable and configure texture coordinate generation for 'texcoord' with 'generation' mode
|
|
glEnable(texGenCoordToGLenum(texcoord));
|
|
glTexGeni(texCoordToGLenum(texcoord), GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);
|
|
break;
|
|
case TexGenSphereMap:
|
|
// Enable and configure texture coordinate generation for 'texcoord' with 'generation' mode
|
|
glEnable(texGenCoordToGLenum(texcoord));
|
|
glTexGeni(texCoordToGLenum(texcoord), GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
|
|
break;
|
|
case TexGenNormalMap:
|
|
// Enable and configure texture coordinate generation for 'texcoord' with 'generation' mode
|
|
glEnable(texGenCoordToGLenum(texcoord));
|
|
glTexGeni(texCoordToGLenum(texcoord), GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP);
|
|
break;
|
|
case TexGenReflectionMap:
|
|
// Enable and configure texture coordinate generation for 'texcoord' with 'generation' mode
|
|
glEnable(texGenCoordToGLenum(texcoord));
|
|
glTexGeni(texCoordToGLenum(texcoord), GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Graphics3::setRenderState(RenderState state, bool on) {
|
|
switch (state) {
|
|
case DepthWrite:
|
|
if (on)
|
|
glDepthMask(GL_TRUE);
|
|
else
|
|
glDepthMask(GL_FALSE);
|
|
depthMask = on;
|
|
break;
|
|
case DepthTest:
|
|
if (on)
|
|
glEnable(GL_DEPTH_TEST);
|
|
else
|
|
glDisable(GL_DEPTH_TEST);
|
|
depthTest = on;
|
|
break;
|
|
case BlendingState:
|
|
if (on)
|
|
glEnable(GL_BLEND);
|
|
else
|
|
glDisable(GL_BLEND);
|
|
break;
|
|
case Lighting:
|
|
// Enable/Disable lighting
|
|
if (on)
|
|
glEnable(GL_LIGHTING);
|
|
else
|
|
glDisable(GL_LIGHTING);
|
|
break;
|
|
case Normalize:
|
|
// Enable/disable automatic normalize of normal vectors for non-uniform scaled models
|
|
if (on)
|
|
glEnable(GL_NORMALIZE);
|
|
else
|
|
glDisable(GL_NORMALIZE);
|
|
break;
|
|
case FogState:
|
|
// Enable/disable fog
|
|
if (on)
|
|
glEnable(GL_FOG);
|
|
else
|
|
glDisable(GL_FOG);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
glCheckErrors();
|
|
|
|
/*switch (state) {
|
|
case Normalize:
|
|
device->SetRenderState(D3DRS_NORMALIZENORMALS, on ? TRUE : FALSE);
|
|
break;
|
|
case BackfaceCulling:
|
|
if (on) device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CCW);
|
|
else device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
|
|
break;
|
|
case FogState:
|
|
device->SetRenderState(D3DRS_FOGENABLE, on ? TRUE : FALSE);
|
|
break;
|
|
case ScissorTestState:
|
|
device->SetRenderState(D3DRS_SCISSORTESTENABLE, on ? TRUE : FALSE);
|
|
break;
|
|
case AlphaTestState:
|
|
device->SetRenderState(D3DRS_ALPHATESTENABLE, on ? TRUE : FALSE);
|
|
device->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);
|
|
break;
|
|
default:
|
|
throw Exception();
|
|
}*/
|
|
}
|
|
|
|
void Graphics3::setRenderState(RenderState state, int v) {
|
|
switch (state) {
|
|
case DepthTestCompare:
|
|
switch (v) {
|
|
default:
|
|
case ZCompareAlways:
|
|
v = GL_ALWAYS;
|
|
break;
|
|
case ZCompareNever:
|
|
v = GL_NEVER;
|
|
break;
|
|
case ZCompareEqual:
|
|
v = GL_EQUAL;
|
|
break;
|
|
case ZCompareNotEqual:
|
|
v = GL_NOTEQUAL;
|
|
break;
|
|
case ZCompareLess:
|
|
v = GL_LESS;
|
|
break;
|
|
case ZCompareLessEqual:
|
|
v = GL_LEQUAL;
|
|
break;
|
|
case ZCompareGreater:
|
|
v = GL_GREATER;
|
|
break;
|
|
case ZCompareGreaterEqual:
|
|
v = GL_GEQUAL;
|
|
break;
|
|
}
|
|
glDepthFunc(v);
|
|
glCheckErrors();
|
|
break;
|
|
case BackfaceCulling:
|
|
switch (v) {
|
|
case Clockwise:
|
|
glEnable(GL_CULL_FACE);
|
|
glCullFace(GL_BACK);
|
|
glCheckErrors();
|
|
break;
|
|
case CounterClockwise:
|
|
glEnable(GL_CULL_FACE);
|
|
glCullFace(GL_FRONT);
|
|
glCheckErrors();
|
|
break;
|
|
case NoCulling:
|
|
glDisable(GL_CULL_FACE);
|
|
glCheckErrors();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case FogType:
|
|
switch (v) {
|
|
case LinearFog:
|
|
glFogi(GL_FOG_MODE, GL_LINEAR);
|
|
break;
|
|
case ExpFog:
|
|
glFogi(GL_FOG_MODE, GL_EXP);
|
|
break;
|
|
case Exp2Fog:
|
|
glFogi(GL_FOG_MODE, GL_EXP2);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
/*switch (state) {
|
|
case DepthTestCompare:
|
|
switch (v) {
|
|
// TODO: Cmp-Konstanten systemabhaengig abgleichen
|
|
default:
|
|
case ZCmp_Always : v = D3DCMP_ALWAYS; break;
|
|
case ZCmp_Never : v = D3DCMP_NEVER; break;
|
|
case ZCmp_Equal : v = D3DCMP_EQUAL; break;
|
|
case ZCmp_NotEqual : v = D3DCMP_NOTEQUAL; break;
|
|
case ZCmp_Less : v = D3DCMP_LESS; break;
|
|
case ZCmp_LessEqual : v = D3DCMP_LESSEQUAL; break;
|
|
case ZCmp_Greater : v = D3DCMP_GREATER; break;
|
|
case ZCmp_GreaterEqual: v = D3DCMP_GREATEREQUAL; break;
|
|
}
|
|
device->SetRenderState(D3DRS_ZFUNC, v);
|
|
break;
|
|
case FogTypeState:
|
|
switch (v) {
|
|
case LinearFog:
|
|
device->SetRenderState(D3DRS_FOGVERTEXMODE, D3DFOG_LINEAR);
|
|
}
|
|
break;
|
|
case AlphaReferenceState:
|
|
device->SetRenderState(D3DRS_ALPHAREF, (DWORD)v);
|
|
break;
|
|
default:
|
|
throw Exception();
|
|
}*/
|
|
}
|
|
|
|
void Graphics3::setRenderState(RenderState state, float value) {
|
|
switch (state) {
|
|
case FogStart:
|
|
glFogf(GL_FOG_START, value);
|
|
glCheckErrors();
|
|
break;
|
|
case FogEnd:
|
|
glFogf(GL_FOG_END, value);
|
|
glCheckErrors();
|
|
break;
|
|
case FogDensity:
|
|
glFogf(GL_FOG_DENSITY, value);
|
|
glCheckErrors();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// "vertex arrays" are not supported in OpenGL 1.x
|
|
// -> "glBindVertexArray" replaced with "glBindBuffer" and several calls to "gl[...]Pointer"
|
|
// -> see VertexBufferImpl::setVertexAttributes
|
|
void Graphics3::setVertexBuffers(VertexBuffer **vertexBuffers, int count) {
|
|
int offset = 0;
|
|
for (int i = 0; i < count; ++i) {
|
|
offset += vertexBuffers[i]->_set(offset);
|
|
}
|
|
}
|
|
|
|
void Graphics3::setIndexBuffer(IndexBuffer &indexBuffer) {
|
|
indexBuffer._set();
|
|
}
|
|
|
|
void Graphics3::setTexture(TextureUnit unit, Texture *texture) {
|
|
texture->_set(unit);
|
|
}
|
|
|
|
void Graphics3::setTextureAddressing(TextureUnit unit, TexDir dir, TextureAddressing addressing) {
|
|
glActiveTexture(GL_TEXTURE0 + unit.unit);
|
|
GLenum texDir;
|
|
switch (dir) {
|
|
case U:
|
|
texDir = GL_TEXTURE_WRAP_S;
|
|
break;
|
|
case V:
|
|
texDir = GL_TEXTURE_WRAP_T;
|
|
break;
|
|
}
|
|
switch (addressing) {
|
|
case Clamp:
|
|
glTexParameteri(GL_TEXTURE_2D, texDir, GL_CLAMP_TO_EDGE);
|
|
break;
|
|
case Repeat:
|
|
glTexParameteri(GL_TEXTURE_2D, texDir, GL_REPEAT);
|
|
break;
|
|
case Border:
|
|
// unsupported
|
|
glTexParameteri(GL_TEXTURE_2D, texDir, GL_CLAMP_TO_EDGE);
|
|
break;
|
|
case Mirror:
|
|
// unsupported
|
|
glTexParameteri(GL_TEXTURE_2D, texDir, GL_REPEAT);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
void Graphics3::setTextureMagnificationFilter(TextureUnit texunit, TextureFilter filter) {
|
|
glActiveTexture(GL_TEXTURE0 + texunit.unit);
|
|
glCheckErrors();
|
|
switch (filter) {
|
|
case PointFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
break;
|
|
case LinearFilter:
|
|
case AnisotropicFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
|
|
namespace {
|
|
void setMinMipFilters(int unit) {
|
|
glActiveTexture(GL_TEXTURE0 + unit);
|
|
glCheckErrors();
|
|
switch (minFilters[System::currentDevice()][unit]) {
|
|
case Graphics3::PointFilter:
|
|
switch (mipFilters[System::currentDevice()][unit]) {
|
|
case Graphics3::NoMipFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
break;
|
|
case Graphics3::PointMipFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
|
|
break;
|
|
case Graphics3::LinearMipFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
|
|
break;
|
|
}
|
|
break;
|
|
case Graphics3::LinearFilter:
|
|
case Graphics3::AnisotropicFilter:
|
|
switch (mipFilters[System::currentDevice()][unit]) {
|
|
case Graphics3::NoMipFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
break;
|
|
case Graphics3::PointMipFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
|
|
break;
|
|
case Graphics3::LinearMipFilter:
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
glCheckErrors();
|
|
}
|
|
}
|
|
|
|
void Graphics3::setTextureMinificationFilter(TextureUnit texunit, TextureFilter filter) {
|
|
minFilters[System::currentDevice()][texunit.unit] = filter;
|
|
setMinMipFilters(texunit.unit);
|
|
}
|
|
|
|
void Graphics3::setTextureMipmapFilter(TextureUnit texunit, MipmapFilter filter) {
|
|
mipFilters[System::currentDevice()][texunit.unit] = filter;
|
|
setMinMipFilters(texunit.unit);
|
|
}
|
|
|
|
namespace {
|
|
GLenum convert(Graphics3::BlendingOperation operation) {
|
|
switch (operation) {
|
|
case Graphics3::BlendZero:
|
|
return GL_ZERO;
|
|
case Graphics3::BlendOne:
|
|
return GL_ONE;
|
|
case Graphics3::SourceAlpha:
|
|
return GL_SRC_ALPHA;
|
|
case Graphics3::DestinationAlpha:
|
|
return GL_DST_ALPHA;
|
|
case Graphics3::InverseSourceAlpha:
|
|
return GL_ONE_MINUS_SRC_ALPHA;
|
|
case Graphics3::InverseDestinationAlpha:
|
|
return GL_ONE_MINUS_DST_ALPHA;
|
|
case Graphics3::SourceColor:
|
|
return GL_SRC_COLOR;
|
|
case Graphics3::DestinationColor:
|
|
return GL_DST_COLOR;
|
|
case Graphics3::InverseSourceColor:
|
|
return GL_ONE_MINUS_SRC_COLOR;
|
|
case Graphics3::InverseDestinationColor:
|
|
return GL_ONE_MINUS_DST_COLOR;
|
|
default:
|
|
return GL_ONE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Graphics3::setTextureOperation(TextureOperation operation, TextureArgument arg1, TextureArgument arg2) {
|
|
// glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
}
|
|
|
|
void Graphics3::setBlendingMode(BlendingOperation source, BlendingOperation destination) {
|
|
glBlendFunc(convert(source), convert(destination));
|
|
glCheckErrors();
|
|
}
|
|
|
|
void Graphics3::setRenderTarget(RenderTarget *texture, int num, int additionalTargets) {
|
|
if (num == 0) {
|
|
// TODO (DK) uneccessary?
|
|
// System::makeCurrent(texture->contextId);
|
|
glBindFramebuffer(GL_FRAMEBUFFER, texture->_framebuffer);
|
|
glCheckErrors();
|
|
glViewport(0, 0, texture->width, texture->height);
|
|
_renderTargetWidth = texture->width;
|
|
_renderTargetHeight = texture->height;
|
|
renderToBackbuffer = false;
|
|
glCheckErrors();
|
|
}
|
|
|
|
if (additionalTargets > 0) {
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + num, GL_TEXTURE_2D, texture->_texture, 0);
|
|
if (num == additionalTargets) {
|
|
GLenum buffers[16];
|
|
for (int i = 0; i <= additionalTargets; ++i)
|
|
buffers[i] = GL_COLOR_ATTACHMENT0 + i;
|
|
#if defined(KINC_OPENGL_ES) && defined(KINC_ANDROID) && KINC_ANDROID_API >= 18
|
|
((void (*)(GLsizei, GLenum *))glesDrawBuffers)(additionalTargets + 1, buffers);
|
|
#elif !defined(KINC_OPENGL_ES)
|
|
glDrawBuffers(additionalTargets + 1, buffers);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
void Graphics3::restoreRenderTarget() {
|
|
glBindFramebuffer(GL_FRAMEBUFFER, originalFramebuffer[System::currentDevice()]);
|
|
glCheckErrors();
|
|
int w = System::windowWidth(System::currentDevice());
|
|
int h = System::windowHeight(System::currentDevice());
|
|
glViewport(0, 0, w, h);
|
|
_renderTargetWidth = w;
|
|
_renderTargetHeight = h;
|
|
renderToBackbuffer = true;
|
|
glCheckErrors();
|
|
}
|
|
|
|
void Graphics3::setLight(Light *light, int num) {
|
|
if (light) {
|
|
light->_set(num);
|
|
}
|
|
else {
|
|
glDisable(GL_LIGHT0 + num);
|
|
}
|
|
}
|
|
|
|
bool Graphics3::renderTargetsInvertedY() {
|
|
return true;
|
|
}
|
|
|
|
bool Graphics3::nonPow2TexturesSupported() {
|
|
return true;
|
|
}
|
|
|
|
void Graphics3::flush() {
|
|
glFlush();
|
|
glCheckErrors();
|
|
}
|