Merge branch 'master' into vegetation

Conflicts:
	src/render/opengl/OpenGLShaderProgram.cpp
	src/render/opengl/OpenGLShaderProgram.h
	src/render/opengl/opengl_global.h
This commit is contained in:
Michaël Lemaire 2015-12-08 01:28:15 +01:00
commit 9196be4c05
40 changed files with 993 additions and 931 deletions

View file

@ -64,7 +64,7 @@ profile_cli:build
gltrace:build gltrace:build
rm -f *.trace 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 qapitrace paysages-modeler.trace
package:build package:build

3
TODO
View file

@ -5,7 +5,8 @@ Technlology Preview 2 :
- Add clouds to OpenGL with 3d textures. - Add clouds to OpenGL with 3d textures.
- Refactor medium traversal to unify clouds, atmosphere and god rays. - Refactor medium traversal to unify clouds, atmosphere and god rays.
- Fix potential holes in land rendering (OpenGL and software). - 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 : Technology Preview 3 :
- Alter aerial perspective using estimation of the amount of light left after cloud layers traversal. - Alter aerial perspective using estimation of the amount of light left after cloud layers traversal.

View file

@ -16,10 +16,10 @@
#include <QGuiApplication> #include <QGuiApplication>
MainModelerWindow::MainModelerWindow() { MainModelerWindow::MainModelerWindow() {
/*QSurfaceFormat new_format = format(); QSurfaceFormat new_format = format();
new_format.setVersion(3, 3); new_format.setVersion(OPENGL_MAJOR_VERSION, OPENGL_MINOR_VERSION);
new_format.setProfile(QSurfaceFormat::CoreProfile); new_format.setProfile(QSurfaceFormat::CoreProfile);
setFormat(new_format);*/ setFormat(new_format);
scenery = new Scenery(); scenery = new Scenery();
scenery->autoPreset(); scenery->autoPreset();

View file

@ -52,8 +52,8 @@ void OpenGLView::paint() {
resized = false; resized = false;
} }
renderer->prepareOpenGLState(); renderer->prepareOpenGLState(false);
renderer->paint(); renderer->paint(false);
if (window) { if (window) {
window->resetOpenGLState(); window->resetOpenGLState();

View file

@ -1,296 +0,0 @@
#include "ExplorerChunkTerrain.h"
#include <cmath>
#include <QImage>
#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<TerrainVertex>();
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;
}

View file

@ -1,74 +0,0 @@
#ifndef EXPLORERCHUNKTERRAIN_H
#define EXPLORERCHUNKTERRAIN_H
#include "opengl_global.h"
#include <QMutex>
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<TerrainVertex> *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

View file

@ -1,32 +1,28 @@
#include "OpenGLPart.h" #include "OpenGLPart.h"
#include <QDir>
#include <cmath>
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include "CameraDefinition.h" #include "OpenGLVertexArray.h"
#include "AtmosphereDefinition.h"
#include "AtmosphereRenderer.h"
#include "Scenery.h"
OpenGLPart::OpenGLPart(OpenGLRenderer *renderer) : renderer(renderer) { OpenGLPart::OpenGLPart(OpenGLRenderer *renderer) : renderer(renderer) {
} }
OpenGLPart::~OpenGLPart() { OpenGLPart::~OpenGLPart() {
QMapIterator<QString, OpenGLShaderProgram *> i(shaders); for (auto &pair: shaders) {
while (i.hasNext()) { delete pair.second;
i.next(); }
delete i.value(); for (auto &array: arrays) {
delete array;
} }
} }
void OpenGLPart::interrupt() { void OpenGLPart::interrupt() {
} }
OpenGLShaderProgram *OpenGLPart::createShader(QString name) { OpenGLShaderProgram *OpenGLPart::createShader(const std::string &name) {
OpenGLShaderProgram *program = new OpenGLShaderProgram(name.toStdString(), renderer); OpenGLShaderProgram *program = new OpenGLShaderProgram(name, renderer);
if (!shaders.contains(name)) { if (shaders.find(name) == shaders.end()) {
shaders[name] = program; shaders[name] = program;
return program; return program;
} else { } else {
@ -34,6 +30,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) { void OpenGLPart::updateScenery(bool onlyCommon) {
// Let subclass do its own collecting // Let subclass do its own collecting
if (not onlyCommon) { if (not onlyCommon) {

View file

@ -3,14 +3,15 @@
#include "opengl_global.h" #include "opengl_global.h"
#include <QMap> #include <map>
#include <QString> #include <vector>
namespace paysages { namespace paysages {
namespace opengl { 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 { class OPENGLSHARED_EXPORT OpenGLPart {
public: public:
OpenGLPart(OpenGLRenderer *renderer); OpenGLPart(OpenGLRenderer *renderer);
@ -41,14 +42,26 @@ class OPENGLSHARED_EXPORT OpenGLPart {
OpenGLFunctions *getOpenGlFunctions() const; OpenGLFunctions *getOpenGlFunctions() const;
protected: 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 // Access to the main scenery renderer
OpenGLRenderer *renderer; OpenGLRenderer *renderer;
private: private:
QMap<QString, OpenGLShaderProgram *> shaders; std::map<std::string, OpenGLShaderProgram *> shaders;
std::vector<OpenGLVertexArray *> arrays;
}; };
} }
} }

View file

@ -111,10 +111,8 @@ void OpenGLRenderer::initialize() {
} }
} }
void OpenGLRenderer::prepareOpenGLState() { void OpenGLRenderer::prepareOpenGLState(bool clear) {
if (ready) { if (ready) {
functions->glDisable(GL_LIGHTING);
functions->glFrontFace(GL_CCW); functions->glFrontFace(GL_CCW);
functions->glCullFace(GL_BACK); functions->glCullFace(GL_BACK);
functions->glEnable(GL_CULL_FACE); functions->glEnable(GL_CULL_FACE);
@ -124,16 +122,16 @@ void OpenGLRenderer::prepareOpenGLState() {
functions->glEnable(GL_DEPTH_TEST); functions->glEnable(GL_DEPTH_TEST);
functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
functions->glEnable(GL_LINE_SMOOTH); /*functions->glEnable(GL_LINE_SMOOTH);
functions->glLineWidth(1.0); functions->glLineWidth(1.0);*/
functions->glDisable(GL_FOG);
functions->glEnable(GL_BLEND); functions->glEnable(GL_BLEND);
functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
functions->glClearColor(0.0, 0.0, 0.0, 0.0); functions->glClearColor(0.0, 0.0, 0.0, 0.0);
if (clear) {
functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
functions->glViewport(0, 0, vp_width, vp_height); functions->glViewport(0, 0, vp_width, vp_height);
} }
@ -160,10 +158,12 @@ void OpenGLRenderer::resize(int width, int height) {
} }
} }
void OpenGLRenderer::paint() { void OpenGLRenderer::paint(bool clear) {
if (ready and not paused) { if (ready and not paused) {
checkForErrors("before_paint"); checkForErrors("before_paint");
if (clear) {
functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
skybox->render(); skybox->render();
checkForErrors("skybox"); checkForErrors("skybox");

View file

@ -44,9 +44,9 @@ class OPENGLSHARED_EXPORT OpenGLRenderer : public SoftwareRenderer {
void checkForErrors(const std::string &domain); void checkForErrors(const std::string &domain);
void initialize(); void initialize();
void prepareOpenGLState(); void prepareOpenGLState(bool clear=true);
void resize(int width, int height); void resize(int width, int height);
void paint(); void paint(bool clear=true);
/** /**
* Reset the whole state (when the scenery has been massively updated). * Reset the whole state (when the scenery has been massively updated).

View file

@ -5,6 +5,7 @@
#include "OpenGLFunctions.h" #include "OpenGLFunctions.h"
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLSharedState.h" #include "OpenGLSharedState.h"
#include "OpenGLVertexArray.h"
#include "Texture2D.h" #include "Texture2D.h"
#include "Texture3D.h" #include "Texture3D.h"
#include "Texture4D.h" #include "Texture4D.h"
@ -24,8 +25,8 @@ OpenGLShaderProgram::~OpenGLShaderProgram() {
delete state; delete state;
} }
void OpenGLShaderProgram::addVertexSource(QString path) { void OpenGLShaderProgram::addVertexSource(const std::string &path) {
QFile file(QString(":/shaders/%1.vert").arg(path)); QFile file(QString(":/shaders/%1.vert").arg(QString::fromStdString(path)));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
source_vertex += QString(file.readAll()).toStdString(); source_vertex += QString(file.readAll()).toStdString();
} else { } else {
@ -33,8 +34,8 @@ void OpenGLShaderProgram::addVertexSource(QString path) {
} }
} }
void OpenGLShaderProgram::addFragmentSource(QString path) { void OpenGLShaderProgram::addFragmentSource(const std::string &path) {
QFile file(QString(":/shaders/%1.frag").arg(path)); QFile file(QString(":/shaders/%1.frag").arg(QString::fromStdString(path)));
if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
source_fragment += QString(file.readAll()).toStdString(); source_fragment += QString(file.readAll()).toStdString();
} else { } else {
@ -43,8 +44,13 @@ void OpenGLShaderProgram::addFragmentSource(QString path) {
} }
void OpenGLShaderProgram::compile() { void OpenGLShaderProgram::compile() {
program->addShaderFromSourceCode(QOpenGLShader::Vertex, QString::fromStdString(source_vertex)); std::string prefix = std::string("#version ") + OPENGL_GLSL_VERSION + "\n\n";
program->addShaderFromSourceCode(QOpenGLShader::Fragment, QString::fromStdString(source_fragment));
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()) { if (not program->link()) {
Logs::warning() << "[OpenGL] Error while compiling shader " << name << std::endl Logs::warning() << "[OpenGL] Error while compiling shader " << name << std::endl
@ -57,7 +63,7 @@ void OpenGLShaderProgram::compile() {
} }
} }
bool OpenGLShaderProgram::bind() { bool OpenGLShaderProgram::bind(OpenGLSharedState *state) {
if (not compiled) { if (not compiled) {
compile(); compile();
compiled = true; compiled = true;
@ -66,7 +72,9 @@ bool OpenGLShaderProgram::bind() {
if (program->bind()) { if (program->bind()) {
int texture_unit = 0; int texture_unit = 0;
renderer->getSharedState()->apply(this, texture_unit); renderer->getSharedState()->apply(this, texture_unit);
if (state) {
state->apply(this, texture_unit); state->apply(this, texture_unit);
}
return true; return true;
} else { } else {
return false; return false;
@ -77,67 +85,9 @@ void OpenGLShaderProgram::release() {
program->release(); program->release();
} }
void OpenGLShaderProgram::drawTriangles(float *vertices, int triangle_count) { void OpenGLShaderProgram::draw(OpenGLVertexArray *vertices, OpenGLSharedState *state, int start, int count) {
if (bind()) { if (bind(state)) {
GLuint array_vertex = program->attributeLocation("vertex"); vertices->render(functions, start, count);
program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3);
program->enableAttributeArray(array_vertex);
functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
program->disableAttributeArray(array_vertex);
release();
}
}
void OpenGLShaderProgram::drawTriangleStrip(float *vertices, int vertex_count) {
if (bind()) {
GLuint array_vertex = program->attributeLocation("vertex");
program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3);
program->enableAttributeArray(array_vertex);
functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count);
program->disableAttributeArray(array_vertex);
release();
}
}
void OpenGLShaderProgram::drawTrianglesUV(float *vertices, float *uv, int triangle_count) {
if (bind()) {
GLuint array_vertex = program->attributeLocation("vertex");
program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3);
program->enableAttributeArray(array_vertex);
GLuint array_uv = program->attributeLocation("uv");
program->setAttributeArray(array_uv, GL_FLOAT, uv, 2);
program->enableAttributeArray(array_uv);
functions->glDrawArrays(GL_TRIANGLES, 0, triangle_count * 3);
program->disableAttributeArray(array_vertex);
program->disableAttributeArray(array_uv);
release();
}
}
void OpenGLShaderProgram::drawTriangleStripUV(float *vertices, float *uv, int vertex_count) {
if (bind()) {
GLuint array_vertex = program->attributeLocation("vertex");
program->setAttributeArray(array_vertex, GL_FLOAT, vertices, 3);
program->enableAttributeArray(array_vertex);
GLuint array_uv = program->attributeLocation("uv");
program->setAttributeArray(array_uv, GL_FLOAT, uv, 2);
program->enableAttributeArray(array_uv);
functions->glDrawArrays(GL_TRIANGLE_STRIP, 0, vertex_count);
program->disableAttributeArray(array_vertex);
program->disableAttributeArray(array_uv);
release(); release();
} }

View file

@ -3,8 +3,6 @@
#include "opengl_global.h" #include "opengl_global.h"
#include <QString>
class QOpenGLShaderProgram; class QOpenGLShaderProgram;
namespace paysages { namespace paysages {
@ -15,21 +13,25 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram {
OpenGLShaderProgram(const std::string &name, OpenGLRenderer *renderer); OpenGLShaderProgram(const std::string &name, OpenGLRenderer *renderer);
~OpenGLShaderProgram(); ~OpenGLShaderProgram();
void addVertexSource(QString path); void addVertexSource(const std::string &path);
void addFragmentSource(QString path); void addFragmentSource(const std::string &path);
void drawTriangles(float *vertices, int triangle_count); /**
void drawTriangleStrip(float *vertices, int vertex_count); * Draw a VertexArray object.
*
void drawTrianglesUV(float *vertices, float *uv, int triangle_count); * This will bind the program (compile it if needed), set the uniform variables, and
void drawTriangleStripUV(float *vertices, float *uv, int vertex_count); * ask the array object to bind its VAO and render itself.
*
bool bind(); * *state* is optional and may add ponctual variables to the global state.
void release(); */
void draw(OpenGLVertexArray *vertices, OpenGLSharedState *state = NULL, int start=0, int count=-1);
inline QOpenGLShaderProgram *getProgram() const { inline QOpenGLShaderProgram *getProgram() const {
return program; return program;
} }
inline OpenGLFunctions *getFunctions() const {
return functions;
}
inline OpenGLRenderer *getRenderer() const { inline OpenGLRenderer *getRenderer() const {
return renderer; return renderer;
} }
@ -41,6 +43,8 @@ class OPENGLSHARED_EXPORT OpenGLShaderProgram {
friend class OpenGLVariable; friend class OpenGLVariable;
private: private:
bool bind(OpenGLSharedState *state = NULL);
void release();
void compile(); void compile();
bool compiled; bool compiled;

View file

@ -6,6 +6,8 @@
#include <map> #include <map>
#include "OpenGLVariable.h" #include "OpenGLVariable.h"
class QImage;
namespace paysages { namespace paysages {
namespace opengl { namespace opengl {
@ -37,6 +39,9 @@ class OPENGLSHARED_EXPORT OpenGLSharedState {
inline void set(const std::string &name, const Texture2D *texture, bool repeat = false, bool color = true) { inline void set(const std::string &name, const Texture2D *texture, bool repeat = false, bool color = true) {
get(name)->set(texture, repeat, color); 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) { inline void set(const std::string &name, const Texture3D *texture, bool repeat = false, bool color = true) {
get(name)->set(texture, repeat, color); get(name)->set(texture, repeat, color);
} }

View file

@ -4,6 +4,7 @@
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include "OpenGLSharedState.h" #include "OpenGLSharedState.h"
#include "OpenGLVertexArray.h"
#include "Scenery.h" #include "Scenery.h"
#include "AtmosphereDefinition.h" #include "AtmosphereDefinition.h"
#include "AtmosphereRenderer.h" #include "AtmosphereRenderer.h"
@ -11,42 +12,43 @@
#include "FloatNode.h" #include "FloatNode.h"
OpenGLSkybox::OpenGLSkybox(OpenGLRenderer *renderer) : OpenGLPart(renderer) { OpenGLSkybox::OpenGLSkybox(OpenGLRenderer *renderer) : OpenGLPart(renderer) {
vertices = new float[14 * 3];
}
OpenGLSkybox::~OpenGLSkybox() {
delete[] vertices;
}
void OpenGLSkybox::initialize() {
program = createShader("skybox"); program = createShader("skybox");
program->addVertexSource("skybox"); program->addVertexSource("skybox");
program->addFragmentSource("atmosphere"); program->addFragmentSource("atmosphere");
program->addFragmentSource("tonemapping"); program->addFragmentSource("tonemapping");
program->addFragmentSource("skybox"); program->addFragmentSource("skybox");
setVertex(0, 1.0f, 1.0f, 1.0f); vertices = createVertexArray(false, true);
setVertex(12, 1.0f, 1.0f, 1.0f);
setVertex(1, 1.0f, -1.0f, 1.0f); vertices->setVertexCount(14);
setVertex(5, 1.0f, -1.0f, 1.0f);
setVertex(13, 1.0f, -1.0f, 1.0f);
setVertex(2, -1.0f, 1.0f, 1.0f); vertices->set(0, Vector3(1.0f, 1.0f, 1.0f));
setVertex(10, -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); vertices->set(2, Vector3(-1.0f, 1.0f, 1.0f));
setVertex(8, -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); vertices->set(4, Vector3(-1.0f, -1.0f, -1.0f));
setVertex(11, 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 // Watch for definition changes
renderer->getScenery()->getAtmosphere()->propDayTime()->addWatcher(this, true); renderer->getScenery()->getAtmosphere()->propDayTime()->addWatcher(this, true);
renderer->getScenery()->getAtmosphere()->propHumidity()->addWatcher(this, true); renderer->getScenery()->getAtmosphere()->propHumidity()->addWatcher(this, true);
@ -61,7 +63,7 @@ void OpenGLSkybox::update() {
} }
void OpenGLSkybox::render() { void OpenGLSkybox::render() {
program->drawTriangleStrip(vertices, 14); program->draw(vertices);
} }
void OpenGLSkybox::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) { 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()); 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;
}

View file

@ -21,10 +21,8 @@ class OPENGLSHARED_EXPORT OpenGLSkybox : public OpenGLPart, public DefinitionWat
virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override; virtual void nodeChanged(const DefinitionNode *node, const DefinitionDiff *diff) override;
private: private:
void setVertex(int i, float x, float y, float z);
OpenGLShaderProgram *program; OpenGLShaderProgram *program;
float *vertices; OpenGLVertexArray *vertices;
}; };
} }
} }

View file

@ -5,7 +5,7 @@
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include "ParallelPool.h" #include "ParallelPool.h"
#include "Thread.h" #include "Thread.h"
#include "ExplorerChunkTerrain.h" #include "OpenGLTerrainChunk.h"
#include "WaterRenderer.h" #include "WaterRenderer.h"
#include "CameraDefinition.h" #include "CameraDefinition.h"
#include "AtmosphereDefinition.h" #include "AtmosphereDefinition.h"
@ -35,6 +35,14 @@ class ChunkMaintenanceThreads : public ParallelPool {
OpenGLTerrain::OpenGLTerrain(OpenGLRenderer *renderer) : OpenGLPart(renderer) { OpenGLTerrain::OpenGLTerrain(OpenGLRenderer *renderer) : OpenGLPart(renderer) {
work = new ChunkMaintenanceThreads(this); work = new ChunkMaintenanceThreads(this);
paused = false; 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() { OpenGLTerrain::~OpenGLTerrain() {
@ -46,15 +54,6 @@ OpenGLTerrain::~OpenGLTerrain() {
} }
void OpenGLTerrain::initialize() { 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 // Add terrain chunks
int chunks = 12; int chunks = 12;
double size = 800.0; double size = 800.0;
@ -62,7 +61,7 @@ void OpenGLTerrain::initialize() {
double start = -size / 2.0; double start = -size / 2.0;
for (int i = 0; i < chunks; i++) { for (int i = 0; i < chunks; i++) {
for (int j = 0; j < chunks; j++) { 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); start + chunksize * (double)j, chunksize, chunks);
_chunks.append(chunk); _chunks.append(chunk);
_updateQueue.append(chunk); _updateQueue.append(chunk);
@ -84,13 +83,9 @@ void OpenGLTerrain::update() {
} }
void OpenGLTerrain::render() { void OpenGLTerrain::render() {
program->bind();
for (int i = 0; i < _chunks.count(); i++) { for (int i = 0; i < _chunks.count(); i++) {
_chunks[i]->render(program->getProgram(), renderer->getOpenGlFunctions()); _chunks[i]->render(program);
} }
program->release();
} }
void OpenGLTerrain::interrupt() { 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; return c1->priority > c2->priority;
} }
void OpenGLTerrain::performChunksMaintenance() { void OpenGLTerrain::performChunksMaintenance() {
CameraDefinition *camera = renderer->getScenery()->getCamera(); CameraDefinition *camera = renderer->getScenery()->getCamera();
ExplorerChunkTerrain *chunk; OpenGLTerrainChunk *chunk;
_lock_chunks.lock(); _lock_chunks.lock();
if (_updateQueue.count() > 0) { if (_updateQueue.count() > 0) {

View file

@ -44,8 +44,8 @@ class OPENGLSHARED_EXPORT OpenGLTerrain : public OpenGLPart, public DefinitionWa
ParallelPool *work; ParallelPool *work;
bool paused; bool paused;
QVector<ExplorerChunkTerrain *> _chunks; QVector<OpenGLTerrainChunk *> _chunks;
QList<ExplorerChunkTerrain *> _updateQueue; QList<OpenGLTerrainChunk *> _updateQueue;
QMutex _lock_chunks; QMutex _lock_chunks;
}; };
} }

View file

@ -0,0 +1,261 @@
#include "OpenGLTerrainChunk.h"
#include <cmath>
#include <cassert>
#include <QImage>
#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;
}

View file

@ -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

View file

@ -1,7 +1,12 @@
#include "OpenGLVariable.h" #include "OpenGLVariable.h"
#include <QOpenGLShaderProgram>
#include <cassert> #include <cassert>
#include <QOpenGLShaderProgram>
#include <QColor>
#include <QVector3D>
#include <QMatrix4x4>
#include <QImage>
#include "Logs.h"
#include "OpenGLFunctions.h" #include "OpenGLFunctions.h"
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
@ -16,6 +21,22 @@ OpenGLVariable::OpenGLVariable(const std::string &name) : name(name) {
type = TYPE_NONE; type = TYPE_NONE;
texture_toupload = false; texture_toupload = false;
texture_id = 0; 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) { void OpenGLVariable::apply(OpenGLShaderProgram *program, int &texture_unit) {
@ -35,13 +56,13 @@ void OpenGLVariable::apply(OpenGLShaderProgram *program, int &texture_unit) {
pr->setUniformValue(name.c_str(), value_float); pr->setUniformValue(name.c_str(), value_float);
break; break;
case TYPE_COLOR: case TYPE_COLOR:
pr->setUniformValue(name.c_str(), value_color); pr->setUniformValue(name.c_str(), *value_color);
break; break;
case TYPE_VECTOR3: case TYPE_VECTOR3:
pr->setUniformValue(name.c_str(), value_vector3); pr->setUniformValue(name.c_str(), *value_vector3);
break; break;
case TYPE_MATRIX4: case TYPE_MATRIX4:
pr->setUniformValue(name.c_str(), value_matrix4); pr->setUniformValue(name.c_str(), *value_matrix4);
break; break;
case TYPE_TEXTURE_2D: case TYPE_TEXTURE_2D:
functions->glActiveTexture(GL_TEXTURE0 + texture_unit); functions->glActiveTexture(GL_TEXTURE0 + texture_unit);
@ -65,7 +86,61 @@ void OpenGLVariable::set(const Texture2D *texture, bool repeat, bool color) {
assert(type == TYPE_NONE or type == TYPE_TEXTURE_2D); assert(type == TYPE_NONE or type == TYPE_TEXTURE_2D);
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_toupload = true;
texture_repeat = repeat; texture_repeat = repeat;
texture_color = color; texture_color = color;
@ -74,8 +149,31 @@ void OpenGLVariable::set(const Texture2D *texture, bool repeat, bool color) {
void OpenGLVariable::set(const Texture3D *texture, bool repeat, bool color) { void OpenGLVariable::set(const Texture3D *texture, bool repeat, bool color) {
assert(type == TYPE_NONE or type == TYPE_TEXTURE_3D); 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; type = TYPE_TEXTURE_3D;
value_tex3d = texture;
texture_toupload = true; texture_toupload = true;
texture_repeat = repeat; texture_repeat = repeat;
texture_color = color; texture_color = color;
@ -84,8 +182,33 @@ void OpenGLVariable::set(const Texture3D *texture, bool repeat, bool color) {
void OpenGLVariable::set(const Texture4D *texture, bool repeat, bool color) { void OpenGLVariable::set(const Texture4D *texture, bool repeat, bool color) {
assert(type == TYPE_NONE or type == TYPE_TEXTURE_4D); 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; type = TYPE_TEXTURE_4D;
value_tex4d = texture;
texture_toupload = true; texture_toupload = true;
texture_repeat = repeat; texture_repeat = repeat;
texture_color = color; texture_color = color;
@ -113,7 +236,7 @@ void OpenGLVariable::set(const QVector3D &vector) {
assert(type == TYPE_NONE or type == TYPE_VECTOR3); assert(type == TYPE_NONE or type == TYPE_VECTOR3);
type = TYPE_VECTOR3; type = TYPE_VECTOR3;
value_vector3 = vector; *value_vector3 = vector;
} }
void OpenGLVariable::set(const Matrix4 &matrix) { void OpenGLVariable::set(const Matrix4 &matrix) {
@ -124,14 +247,14 @@ void OpenGLVariable::set(const QMatrix4x4 &matrix) {
assert(type == TYPE_NONE or type == TYPE_MATRIX4); assert(type == TYPE_NONE or type == TYPE_MATRIX4);
type = TYPE_MATRIX4; type = TYPE_MATRIX4;
value_matrix4 = matrix; *value_matrix4 = matrix;
} }
void OpenGLVariable::set(const Color &color) { void OpenGLVariable::set(const Color &color) {
assert(type == TYPE_NONE or type == TYPE_COLOR); assert(type == TYPE_NONE or type == TYPE_COLOR);
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) { void OpenGLVariable::uploadTexture(OpenGLRenderer *renderer) {
@ -159,61 +282,8 @@ void OpenGLVariable::uploadTexture(OpenGLRenderer *renderer) {
int dest_format = texture_color ? GL_RGBA : GL_RED; int dest_format = texture_color ? GL_RGBA : GL_RED;
if (type == TYPE_TEXTURE_2D) { if (type == TYPE_TEXTURE_2D) {
int sx, sy; functions->glTexImage2D(GL_TEXTURE_2D, 0, dest_format, texture_size_x, texture_size_y, 0, GL_RGBA, GL_FLOAT, value_texture_data);
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;
} else { } else {
int sx, sy, sz, sw; 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);
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;
} }
} }

View file

@ -3,9 +3,10 @@
#include "opengl_global.h" #include "opengl_global.h"
#include <QColor> class QColor;
#include <QVector3D> class QVector3D;
#include <QMatrix4x4> class QMatrix4x4;
class QImage;
namespace paysages { namespace paysages {
namespace opengl { namespace opengl {
@ -29,10 +30,12 @@ class OpenGLVariable {
public: public:
OpenGLVariable(const std::string &name); OpenGLVariable(const std::string &name);
~OpenGLVariable();
void apply(OpenGLShaderProgram *program, int &texture_unit); void apply(OpenGLShaderProgram *program, int &texture_unit);
void set(const Texture2D *texture, bool repeat = false, bool color = true); 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 Texture3D *texture, bool repeat = false, bool color = true);
void set(const Texture4D *texture, bool repeat = false, bool color = true); void set(const Texture4D *texture, bool repeat = false, bool color = true);
void set(int value); void set(int value);
@ -52,13 +55,14 @@ class OpenGLVariable {
int value_int; int value_int;
float value_float; float value_float;
QColor value_color; QColor *value_color;
QVector3D value_vector3; QVector3D *value_vector3;
QMatrix4x4 value_matrix4; QMatrix4x4 *value_matrix4;
const Texture2D *value_tex2d; float *value_texture_data;
const Texture3D *value_tex3d;
const Texture4D *value_tex4d;
int texture_size_x;
int texture_size_y;
int texture_size_z;
bool texture_toupload; bool texture_toupload;
bool texture_repeat; bool texture_repeat;
bool texture_color; bool texture_color;

View file

@ -3,6 +3,7 @@
#include <cassert> #include <cassert>
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include "OpenGLSharedState.h" #include "OpenGLSharedState.h"
#include "OpenGLVertexArray.h"
#include "OpenGLVegetationInstance.h" #include "OpenGLVegetationInstance.h"
#include "Texture2D.h" #include "Texture2D.h"
#include "SoftwareRenderer.h" #include "SoftwareRenderer.h"
@ -31,12 +32,14 @@ static inline Matrix4 matrixForIndex(int index) {
OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) { OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) {
int parts = 4; int parts = 4;
vertices = new float[4 * parts * parts * 3]; vertices = new OpenGLVertexArray(true, true);
uv = new float[4 * 2]; vertices->setVertexCount(4 * parts * parts);
texture_size = partsize * parts; texture_size = partsize * parts;
texture = new Texture2D(texture_size, texture_size); texture = new Texture2D(texture_size, texture_size);
texture_changed = false; texture_changed = false;
state = new OpenGLSharedState();
setVertex(0, 0.0f, 0.0f); setVertex(0, 0.0f, 0.0f);
setVertex(1, 0.0f, 1.0f); setVertex(1, 0.0f, 1.0f);
setVertex(2, 1.0f, 0.0f); setVertex(2, 1.0f, 0.0f);
@ -44,7 +47,8 @@ OpenGLVegetationImpostor::OpenGLVegetationImpostor(int partsize) {
} }
OpenGLVegetationImpostor::~OpenGLVegetationImpostor() { OpenGLVegetationImpostor::~OpenGLVegetationImpostor() {
delete[] vertices; delete vertices;
delete state;
delete texture; delete texture;
} }
@ -52,14 +56,14 @@ void OpenGLVegetationImpostor::render(OpenGLShaderProgram *program, const OpenGL
int instance_index, const Vector3 &camera_location) { int instance_index, const Vector3 &camera_location) {
if (instance_index == 0 or texture_changed) { if (instance_index == 0 or texture_changed) {
texture_changed = false; texture_changed = false;
program->getState()->set("impostorTexture", texture); state->set("impostorTexture", texture);
} }
int index = getIndex(camera_location, instance->getBase()); int index = getIndex(camera_location, instance->getBase());
program->getState()->setInt("index", index); state->setInt("index", index);
program->getState()->set("offset", instance->getBase()); state->set("offset", instance->getBase());
program->getState()->set("size", 2.0 * instance->getSize()); state->set("size", 2.0 * instance->getSize());
program->drawTriangleStripUV(vertices + index * 4 * 3, uv, 4); program->draw(vertices, state, index * 4, 4);
} }
void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &model, const Scenery &environment, void OpenGLVegetationImpostor::prepareTexture(const VegetationModelDefinition &model, const Scenery &environment,
@ -138,11 +142,7 @@ void OpenGLVegetationImpostor::setVertex(int i, float u, float v) {
Matrix4 rotation = matrixForIndex(index); Matrix4 rotation = matrixForIndex(index);
Vector3 vertex = rotation.multPoint(Vector3(1.0, u, -(v - 0.5))); Vector3 vertex = rotation.multPoint(Vector3(1.0, u, -(v - 0.5)));
vertices[index * 4 * 3 + i * 3] = vertex.x; vertices->set(index * 4 + i, vertex, u, v);
vertices[index * 4 * 3 + i * 3 + 1] = vertex.y;
vertices[index * 4 * 3 + i * 3 + 2] = vertex.z;
} }
} }
uv[i * 2] = u;
uv[i * 2 + 1] = v;
} }

View file

@ -38,8 +38,8 @@ class OPENGLSHARED_EXPORT OpenGLVegetationImpostor {
void setVertex(int i, float u, float v); void setVertex(int i, float u, float v);
private: private:
float *vertices; OpenGLVertexArray *vertices;
float *uv; OpenGLSharedState *state;
int texture_size; int texture_size;
bool texture_changed; bool texture_changed;
Texture2D *texture; Texture2D *texture;

View file

@ -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, int start, int count) {
if (changed) {
changed = false;
update(functions);
}
if (vertexcount and vao) {
functions->glBindVertexArray(vao);
functions->glDrawArrays(draw_mode, start, count < 0 ? vertexcount : count);
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);
}

View file

@ -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, int start=0, int count=-1);
/**
* 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

View file

@ -3,6 +3,7 @@
#include "OpenGLRenderer.h" #include "OpenGLRenderer.h"
#include "OpenGLShaderProgram.h" #include "OpenGLShaderProgram.h"
#include "OpenGLSharedState.h" #include "OpenGLSharedState.h"
#include "OpenGLVertexArray.h"
#include "WaterRenderer.h" #include "WaterRenderer.h"
#include "Scenery.h" #include "Scenery.h"
#include "WaterDefinition.h" #include "WaterDefinition.h"
@ -13,15 +14,8 @@
#include "IntNode.h" #include "IntNode.h"
OpenGLWater::OpenGLWater(OpenGLRenderer *renderer) : OpenGLPart(renderer) { OpenGLWater::OpenGLWater(OpenGLRenderer *renderer) : OpenGLPart(renderer) {
vertices = new float[4 * 3];
enabled = true; enabled = true;
}
OpenGLWater::~OpenGLWater() {
delete[] vertices;
}
void OpenGLWater::initialize() {
program = createShader("water"); program = createShader("water");
program->addVertexSource("water"); program->addVertexSource("water");
program->addFragmentSource("atmosphere"); program->addFragmentSource("atmosphere");
@ -31,11 +25,18 @@ void OpenGLWater::initialize() {
program->addFragmentSource("noise"); program->addFragmentSource("noise");
program->addFragmentSource("water"); program->addFragmentSource("water");
setVertex(0, -1.0f, 0.0f, -1.0f); vertices = createVertexArray(false, true);
setVertex(1, -1.0f, 0.0f, 1.0f); vertices->setVertexCount(4);
setVertex(2, 1.0f, 0.0f, -1.0f); vertices->set(0, Vector3(-1.0f, 0.0f, -1.0f));
setVertex(3, 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 // Watch for definition changes
renderer->getScenery()->getTerrain()->propWaterHeight()->addWatcher(this, true); renderer->getScenery()->getTerrain()->propWaterHeight()->addWatcher(this, true);
renderer->getScenery()->getWater()->propReflection()->addWatcher(this, true); renderer->getScenery()->getWater()->propReflection()->addWatcher(this, true);
@ -54,16 +55,10 @@ void OpenGLWater::update() {
void OpenGLWater::render() { void OpenGLWater::render() {
if (enabled) { 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 *) { void OpenGLWater::nodeChanged(const DefinitionNode *node, const DefinitionDiff *) {
if (node->getPath() == "/terrain/water_height") { if (node->getPath() == "/terrain/water_height") {
renderer->getSharedState()->set("waterOffset", renderer->getScenery()->getTerrain()->getWaterOffset()); renderer->getSharedState()->set("waterOffset", renderer->getScenery()->getTerrain()->getWaterOffset());

View file

@ -26,11 +26,9 @@ class OPENGLSHARED_EXPORT OpenGLWater : public OpenGLPart, public DefinitionWatc
void setEnabled(bool enabled); void setEnabled(bool enabled);
private: private:
void setVertex(int i, float x, float y, float z);
bool enabled; bool enabled;
OpenGLShaderProgram *program; OpenGLShaderProgram *program;
float *vertices; OpenGLVertexArray *vertices;
}; };
} }
} }

View file

@ -1,147 +0,0 @@
#ifndef VERTEXARRAY_H
#define VERTEXARRAY_H
#include "opengl_global.h"
#include <cassert>
#include <QOpenGLShaderProgram>
#include "OpenGLFunctions.h"
namespace paysages {
namespace opengl {
/*!
* Wrapper for OpenGL vertex arrays.
*/
template <typename Vertex> 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

View file

@ -16,6 +16,7 @@ class OpenGLRenderer;
class OpenGLShaderProgram; class OpenGLShaderProgram;
class OpenGLSharedState; class OpenGLSharedState;
class OpenGLVariable; class OpenGLVariable;
class OpenGLVertexArray;
class OpenGLSkybox; class OpenGLSkybox;
class OpenGLWater; class OpenGLWater;
class OpenGLTerrain; class OpenGLTerrain;
@ -23,14 +24,16 @@ class OpenGLVegetation;
class OpenGLVegetationLayer; class OpenGLVegetationLayer;
class OpenGLVegetationInstance; class OpenGLVegetationInstance;
class OpenGLVegetationImpostor; class OpenGLVegetationImpostor;
class ExplorerChunkTerrain; class OpenGLTerrainChunk;
template <typename Vertex> class VertexArray; template <typename Vertex> class VertexArray;
} }
} }
using namespace paysages::opengl; 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; class OpenGLFunctions;
#endif // OPENGL_GLOBAL_H #endif // OPENGL_GLOBAL_H

View file

@ -29,7 +29,7 @@ uniform vec4 sunColor;
uniform float dayTime; uniform float dayTime;
uniform float sunRadius; uniform float sunRadius;
varying vec3 unprojected; in vec3 unprojected;
uniform sampler2D transmittanceTexture; uniform sampler2D transmittanceTexture;
uniform sampler3D inscatterTexture; 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); float sr = 1.0 / float(RES_R);
int br = int(floor(uR / sr)); int br = int(floor(uR / sr));
vec4 r1 = texture3D(tex, vec3(uMu, uMuS, float(br) * sr + nu * sr)); vec4 r1 = texture(tex, vec3(uMu, uMuS, float(br) * sr + nu * sr));
vec4 r2 = texture3D(tex, vec3(uMu, uMuS, float(br + 1) * 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); 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) vec4 _transmittance(float r, float mu)
{ {
vec2 uv = _getTransmittanceUV(r, mu); vec2 uv = _getTransmittanceUV(r, mu);
return texture2D(transmittanceTexture, uv); return texture(transmittanceTexture, uv);
} }
vec4 _transmittanceWithShadow(float r, float mu) vec4 _transmittanceWithShadow(float r, float mu)

View file

@ -1,5 +1,7 @@
out vec4 final_color;
void main(void) void main(void)
{ {
gl_FragColor = getSkyColor(cameraLocation, unprojected - cameraLocation); final_color = getSkyColor(cameraLocation, unprojected - cameraLocation);
gl_FragColor = applyToneMapping(gl_FragColor); final_color = applyToneMapping(final_color);
} }

View file

@ -1,7 +1,7 @@
attribute highp vec4 vertex; in highp vec4 vertex;
uniform highp mat4 viewMatrix; uniform highp mat4 viewMatrix;
uniform vec3 cameraLocation; uniform vec3 cameraLocation;
varying vec3 unprojected; out vec3 unprojected;
void main(void) void main(void)
{ {

View file

@ -1,15 +1,16 @@
uniform sampler2D groundTexture; uniform sampler2D groundTexture;
varying vec2 texcoord; in vec2 texcoord;
out vec4 final_color;
void main(void) 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();
} }

View file

@ -1,8 +1,8 @@
attribute highp vec4 vertex; in highp vec4 vertex;
attribute highp vec2 uv; in highp vec2 uv;
uniform highp mat4 viewMatrix; uniform highp mat4 viewMatrix;
varying vec3 unprojected; out vec3 unprojected;
varying vec2 texcoord; out vec2 texcoord;
uniform float waterOffset; uniform float waterOffset;
void main(void) void main(void)

View file

@ -1,15 +1,16 @@
varying vec2 texcoord; in vec2 texcoord;
uniform sampler2D impostorTexture; uniform sampler2D impostorTexture;
out vec4 final_color;
void main(void) void main(void)
{ {
gl_FragColor = texture2D(impostorTexture, texcoord); final_color = texture(impostorTexture, texcoord);
float alpha = gl_FragColor.a; float alpha = final_color.a;
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 = alpha; final_color.a = alpha;
} }

View file

@ -1,11 +1,11 @@
attribute highp vec3 vertex; in highp vec3 vertex;
attribute highp vec2 uv; in highp vec2 uv;
uniform highp mat4 viewMatrix; uniform highp mat4 viewMatrix;
uniform highp vec3 offset; uniform highp vec3 offset;
uniform float size; uniform float size;
uniform int index; uniform int index;
varying vec3 unprojected; out vec3 unprojected;
varying vec2 texcoord; out vec2 texcoord;
uniform float waterOffset; uniform float waterOffset;
void main(void) void main(void)

View file

@ -3,22 +3,23 @@ uniform float waterMaterialReflection;
uniform float waterMaterialShininess; uniform float waterMaterialShininess;
uniform float waterMaterialHardness; uniform float waterMaterialHardness;
uniform float waterReflection; uniform float waterReflection;
out vec4 final_color;
void main(void) void main(void)
{ {
vec3 normal = noiseNormal2d(unprojected.xz, 0.001); 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); vec3 reflected = reflect(unprojected - cameraLocation, normal);
reflected.y = max(reflected.y, 0.0); 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();
} }

View file

@ -1,8 +1,8 @@
attribute highp vec4 vertex; in highp vec4 vertex;
uniform highp mat4 viewMatrix; uniform highp mat4 viewMatrix;
uniform float waterHeight; uniform float waterHeight;
uniform vec3 cameraLocation; uniform vec3 cameraLocation;
varying vec3 unprojected; out vec3 unprojected;
void main(void) void main(void)
{ {

View file

@ -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);
}

View file

@ -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<TestVertex> 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<TestVertex> 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<TestVertex> 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));
}