1923 lines
58 KiB
C++
1923 lines
58 KiB
C++
/*
|
|
* This file is part of the Colobot: Gold Edition source code
|
|
* Copyright (C) 2001-2020, Daniel Roux, EPSITEC SA & TerranovaTeam
|
|
* http://epsitec.ch; http://colobot.info; http://github.com/colobot
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see http://gnu.org/licenses
|
|
*/
|
|
|
|
#include "graphics/opengl/gl33device.h"
|
|
|
|
#include "common/config.h"
|
|
|
|
#include "common/config_file.h"
|
|
#include "common/image.h"
|
|
#include "common/logger.h"
|
|
#include "common/make_unique.h"
|
|
|
|
#include "graphics/core/light.h"
|
|
|
|
#include "graphics/engine/engine.h"
|
|
|
|
#include "graphics/opengl/glframebuffer.h"
|
|
|
|
#include "math/geometry.h"
|
|
|
|
|
|
#include <SDL.h>
|
|
#include <physfs.h>
|
|
|
|
#include <cassert>
|
|
|
|
|
|
// Graphics module namespace
|
|
namespace Gfx
|
|
{
|
|
|
|
CGL33Device::CGL33Device(const DeviceConfig &config)
|
|
: m_config(config)
|
|
{}
|
|
|
|
|
|
CGL33Device::~CGL33Device()
|
|
{
|
|
}
|
|
|
|
void CGL33Device::DebugHook()
|
|
{
|
|
/* This function is only called here, so it can be used
|
|
* as a breakpoint when debugging using gDEBugger */
|
|
glColor3i(0, 0, 0);
|
|
}
|
|
|
|
void CGL33Device::DebugLights()
|
|
{
|
|
Gfx::ColorHSV color(0.0, 1.0, 1.0);
|
|
|
|
glLineWidth(3.0f);
|
|
glDisable(GL_LIGHTING);
|
|
glDepthMask(GL_FALSE);
|
|
glDisable(GL_BLEND);
|
|
|
|
Math::Matrix saveWorldMat = m_worldMat;
|
|
Math::Matrix identity;
|
|
identity.LoadIdentity();
|
|
SetTransform(TRANSFORM_WORLD, identity);
|
|
|
|
for (int i = 0; i < static_cast<int>( m_lights.size() ); ++i)
|
|
{
|
|
color.h = static_cast<float>(i) / static_cast<float>(m_lights.size());
|
|
if (m_lightsEnabled[i])
|
|
{
|
|
const Light& l = m_lights[i];
|
|
if (l.type == LIGHT_DIRECTIONAL)
|
|
{
|
|
Gfx::VertexCol v[2];
|
|
v[0].coord = -Math::Normalize(l.direction) * 100.0f + Math::Vector(0.0f, 0.0f, 1.0f) * i;
|
|
v[0].color = HSV2RGB(color);
|
|
v[1].coord = Math::Normalize(l.direction) * 100.0f + Math::Vector(0.0f, 0.0f, 1.0f) * i;
|
|
v[1].color = HSV2RGB(color);
|
|
while (v[0].coord.y < 60.0f && v[0].coord.y < 60.0f)
|
|
{
|
|
v[0].coord.y += 10.0f;
|
|
v[1].coord.y += 10.0f;
|
|
}
|
|
DrawPrimitive(PRIMITIVE_LINES, v, 2);
|
|
|
|
v[0].coord = v[1].coord + Math::Normalize(v[0].coord - v[1].coord) * 50.0f;
|
|
|
|
glLineWidth(10.0f);
|
|
DrawPrimitive(PRIMITIVE_LINES, v, 2);
|
|
glLineWidth(3.0f);
|
|
}
|
|
else if (l.type == LIGHT_POINT)
|
|
{
|
|
Gfx::VertexCol v[8];
|
|
for (int i = 0; i < 8; ++i)
|
|
v[i].color = HSV2RGB(color);
|
|
|
|
v[0].coord = l.position + Math::Vector(-1.0f, -1.0f, -1.0f) * 4.0f;
|
|
v[1].coord = l.position + Math::Vector( 1.0f, -1.0f, -1.0f) * 4.0f;
|
|
v[2].coord = l.position + Math::Vector( 1.0f, 1.0f, -1.0f) * 4.0f;
|
|
v[3].coord = l.position + Math::Vector(-1.0f, 1.0f, -1.0f) * 4.0f;
|
|
v[4].coord = l.position + Math::Vector(-1.0f, -1.0f, -1.0f) * 4.0f;
|
|
DrawPrimitive(PRIMITIVE_LINE_STRIP, v, 5);
|
|
|
|
v[0].coord = l.position + Math::Vector(-1.0f, -1.0f, 1.0f) * 4.0f;
|
|
v[1].coord = l.position + Math::Vector( 1.0f, -1.0f, 1.0f) * 4.0f;
|
|
v[2].coord = l.position + Math::Vector( 1.0f, 1.0f, 1.0f) * 4.0f;
|
|
v[3].coord = l.position + Math::Vector(-1.0f, 1.0f, 1.0f) * 4.0f;
|
|
v[4].coord = l.position + Math::Vector(-1.0f, -1.0f, 1.0f) * 4.0f;
|
|
DrawPrimitive(PRIMITIVE_LINE_STRIP, v, 5);
|
|
|
|
v[0].coord = l.position + Math::Vector(-1.0f, -1.0f, -1.0f) * 4.0f;
|
|
v[1].coord = l.position + Math::Vector(-1.0f, -1.0f, 1.0f) * 4.0f;
|
|
v[2].coord = l.position + Math::Vector( 1.0f, -1.0f, -1.0f) * 4.0f;
|
|
v[3].coord = l.position + Math::Vector( 1.0f, -1.0f, 1.0f) * 4.0f;
|
|
v[4].coord = l.position + Math::Vector( 1.0f, 1.0f, -1.0f) * 4.0f;
|
|
v[5].coord = l.position + Math::Vector( 1.0f, 1.0f, 1.0f) * 4.0f;
|
|
v[6].coord = l.position + Math::Vector(-1.0f, 1.0f, -1.0f) * 4.0f;
|
|
v[7].coord = l.position + Math::Vector(-1.0f, 1.0f, 1.0f) * 4.0f;
|
|
DrawPrimitive(PRIMITIVE_LINES, v, 8);
|
|
}
|
|
else if (l.type == LIGHT_SPOT)
|
|
{
|
|
Gfx::VertexCol v[5];
|
|
for (int i = 0; i < 5; ++i)
|
|
v[i].color = HSV2RGB(color);
|
|
|
|
v[0].coord = l.position + Math::Vector(-1.0f, 0.0f, -1.0f) * 4.0f;
|
|
v[1].coord = l.position + Math::Vector( 1.0f, 0.0f, -1.0f) * 4.0f;
|
|
v[2].coord = l.position + Math::Vector( 1.0f, 0.0f, 1.0f) * 4.0f;
|
|
v[3].coord = l.position + Math::Vector(-1.0f, 0.0f, 1.0f) * 4.0f;
|
|
v[4].coord = l.position + Math::Vector(-1.0f, 0.0f, -1.0f) * 4.0f;
|
|
DrawPrimitive(PRIMITIVE_LINE_STRIP, v, 5);
|
|
|
|
v[0].coord = l.position;
|
|
v[1].coord = l.position + Math::Normalize(l.direction) * 100.0f;
|
|
glEnable(GL_LINE_STIPPLE);
|
|
glLineStipple(3.0, 0xFF);
|
|
DrawPrimitive(PRIMITIVE_LINES, v, 2);
|
|
glDisable(GL_LINE_STIPPLE);
|
|
}
|
|
}
|
|
}
|
|
|
|
glLineWidth(1.0f);
|
|
glEnable(GL_LIGHTING);
|
|
glDepthMask(GL_TRUE);
|
|
glEnable(GL_BLEND);
|
|
|
|
SetTransform(TRANSFORM_WORLD, saveWorldMat);
|
|
}
|
|
|
|
std::string CGL33Device::GetName()
|
|
{
|
|
return std::string("OpenGL 3.3");
|
|
}
|
|
|
|
bool CGL33Device::Create()
|
|
{
|
|
GetLogger()->Info("Creating CDevice - OpenGL 3.3\n");
|
|
|
|
if (!InitializeGLEW())
|
|
{
|
|
m_errorMessage = "An error occurred while initializing GLEW.";
|
|
return false;
|
|
}
|
|
|
|
// Extract OpenGL version
|
|
int glMajor, glMinor;
|
|
int glVersion = GetOpenGLVersion(glMajor, glMinor);
|
|
|
|
if (glVersion < 32)
|
|
{
|
|
GetLogger()->Error("Unsupported OpenGL version: %d.%d\n", glMajor, glMinor);
|
|
GetLogger()->Error("OpenGL 3.2 or newer is required to use this engine.\n");
|
|
m_errorMessage = "It seems your graphics card does not support OpenGL 3.2.\n";
|
|
m_errorMessage += "Please make sure you have appropriate hardware and newest drivers installed.\n";
|
|
m_errorMessage += "(OpenGL 3.2 is roughly equivalent to Direct3D 10)\n\n";
|
|
m_errorMessage += GetHardwareInfo();
|
|
return false;
|
|
}
|
|
else if (glVersion < 33)
|
|
{
|
|
GetLogger()->Warn("Partially supported OpenGL version: %d.%d\n", glMajor, glMinor);
|
|
GetLogger()->Warn("You may experience problems while running the game on this engine.\n");
|
|
GetLogger()->Warn("OpenGL 3.3 or newer is recommended.\n");
|
|
}
|
|
else
|
|
{
|
|
const char* version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
|
|
const char* renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
|
|
|
|
GetLogger()->Info("OpenGL %s\n", version);
|
|
GetLogger()->Info("%s\n", renderer);
|
|
}
|
|
|
|
// Detect support of anisotropic filtering
|
|
m_capabilities.anisotropySupported = AreExtensionsSupported("GL_EXT_texture_filter_anisotropic");
|
|
if (m_capabilities.anisotropySupported)
|
|
{
|
|
// Obtain maximum anisotropy level available
|
|
float level;
|
|
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &level);
|
|
m_capabilities.maxAnisotropy = static_cast<int>(level);
|
|
|
|
GetLogger()->Info("Anisotropic filtering available\n");
|
|
GetLogger()->Info("Maximum anisotropy: %d\n", m_capabilities.maxAnisotropy);
|
|
}
|
|
else
|
|
{
|
|
GetLogger()->Info("Anisotropic filtering not available\n");
|
|
}
|
|
|
|
m_capabilities.multisamplingSupported = true;
|
|
glGetIntegerv(GL_MAX_SAMPLES, &m_capabilities.maxSamples);
|
|
GetLogger()->Info("Multisampling supported, max samples: %d\n", m_capabilities.maxSamples);
|
|
|
|
// Set just to be sure
|
|
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
|
|
|
|
glViewport(0, 0, m_config.size.x, m_config.size.y);
|
|
|
|
// this is set in shader
|
|
m_capabilities.maxLights = 4;
|
|
|
|
m_lights = std::vector<Light>(m_capabilities.maxLights, Light());
|
|
m_lightsEnabled = std::vector<bool>(m_capabilities.maxLights, false);
|
|
|
|
int maxTextures = 0;
|
|
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures);
|
|
GetLogger()->Info("Maximum texture image units: %d\n", maxTextures);
|
|
|
|
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &m_capabilities.maxTextureSize);
|
|
GetLogger()->Info("Maximum texture size: %d\n", m_capabilities.maxTextureSize);
|
|
|
|
m_capabilities.multitexturingSupported = true;
|
|
m_capabilities.maxTextures = maxTextures;
|
|
|
|
m_currentTextures = std::vector<Texture> (maxTextures, Texture());
|
|
m_texturesEnabled = std::vector<bool> (maxTextures, false);
|
|
m_textureStageParams = std::vector<TextureStageParams>(maxTextures, TextureStageParams());
|
|
|
|
m_capabilities.shadowMappingSupported = true;
|
|
|
|
m_capabilities.framebufferSupported = true;
|
|
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &m_capabilities.maxRenderbufferSize);
|
|
GetLogger()->Info("Maximum renderbuffer size: %d\n", m_capabilities.maxRenderbufferSize);
|
|
|
|
// Create shader program for normal rendering
|
|
GLint shaders[2];
|
|
char filename[64];
|
|
|
|
strcpy(filename, "shaders/gl33/vs_normal.glsl");
|
|
shaders[0] = LoadShader(GL_VERTEX_SHADER, filename);
|
|
if (shaders[0] == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not create vertex shader from file '%s'\n", filename);
|
|
return false;
|
|
}
|
|
|
|
strcpy(filename, "shaders/gl33/fs_normal.glsl");
|
|
shaders[1] = LoadShader(GL_FRAGMENT_SHADER, filename);
|
|
if (shaders[1] == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not create fragment shader from file '%s'\n", filename);
|
|
return false;
|
|
}
|
|
|
|
m_normalProgram = LinkProgram(2, shaders);
|
|
if (m_normalProgram == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not link shader program for normal rendering\n");
|
|
return false;
|
|
}
|
|
|
|
glDeleteShader(shaders[0]);
|
|
glDeleteShader(shaders[1]);
|
|
|
|
// Create program for interface rendering
|
|
strcpy(filename, "shaders/gl33/vs_interface.glsl");
|
|
shaders[0] = LoadShader(GL_VERTEX_SHADER, filename);
|
|
if (shaders[0] == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not create vertex shader from file '%s'\n", filename);
|
|
return false;
|
|
}
|
|
|
|
strcpy(filename, "shaders/gl33/fs_interface.glsl");
|
|
shaders[1] = LoadShader(GL_FRAGMENT_SHADER, filename);
|
|
if (shaders[1] == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not create fragment shader from file '%s'\n", filename);
|
|
return false;
|
|
}
|
|
|
|
m_interfaceProgram = LinkProgram(2, shaders);
|
|
if (m_interfaceProgram == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not link shader program for interface rendering\n");
|
|
return false;
|
|
}
|
|
|
|
glDeleteShader(shaders[0]);
|
|
glDeleteShader(shaders[1]);
|
|
|
|
// Create program for shadow rendering
|
|
strcpy(filename, "shaders/gl33/vs_shadow.glsl");
|
|
shaders[0] = LoadShader(GL_VERTEX_SHADER, filename);
|
|
if (shaders[0] == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not create vertex shader from file '%s'\n", filename);
|
|
return false;
|
|
}
|
|
|
|
strcpy(filename, "shaders/gl33/fs_shadow.glsl");
|
|
shaders[1] = LoadShader(GL_FRAGMENT_SHADER, filename);
|
|
if (shaders[1] == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not create fragment shader from file '%s'\n", filename);
|
|
return false;
|
|
}
|
|
|
|
m_shadowProgram = LinkProgram(2, shaders);
|
|
if (m_shadowProgram == 0)
|
|
{
|
|
m_errorMessage = GetLastShaderError();
|
|
GetLogger()->Error("Cound not link shader program for shadow rendering\n");
|
|
return false;
|
|
}
|
|
|
|
glDeleteShader(shaders[0]);
|
|
glDeleteShader(shaders[1]);
|
|
|
|
// Obtain uniform locations
|
|
// Obtain uniform locations for normal program
|
|
glUseProgram(m_normalProgram);
|
|
|
|
{
|
|
UniformLocations &uni = m_uniforms[0];
|
|
|
|
uni.projectionMatrix = glGetUniformLocation(m_normalProgram, "uni_ProjectionMatrix");
|
|
uni.viewMatrix = glGetUniformLocation(m_normalProgram, "uni_ViewMatrix");
|
|
uni.modelMatrix = glGetUniformLocation(m_normalProgram, "uni_ModelMatrix");
|
|
uni.normalMatrix = glGetUniformLocation(m_normalProgram, "uni_NormalMatrix");
|
|
uni.shadowMatrix = glGetUniformLocation(m_normalProgram, "uni_ShadowMatrix");
|
|
uni.cameraPosition = glGetUniformLocation(m_normalProgram, "uni_CameraPosition");
|
|
|
|
uni.primaryTexture = glGetUniformLocation(m_normalProgram, "uni_PrimaryTexture");
|
|
uni.secondaryTexture = glGetUniformLocation(m_normalProgram, "uni_SecondaryTexture");
|
|
uni.shadowTexture = glGetUniformLocation(m_normalProgram, "uni_ShadowTexture");
|
|
|
|
uni.textureEnabled[0] = glGetUniformLocation(m_normalProgram, "uni_PrimaryTextureEnabled");
|
|
uni.textureEnabled[1] = glGetUniformLocation(m_normalProgram, "uni_SecondaryTextureEnabled");
|
|
uni.textureEnabled[2] = glGetUniformLocation(m_normalProgram, "uni_ShadowTextureEnabled");
|
|
|
|
uni.fogEnabled = glGetUniformLocation(m_normalProgram, "uni_FogEnabled");
|
|
uni.fogRange = glGetUniformLocation(m_normalProgram, "uni_FogRange");
|
|
uni.fogColor = glGetUniformLocation(m_normalProgram, "uni_FogColor");
|
|
|
|
uni.alphaTestEnabled = glGetUniformLocation(m_normalProgram, "uni_AlphaTestEnabled");
|
|
uni.alphaReference = glGetUniformLocation(m_normalProgram, "uni_AlphaReference");
|
|
|
|
uni.shadowColor = glGetUniformLocation(m_normalProgram, "uni_ShadowColor");
|
|
|
|
uni.lightCount = glGetUniformLocation(m_normalProgram, "uni_LightCount");
|
|
|
|
uni.ambientColor = glGetUniformLocation(m_normalProgram, "uni_AmbientColor");
|
|
uni.diffuseColor = glGetUniformLocation(m_normalProgram, "uni_DiffuseColor");
|
|
uni.specularColor = glGetUniformLocation(m_normalProgram, "uni_SpecularColor");
|
|
|
|
GLchar name[64];
|
|
for (int i = 0; i < m_capabilities.maxLights; i++)
|
|
{
|
|
LightLocations &light = uni.lights[i];
|
|
|
|
sprintf(name, "uni_Light[%d].Enabled", i);
|
|
light.enabled = glGetUniformLocation(m_normalProgram, name);
|
|
|
|
sprintf(name, "uni_Light[%d].Position", i);
|
|
light.position = glGetUniformLocation(m_normalProgram, name);
|
|
|
|
sprintf(name, "uni_Light[%d].Ambient", i);
|
|
light.ambient = glGetUniformLocation(m_normalProgram, name);
|
|
|
|
sprintf(name, "uni_Light[%d].Diffuse", i);
|
|
light.diffuse = glGetUniformLocation(m_normalProgram, name);
|
|
|
|
sprintf(name, "uni_Light[%d].Specular", i);
|
|
light.specular = glGetUniformLocation(m_normalProgram, name);
|
|
|
|
sprintf(name, "uni_Light[%d].Attenuation", i);
|
|
light.attenuation = glGetUniformLocation(m_normalProgram, name);
|
|
}
|
|
|
|
// Set default uniform values
|
|
Math::Matrix matrix;
|
|
matrix.LoadIdentity();
|
|
|
|
glUniformMatrix4fv(uni.projectionMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.viewMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.modelMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.normalMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.shadowMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniform3f(uni.cameraPosition, 0.0f, 0.0f, 0.0f);
|
|
|
|
glUniform1i(uni.primaryTexture, 0);
|
|
glUniform1i(uni.secondaryTexture, 1);
|
|
glUniform1i(uni.shadowTexture, 2);
|
|
|
|
glUniform1i(uni.textureEnabled[0], 0);
|
|
glUniform1i(uni.textureEnabled[1], 0);
|
|
glUniform1i(uni.textureEnabled[2], 0);
|
|
|
|
glUniform4f(uni.ambientColor, 0.4f, 0.4f, 0.4f, 1.0f);
|
|
glUniform4f(uni.diffuseColor, 0.8f, 0.8f, 0.8f, 1.0f);
|
|
glUniform4f(uni.specularColor, 0.3f, 0.3f, 0.3f, 1.0f);
|
|
|
|
glUniform1i(uni.fogEnabled, 0);
|
|
glUniform2f(uni.fogRange, 100.0f, 200.0f);
|
|
glUniform4f(uni.fogColor, 0.8f, 0.8f, 0.8f, 1.0f);
|
|
|
|
glUniform1f(uni.shadowColor, 0.5f);
|
|
|
|
glUniform1i(uni.alphaTestEnabled, 0);
|
|
glUniform1f(uni.alphaReference, 0.5f);
|
|
|
|
glUniform1i(uni.lightCount, 0);
|
|
}
|
|
|
|
// Obtain uniform locations for interface program
|
|
glUseProgram(m_interfaceProgram);
|
|
|
|
{
|
|
UniformLocations &uni = m_uniforms[1];
|
|
|
|
uni.projectionMatrix = glGetUniformLocation(m_interfaceProgram, "uni_ProjectionMatrix");
|
|
uni.viewMatrix = glGetUniformLocation(m_interfaceProgram, "uni_ViewMatrix");
|
|
uni.modelMatrix = glGetUniformLocation(m_interfaceProgram, "uni_ModelMatrix");
|
|
|
|
uni.primaryTexture = glGetUniformLocation(m_interfaceProgram, "uni_Texture");
|
|
|
|
uni.textureEnabled[0] = glGetUniformLocation(m_interfaceProgram, "uni_TextureEnabled");
|
|
uni.textureEnabled[1] = -1;
|
|
uni.textureEnabled[2] = -1;
|
|
|
|
// Set default uniform values
|
|
Math::Matrix matrix;
|
|
matrix.LoadIdentity();
|
|
|
|
glUniformMatrix4fv(uni.projectionMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.viewMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.modelMatrix, 1, GL_FALSE, matrix.Array());
|
|
|
|
glUniform1i(uni.primaryTexture, 0);
|
|
|
|
glUniform1i(uni.textureEnabled[0], 0);
|
|
}
|
|
|
|
// Obtain uniform locations for shadow program
|
|
glUseProgram(m_shadowProgram);
|
|
|
|
{
|
|
UniformLocations &uni = m_uniforms[2];
|
|
|
|
uni.projectionMatrix = glGetUniformLocation(m_shadowProgram, "uni_ProjectionMatrix");
|
|
uni.viewMatrix = glGetUniformLocation(m_shadowProgram, "uni_ViewMatrix");
|
|
uni.modelMatrix = glGetUniformLocation(m_shadowProgram, "uni_ModelMatrix");
|
|
|
|
uni.primaryTexture = glGetUniformLocation(m_shadowProgram, "uni_Texture");
|
|
|
|
uni.textureEnabled[0] = glGetUniformLocation(m_shadowProgram, "uni_TextureEnabled");
|
|
uni.textureEnabled[1] = -1;
|
|
uni.textureEnabled[2] = -1;
|
|
|
|
uni.alphaTestEnabled = glGetUniformLocation(m_shadowProgram, "uni_AlphaTestEnabled");
|
|
uni.alphaReference = glGetUniformLocation(m_shadowProgram, "uni_AlphaReference");
|
|
|
|
// Set default uniform values
|
|
Math::Matrix matrix;
|
|
matrix.LoadIdentity();
|
|
|
|
glUniformMatrix4fv(uni.projectionMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.viewMatrix, 1, GL_FALSE, matrix.Array());
|
|
glUniformMatrix4fv(uni.modelMatrix, 1, GL_FALSE, matrix.Array());
|
|
|
|
glUniform1i(uni.primaryTexture, 0);
|
|
|
|
glUniform1i(uni.textureEnabled[0], 0);
|
|
|
|
glUniform1i(uni.alphaTestEnabled, 0);
|
|
glUniform1f(uni.alphaReference, 1.0f);
|
|
}
|
|
|
|
SetRenderMode(RENDER_MODE_NORMAL);
|
|
|
|
// create default framebuffer object
|
|
FramebufferParams framebufferParams;
|
|
|
|
framebufferParams.width = m_config.size.x;
|
|
framebufferParams.height = m_config.size.y;
|
|
framebufferParams.depth = m_config.depthSize;
|
|
|
|
m_framebuffers["default"] = MakeUnique<CDefaultFramebuffer>(framebufferParams);
|
|
|
|
// create dynamic buffer
|
|
glGenVertexArrays(1, &m_dynamicBuffer.vao);
|
|
|
|
m_dynamicBuffer.size = 4 * 1024 * 1024;
|
|
m_dynamicBuffer.offset = 0;
|
|
|
|
glGenBuffers(1, &m_dynamicBuffer.vbo);
|
|
glBindBuffer(GL_ARRAY_BUFFER, m_dynamicBuffer.vbo);
|
|
glBufferData(GL_ARRAY_BUFFER, m_dynamicBuffer.size, nullptr, GL_STREAM_DRAW);
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
m_vboMemory += m_dynamicBuffer.size;
|
|
|
|
GetLogger()->Info("CDevice created successfully\n");
|
|
|
|
return true;
|
|
}
|
|
|
|
void CGL33Device::Destroy()
|
|
{
|
|
// delete shader program
|
|
glUseProgram(0);
|
|
glDeleteProgram(m_normalProgram);
|
|
glDeleteProgram(m_interfaceProgram);
|
|
glDeleteProgram(m_shadowProgram);
|
|
|
|
// delete framebuffers
|
|
for (auto& framebuffer : m_framebuffers)
|
|
framebuffer.second->Destroy();
|
|
|
|
m_framebuffers.clear();
|
|
|
|
// Delete the remaining textures
|
|
// Should not be strictly necessary, but just in case
|
|
DestroyAllTextures();
|
|
|
|
// delete dynamic buffer
|
|
glDeleteVertexArrays(1, &m_dynamicBuffer.vao);
|
|
glDeleteBuffers(1, &m_dynamicBuffer.vbo);
|
|
|
|
m_vboMemory -= m_dynamicBuffer.size;
|
|
|
|
m_lights.clear();
|
|
m_lightsEnabled.clear();
|
|
|
|
m_currentTextures.clear();
|
|
m_texturesEnabled.clear();
|
|
m_textureStageParams.clear();
|
|
}
|
|
|
|
void CGL33Device::ConfigChanged(const DeviceConfig& newConfig)
|
|
{
|
|
m_config = newConfig;
|
|
|
|
// Reset state
|
|
m_lighting = false;
|
|
|
|
glViewport(0, 0, m_config.size.x, m_config.size.y);
|
|
|
|
// create default framebuffer object
|
|
FramebufferParams framebufferParams;
|
|
|
|
framebufferParams.width = m_config.size.x;
|
|
framebufferParams.height = m_config.size.y;
|
|
framebufferParams.depth = m_config.depthSize;
|
|
|
|
m_framebuffers["default"] = MakeUnique<CDefaultFramebuffer>(framebufferParams);
|
|
}
|
|
|
|
void CGL33Device::BeginScene()
|
|
{
|
|
Clear();
|
|
|
|
glUniformMatrix4fv(m_uni->projectionMatrix, 1, GL_FALSE, m_projectionMat.Array());
|
|
glUniformMatrix4fv(m_uni->viewMatrix, 1, GL_FALSE, m_viewMat.Array());
|
|
glUniformMatrix4fv(m_uni->modelMatrix, 1, GL_FALSE, m_worldMat.Array());
|
|
}
|
|
|
|
void CGL33Device::EndScene()
|
|
{
|
|
#ifdef DEV_BUILD
|
|
CheckGLErrors();
|
|
#endif
|
|
}
|
|
|
|
void CGL33Device::Clear()
|
|
{
|
|
glDepthMask(GL_TRUE);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
|
|
void CGL33Device::SetRenderMode(RenderMode mode)
|
|
{
|
|
switch (mode)
|
|
{
|
|
case RENDER_MODE_NORMAL:
|
|
glUseProgram(m_normalProgram);
|
|
m_mode = 0;
|
|
break;
|
|
case RENDER_MODE_INTERFACE:
|
|
glUseProgram(m_interfaceProgram);
|
|
m_mode = 1;
|
|
break;
|
|
case RENDER_MODE_SHADOW:
|
|
glUseProgram(m_shadowProgram);
|
|
m_mode = 2;
|
|
break;
|
|
default:
|
|
assert(false);
|
|
return;
|
|
}
|
|
|
|
m_uni = &m_uniforms[m_mode];
|
|
|
|
UpdateTextureState(0);
|
|
UpdateTextureState(1);
|
|
UpdateTextureState(2);
|
|
}
|
|
|
|
void CGL33Device::SetTransform(TransformType type, const Math::Matrix &matrix)
|
|
{
|
|
if (type == TRANSFORM_WORLD)
|
|
{
|
|
m_worldMat = matrix;
|
|
glUniformMatrix4fv(m_uni->modelMatrix, 1, GL_FALSE, m_worldMat.Array());
|
|
|
|
m_modelviewMat = Math::MultiplyMatrices(m_viewMat, m_worldMat);
|
|
m_combinedMatrixOutdated = true;
|
|
|
|
// normal transform
|
|
Math::Matrix normalMat = matrix;
|
|
|
|
if (fabs(normalMat.Det()) > 1e-6)
|
|
normalMat = normalMat.Inverse();
|
|
|
|
glUniformMatrix4fv(m_uni->normalMatrix, 1, GL_TRUE, normalMat.Array());
|
|
}
|
|
else if (type == TRANSFORM_VIEW)
|
|
{
|
|
Math::Matrix scale;
|
|
Math::Vector cameraPosition;
|
|
scale.Set(3, 3, -1.0f);
|
|
m_viewMat = Math::MultiplyMatrices(scale, matrix);
|
|
|
|
m_modelviewMat = Math::MultiplyMatrices(m_viewMat, m_worldMat);
|
|
m_combinedMatrixOutdated = true;
|
|
|
|
glUniformMatrix4fv(m_uni->viewMatrix, 1, GL_FALSE, m_viewMat.Array());
|
|
|
|
if (m_uni->cameraPosition >= 0) {
|
|
cameraPosition.LoadZero();
|
|
cameraPosition = MatrixVectorMultiply(m_viewMat.Inverse(), cameraPosition);
|
|
glUniform3fv(m_uni->cameraPosition, 1, cameraPosition.Array());
|
|
}
|
|
}
|
|
else if (type == TRANSFORM_PROJECTION)
|
|
{
|
|
m_projectionMat = matrix;
|
|
m_combinedMatrixOutdated = true;
|
|
|
|
glUniformMatrix4fv(m_uni->projectionMatrix, 1, GL_FALSE, m_projectionMat.Array());
|
|
}
|
|
else if (type == TRANSFORM_SHADOW)
|
|
{
|
|
Math::Matrix temp = matrix;
|
|
glUniformMatrix4fv(m_uni->shadowMatrix, 1, GL_FALSE, temp.Array());
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
void CGL33Device::SetMaterial(const Material &material)
|
|
{
|
|
m_material = material;
|
|
|
|
glUniform4fv(m_uni->ambientColor, 1, m_material.ambient.Array());
|
|
glUniform4fv(m_uni->diffuseColor, 1, m_material.diffuse.Array());
|
|
glUniform4fv(m_uni->specularColor, 1, m_material.specular.Array());
|
|
}
|
|
|
|
int CGL33Device::GetMaxLightCount()
|
|
{
|
|
return m_capabilities.maxLights;
|
|
}
|
|
|
|
void CGL33Device::SetLight(int index, const Light &light)
|
|
{
|
|
assert(index >= 0);
|
|
assert(index < static_cast<int>( m_lights.size() ));
|
|
|
|
m_lights[index] = light;
|
|
|
|
m_updateLights = true;
|
|
}
|
|
|
|
void CGL33Device::SetLightEnabled(int index, bool enabled)
|
|
{
|
|
assert(index >= 0);
|
|
assert(index < static_cast<int>( m_lights.size() ));
|
|
|
|
m_lightsEnabled[index] = enabled;
|
|
|
|
m_updateLights = true;
|
|
}
|
|
|
|
/** If image is invalid, returns invalid texture.
|
|
Otherwise, returns pointer to new Texture struct.
|
|
This struct must not be deleted in other way than through DeleteTexture() */
|
|
Texture CGL33Device::CreateTexture(CImage *image, const TextureCreateParams ¶ms)
|
|
{
|
|
ImageData *data = image->GetData();
|
|
if (data == nullptr)
|
|
{
|
|
GetLogger()->Error("Invalid texture data\n");
|
|
return Texture(); // invalid texture
|
|
}
|
|
|
|
Math::IntPoint originalSize = image->GetSize();
|
|
|
|
if (params.padToNearestPowerOfTwo)
|
|
image->PadToNearestPowerOfTwo();
|
|
|
|
Texture tex = CreateTexture(data, params);
|
|
tex.originalSize = originalSize;
|
|
|
|
return tex;
|
|
}
|
|
|
|
Texture CGL33Device::CreateTexture(ImageData *data, const TextureCreateParams ¶ms)
|
|
{
|
|
Texture result;
|
|
|
|
result.size.x = data->surface->w;
|
|
result.size.y = data->surface->h;
|
|
|
|
result.originalSize = result.size;
|
|
|
|
glGenTextures(1, &result.id);
|
|
|
|
BindTexture(m_freeTexture, result.id);
|
|
|
|
// Set texture parameters
|
|
GLint minF = GL_NEAREST, magF = GL_NEAREST;
|
|
int mipmapLevel = 1;
|
|
|
|
switch (params.filter)
|
|
{
|
|
case TEX_FILTER_NEAREST:
|
|
minF = GL_NEAREST;
|
|
magF = GL_NEAREST;
|
|
break;
|
|
case TEX_FILTER_BILINEAR:
|
|
minF = GL_LINEAR;
|
|
magF = GL_LINEAR;
|
|
break;
|
|
case TEX_FILTER_TRILINEAR:
|
|
minF = GL_LINEAR_MIPMAP_LINEAR;
|
|
magF = GL_LINEAR;
|
|
mipmapLevel = CEngine::GetInstance().GetTextureMipmapLevel();
|
|
break;
|
|
}
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minF);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magF);
|
|
|
|
// Set mipmap level and automatic mipmap generation if neccesary
|
|
if (params.mipmap)
|
|
{
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, mipmapLevel - 1);
|
|
}
|
|
else
|
|
{
|
|
// Has to be set to 0 because no mipmaps are generated
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
|
|
}
|
|
|
|
// Set anisotropy level if available
|
|
if (m_capabilities.anisotropySupported)
|
|
{
|
|
float level = Math::Min(m_capabilities.maxAnisotropy, CEngine::GetInstance().GetTextureAnisotropyLevel());
|
|
|
|
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, level);
|
|
}
|
|
|
|
PreparedTextureData texData = PrepareTextureData(data, params.format);
|
|
result.alpha = texData.alpha;
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, texData.actualSurface->pitch / texData.actualSurface->format->BytesPerPixel);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texData.actualSurface->w, texData.actualSurface->h,
|
|
0, texData.sourceFormat, GL_UNSIGNED_BYTE, texData.actualSurface->pixels);
|
|
|
|
if (params.mipmap)
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
|
|
SDL_FreeSurface(texData.convertedSurface);
|
|
|
|
m_allTextures.insert(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
Texture CGL33Device::CreateDepthTexture(int width, int height, int depth)
|
|
{
|
|
Texture result;
|
|
|
|
result.alpha = false;
|
|
result.size.x = width;
|
|
result.size.y = height;
|
|
|
|
glGenTextures(1, &result.id);
|
|
|
|
BindTexture(m_freeTexture, result.id);
|
|
|
|
GLuint format = GL_DEPTH_COMPONENT;
|
|
|
|
switch (depth)
|
|
{
|
|
case 16:
|
|
format = GL_DEPTH_COMPONENT16;
|
|
break;
|
|
case 24:
|
|
format = GL_DEPTH_COMPONENT24;
|
|
break;
|
|
case 32:
|
|
format = GL_DEPTH_COMPONENT32;
|
|
break;
|
|
}
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, GL_DEPTH_COMPONENT, GL_INT, nullptr);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
|
|
|
|
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
|
|
|
|
return result;
|
|
}
|
|
|
|
void CGL33Device::UpdateTexture(const Texture& texture, Math::IntPoint offset, ImageData* data, TexImgFormat format)
|
|
{
|
|
if (texture.id == 0) return;
|
|
|
|
BindTexture(m_freeTexture, texture.id);
|
|
|
|
PreparedTextureData texData = PrepareTextureData(data, format);
|
|
|
|
glPixelStorei(GL_UNPACK_ROW_LENGTH, texData.actualSurface->pitch / texData.actualSurface->format->BytesPerPixel);
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, offset.x, offset.y, texData.actualSurface->w, texData.actualSurface->h,
|
|
texData.sourceFormat, GL_UNSIGNED_BYTE, texData.actualSurface->pixels);
|
|
|
|
SDL_FreeSurface(texData.convertedSurface);
|
|
}
|
|
|
|
void CGL33Device::DestroyTexture(const Texture &texture)
|
|
{
|
|
// Unbind the texture if in use anywhere
|
|
for (int index = 0; index < static_cast<int>( m_currentTextures.size() ); ++index)
|
|
{
|
|
if (m_currentTextures[index] == texture)
|
|
SetTexture(index, Texture()); // set to invalid texture
|
|
}
|
|
|
|
auto it = m_allTextures.find(texture);
|
|
if (it != m_allTextures.end())
|
|
{
|
|
glDeleteTextures(1, &texture.id);
|
|
m_allTextures.erase(it);
|
|
}
|
|
}
|
|
|
|
void CGL33Device::DestroyAllTextures()
|
|
{
|
|
// Unbind all texture stages
|
|
for (int index = 0; index < static_cast<int>( m_currentTextures.size() ); ++index)
|
|
SetTexture(index, Texture());
|
|
|
|
for (auto it = m_allTextures.begin(); it != m_allTextures.end(); ++it)
|
|
glDeleteTextures(1, &(*it).id);
|
|
|
|
m_allTextures.clear();
|
|
}
|
|
|
|
int CGL33Device::GetMaxTextureStageCount()
|
|
{
|
|
return m_currentTextures.size();
|
|
}
|
|
|
|
/**
|
|
If \a texture is invalid, unbinds the given texture.
|
|
If valid, binds the texture and enables the given texture stage.
|
|
The setting is remembered, even if texturing is disabled at the moment. */
|
|
void CGL33Device::SetTexture(int index, const Texture &texture)
|
|
{
|
|
assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
|
|
|
|
if (m_currentTextures[index].id == texture.id)
|
|
return;
|
|
|
|
BindTexture(index, texture.id);
|
|
|
|
m_currentTextures[index] = texture; // remember the new value
|
|
|
|
// Params need to be updated for the new bound texture
|
|
UpdateTextureParams(index);
|
|
UpdateTextureState(index);
|
|
}
|
|
|
|
void CGL33Device::SetTexture(int index, unsigned int textureId)
|
|
{
|
|
assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
|
|
|
|
if (m_currentTextures[index].id == textureId)
|
|
return; // nothing to do
|
|
|
|
BindTexture(index, textureId);
|
|
|
|
m_currentTextures[index].id = textureId;
|
|
|
|
// Params need to be updated for the new bound texture
|
|
UpdateTextureParams(index);
|
|
UpdateTextureState(index);
|
|
}
|
|
|
|
void CGL33Device::SetTextureEnabled(int index, bool enabled)
|
|
{
|
|
assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
|
|
|
|
if (m_texturesEnabled[index] == enabled)
|
|
return;
|
|
|
|
m_texturesEnabled[index] = enabled;
|
|
|
|
UpdateTextureState(index);
|
|
}
|
|
|
|
/**
|
|
Sets the texture parameters for the given texture stage.
|
|
If the given texture was not set (bound) yet, nothing happens.
|
|
The settings are remembered, even if texturing is disabled at the moment. */
|
|
void CGL33Device::SetTextureStageParams(int index, const TextureStageParams ¶ms)
|
|
{
|
|
assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
|
|
|
|
// Remember the settings
|
|
m_textureStageParams[index] = params;
|
|
|
|
UpdateTextureParams(index);
|
|
}
|
|
|
|
void CGL33Device::UpdateTextureParams(int index)
|
|
{
|
|
assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
|
|
|
|
// Don't actually do anything if texture not set
|
|
if (! m_currentTextures[index].Valid())
|
|
return;
|
|
|
|
const TextureStageParams ¶ms = m_textureStageParams[index];
|
|
|
|
glActiveTexture(GL_TEXTURE0 + index);
|
|
|
|
if (params.wrapS == TEX_WRAP_CLAMP)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
else if (params.wrapS == TEX_WRAP_CLAMP_TO_BORDER)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
else if (params.wrapS == TEX_WRAP_REPEAT)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
else assert(false);
|
|
|
|
if (params.wrapT == TEX_WRAP_CLAMP)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
else if (params.wrapT == TEX_WRAP_CLAMP_TO_BORDER)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
else if (params.wrapT == TEX_WRAP_REPEAT)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
else assert(false);
|
|
}
|
|
|
|
void CGL33Device::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode wrapT)
|
|
{
|
|
assert(index >= 0 && index < static_cast<int>( m_currentTextures.size() ));
|
|
|
|
// Remember the settings
|
|
m_textureStageParams[index].wrapS = wrapS;
|
|
m_textureStageParams[index].wrapT = wrapT;
|
|
|
|
// Don't actually do anything if texture not set
|
|
if (! m_currentTextures[index].Valid())
|
|
return;
|
|
|
|
glActiveTexture(GL_TEXTURE0 + index);
|
|
|
|
if (wrapS == TEX_WRAP_CLAMP)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
else if (wrapS == TEX_WRAP_CLAMP_TO_BORDER)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
|
else if (wrapS == TEX_WRAP_REPEAT)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
else assert(false);
|
|
|
|
if (wrapT == TEX_WRAP_CLAMP)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
else if (wrapT == TEX_WRAP_CLAMP_TO_BORDER)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
|
|
else if (wrapT == TEX_WRAP_REPEAT)
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
else assert(false);
|
|
}
|
|
|
|
void CGL33Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int vertexCount, Color color)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
Vertex* vs = const_cast<Vertex*>(vertices);
|
|
|
|
unsigned int size = vertexCount * sizeof(Vertex);
|
|
|
|
DynamicBuffer& buffer = m_dynamicBuffer;
|
|
|
|
BindVAO(buffer.vao);
|
|
BindVBO(buffer.vbo);
|
|
|
|
unsigned int offset = UploadVertexData(buffer, vs, size);
|
|
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
reinterpret_cast<void*>(offset + offsetof(Vertex, coord)));
|
|
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
reinterpret_cast<void*>(offset + offsetof(Vertex, normal)));
|
|
|
|
// Color
|
|
glDisableVertexAttribArray(2);
|
|
glVertexAttrib4fv(2, color.Array());
|
|
|
|
// Texture coordinate 0
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
reinterpret_cast<void*>(offset + offsetof(Vertex, texCoord)));
|
|
|
|
// Texture coordinate 1
|
|
glDisableVertexAttribArray(4);
|
|
glVertexAttrib2f(4, 0.0f, 0.0f);
|
|
|
|
glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount);
|
|
}
|
|
|
|
void CGL33Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
VertexTex2* vs = const_cast<VertexTex2*>(vertices);
|
|
|
|
unsigned int size = vertexCount * sizeof(VertexTex2);
|
|
|
|
DynamicBuffer& buffer = m_dynamicBuffer;
|
|
|
|
BindVAO(buffer.vao);
|
|
BindVBO(buffer.vbo);
|
|
|
|
unsigned int offset = UploadVertexData(buffer, vs, size);
|
|
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, coord)));
|
|
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, normal)));
|
|
|
|
// Color
|
|
glDisableVertexAttribArray(2);
|
|
glVertexAttrib4fv(2, color.Array());
|
|
|
|
// Texture coordinate 0
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, texCoord)));
|
|
|
|
// Texture coordinate 1
|
|
glEnableVertexAttribArray(4);
|
|
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, texCoord2)));
|
|
|
|
glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount);
|
|
}
|
|
|
|
void CGL33Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int vertexCount)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
VertexCol* vs = const_cast<VertexCol*>(vertices);
|
|
|
|
unsigned int size = vertexCount * sizeof(VertexCol);
|
|
|
|
DynamicBuffer& buffer = m_dynamicBuffer;
|
|
|
|
BindVAO(buffer.vao);
|
|
BindVBO(buffer.vbo);
|
|
|
|
unsigned int offset = UploadVertexData(buffer, vs, size);
|
|
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexCol, coord)));
|
|
|
|
// Normal
|
|
glDisableVertexAttribArray(1);
|
|
glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f);
|
|
|
|
// Color
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexCol, color)));
|
|
|
|
// Texture coordinate 0
|
|
glDisableVertexAttribArray(3);
|
|
glVertexAttrib2f(3, 0.0f, 0.0f);
|
|
|
|
// Texture coordinate 1
|
|
glDisableVertexAttribArray(4);
|
|
glVertexAttrib2f(4, 0.0f, 0.0f);
|
|
|
|
glDrawArrays(TranslateGfxPrimitive(type), 0, vertexCount);
|
|
}
|
|
|
|
void CGL33Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices,
|
|
int first[], int count[], int drawCount, Color color)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
Vertex* vs = const_cast<Vertex*>(vertices);
|
|
|
|
int vertexCount = 0;
|
|
|
|
for (int i = 0; i < drawCount; i++)
|
|
{
|
|
int currentCount = first[i] + count[i];
|
|
|
|
if (currentCount > vertexCount)
|
|
vertexCount = currentCount;
|
|
}
|
|
|
|
unsigned int size = vertexCount * sizeof(Vertex);
|
|
|
|
DynamicBuffer& buffer = m_dynamicBuffer;
|
|
|
|
BindVAO(buffer.vao);
|
|
BindVBO(buffer.vbo);
|
|
|
|
unsigned int offset = UploadVertexData(buffer, vs, size);
|
|
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
reinterpret_cast<void*>(offset + offsetof(Vertex, coord)));
|
|
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
reinterpret_cast<void*>(offset + offsetof(Vertex, normal)));
|
|
|
|
// Color
|
|
glDisableVertexAttribArray(2);
|
|
glVertexAttrib4fv(2, color.Array());
|
|
|
|
// Texture coordinate 0
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
|
|
reinterpret_cast<void*>(offset + offsetof(Vertex, texCoord)));
|
|
|
|
// Texture coordinate 1
|
|
glDisableVertexAttribArray(4);
|
|
glVertexAttrib2f(4, 0.0f, 0.0f);
|
|
|
|
glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount);
|
|
}
|
|
|
|
void CGL33Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices,
|
|
int first[], int count[], int drawCount, Color color)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
VertexTex2* vs = const_cast<VertexTex2*>(vertices);
|
|
|
|
int vertexCount = 0;
|
|
|
|
for (int i = 0; i < drawCount; i++)
|
|
{
|
|
int currentCount = first[i] + count[i];
|
|
|
|
if (currentCount > vertexCount)
|
|
vertexCount = currentCount;
|
|
}
|
|
|
|
unsigned int size = vertexCount * sizeof(VertexTex2);
|
|
|
|
DynamicBuffer& buffer = m_dynamicBuffer;
|
|
|
|
BindVAO(buffer.vao);
|
|
BindVBO(buffer.vbo);
|
|
|
|
unsigned int offset = UploadVertexData(buffer, vs, size);
|
|
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, coord)));
|
|
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, normal)));
|
|
|
|
// Color
|
|
glDisableVertexAttribArray(2);
|
|
glVertexAttrib4fv(2, color.Array());
|
|
|
|
// Texture coordinate 0
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, texCoord)));
|
|
|
|
// Texture coordinate 1
|
|
glEnableVertexAttribArray(4);
|
|
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexTex2, texCoord2)));
|
|
|
|
glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount);
|
|
}
|
|
|
|
void CGL33Device::DrawPrimitives(PrimitiveType type, const VertexCol *vertices,
|
|
int first[], int count[], int drawCount)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
VertexCol* vs = const_cast<VertexCol*>(vertices);
|
|
|
|
int vertexCount = 0;
|
|
|
|
for (int i = 0; i < drawCount; i++)
|
|
{
|
|
int currentCount = first[i] + count[i];
|
|
|
|
if (currentCount > vertexCount)
|
|
vertexCount = currentCount;
|
|
}
|
|
|
|
unsigned int size = vertexCount * sizeof(VertexCol);
|
|
|
|
DynamicBuffer& buffer = m_dynamicBuffer;
|
|
|
|
BindVAO(buffer.vao);
|
|
BindVBO(buffer.vbo);
|
|
|
|
unsigned int offset = UploadVertexData(buffer, vs, size);
|
|
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexCol, coord)));
|
|
|
|
// Normal
|
|
glDisableVertexAttribArray(1);
|
|
glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f);
|
|
|
|
// Color
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol),
|
|
reinterpret_cast<void*>(offset + offsetof(VertexCol, color)));
|
|
|
|
// Texture coordinate 0
|
|
glDisableVertexAttribArray(3);
|
|
glVertexAttrib2f(3, 0.0f, 0.0f);
|
|
|
|
// Texture coordinate 1
|
|
glDisableVertexAttribArray(4);
|
|
glVertexAttrib2f(4, 0.0f, 0.0f);
|
|
|
|
glMultiDrawArrays(TranslateGfxPrimitive(type), first, count, drawCount);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
template <typename Vertex> void SetVertexAttributes();
|
|
|
|
template <> void SetVertexAttributes<Vertex>()
|
|
{
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, coord)));
|
|
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)));
|
|
|
|
// Color
|
|
glDisableVertexAttribArray(2);
|
|
glVertexAttrib4f(2, 1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
// Texture coordinate 0
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, texCoord)));
|
|
|
|
// Texture coordinate 1
|
|
glDisableVertexAttribArray(4);
|
|
glVertexAttrib2f(4, 0.0f, 0.0f);
|
|
}
|
|
|
|
template <> void SetVertexAttributes<VertexTex2>()
|
|
{
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast<void*>(offsetof(VertexTex2, coord)));
|
|
|
|
// Normal
|
|
glEnableVertexAttribArray(1);
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast<void*>(offsetof(VertexTex2, normal)));
|
|
|
|
// Color
|
|
glDisableVertexAttribArray(2);
|
|
glVertexAttrib4f(2, 1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
// Texture coordinate 0
|
|
glEnableVertexAttribArray(3);
|
|
glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast<void*>(offsetof(VertexTex2, texCoord)));
|
|
|
|
// Texture coordinate 1
|
|
glEnableVertexAttribArray(4);
|
|
glVertexAttribPointer(4, 2, GL_FLOAT, GL_FALSE, sizeof(VertexTex2), reinterpret_cast<void*>(offsetof(VertexTex2, texCoord2)));
|
|
}
|
|
|
|
template <> void SetVertexAttributes<VertexCol>()
|
|
{
|
|
// Vertex coordinate
|
|
glEnableVertexAttribArray(0);
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast<void*>(offsetof(VertexCol, coord)));
|
|
|
|
// Normal
|
|
glDisableVertexAttribArray(1);
|
|
glVertexAttrib3f(1, 0.0f, 0.0f, 1.0f);
|
|
|
|
// Color
|
|
glEnableVertexAttribArray(2);
|
|
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, sizeof(VertexCol), reinterpret_cast<void*>(offsetof(VertexCol, color)));
|
|
|
|
// Texture coordinate 0
|
|
glDisableVertexAttribArray(3);
|
|
glVertexAttrib2f(3, 0.0f, 0.0f);
|
|
|
|
// Texture coordinate 1
|
|
glDisableVertexAttribArray(4);
|
|
glVertexAttrib2f(4, 0.0f, 0.0f);
|
|
}
|
|
} // namespace
|
|
|
|
template <typename Vertex>
|
|
unsigned int CGL33Device::CreateStaticBufferImpl(PrimitiveType primitiveType, const Vertex* vertices, int vertexCount)
|
|
{
|
|
unsigned int id = 0;
|
|
|
|
id = ++m_lastVboId;
|
|
|
|
VertexBufferInfo info;
|
|
info.primitiveType = primitiveType;
|
|
info.vertexType = Vertex::VERTEX_TYPE;
|
|
info.vertexCount = vertexCount;
|
|
info.size = vertexCount * sizeof(Vertex);
|
|
|
|
glGenVertexArrays(1, &info.vao);
|
|
BindVAO(info.vao);
|
|
|
|
glGenBuffers(1, &info.vbo);
|
|
BindVBO(info.vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, info.size, vertices, GL_STATIC_DRAW);
|
|
m_vboMemory += info.size;
|
|
|
|
SetVertexAttributes<Vertex>();
|
|
|
|
m_vboObjects[id] = info;
|
|
|
|
return id;
|
|
}
|
|
|
|
template <typename Vertex>
|
|
void CGL33Device::UpdateStaticBufferImpl(unsigned int bufferId, PrimitiveType primitiveType, const Vertex* vertices, int vertexCount)
|
|
{
|
|
auto it = m_vboObjects.find(bufferId);
|
|
if (it == m_vboObjects.end())
|
|
return;
|
|
|
|
VertexBufferInfo& info = (*it).second;
|
|
|
|
unsigned int size = vertexCount * sizeof(Vertex);
|
|
|
|
bool changed = (info.vertexType != Vertex::VERTEX_TYPE) || (size > info.size);
|
|
|
|
if (info.vertexType != Vertex::VERTEX_TYPE) CLogger::GetInstance().Debug("Changing static buffer type\n");
|
|
|
|
info.primitiveType = primitiveType;
|
|
info.vertexType = Vertex::VERTEX_TYPE;
|
|
info.vertexCount = vertexCount;
|
|
|
|
BindVBO(info.vbo);
|
|
|
|
if (info.size < size)
|
|
{
|
|
CLogger::GetInstance().Debug("Resizing static buffer: %d->%d\n", info.size, size);
|
|
glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
|
|
m_vboMemory -= info.size;
|
|
info.size = size;
|
|
m_vboMemory += info.size;
|
|
}
|
|
else
|
|
{
|
|
glBufferSubData(GL_ARRAY_BUFFER, 0, size, vertices);
|
|
}
|
|
|
|
if (changed) // Update vertex array bindings
|
|
{
|
|
BindVAO(info.vao);
|
|
|
|
SetVertexAttributes<Vertex>();
|
|
}
|
|
}
|
|
|
|
void CGL33Device::DrawStaticBuffer(unsigned int bufferId)
|
|
{
|
|
if (m_updateLights) UpdateLights();
|
|
|
|
auto it = m_vboObjects.find(bufferId);
|
|
if (it == m_vboObjects.end())
|
|
return;
|
|
|
|
VertexBufferInfo &info = (*it).second;
|
|
|
|
BindVAO(info.vao);
|
|
|
|
GLenum mode = TranslateGfxPrimitive(info.primitiveType);
|
|
glDrawArrays(mode, 0, info.vertexCount);
|
|
}
|
|
|
|
void CGL33Device::DestroyStaticBuffer(unsigned int bufferId)
|
|
{
|
|
auto it = m_vboObjects.find(bufferId);
|
|
if (it == m_vboObjects.end())
|
|
return;
|
|
|
|
VertexBufferInfo &info = (*it).second;
|
|
|
|
if (m_currentVAO == info.vao)
|
|
BindVAO(0);
|
|
if (m_currentVBO == info.vbo)
|
|
BindVBO(0);
|
|
|
|
m_vboMemory -= info.size;
|
|
|
|
glDeleteBuffers(1, &info.vbo);
|
|
glDeleteVertexArrays(1, &info.vao);
|
|
|
|
info.vbo = 0;
|
|
info.vao = 0;
|
|
|
|
m_vboObjects.erase(it);
|
|
}
|
|
|
|
/* Based on libwine's implementation */
|
|
|
|
int CGL33Device::ComputeSphereVisibility(const Math::Vector ¢er, float radius)
|
|
{
|
|
if (m_combinedMatrixOutdated)
|
|
{
|
|
m_combinedMatrix = Math::MultiplyMatrices(m_projectionMat, m_modelviewMat);
|
|
m_combinedMatrixOutdated = false;
|
|
}
|
|
|
|
Math::Matrix &m = m_combinedMatrix;
|
|
|
|
Math::Vector vec[6];
|
|
float originPlane[6];
|
|
|
|
// Left plane
|
|
vec[0].x = m.Get(4, 1) + m.Get(1, 1);
|
|
vec[0].y = m.Get(4, 2) + m.Get(1, 2);
|
|
vec[0].z = m.Get(4, 3) + m.Get(1, 3);
|
|
float l1 = vec[0].Length();
|
|
vec[0].Normalize();
|
|
originPlane[0] = (m.Get(4, 4) + m.Get(1, 4)) / l1;
|
|
|
|
// Right plane
|
|
vec[1].x = m.Get(4, 1) - m.Get(1, 1);
|
|
vec[1].y = m.Get(4, 2) - m.Get(1, 2);
|
|
vec[1].z = m.Get(4, 3) - m.Get(1, 3);
|
|
float l2 = vec[1].Length();
|
|
vec[1].Normalize();
|
|
originPlane[1] = (m.Get(4, 4) - m.Get(1, 4)) / l2;
|
|
|
|
// Bottom plane
|
|
vec[2].x = m.Get(4, 1) + m.Get(2, 1);
|
|
vec[2].y = m.Get(4, 2) + m.Get(2, 2);
|
|
vec[2].z = m.Get(4, 3) + m.Get(2, 3);
|
|
float l3 = vec[2].Length();
|
|
vec[2].Normalize();
|
|
originPlane[2] = (m.Get(4, 4) + m.Get(2, 4)) / l3;
|
|
|
|
// Top plane
|
|
vec[3].x = m.Get(4, 1) - m.Get(2, 1);
|
|
vec[3].y = m.Get(4, 2) - m.Get(2, 2);
|
|
vec[3].z = m.Get(4, 3) - m.Get(2, 3);
|
|
float l4 = vec[3].Length();
|
|
vec[3].Normalize();
|
|
originPlane[3] = (m.Get(4, 4) - m.Get(2, 4)) / l4;
|
|
|
|
// Front plane
|
|
vec[4].x = m.Get(4, 1) + m.Get(3, 1);
|
|
vec[4].y = m.Get(4, 2) + m.Get(3, 2);
|
|
vec[4].z = m.Get(4, 3) + m.Get(3, 3);
|
|
float l5 = vec[4].Length();
|
|
vec[4].Normalize();
|
|
originPlane[4] = (m.Get(4, 4) + m.Get(3, 4)) / l5;
|
|
|
|
// Back plane
|
|
vec[5].x = m.Get(4, 1) - m.Get(3, 1);
|
|
vec[5].y = m.Get(4, 2) - m.Get(3, 2);
|
|
vec[5].z = m.Get(4, 3) - m.Get(3, 3);
|
|
float l6 = vec[5].Length();
|
|
vec[5].Normalize();
|
|
originPlane[5] = (m.Get(4, 4) - m.Get(3, 4)) / l6;
|
|
|
|
int result = 0;
|
|
|
|
if (InPlane(vec[0], originPlane[0], center, radius))
|
|
result |= FRUSTUM_PLANE_LEFT;
|
|
if (InPlane(vec[1], originPlane[1], center, radius))
|
|
result |= FRUSTUM_PLANE_RIGHT;
|
|
if (InPlane(vec[2], originPlane[2], center, radius))
|
|
result |= FRUSTUM_PLANE_BOTTOM;
|
|
if (InPlane(vec[3], originPlane[3], center, radius))
|
|
result |= FRUSTUM_PLANE_TOP;
|
|
if (InPlane(vec[4], originPlane[4], center, radius))
|
|
result |= FRUSTUM_PLANE_FRONT;
|
|
if (InPlane(vec[5], originPlane[5], center, radius))
|
|
result |= FRUSTUM_PLANE_BACK;
|
|
|
|
return result;
|
|
}
|
|
|
|
void CGL33Device::SetViewport(int x, int y, int width, int height)
|
|
{
|
|
glViewport(x, y, width, height);
|
|
}
|
|
|
|
void CGL33Device::SetRenderState(RenderState state, bool enabled)
|
|
{
|
|
if (state == RENDER_STATE_DEPTH_WRITE)
|
|
{
|
|
glDepthMask(enabled ? GL_TRUE : GL_FALSE);
|
|
return;
|
|
}
|
|
else if (state == RENDER_STATE_LIGHTING)
|
|
{
|
|
m_lighting = enabled;
|
|
|
|
m_updateLights = true;
|
|
|
|
//glUniform1i(m_uni->lightingEnabled, enabled ? 1 : 0);
|
|
|
|
return;
|
|
}
|
|
else if (state == RENDER_STATE_FOG)
|
|
{
|
|
glUniform1i(m_uni->fogEnabled, enabled ? 1 : 0);
|
|
|
|
return;
|
|
}
|
|
else if (state == RENDER_STATE_ALPHA_TEST)
|
|
{
|
|
glUniform1i(m_uni->alphaTestEnabled, enabled ? 1 : 0);
|
|
|
|
return;
|
|
}
|
|
else if (state == RENDER_STATE_SHADOW_MAPPING)
|
|
{
|
|
SetTextureEnabled(TEXTURE_SHADOW, enabled);
|
|
|
|
return;
|
|
}
|
|
|
|
GLenum flag = 0;
|
|
|
|
switch (state)
|
|
{
|
|
case RENDER_STATE_BLENDING: flag = GL_BLEND; break;
|
|
case RENDER_STATE_DEPTH_TEST: flag = GL_DEPTH_TEST; break;
|
|
case RENDER_STATE_CULLING: flag = GL_CULL_FACE; break;
|
|
case RENDER_STATE_DEPTH_BIAS: flag = GL_POLYGON_OFFSET_FILL; break;
|
|
default: assert(false); break;
|
|
}
|
|
|
|
if (enabled)
|
|
glEnable(flag);
|
|
else
|
|
glDisable(flag);
|
|
}
|
|
|
|
void CGL33Device::SetColorMask(bool red, bool green, bool blue, bool alpha)
|
|
{
|
|
glColorMask(red, green, blue, alpha);
|
|
}
|
|
|
|
void CGL33Device::SetDepthTestFunc(CompFunc func)
|
|
{
|
|
glDepthFunc(TranslateGfxCompFunc(func));
|
|
}
|
|
|
|
void CGL33Device::SetDepthBias(float factor, float units)
|
|
{
|
|
glPolygonOffset(factor, units);
|
|
}
|
|
|
|
void CGL33Device::SetAlphaTestFunc(CompFunc func, float refValue)
|
|
{
|
|
glUniform1f(m_uni->alphaReference, refValue);
|
|
}
|
|
|
|
void CGL33Device::SetBlendFunc(BlendFunc srcBlend, BlendFunc dstBlend)
|
|
{
|
|
glBlendFunc(TranslateGfxBlendFunc(srcBlend), TranslateGfxBlendFunc(dstBlend));
|
|
}
|
|
|
|
void CGL33Device::SetClearColor(const Color &color)
|
|
{
|
|
glClearColor(color.r, color.g, color.b, color.a);
|
|
}
|
|
|
|
void CGL33Device::SetGlobalAmbient(const Color &color)
|
|
{
|
|
//glLightModelfv(GL_LIGHT_MODEL_AMBIENT, color.Array());
|
|
}
|
|
|
|
void CGL33Device::SetFogParams(FogMode mode, const Color &color, float start, float end, float density)
|
|
{
|
|
// TODO: reimplement
|
|
|
|
glUniform2f(m_uni->fogRange, start, end);
|
|
glUniform4f(m_uni->fogColor, color.r, color.g, color.b, color.a);
|
|
|
|
/*
|
|
if (mode == FOG_LINEAR) glFogi(GL_FOG_MODE, GL_LINEAR);
|
|
else if (mode == FOG_EXP) glFogi(GL_FOG_MODE, GL_EXP);
|
|
else if (mode == FOG_EXP2) glFogi(GL_FOG_MODE, GL_EXP2);
|
|
else assert(false);
|
|
|
|
glFogf(GL_FOG_START, start);
|
|
glFogf(GL_FOG_END, end);
|
|
glFogf(GL_FOG_DENSITY, density);
|
|
glFogfv(GL_FOG_COLOR, color.Array());
|
|
// */
|
|
}
|
|
|
|
void CGL33Device::SetCullMode(CullMode mode)
|
|
{
|
|
// Cull clockwise back faces, so front face is the opposite
|
|
// (assuming GL_CULL_FACE is GL_BACK)
|
|
if (mode == CULL_CW ) glFrontFace(GL_CCW);
|
|
else if (mode == CULL_CCW) glFrontFace(GL_CW);
|
|
else assert(false);
|
|
}
|
|
|
|
void CGL33Device::SetShadeModel(ShadeModel model)
|
|
{
|
|
//glUniform1i(uni_SmoothShading, (model == SHADE_SMOOTH ? 1 : 0));
|
|
}
|
|
|
|
void CGL33Device::SetShadowColor(float value)
|
|
{
|
|
glUniform1f(m_uni->shadowColor, value);
|
|
}
|
|
|
|
void CGL33Device::SetFillMode(FillMode mode)
|
|
{
|
|
if (mode == FILL_POINT) glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
|
|
else if (mode == FILL_LINES) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
|
else if (mode == FILL_POLY) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
|
else assert(false);
|
|
}
|
|
|
|
void CGL33Device::CopyFramebufferToTexture(Texture& texture, int xOffset, int yOffset, int x, int y, int width, int height)
|
|
{
|
|
if (texture.id == 0) return;
|
|
|
|
BindTexture(m_freeTexture, texture.id);
|
|
|
|
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, x, y, width, height);
|
|
}
|
|
|
|
std::unique_ptr<CFrameBufferPixels> CGL33Device::GetFrameBufferPixels() const
|
|
{
|
|
return GetGLFrameBufferPixels(m_config.size);
|
|
}
|
|
|
|
CFramebuffer* CGL33Device::GetFramebuffer(std::string name)
|
|
{
|
|
auto it = m_framebuffers.find(name);
|
|
if (it == m_framebuffers.end())
|
|
return nullptr;
|
|
|
|
return it->second.get();
|
|
}
|
|
|
|
CFramebuffer* CGL33Device::CreateFramebuffer(std::string name, const FramebufferParams& params)
|
|
{
|
|
// existing framebuffer was found
|
|
if (m_framebuffers.find(name) != m_framebuffers.end())
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
auto framebuffer = MakeUnique<CGLFramebuffer>(params);
|
|
if (!framebuffer->Create()) return nullptr;
|
|
|
|
CFramebuffer* framebufferPtr = framebuffer.get();
|
|
m_framebuffers[name] = std::move(framebuffer);
|
|
return framebufferPtr;
|
|
}
|
|
|
|
void CGL33Device::DeleteFramebuffer(std::string name)
|
|
{
|
|
// can't delete default framebuffer
|
|
if (name == "default") return;
|
|
|
|
auto it = m_framebuffers.find(name);
|
|
if (it != m_framebuffers.end())
|
|
{
|
|
it->second->Destroy();
|
|
m_framebuffers.erase(it);
|
|
}
|
|
}
|
|
|
|
inline void CGL33Device::UpdateTextureState(int index)
|
|
{
|
|
bool enabled = m_texturesEnabled[index] && (m_currentTextures[index].id != 0);
|
|
glUniform1i(m_uni->textureEnabled[index], enabled ? 1 : 0);
|
|
}
|
|
|
|
void CGL33Device::UpdateLights()
|
|
{
|
|
m_updateLights = false;
|
|
|
|
// If not in normal rendering mode, return immediately
|
|
if (m_mode != 0) return;
|
|
|
|
// Lighting enabled
|
|
if (m_lighting)
|
|
{
|
|
int index = 0;
|
|
|
|
// Iterate all lights
|
|
for (unsigned int i = 0; i < m_lights.size(); i++)
|
|
{
|
|
// If disabled, ignore and continue
|
|
if (!m_lightsEnabled[i]) continue;
|
|
|
|
// If not directional, ignore and continue
|
|
if (m_lights[i].type != LIGHT_DIRECTIONAL) continue;
|
|
|
|
Light &light = m_lights[i];
|
|
LightLocations &uni = m_uni->lights[index];
|
|
|
|
glUniform4fv(uni.ambient, 1, light.ambient.Array());
|
|
glUniform4fv(uni.diffuse, 1, light.diffuse.Array());
|
|
glUniform4fv(uni.specular, 1, light.specular.Array());
|
|
|
|
glUniform4f(uni.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f);
|
|
|
|
index++;
|
|
}
|
|
|
|
glUniform1i(m_uni->lightCount, index);
|
|
}
|
|
// Lighting disabled
|
|
else
|
|
{
|
|
glUniform1i(m_uni->lightCount, 0);
|
|
}
|
|
}
|
|
|
|
inline void CGL33Device::BindVBO(GLuint vbo)
|
|
{
|
|
if (m_currentVBO == vbo) return;
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
m_currentVBO = vbo;
|
|
}
|
|
|
|
inline void CGL33Device::BindVAO(GLuint vao)
|
|
{
|
|
if (m_currentVAO == vao) return;
|
|
|
|
glBindVertexArray(vao);
|
|
m_currentVAO = vao;
|
|
}
|
|
|
|
inline void CGL33Device::BindTexture(int index, GLuint texture)
|
|
{
|
|
glActiveTexture(GL_TEXTURE0 + index);
|
|
glBindTexture(GL_TEXTURE_2D, texture);
|
|
}
|
|
|
|
unsigned int CGL33Device::UploadVertexData(DynamicBuffer& buffer, const void* data, unsigned int size)
|
|
{
|
|
unsigned int nextOffset = buffer.offset + size;
|
|
|
|
// buffer limit exceeded
|
|
// invalidate buffer for the next round of buffer streaming
|
|
if (nextOffset > buffer.size)
|
|
{
|
|
glBufferData(GL_ARRAY_BUFFER, buffer.size, nullptr, GL_STREAM_DRAW);
|
|
|
|
buffer.offset = 0;
|
|
nextOffset = size;
|
|
}
|
|
|
|
unsigned int currentOffset = buffer.offset;
|
|
|
|
// map buffer for unsynchronized copying
|
|
void* ptr = glMapBufferRange(GL_ARRAY_BUFFER, currentOffset, size,
|
|
GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
|
|
|
|
if (ptr != nullptr)
|
|
{
|
|
memcpy(ptr, data, size);
|
|
|
|
glUnmapBuffer(GL_ARRAY_BUFFER);
|
|
}
|
|
// mapping failed, we must upload data with glBufferSubData
|
|
else
|
|
{
|
|
GetLogger()->Debug("Buffer mapping failed (offset %d, size %d)\n", currentOffset, size);
|
|
glBufferSubData(GL_ARRAY_BUFFER, currentOffset, size, data);
|
|
}
|
|
|
|
buffer.offset = nextOffset;
|
|
|
|
return currentOffset;
|
|
}
|
|
|
|
bool CGL33Device::IsAnisotropySupported()
|
|
{
|
|
return m_capabilities.anisotropySupported;
|
|
}
|
|
|
|
int CGL33Device::GetMaxAnisotropyLevel()
|
|
{
|
|
return m_capabilities.maxAnisotropy;
|
|
}
|
|
|
|
int CGL33Device::GetMaxSamples()
|
|
{
|
|
return m_capabilities.maxSamples;
|
|
}
|
|
|
|
bool CGL33Device::IsShadowMappingSupported()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int CGL33Device::GetMaxTextureSize()
|
|
{
|
|
return m_capabilities.maxTextureSize;
|
|
}
|
|
|
|
bool CGL33Device::IsFramebufferSupported()
|
|
{
|
|
return true;
|
|
}
|
|
|
|
} // namespace Gfx
|