From f242f55f8106b09be8b456037f87ce86883d4c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Mon, 7 Dec 2015 23:32:55 +0100 Subject: [PATCH] Switched OpenGL to core profile --- Makefile | 2 +- TODO | 3 +- .../modeler/quickapp/MainModelerWindow.cpp | 6 +- src/interface/modeler/quickapp/OpenGLView.cpp | 4 +- src/render/opengl/ExplorerChunkTerrain.cpp | 296 ------------------ src/render/opengl/ExplorerChunkTerrain.h | 74 ----- src/render/opengl/OpenGLPart.cpp | 30 +- src/render/opengl/OpenGLPart.h | 27 +- src/render/opengl/OpenGLRenderer.cpp | 20 +- src/render/opengl/OpenGLRenderer.h | 4 +- src/render/opengl/OpenGLShaderProgram.cpp | 49 ++- src/render/opengl/OpenGLShaderProgram.h | 25 +- src/render/opengl/OpenGLSharedState.h | 5 + src/render/opengl/OpenGLSkybox.cpp | 54 ++-- src/render/opengl/OpenGLSkybox.h | 4 +- src/render/opengl/OpenGLTerrain.cpp | 31 +- src/render/opengl/OpenGLTerrain.h | 4 +- src/render/opengl/OpenGLTerrainChunk.cpp | 261 +++++++++++++++ src/render/opengl/OpenGLTerrainChunk.h | 87 +++++ src/render/opengl/OpenGLVariable.cpp | 200 ++++++++---- src/render/opengl/OpenGLVariable.h | 22 +- src/render/opengl/OpenGLVertexArray.cpp | 121 +++++++ src/render/opengl/OpenGLVertexArray.h | 94 ++++++ src/render/opengl/OpenGLWater.cpp | 31 +- src/render/opengl/OpenGLWater.h | 4 +- src/render/opengl/VertexArray.h | 147 --------- src/render/opengl/opengl_global.h | 9 +- src/render/opengl/shaders/atmosphere.frag | 8 +- src/render/opengl/shaders/skybox.frag | 6 +- src/render/opengl/shaders/skybox.vert | 4 +- src/render/opengl/shaders/terrain.frag | 13 +- src/render/opengl/shaders/terrain.vert | 8 +- src/render/opengl/shaders/water.frag | 13 +- src/render/opengl/shaders/water.vert | 4 +- src/tests/OpenGLTerrainChunk_Test.cpp | 68 ++++ src/tests/VertexArray_Test.cpp | 92 ------ 36 files changed, 966 insertions(+), 864 deletions(-) delete mode 100644 src/render/opengl/ExplorerChunkTerrain.cpp delete mode 100644 src/render/opengl/ExplorerChunkTerrain.h create mode 100644 src/render/opengl/OpenGLTerrainChunk.cpp create mode 100644 src/render/opengl/OpenGLTerrainChunk.h create mode 100644 src/render/opengl/OpenGLVertexArray.cpp create mode 100644 src/render/opengl/OpenGLVertexArray.h delete mode 100644 src/render/opengl/VertexArray.h create mode 100644 src/tests/OpenGLTerrainChunk_Test.cpp delete mode 100644 src/tests/VertexArray_Test.cpp diff --git a/Makefile b/Makefile index 4ded77d..77a229b 100644 --- a/Makefile +++ b/Makefile @@ -64,7 +64,7 @@ profile_cli:build gltrace:build rm -f *.trace - LD_PRELOAD=/usr/lib/x86_64-linux-gnu/apitrace/wrappers/glxtrace.so LD_LIBRARY_PATH=$(LIBRARY_PATH) ${BUILDPATH}/interface/modeler/quickapp/paysages-modeler $(ARGS) + LD_PRELOAD="$(wildcard /usr/lib/x86_64-linux-gnu/apitrace/wrappers/glxtrace.so /usr/local/lib/x86_64-linux-gnu/apitrace/wrappers/glxtrace.so)" LD_LIBRARY_PATH=$(LIBRARY_PATH) ${BUILDPATH}/interface/modeler/quickapp/paysages-modeler $(ARGS) qapitrace paysages-modeler.trace package:build diff --git a/TODO b/TODO index c88912f..6858107 100644 --- a/TODO +++ b/TODO @@ -5,7 +5,8 @@ Technlology Preview 2 : - Add clouds to OpenGL with 3d textures. - Refactor medium traversal to unify clouds, atmosphere and god rays. - Fix potential holes in land rendering (OpenGL and software). -- Fix sun size not being consistent between opengl and software +- Fix sun size not being consistent between opengl and software. +- Remove Qt dependency in OpenGL renderer, except for OpenGLFunctions. Technology Preview 3 : - Alter aerial perspective using estimation of the amount of light left after cloud layers traversal. diff --git a/src/interface/modeler/quickapp/MainModelerWindow.cpp b/src/interface/modeler/quickapp/MainModelerWindow.cpp index 80b64be..f8559b1 100644 --- a/src/interface/modeler/quickapp/MainModelerWindow.cpp +++ b/src/interface/modeler/quickapp/MainModelerWindow.cpp @@ -16,10 +16,10 @@ #include MainModelerWindow::MainModelerWindow() { - /*QSurfaceFormat new_format = format(); - new_format.setVersion(3, 3); + QSurfaceFormat new_format = format(); + new_format.setVersion(OPENGL_MAJOR_VERSION, OPENGL_MINOR_VERSION); new_format.setProfile(QSurfaceFormat::CoreProfile); - setFormat(new_format);*/ + setFormat(new_format); scenery = new Scenery(); scenery->autoPreset(); diff --git a/src/interface/modeler/quickapp/OpenGLView.cpp b/src/interface/modeler/quickapp/OpenGLView.cpp index 39a47e0..3380d4a 100644 --- a/src/interface/modeler/quickapp/OpenGLView.cpp +++ b/src/interface/modeler/quickapp/OpenGLView.cpp @@ -52,8 +52,8 @@ void OpenGLView::paint() { resized = false; } - renderer->prepareOpenGLState(); - renderer->paint(); + renderer->prepareOpenGLState(false); + renderer->paint(false); if (window) { window->resetOpenGLState(); diff --git a/src/render/opengl/ExplorerChunkTerrain.cpp b/src/render/opengl/ExplorerChunkTerrain.cpp deleted file mode 100644 index c1578ba..0000000 --- a/src/render/opengl/ExplorerChunkTerrain.cpp +++ /dev/null @@ -1,296 +0,0 @@ -#include "ExplorerChunkTerrain.h" - -#include -#include -#include "OpenGLFunctions.h" -#include "ColorProfile.h" -#include "CameraDefinition.h" -#include "OpenGLRenderer.h" -#include "TerrainRenderer.h" -#include "VertexArray.h" -#include "Scenery.h" -#include "TerrainDefinition.h" - -ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer *renderer, double x, double z, double size, int nbchunks) - : _renderer(renderer) { - priority = 0.0; - _reset_topology = false; - _reset_texture = false; - - interrupt = false; - - _texture = new QImage(1, 1, QImage::Format_RGBA8888); - texture_id = 0; - _texture_changed = false; - _texture_current_size = 0; - _texture_wanted_size = 0; - _texture_max_size = 256; - - _startx = x; - _startz = z; - _size = size; - _overall_step = size * (double)nbchunks; - - distance_to_camera = 0.0; - underwater = false; - lowest = 0.0; - highest = 0.0; - - tessellation_count = 65; - tessellated = new VertexArray(); - tessellated->setGridSize(tessellation_count); - tessellated->setAutoGridIndices(tessellation_count); - _tessellation_max_size = tessellation_count - 1; - _tessellation_current_size = 0; - _tessellation_step = _size / (double)_tessellation_max_size; - - maintain(); -} - -ExplorerChunkTerrain::~ExplorerChunkTerrain() { - _lock_data.lock(); - delete _texture; - delete tessellated; - _lock_data.unlock(); -} - -bool ExplorerChunkTerrain::maintain() { - bool subchanged; - - _lock_data.lock(); - if (_reset_topology) { - _reset_topology = false; - _tessellation_current_size = 0; - lowest = 10000.0; - highest = -10000.0; - underwater = false; - } - if (_reset_texture) { - _reset_texture = false; - _texture_current_size = 0; - underwater = false; - } - _lock_data.unlock(); - - if (underwater) { - return false; - } - - // Improve heightmap resolution - if (_tessellation_current_size < _tessellation_max_size) { - while (_tessellation_current_size < _tessellation_max_size) { - int new_tessellation_size = _tessellation_current_size ? _tessellation_current_size * 4 : 4; - int old_tessellation_inc = - _tessellation_current_size ? _tessellation_max_size / _tessellation_current_size : 1; - int new_tessellation_inc = _tessellation_max_size / new_tessellation_size; - float internal_step = 1.0f / (float)_tessellation_max_size; - for (int j = 0; j <= _tessellation_max_size; j += new_tessellation_inc) { - for (int i = 0; i <= _tessellation_max_size; i += new_tessellation_inc) { - if (_tessellation_current_size == 0 || i % old_tessellation_inc != 0 || - j % old_tessellation_inc != 0) { - double x = _startx + _tessellation_step * (float)i; - double z = _startz + _tessellation_step * (float)j; - - double height = _renderer->getTerrainRenderer()->getHeight(x, z, true, false); - if (height >= highest) { - highest = height; - } - if (height <= lowest) { - lowest = height; - } - - TerrainVertex v; - - v.uv[0] = internal_step * (float)i; - v.uv[1] = internal_step * (float)j; - - v.location[0] = x; - v.location[1] = height; - v.location[2] = z; - - tessellated->setGridVertex(tessellation_count, i, j, v); - } - } - if (interrupt or _reset_topology) { - return false; - } - } - - underwater = highest < -_renderer->getScenery()->getTerrain()->getWaterOffset(); - - _lock_data.lock(); - _tessellation_current_size = new_tessellation_size; - tessellated->setAutoGridIndices(tessellation_count, new_tessellation_inc); - _lock_data.unlock(); - - if (_tessellation_current_size >= 4) { - break; - } - } - subchanged = true; - } else { - subchanged = false; - } - - // Improve texture resolution - if (_texture_current_size < _texture_wanted_size) { - int new_texture_size = _texture_current_size ? _texture_current_size * 2 : 1; - QImage *new_image = new QImage(_texture->scaled(new_texture_size + 1, new_texture_size + 1, - Qt::IgnoreAspectRatio, Qt::FastTransformation)); - for (int j = 0; j <= new_texture_size; j++) { - for (int i = 0; i <= new_texture_size; i++) { - if (_texture_current_size <= 1 || i % 2 != 0 || j % 2 != 0) { - Color color = - getTextureColor((double)i / (double)new_texture_size, (double)j / (double)new_texture_size); - color.normalize(); - new_image->setPixel(i, j, color.to32BitRGBA()); - } - } - - if (interrupt or _reset_texture) { - return false; - } - } - - _lock_data.lock(); - delete _texture; - _texture = new_image; - _texture_current_size = new_texture_size; - _texture_changed = true; - _lock_data.unlock(); - - return true; - } else { - return subchanged; - } -} - -void ExplorerChunkTerrain::updatePriority(CameraDefinition *camera) { - // Under water check - underwater = highest < -_renderer->getScenery()->getTerrain()->getWaterOffset(); - if (underwater) { - priority = -10000.0; - return; - } - - Vector3 camera_location = camera->getLocation(); - - // Handle position - _lock_data.lock(); - if (camera_location.x > _startx + _overall_step * 0.5) { - _startx += _overall_step; - askReset(); - } - if (camera_location.z > _startz + _overall_step * 0.5) { - _startz += _overall_step; - askReset(); - } - if (camera_location.x < _startx - _overall_step * 0.5) { - _startx -= _overall_step; - askReset(); - } - if (camera_location.z < _startz - _overall_step * 0.5) { - _startz -= _overall_step; - askReset(); - } - distance_to_camera = getCenter().sub(camera_location).getNorm(); - _lock_data.unlock(); - - // Update wanted LOD - if (distance_to_camera < 60.0) { - _texture_wanted_size = _texture_max_size; - } else if (distance_to_camera < 140.0) { - _texture_wanted_size = _texture_max_size / 4; - } else if (distance_to_camera < 300.0) { - _texture_wanted_size = _texture_max_size / 8; - } else { - _texture_wanted_size = 8; - } - - // Update priority - if (_reset_topology || _reset_texture || (_texture_max_size > 1 && _texture_current_size <= 1)) { - priority = 1000.0; - } else if (_texture_current_size == _texture_wanted_size) { - priority = -1000.0; - } else { - priority = _texture_wanted_size / _texture_current_size; - } -} - -void ExplorerChunkTerrain::render(QOpenGLShaderProgram *program, OpenGLFunctions *functions) { - if (underwater) { - return; - } - - // Put texture in place - _lock_data.lock(); - if (_texture_changed) { - _texture_changed = false; - - // TODO Only do the scale if not power-of-two textures are unsupported by GPU - QImage tex = _texture->scaled(_texture_current_size, _texture_current_size, Qt::IgnoreAspectRatio, - Qt::SmoothTransformation); - - if (texture_id == 0) { - GLuint texid; - functions->glGenTextures(1, &texid); - texture_id = texid; - } - - functions->glBindTexture(GL_TEXTURE_2D, texture_id); - functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - functions->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - functions->glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, - tex.bits()); - } - _lock_data.unlock(); - - // Render tessellated mesh - if (!_reset_topology) { - _lock_data.lock(); - int tessellation_size = _tessellation_current_size; - _lock_data.unlock(); - - if (tessellation_size <= 1) { - return; - } - - _lock_data.lock(); - // TEMP - functions->glActiveTexture(GL_TEXTURE0 + 7); - functions->glBindTexture(GL_TEXTURE_2D, texture_id); - program->setUniformValue("groundTexture", 7); - tessellated->render(program, functions); - _lock_data.unlock(); - } -} - -void ExplorerChunkTerrain::askReset(bool topology, bool texture) { - _reset_topology = _reset_topology or topology; - _reset_texture = _reset_texture or texture; -} - -void ExplorerChunkTerrain::askInterrupt() { - interrupt = true; -} - -void ExplorerChunkTerrain::askResume() { - interrupt = false; -} - -Color ExplorerChunkTerrain::getTextureColor(double x, double y) { - Vector3 location = {_startx + x * _size, 0.0, _startz + y * _size}; - return _renderer->getTerrainRenderer()->getFinalColor(location, 0.01); -} - -Vector3 ExplorerChunkTerrain::getCenter() { - Vector3 result; - - result.x = _startx + _size / 2.0; - result.y = 0.0; - result.z = _startz + _size / 2.0; - - return result; -} diff --git a/src/render/opengl/ExplorerChunkTerrain.h b/src/render/opengl/ExplorerChunkTerrain.h deleted file mode 100644 index 69213d5..0000000 --- a/src/render/opengl/ExplorerChunkTerrain.h +++ /dev/null @@ -1,74 +0,0 @@ -#ifndef EXPLORERCHUNKTERRAIN_H -#define EXPLORERCHUNKTERRAIN_H - -#include "opengl_global.h" - -#include -class QImage; -class QOpenGLShaderProgram; - -namespace paysages { -namespace opengl { - -class OPENGLSHARED_EXPORT ExplorerChunkTerrain { - public: - typedef struct { - float location[3]; - float uv[2]; - } TerrainVertex; - - public: - ExplorerChunkTerrain(OpenGLRenderer *renderer, double x, double z, double size, int nbchunks); - ~ExplorerChunkTerrain(); - - bool maintain(); - void updatePriority(CameraDefinition *camera); - void render(QOpenGLShaderProgram *program, OpenGLFunctions *functions); - - void askReset(bool topology = true, bool texture = true); - void askInterrupt(); - void askResume(); - - Color getTextureColor(double x, double y); - - double priority; - - private: - Vector3 getCenter(); - - double _startx; - double _startz; - double _size; - double _overall_step; - - int tessellation_count; - VertexArray *tessellated; - int _tessellation_max_size; - int _tessellation_current_size; - double _tessellation_step; - - QMutex _lock_data; - - OpenGLRenderer *_renderer; - - bool _reset_topology; - bool _reset_texture; - bool interrupt; - - QImage *_texture; - unsigned int texture_id; - bool _texture_changed; - int _texture_current_size; - int _texture_wanted_size; - int _texture_max_size; - - // LOD control - bool underwater; - double lowest; - double highest; - double distance_to_camera; -}; -} -} - -#endif // EXPLORERCHUNKTERRAIN_H diff --git a/src/render/opengl/OpenGLPart.cpp b/src/render/opengl/OpenGLPart.cpp index cfefc62..a0bc65e 100644 --- a/src/render/opengl/OpenGLPart.cpp +++ b/src/render/opengl/OpenGLPart.cpp @@ -1,32 +1,27 @@ #include "OpenGLPart.h" -#include -#include -#include "OpenGLRenderer.h" #include "OpenGLShaderProgram.h" -#include "CameraDefinition.h" -#include "AtmosphereDefinition.h" -#include "AtmosphereRenderer.h" -#include "Scenery.h" +#include "OpenGLVertexArray.h" OpenGLPart::OpenGLPart(OpenGLRenderer *renderer) : renderer(renderer) { } OpenGLPart::~OpenGLPart() { - QMapIterator i(shaders); - while (i.hasNext()) { - i.next(); - delete i.value(); + for (auto &pair: shaders) { + delete pair.second; + } + for (auto &array: arrays) { + delete array; } } void OpenGLPart::interrupt() { } -OpenGLShaderProgram *OpenGLPart::createShader(QString name) { - OpenGLShaderProgram *program = new OpenGLShaderProgram(name.toStdString(), renderer); +OpenGLShaderProgram *OpenGLPart::createShader(const std::string &name) { + OpenGLShaderProgram *program = new OpenGLShaderProgram(name, renderer); - if (!shaders.contains(name)) { + if (shaders.find(name) == shaders.end()) { shaders[name] = program; return program; } else { @@ -34,6 +29,13 @@ OpenGLShaderProgram *OpenGLPart::createShader(QString name) { } } +OpenGLVertexArray *OpenGLPart::createVertexArray(bool has_uv, bool strip) +{ + OpenGLVertexArray *result = new OpenGLVertexArray(has_uv, strip); + arrays.push_back(result); + return result; +} + void OpenGLPart::updateScenery(bool onlyCommon) { // Let subclass do its own collecting if (not onlyCommon) { diff --git a/src/render/opengl/OpenGLPart.h b/src/render/opengl/OpenGLPart.h index fedef49..bea0e1a 100644 --- a/src/render/opengl/OpenGLPart.h +++ b/src/render/opengl/OpenGLPart.h @@ -3,14 +3,15 @@ #include "opengl_global.h" -#include -#include +#include +#include namespace paysages { namespace opengl { -// Class that can be inherited by scenery parts, to use OpenGL features - +/** + * Class that can be inherited by scenery parts, to use OpenGL features. + */ class OPENGLSHARED_EXPORT OpenGLPart { public: OpenGLPart(OpenGLRenderer *renderer); @@ -31,14 +32,26 @@ class OPENGLSHARED_EXPORT OpenGLPart { void updateScenery(bool onlyCommon = false); protected: - // Create a shader program - OpenGLShaderProgram *createShader(QString name); + /** + * Create a shader program. + * + * The returned shader's ownership remains in this object. It will taks care of the destruction. + */ + OpenGLShaderProgram *createShader(const std::string &name); + + /** + * Create a vertex array. + * + * The returned array's ownership remains in this object. It will taks care of the destruction. + */ + OpenGLVertexArray *createVertexArray(bool has_uv, bool strip); // Access to the main scenery renderer OpenGLRenderer *renderer; private: - QMap shaders; + std::map shaders; + std::vector arrays; }; } } diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 075ac39..61ed6ed 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -97,10 +97,8 @@ void OpenGLRenderer::initialize() { } } -void OpenGLRenderer::prepareOpenGLState() { +void OpenGLRenderer::prepareOpenGLState(bool clear) { if (ready) { - functions->glDisable(GL_LIGHTING); - functions->glFrontFace(GL_CCW); functions->glCullFace(GL_BACK); functions->glEnable(GL_CULL_FACE); @@ -110,16 +108,16 @@ void OpenGLRenderer::prepareOpenGLState() { functions->glEnable(GL_DEPTH_TEST); functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - functions->glEnable(GL_LINE_SMOOTH); - functions->glLineWidth(1.0); - - functions->glDisable(GL_FOG); + /*functions->glEnable(GL_LINE_SMOOTH); + functions->glLineWidth(1.0);*/ functions->glEnable(GL_BLEND); functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); functions->glClearColor(0.0, 0.0, 0.0, 0.0); - functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (clear) { + functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } functions->glViewport(0, 0, vp_width, vp_height); } @@ -146,10 +144,12 @@ void OpenGLRenderer::resize(int width, int height) { } } -void OpenGLRenderer::paint() { +void OpenGLRenderer::paint(bool clear) { if (ready and not paused) { checkForErrors("before_paint"); - functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + if (clear) { + functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } skybox->render(); checkForErrors("skybox"); diff --git a/src/render/opengl/OpenGLRenderer.h b/src/render/opengl/OpenGLRenderer.h index dcd0250..c7ede8b 100644 --- a/src/render/opengl/OpenGLRenderer.h +++ b/src/render/opengl/OpenGLRenderer.h @@ -39,9 +39,9 @@ class OPENGLSHARED_EXPORT OpenGLRenderer : public SoftwareRenderer { void checkForErrors(const std::string &domain); void initialize(); - void prepareOpenGLState(); + void prepareOpenGLState(bool clear=true); void resize(int width, int height); - void paint(); + void paint(bool clear=true); /** * Reset the whole state (when the scenery has been massively updated). diff --git a/src/render/opengl/OpenGLShaderProgram.cpp b/src/render/opengl/OpenGLShaderProgram.cpp index f6adc8a..39c7351 100644 --- a/src/render/opengl/OpenGLShaderProgram.cpp +++ b/src/render/opengl/OpenGLShaderProgram.cpp @@ -5,6 +5,7 @@ #include "OpenGLFunctions.h" #include "OpenGLRenderer.h" #include "OpenGLSharedState.h" +#include "OpenGLVertexArray.h" #include "Texture2D.h" #include "Texture3D.h" #include "Texture4D.h" @@ -22,8 +23,8 @@ OpenGLShaderProgram::~OpenGLShaderProgram() { delete program; } -void OpenGLShaderProgram::addVertexSource(QString path) { - QFile file(QString(":/shaders/%1.vert").arg(path)); +void OpenGLShaderProgram::addVertexSource(const std::string &path) { + QFile file(QString(":/shaders/%1.vert").arg(QString::fromStdString(path))); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { source_vertex += QString(file.readAll()).toStdString(); } else { @@ -31,8 +32,8 @@ void OpenGLShaderProgram::addVertexSource(QString path) { } } -void OpenGLShaderProgram::addFragmentSource(QString path) { - QFile file(QString(":/shaders/%1.frag").arg(path)); +void OpenGLShaderProgram::addFragmentSource(const std::string &path) { + QFile file(QString(":/shaders/%1.frag").arg(QString::fromStdString(path))); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { source_fragment += QString(file.readAll()).toStdString(); } else { @@ -41,8 +42,13 @@ void OpenGLShaderProgram::addFragmentSource(QString path) { } void OpenGLShaderProgram::compile() { - program->addShaderFromSourceCode(QOpenGLShader::Vertex, QString::fromStdString(source_vertex)); - program->addShaderFromSourceCode(QOpenGLShader::Fragment, QString::fromStdString(source_fragment)); + std::string prefix = std::string("#version ") + OPENGL_GLSL_VERSION + "\n\n"; + + program->addShaderFromSourceCode(QOpenGLShader::Vertex, QString::fromStdString(prefix + source_vertex)); + program->addShaderFromSourceCode(QOpenGLShader::Fragment, QString::fromStdString(prefix + source_fragment)); + + program->bindAttributeLocation("vertex", 0); + program->bindAttributeLocation("uv", 1); if (not program->link()) { Logs::warning() << "[OpenGL] Error while compiling shader " << name << std::endl @@ -55,7 +61,7 @@ void OpenGLShaderProgram::compile() { } } -bool OpenGLShaderProgram::bind() { +bool OpenGLShaderProgram::bind(OpenGLSharedState *state) { if (not compiled) { compile(); compiled = true; @@ -64,6 +70,9 @@ bool OpenGLShaderProgram::bind() { if (program->bind()) { int texture_unit = 0; renderer->getSharedState()->apply(this, texture_unit); + if (state) { + state->apply(this, texture_unit); + } return true; } else { return false; @@ -74,29 +83,9 @@ void OpenGLShaderProgram::release() { program->release(); } -void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) { - if (bind()) { - GLuint vertex = program->attributeLocation("vertex"); - program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); - program->enableAttributeArray(vertex); - - functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3); - - program->disableAttributeArray(vertex); - - release(); - } -} - -void OpenGLShaderProgram::drawTriangleStrip(float *vertices, int vertex_count) { - if (bind()) { - GLuint vertex = program->attributeLocation("vertex"); - program->setAttributeArray(vertex, GL_FLOAT, vertices, 3); - program->enableAttributeArray(vertex); - - functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count); - - program->disableAttributeArray(vertex); +void OpenGLShaderProgram::draw(OpenGLVertexArray *vertices, OpenGLSharedState *state) { + if (bind(state)) { + vertices->render(functions); release(); } diff --git a/src/render/opengl/OpenGLShaderProgram.h b/src/render/opengl/OpenGLShaderProgram.h index 6ca2b84..3eedf94 100644 --- a/src/render/opengl/OpenGLShaderProgram.h +++ b/src/render/opengl/OpenGLShaderProgram.h @@ -3,8 +3,6 @@ #include "opengl_global.h" -#include - class QOpenGLShaderProgram; namespace paysages { @@ -15,18 +13,25 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram { OpenGLShaderProgram(const std::string &name, OpenGLRenderer *renderer); ~OpenGLShaderProgram(); - void addVertexSource(QString path); - void addFragmentSource(QString path); + void addVertexSource(const std::string &path); + void addFragmentSource(const std::string &path); - void drawTriangles(float *vertices, int triangle_count); - void drawTriangleStrip(float *vertices, int vertex_count); - - bool bind(); - void release(); + /** + * Draw a VertexArray object. + * + * This will bind the program (compile it if needed), set the uniform variables, and + * ask the array object to bind its VAO and render itself. + * + * *state* is optional and may add ponctual variables to the global state. + */ + void draw(OpenGLVertexArray *vertices, OpenGLSharedState *state = NULL); inline QOpenGLShaderProgram *getProgram() const { return program; } + inline OpenGLFunctions *getFunctions() const { + return functions; + } inline OpenGLRenderer *getRenderer() const { return renderer; } @@ -35,6 +40,8 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram { friend class OpenGLVariable; private: + bool bind(OpenGLSharedState *state = NULL); + void release(); void compile(); bool compiled; diff --git a/src/render/opengl/OpenGLSharedState.h b/src/render/opengl/OpenGLSharedState.h index 974e1d8..15bdf48 100644 --- a/src/render/opengl/OpenGLSharedState.h +++ b/src/render/opengl/OpenGLSharedState.h @@ -6,6 +6,8 @@ #include #include "OpenGLVariable.h" +class QImage; + namespace paysages { namespace opengl { @@ -31,6 +33,9 @@ class OPENGLSHARED_EXPORT OpenGLSharedState { inline void set(const std::string &name, const Texture2D *texture, bool repeat = false, bool color = true) { get(name)->set(texture, repeat, color); } + inline void set(const std::string &name, const QImage &texture, bool repeat = false, bool color = true) { + get(name)->set(texture, repeat, color); + } inline void set(const std::string &name, const Texture3D *texture, bool repeat = false, bool color = true) { get(name)->set(texture, repeat, color); } diff --git a/src/render/opengl/OpenGLSkybox.cpp b/src/render/opengl/OpenGLSkybox.cpp index a59722b..87718d7 100644 --- a/src/render/opengl/OpenGLSkybox.cpp +++ b/src/render/opengl/OpenGLSkybox.cpp @@ -4,6 +4,7 @@ #include "OpenGLRenderer.h" #include "OpenGLShaderProgram.h" #include "OpenGLSharedState.h" +#include "OpenGLVertexArray.h" #include "Scenery.h" #include "AtmosphereDefinition.h" #include "AtmosphereRenderer.h" @@ -11,42 +12,43 @@ #include "FloatNode.h" OpenGLSkybox::OpenGLSkybox(OpenGLRenderer *renderer) : OpenGLPart(renderer) { - vertices = new float[14 * 3]; -} - -OpenGLSkybox::~OpenGLSkybox() { - delete[] vertices; -} - -void OpenGLSkybox::initialize() { program = createShader("skybox"); program->addVertexSource("skybox"); program->addFragmentSource("atmosphere"); program->addFragmentSource("tonemapping"); program->addFragmentSource("skybox"); - setVertex(0, 1.0f, 1.0f, 1.0f); - setVertex(12, 1.0f, 1.0f, 1.0f); + vertices = createVertexArray(false, true); - setVertex(1, 1.0f, -1.0f, 1.0f); - setVertex(5, 1.0f, -1.0f, 1.0f); - setVertex(13, 1.0f, -1.0f, 1.0f); + vertices->setVertexCount(14); - setVertex(2, -1.0f, 1.0f, 1.0f); - setVertex(10, -1.0f, 1.0f, 1.0f); + vertices->set(0, Vector3(1.0f, 1.0f, 1.0f)); + vertices->set(12, Vector3(1.0f, 1.0f, 1.0f)); - setVertex(3, -1.0f, -1.0f, 1.0f); + vertices->set(1, Vector3(1.0f, -1.0f, 1.0f)); + vertices->set(5, Vector3(1.0f, -1.0f, 1.0f)); + vertices->set(13, Vector3(1.0f, -1.0f, 1.0f)); - setVertex(4, -1.0f, -1.0f, -1.0f); - setVertex(8, -1.0f, -1.0f, -1.0f); + vertices->set(2, Vector3(-1.0f, 1.0f, 1.0f)); + vertices->set(10, Vector3(-1.0f, 1.0f, 1.0f)); - setVertex(6, 1.0f, -1.0f, -1.0f); + vertices->set(3, Vector3(-1.0f, -1.0f, 1.0f)); - setVertex(7, 1.0f, 1.0f, -1.0f); - setVertex(11, 1.0f, 1.0f, -1.0f); + vertices->set(4, Vector3(-1.0f, -1.0f, -1.0f)); + vertices->set(8, Vector3(-1.0f, -1.0f, -1.0f)); - setVertex(9, -1.0f, 1.0f, -1.0f); + vertices->set(6, Vector3(1.0f, -1.0f, -1.0f)); + vertices->set(7, Vector3(1.0f, 1.0f, -1.0f)); + vertices->set(11, Vector3(1.0f, 1.0f, -1.0f)); + + vertices->set(9, Vector3(-1.0f, 1.0f, -1.0f)); +} + +OpenGLSkybox::~OpenGLSkybox() { +} + +void OpenGLSkybox::initialize() { // Watch for definition changes renderer->getScenery()->getAtmosphere()->propDayTime()->addWatcher(this, true); renderer->getScenery()->getAtmosphere()->propHumidity()->addWatcher(this, true); @@ -61,7 +63,7 @@ void OpenGLSkybox::update() { } void OpenGLSkybox::render() { - program->drawTriangleStrip(vertices, 14); + program->draw(vertices); } void OpenGLSkybox::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) { @@ -81,9 +83,3 @@ void OpenGLSkybox::nodeChanged(const DefinitionNode *node, const DefinitionDiff renderer->getScenery()->getAtmosphere()->propSunRadius()->getValue()); } } - -void OpenGLSkybox::setVertex(int i, float x, float y, float z) { - vertices[i * 3] = x; - vertices[i * 3 + 1] = y; - vertices[i * 3 + 2] = z; -} diff --git a/src/render/opengl/OpenGLSkybox.h b/src/render/opengl/OpenGLSkybox.h index 794ee89..ab76cd6 100644 --- a/src/render/opengl/OpenGLSkybox.h +++ b/src/render/opengl/OpenGLSkybox.h @@ -21,10 +21,8 @@ class OPENGLSHARED_EXPORT OpenGLSkybox : public OpenGLPart, public DefinitionWat virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override; private: - void setVertex(int i, float x, float y, float z); - OpenGLShaderProgram *program; - float *vertices; + OpenGLVertexArray *vertices; }; } } diff --git a/src/render/opengl/OpenGLTerrain.cpp b/src/render/opengl/OpenGLTerrain.cpp index 98cc06b..18c67a4 100644 --- a/src/render/opengl/OpenGLTerrain.cpp +++ b/src/render/opengl/OpenGLTerrain.cpp @@ -5,7 +5,7 @@ #include "OpenGLShaderProgram.h" #include "ParallelPool.h" #include "Thread.h" -#include "ExplorerChunkTerrain.h" +#include "OpenGLTerrainChunk.h" #include "WaterRenderer.h" #include "CameraDefinition.h" #include "AtmosphereDefinition.h" @@ -35,6 +35,14 @@ class ChunkMaintenanceThreads : public ParallelPool { OpenGLTerrain::OpenGLTerrain(OpenGLRenderer *renderer) : OpenGLPart(renderer) { work = new ChunkMaintenanceThreads(this); paused = false; + + program = createShader("terrain"); + program->addVertexSource("terrain"); + program->addFragmentSource("atmosphere"); + program->addFragmentSource("tonemapping"); + program->addFragmentSource("fadeout"); + program->addFragmentSource("ui"); + program->addFragmentSource("terrain"); } OpenGLTerrain::~OpenGLTerrain() { @@ -46,15 +54,6 @@ OpenGLTerrain::~OpenGLTerrain() { } void OpenGLTerrain::initialize() { - // Prepare shader programs - program = createShader("terrain"); - program->addVertexSource("terrain"); - program->addFragmentSource("atmosphere"); - program->addFragmentSource("tonemapping"); - program->addFragmentSource("fadeout"); - program->addFragmentSource("ui"); - program->addFragmentSource("terrain"); - // Add terrain chunks int chunks = 12; double size = 800.0; @@ -62,7 +61,7 @@ void OpenGLTerrain::initialize() { double start = -size / 2.0; for (int i = 0; i < chunks; i++) { for (int j = 0; j < chunks; j++) { - ExplorerChunkTerrain *chunk = new ExplorerChunkTerrain(renderer, start + chunksize * (double)i, + OpenGLTerrainChunk *chunk = new OpenGLTerrainChunk(renderer, start + chunksize * (double)i, start + chunksize * (double)j, chunksize, chunks); _chunks.append(chunk); _updateQueue.append(chunk); @@ -84,13 +83,9 @@ void OpenGLTerrain::update() { } void OpenGLTerrain::render() { - program->bind(); - for (int i = 0; i < _chunks.count(); i++) { - _chunks[i]->render(program->getProgram(), renderer->getOpenGlFunctions()); + _chunks[i]->render(program); } - - program->release(); } void OpenGLTerrain::interrupt() { @@ -117,13 +112,13 @@ void OpenGLTerrain::resetTextures() { } } -static bool _cmpChunks(const ExplorerChunkTerrain *c1, const ExplorerChunkTerrain *c2) { +static bool _cmpChunks(const OpenGLTerrainChunk *c1, const OpenGLTerrainChunk *c2) { return c1->priority > c2->priority; } void OpenGLTerrain::performChunksMaintenance() { CameraDefinition *camera = renderer->getScenery()->getCamera(); - ExplorerChunkTerrain *chunk; + OpenGLTerrainChunk *chunk; _lock_chunks.lock(); if (_updateQueue.count() > 0) { diff --git a/src/render/opengl/OpenGLTerrain.h b/src/render/opengl/OpenGLTerrain.h index b2850be..2d24677 100644 --- a/src/render/opengl/OpenGLTerrain.h +++ b/src/render/opengl/OpenGLTerrain.h @@ -44,8 +44,8 @@ class OPENGLSHARED_EXPORT OpenGLTerrain : public OpenGLPart, public DefinitionWa ParallelPool *work; bool paused; - QVector _chunks; - QList _updateQueue; + QVector _chunks; + QList _updateQueue; QMutex _lock_chunks; }; } diff --git a/src/render/opengl/OpenGLTerrainChunk.cpp b/src/render/opengl/OpenGLTerrainChunk.cpp new file mode 100644 index 0000000..ac07a5e --- /dev/null +++ b/src/render/opengl/OpenGLTerrainChunk.cpp @@ -0,0 +1,261 @@ +#include "OpenGLTerrainChunk.h" + +#include +#include +#include +#include "OpenGLShaderProgram.h" +#include "OpenGLVertexArray.h" +#include "OpenGLSharedState.h" +#include "ColorProfile.h" +#include "CameraDefinition.h" +#include "OpenGLRenderer.h" +#include "TexturesRenderer.h" +#include "Scenery.h" +#include "TerrainDefinition.h" +#include "Texture2D.h" +#include "Mutex.h" +#include "Logs.h" + +OpenGLTerrainChunk::OpenGLTerrainChunk(OpenGLRenderer *renderer, double x, double z, double size, int nbchunks) + : _renderer(renderer) { + priority = 0.0; + _reset_topology = false; + _reset_texture = false; + + interrupt = false; + + _lock_data = new Mutex(); + + _texture = new QImage(1, 1, QImage::Format_RGBA8888); + _texture_changed = false; + _texture_current_size = 0; + _texture_wanted_size = 0; + _texture_max_size = 256; + + _startx = x; + _startz = z; + _size = size; + _overall_step = size * (double)nbchunks; + + distance_to_camera = 0.0; + + vertices = new OpenGLVertexArray(true); + vertices_level = 0; + glstate = new OpenGLSharedState(); + Texture2D empty(1, 1); + glstate->set("groundTexture", &empty); +} + +OpenGLTerrainChunk::~OpenGLTerrainChunk() { + _lock_data->acquire(); + + delete _texture; + delete vertices; + delete glstate; + + _lock_data->release(); + + delete _lock_data; +} + +bool OpenGLTerrainChunk::maintain() { + bool subchanged; + + _lock_data->acquire(); + if (_reset_topology) { + _reset_topology = false; + vertices_level = 0; + } + if (_reset_texture) { + _reset_texture = false; + _texture_current_size = 0; + } + + _lock_data->release(); + + // Improve heightmap resolution + if (vertices_level < 8) { + if (vertices_level) { + augmentVertices(); + } else { + setFirstStepVertices(); + } + return true; + } else if (vertices_level < 64) { + augmentVertices(); + subchanged = true; + } + + // Improve texture resolution + if (_texture_current_size < _texture_wanted_size) { + int new_texture_size = _texture_current_size ? _texture_current_size * 2 : 1; + QImage *new_image = new QImage(_texture->scaled(new_texture_size + 1, new_texture_size + 1, + Qt::IgnoreAspectRatio, Qt::FastTransformation)); + double factor = _size / (double)new_texture_size; + for (int j = 0; j <= new_texture_size; j++) { + for (int i = 0; i <= new_texture_size; i++) { + if (_texture_current_size <= 1 || i % 2 != 0 || j % 2 != 0) { + double x = _startx + factor * (double)i; + double z = _startz + factor * (double)j; + Color color = _renderer->getTexturesRenderer()->applyToTerrain(x, z).final_color; + color.normalize(); + new_image->setPixel(i, j, color.to32BitRGBA()); + } + } + + if (interrupt or _reset_texture) { + return false; + } + } + + _lock_data->acquire(); + delete _texture; + _texture = new_image; + _texture_current_size = new_texture_size; + _texture_changed = true; + _lock_data->release(); + + return true; + } else { + return subchanged; + } +} + +void OpenGLTerrainChunk::updatePriority(CameraDefinition *camera) { + Vector3 camera_location = camera->getLocation(); + + // Handle position + _lock_data->acquire(); + if (camera_location.x > _startx + _overall_step * 0.5) { + _startx += _overall_step; + askReset(); + } + if (camera_location.z > _startz + _overall_step * 0.5) { + _startz += _overall_step; + askReset(); + } + if (camera_location.x < _startx - _overall_step * 0.5) { + _startx -= _overall_step; + askReset(); + } + if (camera_location.z < _startz - _overall_step * 0.5) { + _startz -= _overall_step; + askReset(); + } + distance_to_camera = getCenter().sub(camera_location).getNorm(); + _lock_data->release(); + + // Update wanted LOD + if (distance_to_camera < 60.0) { + _texture_wanted_size = _texture_max_size; + } else if (distance_to_camera < 140.0) { + _texture_wanted_size = _texture_max_size / 4; + } else if (distance_to_camera < 300.0) { + _texture_wanted_size = _texture_max_size / 8; + } else { + _texture_wanted_size = 8; + } + + // Update priority + if (_reset_topology || _reset_texture || (_texture_max_size > 1 && _texture_current_size <= 1) || + vertices_level < 8) { + priority = 1000.0 - (double)vertices_level; + } else if (_texture_current_size == _texture_wanted_size) { + priority = -1000.0; + } else { + priority = _texture_wanted_size / _texture_current_size; + } +} + +void OpenGLTerrainChunk::render(OpenGLShaderProgram *program) { + // Put texture in place + _lock_data->acquire(); + if (_texture_changed) { + _texture_changed = false; + glstate->set("groundTexture", *_texture); + /*glstate->set("groundTexture", _texture->scaled(_texture_current_size, _texture_current_size, + Qt::IgnoreAspectRatio, Qt::SmoothTransformation));*/ + } + _lock_data->release(); + + // FIXME Should update *vertices* inside lock + program->draw(vertices, glstate); +} + +void OpenGLTerrainChunk::askReset(bool topology, bool texture) { + _reset_topology = _reset_topology or topology; + _reset_texture = _reset_texture or texture; +} + +void OpenGLTerrainChunk::askInterrupt() { + interrupt = true; +} + +void OpenGLTerrainChunk::askResume() { + interrupt = false; +} + +void OpenGLTerrainChunk::setFirstStepVertices() { + OpenGLVertexArray next(true); + next.setVertexCount(6); + fillVerticesFromSquare(&next, 0, _startx, _startz, _size); + updateVertices(next, 1); +} + +void OpenGLTerrainChunk::augmentVertices() { + OpenGLVertexArray next(true); + next.setVertexCount(vertices->getVertexCount() * 4); + int next_vertices_level = vertices_level * 2; + + // TODO Re-use existing vertices from previous level when possible + double quad_size = _size / (double)next_vertices_level; + for (int iz = 0; iz < next_vertices_level; iz++) { + for (int ix = 0; ix < next_vertices_level; ix++) { + fillVerticesFromSquare(&next, (iz * next_vertices_level + ix) * 6, _startx + quad_size * (double)ix, + _startz + quad_size * (double)iz, quad_size); + } + } + + updateVertices(next, next_vertices_level); +} + +void OpenGLTerrainChunk::updateVertices(const OpenGLVertexArray &source, int vertice_level) { + assert(source.getVertexCount() == vertice_level * vertice_level * 6); + + _lock_data->acquire(); + + source.copyTo(vertices); + vertices_level = vertice_level; + + _lock_data->release(); +} + +void OpenGLTerrainChunk::fillVerticesFromSquare(OpenGLVertexArray *array, int index_offset, double x, double z, + double size) { + Vector3 c1(x, _renderer->getTerrainRenderer()->getHeight(x, z, true, false), z); + Vector3 c2(x, _renderer->getTerrainRenderer()->getHeight(x, z + size, true, false), z + size); + Vector3 c3(x + size, _renderer->getTerrainRenderer()->getHeight(x + size, z + size, true, false), z + size); + Vector3 c4(x + size, _renderer->getTerrainRenderer()->getHeight(x + size, z, true, false), z); + + double u = (x - _startx) / _size; + double v = (z - _startz) / _size; + double dt = size / _size; + + array->set(index_offset, c1, u, v); + array->set(index_offset + 1, c2, u, v + dt); + array->set(index_offset + 2, c4, u + dt, v); + + array->set(index_offset + 3, c3, u + dt, v + dt); + array->set(index_offset + 4, c4, u + dt, v); + array->set(index_offset + 5, c2, u, v + dt); +} + +Vector3 OpenGLTerrainChunk::getCenter() { + Vector3 result; + + result.x = _startx + _size / 2.0; + result.y = 0.0; + result.z = _startz + _size / 2.0; + + return result; +} diff --git a/src/render/opengl/OpenGLTerrainChunk.h b/src/render/opengl/OpenGLTerrainChunk.h new file mode 100644 index 0000000..4eeff97 --- /dev/null +++ b/src/render/opengl/OpenGLTerrainChunk.h @@ -0,0 +1,87 @@ +#ifndef OPENGLTERRAINCHUNK_H +#define OPENGLTERRAINCHUNK_H + +#include "opengl_global.h" + +class QImage; + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLTerrainChunk { + public: + OpenGLTerrainChunk(OpenGLRenderer *renderer, double x, double z, double size, int nbchunks); + ~OpenGLTerrainChunk(); + + bool maintain(); + void updatePriority(CameraDefinition *camera); + void render(OpenGLShaderProgram *program); + + void askReset(bool topology = true, bool texture = true); + void askInterrupt(); + void askResume(); + + inline int getVerticesLevel() const { + return vertices_level; + } + inline const OpenGLVertexArray *getVertices() const { + return vertices; + } + + /** + * Fill *vertices* with a quick initial set of vertices, that can be augmented later using *augmentVertices*. + */ + void setFirstStepVertices(); + + /** + * Improve the level of detail of tessellated vertices in *vertices*. + * + * This will double the existing resolution. + */ + void augmentVertices(); + + /** + * Update *vertices* using *source*. + */ + void updateVertices(const OpenGLVertexArray &source, int vertice_level); + + /** + * Set a square (two triangles) in *vertices_next*. + */ + void fillVerticesFromSquare(OpenGLVertexArray *array, int index_offset, double x, double z, double size); + + double priority; + + private: + Vector3 getCenter(); + + double _startx; + double _startz; + double _size; + double _overall_step; + + OpenGLVertexArray *vertices; + int vertices_level; + + Mutex *_lock_data; + + OpenGLRenderer *_renderer; + OpenGLSharedState *glstate; + + bool _reset_topology; + bool _reset_texture; + bool interrupt; + + QImage *_texture; + bool _texture_changed; + int _texture_current_size; + int _texture_wanted_size; + int _texture_max_size; + + // LOD control + double distance_to_camera; +}; +} +} + +#endif // OPENGLTERRAINCHUNK_H diff --git a/src/render/opengl/OpenGLVariable.cpp b/src/render/opengl/OpenGLVariable.cpp index bf4ab86..31bad2e 100644 --- a/src/render/opengl/OpenGLVariable.cpp +++ b/src/render/opengl/OpenGLVariable.cpp @@ -1,7 +1,12 @@ #include "OpenGLVariable.h" -#include #include +#include +#include +#include +#include +#include +#include "Logs.h" #include "OpenGLFunctions.h" #include "OpenGLRenderer.h" #include "OpenGLShaderProgram.h" @@ -16,6 +21,22 @@ OpenGLVariable::OpenGLVariable(const std::string &name) : name(name) { type = TYPE_NONE; texture_toupload = false; texture_id = 0; + + value_color = new QColor; + value_matrix4 = new QMatrix4x4; + value_vector3 = new QVector3D; + value_texture_data = new float[1]; +} + +OpenGLVariable::~OpenGLVariable() { + delete value_color; + delete value_matrix4; + delete value_vector3; + delete[] value_texture_data; + + if (texture_id) { + Logs::warning() << "[OpenGL] Texture ID not freed " << texture_id << std::endl; + } } void OpenGLVariable::apply(OpenGLShaderProgram *program, int &texture_unit) { @@ -32,13 +53,13 @@ void OpenGLVariable::apply(OpenGLShaderProgram *program, int &texture_unit) { pr->setUniformValue(name.c_str(), value_float); break; case TYPE_COLOR: - pr->setUniformValue(name.c_str(), value_color); + pr->setUniformValue(name.c_str(), *value_color); break; case TYPE_VECTOR3: - pr->setUniformValue(name.c_str(), value_vector3); + pr->setUniformValue(name.c_str(), *value_vector3); break; case TYPE_MATRIX4: - pr->setUniformValue(name.c_str(), value_matrix4); + pr->setUniformValue(name.c_str(), *value_matrix4); break; case TYPE_TEXTURE_2D: functions->glActiveTexture(GL_TEXTURE0 + texture_unit); @@ -62,7 +83,61 @@ void OpenGLVariable::set(const Texture2D *texture, bool repeat, bool color) { assert(type == TYPE_NONE or type == TYPE_TEXTURE_2D); type = TYPE_TEXTURE_2D; - value_tex2d = texture; + + int sx, sy; + texture->getSize(&sx, &sy); + float *pixels = new float[sx * sy * 4]; + for (int x = 0; x < sx; x++) { + for (int y = 0; y < sy; y++) { + float *pixel = pixels + (y * sx + x) * 4; + Color col = texture->getPixel(x, y); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + + float *old_pixels = value_texture_data; + value_texture_data = pixels; + delete[] old_pixels; + + texture_size_x = sx; + texture_size_y = sy; + texture_size_z = 0; + + texture_toupload = true; + texture_repeat = repeat; + texture_color = color; +} + +void OpenGLVariable::set(const QImage &texture, bool repeat, bool color) +{ + assert(type == TYPE_NONE or type == TYPE_TEXTURE_2D); + + type = TYPE_TEXTURE_2D; + + int sx = texture.width(), sy = texture.height(); + float *pixels = new float[sx * sy * 4]; + for (int x = 0; x < sx; x++) { + for (int y = 0; y < sy; y++) { + float *pixel = pixels + (y * sx + x) * 4; + Color col = Color::from32BitRGBA(texture.pixel(x, y)); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + + float *old_pixels = value_texture_data; + value_texture_data = pixels; + delete[] old_pixels; + + texture_size_x = sx; + texture_size_y = sy; + texture_size_z = 0; + texture_toupload = true; texture_repeat = repeat; texture_color = color; @@ -71,8 +146,31 @@ void OpenGLVariable::set(const Texture2D *texture, bool repeat, bool color) { void OpenGLVariable::set(const Texture3D *texture, bool repeat, bool color) { assert(type == TYPE_NONE or type == TYPE_TEXTURE_3D); + int sx, sy, sz; + texture->getSize(&sx, &sy, &sz); + float *pixels = new float[sx * sy * sz * 4]; + for (int x = 0; x < sx; x++) { + for (int y = 0; y < sy; y++) { + for (int z = 0; z < sz; z++) { + float *pixel = pixels + (z * (sx * sy) + y * sx + x) * 4; + Color col = texture->getPixel(x, y, z); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + } + + float *old_pixels = value_texture_data; + value_texture_data = pixels; + delete[] old_pixels; + + texture_size_x = sx; + texture_size_y = sy; + texture_size_z = sz; + type = TYPE_TEXTURE_3D; - value_tex3d = texture; texture_toupload = true; texture_repeat = repeat; texture_color = color; @@ -81,8 +179,33 @@ void OpenGLVariable::set(const Texture3D *texture, bool repeat, bool color) { void OpenGLVariable::set(const Texture4D *texture, bool repeat, bool color) { assert(type == TYPE_NONE or type == TYPE_TEXTURE_4D); + int sx, sy, sz, sw; + texture->getSize(&sx, &sy, &sz, &sw); + float *pixels = new float[sx * sy * sz * sw * 4]; + for (int x = 0; x < sx; x++) { + for (int y = 0; y < sy; y++) { + for (int z = 0; z < sz; z++) { + for (int w = 0; w < sw; w++) { + float *pixel = pixels + (w * (sx * sy * sz) + z * (sx * sy) + y * sx + x) * 4; + Color col = texture->getPixel(x, y, z, w); + pixel[0] = (float)col.r; + pixel[1] = (float)col.g; + pixel[2] = (float)col.b; + pixel[3] = (float)col.a; + } + } + } + } + + float *old_pixels = value_texture_data; + value_texture_data = pixels; + delete[] old_pixels; + + texture_size_x = sx; + texture_size_y = sy; + texture_size_z = sz * sw; + type = TYPE_TEXTURE_4D; - value_tex4d = texture; texture_toupload = true; texture_repeat = repeat; texture_color = color; @@ -103,7 +226,7 @@ void OpenGLVariable::set(const QVector3D &vector) { assert(type == TYPE_NONE or type == TYPE_VECTOR3); type = TYPE_VECTOR3; - value_vector3 = vector; + *value_vector3 = vector; } void OpenGLVariable::set(const Matrix4 &matrix) { @@ -114,14 +237,14 @@ void OpenGLVariable::set(const QMatrix4x4 &matrix) { assert(type == TYPE_NONE or type == TYPE_MATRIX4); type = TYPE_MATRIX4; - value_matrix4 = matrix; + *value_matrix4 = matrix; } void OpenGLVariable::set(const Color &color) { assert(type == TYPE_NONE or type == TYPE_COLOR); type = TYPE_COLOR; - value_color = QColor::fromRgbF(color.r, color.g, color.b); + *value_color = QColor::fromRgbF(color.r, color.g, color.b); } void OpenGLVariable::uploadTexture(OpenGLRenderer *renderer) { @@ -149,61 +272,8 @@ void OpenGLVariable::uploadTexture(OpenGLRenderer *renderer) { int dest_format = texture_color ? GL_RGBA : GL_RED; if (type == TYPE_TEXTURE_2D) { - int sx, sy; - value_tex2d->getSize(&sx, &sy); - float *pixels = new float[sx * sy * 4]; - for (int x = 0; x < sx; x++) { - for (int y = 0; y < sy; y++) { - float *pixel = pixels + (y * sx + x) * 4; - Color col = value_tex2d->getPixel(x, y); - pixel[0] = (float)col.r; - pixel[1] = (float)col.g; - pixel[2] = (float)col.b; - pixel[3] = (float)col.a; - } - } - - functions->glTexImage2D(GL_TEXTURE_2D, 0, dest_format, sx, sy, 0, GL_RGBA, GL_FLOAT, pixels); - delete[] pixels; - } else if (type == TYPE_TEXTURE_3D) { - int sx, sy, sz; - value_tex3d->getSize(&sx, &sy, &sz); - float *pixels = new float[sx * sy * sz * 4]; - for (int x = 0; x < sx; x++) { - for (int y = 0; y < sy; y++) { - for (int z = 0; z < sz; z++) { - float *pixel = pixels + (z * (sx * sy) + y * sx + x) * 4; - Color col = value_tex3d->getPixel(x, y, z); - pixel[0] = (float)col.r; - pixel[1] = (float)col.g; - pixel[2] = (float)col.b; - pixel[3] = (float)col.a; - } - } - } - - functions->glTexImage3D(GL_TEXTURE_3D, 0, dest_format, sx, sy, sz, 0, GL_RGBA, GL_FLOAT, pixels); - delete[] pixels; + functions->glTexImage2D(GL_TEXTURE_2D, 0, dest_format, texture_size_x, texture_size_y, 0, GL_RGBA, GL_FLOAT, value_texture_data); } else { - int sx, sy, sz, sw; - value_tex4d->getSize(&sx, &sy, &sz, &sw); - float *pixels = new float[sx * sy * sz * sw * 4]; - for (int x = 0; x < sx; x++) { - for (int y = 0; y < sy; y++) { - for (int z = 0; z < sz; z++) { - for (int w = 0; w < sw; w++) { - float *pixel = pixels + (w * (sx * sy * sz) + z * (sx * sy) + y * sx + x) * 4; - Color col = value_tex4d->getPixel(x, y, z, w); - pixel[0] = (float)col.r; - pixel[1] = (float)col.g; - pixel[2] = (float)col.b; - pixel[3] = (float)col.a; - } - } - } - } - - functions->glTexImage3D(GL_TEXTURE_3D, 0, dest_format, sx, sy, sz * sw, 0, GL_RGBA, GL_FLOAT, pixels); - delete[] pixels; + functions->glTexImage3D(GL_TEXTURE_3D, 0, dest_format, texture_size_x, texture_size_y, texture_size_z, 0, GL_RGBA, GL_FLOAT, value_texture_data); } } diff --git a/src/render/opengl/OpenGLVariable.h b/src/render/opengl/OpenGLVariable.h index 5da2869..6d977a9 100644 --- a/src/render/opengl/OpenGLVariable.h +++ b/src/render/opengl/OpenGLVariable.h @@ -3,9 +3,10 @@ #include "opengl_global.h" -#include -#include -#include +class QColor; +class QVector3D; +class QMatrix4x4; +class QImage; namespace paysages { namespace opengl { @@ -28,10 +29,12 @@ class OpenGLVariable { public: OpenGLVariable(const std::string &name); + ~OpenGLVariable(); void apply(OpenGLShaderProgram *program, int &texture_unit); void set(const Texture2D *texture, bool repeat = false, bool color = true); + void set(const QImage &texture, bool repeat = false, bool color = true); void set(const Texture3D *texture, bool repeat = false, bool color = true); void set(const Texture4D *texture, bool repeat = false, bool color = true); void set(float value); @@ -49,13 +52,14 @@ class OpenGLVariable { OpenGLVariableType type; float value_float; - QColor value_color; - QVector3D value_vector3; - QMatrix4x4 value_matrix4; - const Texture2D *value_tex2d; - const Texture3D *value_tex3d; - const Texture4D *value_tex4d; + QColor *value_color; + QVector3D *value_vector3; + QMatrix4x4 *value_matrix4; + float *value_texture_data; + int texture_size_x; + int texture_size_y; + int texture_size_z; bool texture_toupload; bool texture_repeat; bool texture_color; diff --git a/src/render/opengl/OpenGLVertexArray.cpp b/src/render/opengl/OpenGLVertexArray.cpp new file mode 100644 index 0000000..8d6ad34 --- /dev/null +++ b/src/render/opengl/OpenGLVertexArray.cpp @@ -0,0 +1,121 @@ +#include "OpenGLVertexArray.h" + +#include "OpenGLFunctions.h" +#include "Logs.h" +#include "Vector3.h" + +OpenGLVertexArray::OpenGLVertexArray(bool has_uv, bool strip) : has_uv(has_uv) { + if (strip) { + draw_mode = GL_TRIANGLE_STRIP; + } else { + draw_mode = GL_TRIANGLES; + } + + vao = 0; + vbo_vertex = 0; + vbo_uv = 0; + + changed = false; + vertexcount = 0; + array_vertex = (float *)malloc(sizeof(float)); + array_uv = (float *)malloc(sizeof(float)); +} + +OpenGLVertexArray::~OpenGLVertexArray() { + if (vao || vbo_vertex || vbo_uv) { + Logs::warning() << "[OpenGL] VertexArray not freed in OpenGL state before destructor called" << std::endl; + } + + free(array_vertex); + free(array_uv); +} + +void OpenGLVertexArray::destroy() { + // TODO +} + +void OpenGLVertexArray::render(OpenGLFunctions *functions) { + if (changed) { + changed = false; + update(functions); + } + + if (vertexcount and vao) { + functions->glBindVertexArray(vao); + functions->glDrawArrays(draw_mode, 0, vertexcount); + functions->glBindVertexArray(0); + } +} + +void OpenGLVertexArray::setVertexCount(int count) { + if (count != vertexcount) { + vertexcount = count; + if (count < 1) { + count = 1; + } + array_vertex = (float *)realloc(array_vertex, sizeof(float) * count * 3); + array_uv = (float *)realloc(array_uv, sizeof(float) * count * 2); + changed = true; + } +} + +void OpenGLVertexArray::set(int index, const Vector3 &location, double u, double v) { + if (index >= 0 and index < vertexcount) { + array_vertex[index * 3] = location.x; + array_vertex[index * 3 + 1] = location.y; + array_vertex[index * 3 + 2] = location.z; + array_uv[index * 2] = u; + array_uv[index * 2 + 1] = v; + changed = true; + } else { + Logs::error() << "[OpenGL] Setting vertex data outside of array bounds" << std::endl; + } +} + +void OpenGLVertexArray::get(int index, Vector3 *location, double *u, double *v) const +{ + if (index >= 0 and index < vertexcount) { + location->x = array_vertex[index * 3]; + location->y = array_vertex[index * 3 + 1]; + location->z = array_vertex[index * 3 + 2]; + *u = array_uv[index * 2]; + *v = array_uv[index * 2 + 1]; + } else { + Logs::error() << "[OpenGL] Getting vertex data outside of array bounds" << std::endl; + } +} + +void OpenGLVertexArray::copyTo(OpenGLVertexArray *destination) const +{ + destination->setVertexCount(vertexcount); + if (vertexcount) { + memcpy(destination->array_vertex, array_vertex, sizeof(float) * vertexcount * 3); + memcpy(destination->array_uv, array_uv, sizeof(float) * vertexcount * 2); + } + destination->changed = true; +} + +void OpenGLVertexArray::update(OpenGLFunctions *functions) { + if (not vao) { + functions->glGenVertexArrays(1, &vao); + } + functions->glBindVertexArray(vao); + + if (not vbo_vertex) { + functions->glGenBuffers(1, &vbo_vertex); + } + functions->glBindBuffer(GL_ARRAY_BUFFER, vbo_vertex); + functions->glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexcount * 3, array_vertex, GL_STATIC_DRAW); + functions->glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); + functions->glEnableVertexAttribArray(0); + + if (not vbo_uv) { + functions->glGenBuffers(1, &vbo_uv); + } + functions->glBindBuffer(GL_ARRAY_BUFFER, vbo_uv); + functions->glBufferData(GL_ARRAY_BUFFER, sizeof(float) * vertexcount * 2, array_uv, GL_STATIC_DRAW); + functions->glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); + functions->glEnableVertexAttribArray(1); + + functions->glBindVertexArray(0); +} diff --git a/src/render/opengl/OpenGLVertexArray.h b/src/render/opengl/OpenGLVertexArray.h new file mode 100644 index 0000000..fe9ea9b --- /dev/null +++ b/src/render/opengl/OpenGLVertexArray.h @@ -0,0 +1,94 @@ +#ifndef OPENGLVERTEXARRAY_H +#define OPENGLVERTEXARRAY_H + +#include "opengl_global.h" + +namespace paysages { +namespace opengl { + +/** + * Vertex arrays storage and binding, to render triangles. + * + * This will handle VAOs and VBOs automatically. + */ +class OpenGLVertexArray { + public: + OpenGLVertexArray(bool has_uv, bool strip = false); + ~OpenGLVertexArray(); + + inline int getVertexCount() const { + return vertexcount; + } + + /** + * Release any allocated resource in the opengl context. + * + * Must be called in the opengl rendering thread, and before the destructor is called. + */ + void destroy(); + + /** + * Render this array in current opengl context. + * + * Must be called in the opengl rendering thread. + * + * A shader program must be bound (and uniforms defined) when calling this. + */ + void render(OpenGLFunctions *functions); + + /** + * Set the vertex total count. + */ + void setVertexCount(int count); + + /** + * Set vertex data in the array. + * + * setVertexCount must have been called before to make room for this vertex. + */ + void set(int index, const Vector3 &location, double u = 0.0, double v = 0.0); + + /** + * Retrieve vertex data in the array. + * + * This is not optimized, only use for testing. + */ + void get(int index, Vector3 *location, double *u, double *v) const; + + /** + * Copy this vertex array to another. + * + * This does not check it the arrays have the same config, but they certainly should. + */ + void copyTo(OpenGLVertexArray *destination) const; + + private: + /** + * Update the opengl state. + * + * Should only be called when the data changed. + * + * Must be called in the opengl rendering thread. + */ + void update(OpenGLFunctions *functions); + + private: + // Config + bool has_uv; + int draw_mode; + + // OpenGL IDs + unsigned int vao; + unsigned int vbo_vertex; + unsigned int vbo_uv; + + // Data + bool changed; + int vertexcount; + float *array_vertex; + float *array_uv; +}; +} +} + +#endif // OPENGLVERTEXARRAY_H diff --git a/src/render/opengl/OpenGLWater.cpp b/src/render/opengl/OpenGLWater.cpp index 257c432..aa394fa 100644 --- a/src/render/opengl/OpenGLWater.cpp +++ b/src/render/opengl/OpenGLWater.cpp @@ -3,6 +3,7 @@ #include "OpenGLRenderer.h" #include "OpenGLShaderProgram.h" #include "OpenGLSharedState.h" +#include "OpenGLVertexArray.h" #include "WaterRenderer.h" #include "Scenery.h" #include "WaterDefinition.h" @@ -13,15 +14,8 @@ #include "IntNode.h" OpenGLWater::OpenGLWater(OpenGLRenderer *renderer) : OpenGLPart(renderer) { - vertices = new float[4 * 3]; enabled = true; -} -OpenGLWater::~OpenGLWater() { - delete[] vertices; -} - -void OpenGLWater::initialize() { program = createShader("water"); program->addVertexSource("water"); program->addFragmentSource("atmosphere"); @@ -31,11 +25,18 @@ void OpenGLWater::initialize() { program->addFragmentSource("noise"); program->addFragmentSource("water"); - setVertex(0, -1.0f, 0.0f, -1.0f); - setVertex(1, -1.0f, 0.0f, 1.0f); - setVertex(2, 1.0f, 0.0f, -1.0f); - setVertex(3, 1.0f, 0.0f, 1.0f); + vertices = createVertexArray(false, true); + vertices->setVertexCount(4); + vertices->set(0, Vector3(-1.0f, 0.0f, -1.0f)); + vertices->set(1, Vector3(-1.0f, 0.0f, 1.0f)); + vertices->set(2, Vector3(1.0f, 0.0f, -1.0f)); + vertices->set(3, Vector3(1.0f, 0.0f, 1.0f)); +} +OpenGLWater::~OpenGLWater() { +} + +void OpenGLWater::initialize() { // Watch for definition changes renderer->getScenery()->getTerrain()->propWaterHeight()->addWatcher(this, true); renderer->getScenery()->getWater()->propReflection()->addWatcher(this, true); @@ -54,16 +55,10 @@ void OpenGLWater::update() { void OpenGLWater::render() { if (enabled) { - program->drawTriangleStrip(vertices, 4); + program->draw(vertices); } } -void OpenGLWater::setVertex(int i, float x, float y, float z) { - vertices[i * 3] = x; - vertices[i * 3 + 1] = y; - vertices[i * 3 + 2] = z; -} - void OpenGLWater::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) { if (node->getPath() == "/terrain/water_height") { renderer->getSharedState()->set("waterOffset", renderer->getScenery()->getTerrain()->getWaterOffset()); diff --git a/src/render/opengl/OpenGLWater.h b/src/render/opengl/OpenGLWater.h index ed968ad..6c9a1bf 100644 --- a/src/render/opengl/OpenGLWater.h +++ b/src/render/opengl/OpenGLWater.h @@ -26,11 +26,9 @@ class OPENGLSHARED_EXPORT OpenGLWater : public OpenGLPart, public DefinitionWatc void setEnabled(bool enabled); private: - void setVertex(int i, float x, float y, float z); - bool enabled; OpenGLShaderProgram *program; - float *vertices; + OpenGLVertexArray *vertices; }; } } diff --git a/src/render/opengl/VertexArray.h b/src/render/opengl/VertexArray.h deleted file mode 100644 index cecd713..0000000 --- a/src/render/opengl/VertexArray.h +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef VERTEXARRAY_H -#define VERTEXARRAY_H - -#include "opengl_global.h" - -#include -#include -#include "OpenGLFunctions.h" - -namespace paysages { -namespace opengl { - -/*! - * Wrapper for OpenGL vertex arrays. - */ -template class VertexArray { - public: - VertexArray() { - ready = false; - changed = false; - vertex_count = 1; - vertices = new Vertex[1]; - index_count = 1; - indices = new unsigned short[1]; - } - - ~VertexArray() { - delete[] vertices; - delete[] indices; - } - - inline int getVertexCount() { - return vertex_count; - } - inline int getIndexCount() { - return index_count; - } - inline bool isReady() { - return ready; - } - inline bool isChanged() { - return changed; - } - - void setVertexCount(int count) { - assert(count > 0 and count <= 16384); - - delete[] vertices; - vertices = new Vertex[count]; - - vertex_count = count; - } - - void setGridSize(int edge_vertex_count) { - assert(edge_vertex_count >= 2); - - setVertexCount(edge_vertex_count * edge_vertex_count); - } - - void setVertex(int position, const Vertex &vertex) { - assert(position >= 0 and position < vertex_count); - - vertices[position] = vertex; - - changed = true; - } - - void setGridVertex(int edge_vertex_count, int x, int y, const Vertex &vertex) { - setVertex(y * edge_vertex_count + x, vertex); - } - - void setAutoGridIndices(int edge_vertex_count, int stride = 1) { - assert(stride >= 1); - - delete[] indices; - int cell_count = edge_vertex_count - 1; - - index_count = (cell_count / stride) * (cell_count / stride) * 6; - indices = new unsigned short[index_count]; - - int idx = 0; - for (int y = 0; y < cell_count; y += stride) { - for (int x = 0; x < cell_count; x += stride) { - int base = y * edge_vertex_count + x; - indices[idx++] = base; - indices[idx++] = base + edge_vertex_count * stride; - indices[idx++] = base + stride; - indices[idx++] = base + stride; - indices[idx++] = base + edge_vertex_count * stride; - indices[idx++] = base + edge_vertex_count * stride + stride; - } - } - } - - Vertex getVertex(int position) { - assert(position >= 0 and position < vertex_count); - - return vertices[position]; - } - - Vertex getVertexByIndex(unsigned short index) { - assert(index >= 0 and index < index_count); - - return getVertex(indices[index]); - } - - Vertex getGridVertex(int edge_vertex_count, int x, int y) { - return getVertex(y * edge_vertex_count + x); - } - - unsigned short getIndex(int position) { - assert(position >= 0 and position < index_count); - - return indices[position]; - } - - void render(QOpenGLShaderProgram *program, OpenGLFunctions *functions) { - size_t ptr = (size_t)vertices; - - GLuint vertex = program->attributeLocation("vertex"); - program->setAttributeArray(vertex, GL_FLOAT, (void *)(ptr + offsetof(Vertex, location)), 3, sizeof(Vertex)); - program->enableAttributeArray(vertex); - - GLuint uv = program->attributeLocation("uv"); - program->setAttributeArray(uv, GL_FLOAT, (void *)(ptr + offsetof(Vertex, uv)), 2, sizeof(Vertex)); - program->enableAttributeArray(uv); - - functions->glDrawRangeElements(GL_TRIANGLES, 0, vertex_count - 1, index_count, GL_UNSIGNED_SHORT, indices); - - program->disableAttributeArray(vertex); - program->disableAttributeArray(uv); - } - - private: - bool ready; - bool changed; - - int vertex_count; - Vertex *vertices; - - int index_count; - unsigned short *indices; -}; -} -} - -#endif // VERTEXARRAY_H diff --git a/src/render/opengl/opengl_global.h b/src/render/opengl/opengl_global.h index 3c33a24..1acd4d8 100644 --- a/src/render/opengl/opengl_global.h +++ b/src/render/opengl/opengl_global.h @@ -16,17 +16,20 @@ class OpenGLRenderer; class OpenGLShaderProgram; class OpenGLSharedState; class OpenGLVariable; +class OpenGLVertexArray; class OpenGLSkybox; class OpenGLWater; class OpenGLTerrain; -class ExplorerChunkTerrain; +class OpenGLTerrainChunk; template class VertexArray; } } using namespace paysages::opengl; -#define OpenGLFunctions QOpenGLFunctions_3_0 -//#define OpenGLFunctions QOpenGLFunctions_3_3_Core +#define OpenGLFunctions QOpenGLFunctions_3_3_Core +#define OPENGL_GLSL_VERSION "330 core" +#define OPENGL_MAJOR_VERSION 3 +#define OPENGL_MINOR_VERSION 3 class OpenGLFunctions; #endif // OPENGL_GLOBAL_H diff --git a/src/render/opengl/shaders/atmosphere.frag b/src/render/opengl/shaders/atmosphere.frag index 1fca38f..be927dc 100644 --- a/src/render/opengl/shaders/atmosphere.frag +++ b/src/render/opengl/shaders/atmosphere.frag @@ -29,7 +29,7 @@ uniform vec4 sunColor; uniform float dayTime; uniform float sunRadius; -varying vec3 unprojected; +in vec3 unprojected; uniform sampler2D transmittanceTexture; uniform sampler3D inscatterTexture; @@ -48,8 +48,8 @@ vec4 texture4D(sampler3D tex, float r, float mu, float muS, float nu) float sr = 1.0 / float(RES_R); int br = int(floor(uR / sr)); - vec4 r1 = texture3D(tex, vec3(uMu, uMuS, float(br) * sr + nu * sr)); - vec4 r2 = texture3D(tex, vec3(uMu, uMuS, float(br + 1) * sr + nu * sr)); + vec4 r1 = texture(tex, vec3(uMu, uMuS, float(br) * sr + nu * sr)); + vec4 r2 = texture(tex, vec3(uMu, uMuS, float(br + 1) * sr + nu * sr)); return mix(r1, r2, (uR - float(br) * sr) / sr); } @@ -77,7 +77,7 @@ vec2 _getTransmittanceUV(float r, float mu) vec4 _transmittance(float r, float mu) { vec2 uv = _getTransmittanceUV(r, mu); - return texture2D(transmittanceTexture, uv); + return texture(transmittanceTexture, uv); } vec4 _transmittanceWithShadow(float r, float mu) diff --git a/src/render/opengl/shaders/skybox.frag b/src/render/opengl/shaders/skybox.frag index d2add99..fac1b38 100644 --- a/src/render/opengl/shaders/skybox.frag +++ b/src/render/opengl/shaders/skybox.frag @@ -1,5 +1,7 @@ +out vec4 final_color; + void main(void) { - gl_FragColor = getSkyColor(cameraLocation, unprojected - cameraLocation); - gl_FragColor = applyToneMapping(gl_FragColor); + final_color = getSkyColor(cameraLocation, unprojected - cameraLocation); + final_color = applyToneMapping(final_color); } diff --git a/src/render/opengl/shaders/skybox.vert b/src/render/opengl/shaders/skybox.vert index ff142a5..6a14289 100644 --- a/src/render/opengl/shaders/skybox.vert +++ b/src/render/opengl/shaders/skybox.vert @@ -1,7 +1,7 @@ -attribute highp vec4 vertex; +in highp vec4 vertex; uniform highp mat4 viewMatrix; uniform vec3 cameraLocation; -varying vec3 unprojected; +out vec3 unprojected; void main(void) { diff --git a/src/render/opengl/shaders/terrain.frag b/src/render/opengl/shaders/terrain.frag index 5343e7f..470321d 100644 --- a/src/render/opengl/shaders/terrain.frag +++ b/src/render/opengl/shaders/terrain.frag @@ -1,15 +1,16 @@ uniform sampler2D groundTexture; -varying vec2 texcoord; +in vec2 texcoord; +out vec4 final_color; void main(void) { - gl_FragColor = texture2D(groundTexture, texcoord); + final_color = texture(groundTexture, texcoord); - gl_FragColor = applyAerialPerspective(gl_FragColor); + final_color = applyAerialPerspective(final_color); - gl_FragColor = applyToneMapping(gl_FragColor); + final_color = applyToneMapping(final_color); - gl_FragColor = applyMouseTracking(unprojected, gl_FragColor); + final_color = applyMouseTracking(unprojected, final_color); - gl_FragColor.a = distanceFadeout(); + final_color.a = distanceFadeout(); } diff --git a/src/render/opengl/shaders/terrain.vert b/src/render/opengl/shaders/terrain.vert index 697e1e0..d684555 100644 --- a/src/render/opengl/shaders/terrain.vert +++ b/src/render/opengl/shaders/terrain.vert @@ -1,8 +1,8 @@ -attribute highp vec4 vertex; -attribute highp vec2 uv; +in highp vec4 vertex; +in highp vec2 uv; uniform highp mat4 viewMatrix; -varying vec3 unprojected; -varying vec2 texcoord; +out vec3 unprojected; +out vec2 texcoord; uniform float waterOffset; void main(void) diff --git a/src/render/opengl/shaders/water.frag b/src/render/opengl/shaders/water.frag index a20d106..b2c9e88 100644 --- a/src/render/opengl/shaders/water.frag +++ b/src/render/opengl/shaders/water.frag @@ -3,22 +3,23 @@ uniform float waterMaterialReflection; uniform float waterMaterialShininess; uniform float waterMaterialHardness; uniform float waterReflection; +out vec4 final_color; void main(void) { vec3 normal = noiseNormal2d(unprojected.xz, 0.001); - gl_FragColor = applyLighting(unprojected, normal, waterMaterialColor, waterMaterialReflection, waterMaterialShininess, waterMaterialHardness); + final_color = applyLighting(unprojected, normal, waterMaterialColor, waterMaterialReflection, waterMaterialShininess, waterMaterialHardness); vec3 reflected = reflect(unprojected - cameraLocation, normal); reflected.y = max(reflected.y, 0.0); - gl_FragColor += getSkyColor(unprojected, reflected) * waterReflection; + final_color += getSkyColor(unprojected, reflected) * waterReflection; - gl_FragColor = applyAerialPerspective(gl_FragColor); + final_color = applyAerialPerspective(final_color); - gl_FragColor = applyToneMapping(gl_FragColor); + final_color = applyToneMapping(final_color); - gl_FragColor = applyMouseTracking(unprojected, gl_FragColor); + final_color = applyMouseTracking(unprojected, final_color); - gl_FragColor.a = distanceFadeout(); + final_color.a = distanceFadeout(); } diff --git a/src/render/opengl/shaders/water.vert b/src/render/opengl/shaders/water.vert index fba4e1b..3001368 100644 --- a/src/render/opengl/shaders/water.vert +++ b/src/render/opengl/shaders/water.vert @@ -1,8 +1,8 @@ -attribute highp vec4 vertex; +in highp vec4 vertex; uniform highp mat4 viewMatrix; uniform float waterHeight; uniform vec3 cameraLocation; -varying vec3 unprojected; +out vec3 unprojected; void main(void) { diff --git a/src/tests/OpenGLTerrainChunk_Test.cpp b/src/tests/OpenGLTerrainChunk_Test.cpp new file mode 100644 index 0000000..81f12dc --- /dev/null +++ b/src/tests/OpenGLTerrainChunk_Test.cpp @@ -0,0 +1,68 @@ +#include "BaseTestCase.h" +#include "OpenGLTerrainChunk.h" + +#include "Scenery.h" +#include "OpenGLRenderer.h" +#include "OpenGLVertexArray.h" +#include "Vector3.h" + +static void checkVertex(const OpenGLVertexArray *array, int index, const Vector3 &expected_location, double expected_u, double expected_v) { + Vector3 location; + double u, v; + + array->get(index, &location, &u, &v); + + EXPECT_VECTOR3_COORDS(location, expected_location.x, expected_location.y, expected_location.z); + EXPECT_DOUBLE_EQ(expected_u, u); + EXPECT_DOUBLE_EQ(expected_v, v); +} + +TEST(OpenGLTerrainChunk, setFirstStepVertices) { + Scenery scenery; + OpenGLRenderer renderer(&scenery); + OpenGLTerrainChunk chunk(&renderer, 0.0, 0.0, 1.0, 1); + + ASSERT_EQ(0, chunk.getVerticesLevel()); + + chunk.setFirstStepVertices(); + + EXPECT_EQ(1, chunk.getVerticesLevel()); + ASSERT_EQ(6, chunk.getVertices()->getVertexCount()); + const OpenGLVertexArray *array = chunk.getVertices(); + checkVertex(array, 0, Vector3(0.0, 0.0, 0.0), 0.0, 0.0); + checkVertex(array, 1, Vector3(0.0, 0.0, 1.0), 0.0, 1.0); + checkVertex(array, 2, Vector3(1.0, 0.0, 0.0), 1.0, 0.0); + checkVertex(array, 3, Vector3(1.0, 0.0, 1.0), 1.0, 1.0); + checkVertex(array, 4, Vector3(1.0, 0.0, 0.0), 1.0, 0.0); + checkVertex(array, 5, Vector3(0.0, 0.0, 1.0), 0.0, 1.0); +} + +TEST(OpenGLTerrainChunk, augmentVertices) { + Scenery scenery; + OpenGLRenderer renderer(&scenery); + OpenGLTerrainChunk chunk(&renderer, 0.0, 0.0, 1.0, 1); + + ASSERT_EQ(0, chunk.getVerticesLevel()); + + chunk.setFirstStepVertices(); + + ASSERT_EQ(1, chunk.getVerticesLevel()); + + chunk.augmentVertices(); + + ASSERT_EQ(2, chunk.getVerticesLevel()); + EXPECT_EQ(24, chunk.getVertices()->getVertexCount()); + const OpenGLVertexArray *array = chunk.getVertices(); + checkVertex(array, 0, Vector3(0.0, 0.0, 0.0), 0.0, 0.0); + checkVertex(array, 1, Vector3(0.0, 0.0, 0.5), 0.0, 0.5); + checkVertex(array, 2, Vector3(0.5, 0.0, 0.0), 0.5, 0.0); + checkVertex(array, 3, Vector3(0.5, 0.0, 0.5), 0.5, 0.5); + checkVertex(array, 4, Vector3(0.5, 0.0, 0.0), 0.5, 0.0); + checkVertex(array, 5, Vector3(0.0, 0.0, 0.5), 0.0, 0.5); + checkVertex(array, 6, Vector3(0.5, 0.0, 0.0), 0.5, 0.0); + checkVertex(array, 7, Vector3(0.5, 0.0, 0.5), 0.5, 0.5); + checkVertex(array, 8, Vector3(1.0, 0.0, 0.0), 1.0, 0.0); + checkVertex(array, 9, Vector3(1.0, 0.0, 0.5), 1.0, 0.5); + checkVertex(array, 10, Vector3(1.0, 0.0, 0.0), 1.0, 0.0); + checkVertex(array, 11, Vector3(0.5, 0.0, 0.5), 0.5, 0.5); +} diff --git a/src/tests/VertexArray_Test.cpp b/src/tests/VertexArray_Test.cpp deleted file mode 100644 index ba061d4..0000000 --- a/src/tests/VertexArray_Test.cpp +++ /dev/null @@ -1,92 +0,0 @@ -#include "BaseTestCase.h" - -#include "VertexArray.h" - -class TestVertex { - public: - float uv[2]; - int loc[3]; - - bool operator==(const TestVertex &other) const { - return other.uv[0] == uv[0] and other.uv[1] == uv[1] and other.loc[0] == loc[0] and other.loc[1] == loc[1] and - other.loc[2] == loc[2]; - } -}; - -TEST(VertexArray, grid) { - VertexArray array; - - array.setGridSize(3); - - ASSERT_EQ(9, array.getVertexCount()); - - TestVertex v1 = {{0.1, 0.2}, {1, 2, 3}}; - TestVertex vgot; - - array.setGridVertex(3, 1, 2, v1); - vgot = array.getGridVertex(3, 1, 2); - EXPECT_EQ(v1, vgot); - vgot = array.getVertex(7); - EXPECT_EQ(v1, vgot); -} - -TEST(VertexArray, gridIndices) { - VertexArray array; - - array.setGridSize(3); - - array.setAutoGridIndices(3); - ASSERT_EQ(24, array.getIndexCount()); - - EXPECT_EQ(0, array.getIndex(0)); - EXPECT_EQ(3, array.getIndex(1)); - EXPECT_EQ(1, array.getIndex(2)); - - EXPECT_EQ(1, array.getIndex(3)); - EXPECT_EQ(3, array.getIndex(4)); - EXPECT_EQ(4, array.getIndex(5)); - - EXPECT_EQ(1, array.getIndex(6)); - EXPECT_EQ(4, array.getIndex(7)); - EXPECT_EQ(2, array.getIndex(8)); - - EXPECT_EQ(2, array.getIndex(9)); - EXPECT_EQ(4, array.getIndex(10)); - EXPECT_EQ(5, array.getIndex(11)); - - EXPECT_EQ(3, array.getIndex(12)); - EXPECT_EQ(6, array.getIndex(13)); - EXPECT_EQ(4, array.getIndex(14)); -} - -TEST(VertexArray, gridIndicesStride) { - VertexArray array; - - array.setGridSize(5); - - array.setAutoGridIndices(5, 1); - ASSERT_EQ(96, array.getIndexCount()); - - array.setAutoGridIndices(5, 2); - ASSERT_EQ(24, array.getIndexCount()); - - EXPECT_EQ(0, array.getIndex(0)); - EXPECT_EQ(10, array.getIndex(1)); - EXPECT_EQ(2, array.getIndex(2)); - - EXPECT_EQ(2, array.getIndex(3)); - EXPECT_EQ(10, array.getIndex(4)); - EXPECT_EQ(12, array.getIndex(5)); - - EXPECT_EQ(2, array.getIndex(6)); - EXPECT_EQ(12, array.getIndex(7)); - EXPECT_EQ(4, array.getIndex(8)); - - EXPECT_EQ(4, array.getIndex(9)); - EXPECT_EQ(12, array.getIndex(10)); - EXPECT_EQ(14, array.getIndex(11)); - - EXPECT_EQ(10, array.getIndex(12)); - EXPECT_EQ(20, array.getIndex(13)); - EXPECT_EQ(12, array.getIndex(14)); -}