diff --git a/src/basics/ColorProfile.cpp b/src/basics/ColorProfile.cpp index 25c2ff3..99af627 100644 --- a/src/basics/ColorProfile.cpp +++ b/src/basics/ColorProfile.cpp @@ -4,7 +4,7 @@ ColorProfile::ColorProfile() { - setToneMapping(TONE_MAPPING_UNCHARTED, 2.0); + setToneMapping(TONE_MAPPING_UNCHARTED, 1.6); } ColorProfile::ColorProfile(ToneMappingOperator tonemapper, double exposure) diff --git a/src/basics/Matrix4.h b/src/basics/Matrix4.h index a53d3f3..42954c7 100644 --- a/src/basics/Matrix4.h +++ b/src/basics/Matrix4.h @@ -4,6 +4,9 @@ #include "basics_global.h" #include "Vector3.h" +#ifdef QT_GUI_LIB +#include +#endif namespace paysages { namespace basics { @@ -35,6 +38,15 @@ public: double getDeterminant() const; +#ifdef QT_GUI_LIB + inline QMatrix4x4 toQMatrix() const { + return QMatrix4x4(a, b, c, d, + e, f, g, h, + i, j, k, l, + m, n, o, p); + } +#endif + private: double a; double b; diff --git a/src/basics/Texture2D.cpp b/src/basics/Texture2D.cpp index 5950490..13e71e7 100644 --- a/src/basics/Texture2D.cpp +++ b/src/basics/Texture2D.cpp @@ -19,7 +19,7 @@ Texture2D::~Texture2D() delete[] data; } -void Texture2D::getSize(int* xsize, int* ysize) +void Texture2D::getSize(int* xsize, int* ysize) const { *xsize = this->xsize; *ysize = this->ysize; @@ -33,7 +33,7 @@ void Texture2D::setPixel(int x, int y, Color col) data[y * xsize + x] = col; } -Color Texture2D::getPixel(int x, int y) +Color Texture2D::getPixel(int x, int y) const { assert(x >= 0 && x < xsize); assert(y >= 0 && y < ysize); @@ -41,7 +41,7 @@ Color Texture2D::getPixel(int x, int y) return data[y * xsize + x]; } -Color Texture2D::getNearest(double dx, double dy) +Color Texture2D::getNearest(double dx, double dy) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; @@ -57,7 +57,7 @@ Color Texture2D::getNearest(double dx, double dy) return this->data[iy * this->xsize + ix]; } -Color Texture2D::getLinear(double dx, double dy) +Color Texture2D::getLinear(double dx, double dy) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; @@ -89,7 +89,7 @@ Color Texture2D::getLinear(double dx, double dy) return c1.lerp(c2, dy); } -Color Texture2D::getCubic(double dx, double dy) +Color Texture2D::getCubic(double dx, double dy) const { /* TODO */ return getLinear(dx, dy); @@ -122,7 +122,7 @@ void Texture2D::add(Texture2D* source) } } -void Texture2D::save(PackStream* stream) +void Texture2D::save(PackStream* stream) const { int i, n; stream->write(&this->xsize); @@ -151,17 +151,17 @@ void Texture2D::load(PackStream* stream) class Texture2DWriter:public PictureWriter { public: - Texture2DWriter(Texture2D *tex): tex(tex) {} + Texture2DWriter(const Texture2D *tex): tex(tex) {} virtual unsigned int getPixel(int x, int y) override { return tex->getPixel(x, y).to32BitBGRA(); } private: - Texture2D *tex; + const Texture2D *tex; }; -void Texture2D::saveToFile(const std::string &filepath) +void Texture2D::saveToFile(const std::string &filepath) const { Texture2DWriter writer(this); writer.save(filepath, xsize, ysize); diff --git a/src/basics/Texture2D.h b/src/basics/Texture2D.h index 94bc39f..67d1649 100644 --- a/src/basics/Texture2D.h +++ b/src/basics/Texture2D.h @@ -12,17 +12,17 @@ public: Texture2D(int xsize, int ysize); ~Texture2D(); - void getSize(int* xsize, int* ysize); + void getSize(int* xsize, int* ysize) const; void setPixel(int x, int y, Color col); - Color getPixel(int x, int y); - Color getNearest(double dx, double dy); - Color getLinear(double dx, double dy); - Color getCubic(double dx, double dy); + Color getPixel(int x, int y) const; + Color getNearest(double dx, double dy) const; + Color getLinear(double dx, double dy) const; + Color getCubic(double dx, double dy) const; void fill(Color col); void add(Texture2D* other); - void save(PackStream* stream); + void save(PackStream* stream) const; void load(PackStream* stream); - void saveToFile(const std::string &filepath); + void saveToFile(const std::string &filepath) const; private: int xsize; diff --git a/src/basics/Texture3D.cpp b/src/basics/Texture3D.cpp index bca20a7..55400e2 100644 --- a/src/basics/Texture3D.cpp +++ b/src/basics/Texture3D.cpp @@ -20,7 +20,7 @@ Texture3D::~Texture3D() delete[] data; } -void Texture3D::getSize(int* xsize, int* ysize, int* zsize) +void Texture3D::getSize(int* xsize, int* ysize, int* zsize) const { *xsize = this->xsize; *ysize = this->ysize; @@ -36,7 +36,7 @@ void Texture3D::setPixel(int x, int y, int z, Color col) this->data[z * this->xsize * this->ysize + y * this->xsize + x] = col; } -Color Texture3D::getPixel(int x, int y, int z) +Color Texture3D::getPixel(int x, int y, int z) const { assert(x >= 0 && x < this->xsize); assert(y >= 0 && y < this->ysize); @@ -45,7 +45,7 @@ Color Texture3D::getPixel(int x, int y, int z) return this->data[z * this->xsize * this->ysize + y * this->xsize + x]; } -Color Texture3D::getNearest(double dx, double dy, double dz) +Color Texture3D::getNearest(double dx, double dy, double dz) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; @@ -65,7 +65,7 @@ Color Texture3D::getNearest(double dx, double dy, double dz) return this->data[iz * this->xsize * this->ysize + iy * this->xsize + ix]; } -Color Texture3D::getLinear(double dx, double dy, double dz) +Color Texture3D::getLinear(double dx, double dy, double dz) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; @@ -112,7 +112,7 @@ Color Texture3D::getLinear(double dx, double dy, double dz) return cy1.lerp(cy2, dz); } -Color Texture3D::getCubic(double dx, double dy, double dz) +Color Texture3D::getCubic(double dx, double dy, double dz) const { /* TODO */ return getLinear(dx, dy, dz); @@ -146,7 +146,7 @@ void Texture3D::add(Texture3D* source) } } -void Texture3D::save(PackStream* stream) +void Texture3D::save(PackStream* stream) const { int i, n; stream->write(&this->xsize); @@ -177,7 +177,7 @@ void Texture3D::load(PackStream* stream) class Texture3DWriter:public PictureWriter { public: - Texture3DWriter(Texture3D *tex): tex(tex) {} + Texture3DWriter(const Texture3D *tex): tex(tex) {} virtual unsigned int getPixel(int x, int y) override { @@ -190,10 +190,10 @@ public: return tex->getPixel(x, y, z).to32BitBGRA(); } private: - Texture3D *tex; + const Texture3D *tex; }; -void Texture3D::saveToFile(const std::string &filepath) +void Texture3D::saveToFile(const std::string &filepath) const { Texture3DWriter writer(this); writer.save(filepath, xsize, ysize * zsize); diff --git a/src/basics/Texture3D.h b/src/basics/Texture3D.h index 3c40f16..8a68208 100644 --- a/src/basics/Texture3D.h +++ b/src/basics/Texture3D.h @@ -12,17 +12,17 @@ public: Texture3D(int xsize, int ysize, int zsize); ~Texture3D(); - void getSize(int* xsize, int* ysize, int* zsize); + void getSize(int* xsize, int* ysize, int* zsize) const; void setPixel(int x, int y, int z, Color col); - Color getPixel(int x, int y, int z); - Color getNearest(double dx, double dy, double dz); - Color getLinear(double dx, double dy, double dz); - Color getCubic(double dx, double dy, double dz); + Color getPixel(int x, int y, int z) const; + Color getNearest(double dx, double dy, double dz) const; + Color getLinear(double dx, double dy, double dz) const; + Color getCubic(double dx, double dy, double dz) const; void fill(Color col); void add(Texture3D* other); - void save(PackStream* stream); + void save(PackStream* stream) const; void load(PackStream* stream); - void saveToFile(const std::string &filepath); + void saveToFile(const std::string &filepath) const; private: int xsize; diff --git a/src/basics/Texture4D.cpp b/src/basics/Texture4D.cpp index 8700ecd..877ea4b 100644 --- a/src/basics/Texture4D.cpp +++ b/src/basics/Texture4D.cpp @@ -21,7 +21,7 @@ Texture4D::~Texture4D() delete[] data; } -void Texture4D::getSize(int* xsize, int* ysize, int* zsize, int* wsize) +void Texture4D::getSize(int* xsize, int* ysize, int* zsize, int* wsize) const { *xsize = this->xsize; *ysize = this->ysize; @@ -39,7 +39,7 @@ void Texture4D::setPixel(int x, int y, int z, int w, Color col) this->data[w * this->xsize * this->ysize * this->zsize + z * this->xsize * this->ysize + y * this->xsize + x] = col; } -Color Texture4D::getPixel(int x, int y, int z, int w) +Color Texture4D::getPixel(int x, int y, int z, int w) const { assert(x >= 0 && x < this->xsize); assert(y >= 0 && y < this->ysize); @@ -49,7 +49,7 @@ Color Texture4D::getPixel(int x, int y, int z, int w) return this->data[w * this->xsize * this->ysize * this->zsize + z * this->xsize * this->ysize + y * this->xsize + x]; } -Color Texture4D::getNearest(double dx, double dy, double dz, double dw) +Color Texture4D::getNearest(double dx, double dy, double dz, double dw) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; @@ -73,7 +73,7 @@ Color Texture4D::getNearest(double dx, double dy, double dz, double dw) return this->data[iw * this->xsize * this->ysize * this->zsize + iz * this->xsize * this->ysize + iy * this->xsize + ix]; } -Color Texture4D::getLinear(double dx, double dy, double dz, double dw) +Color Texture4D::getLinear(double dx, double dy, double dz, double dw) const { if (dx < 0.0) dx = 0.0; if (dx > 1.0) dx = 1.0; @@ -143,7 +143,7 @@ Color Texture4D::getLinear(double dx, double dy, double dz, double dw) return cz1.lerp(cz2, dw); } -Color Texture4D::getCubic(double dx, double dy, double dz, double dw) +Color Texture4D::getCubic(double dx, double dy, double dz, double dw) const { /* TODO */ return getLinear(dx, dy, dz, dw); @@ -178,7 +178,7 @@ void Texture4D::add(Texture4D* source) } } -void Texture4D::save(PackStream* stream) +void Texture4D::save(PackStream* stream) const { int i, n; stream->write(&this->xsize); @@ -212,7 +212,7 @@ void Texture4D::load(PackStream* stream) class Texture4DWriter:public PictureWriter { public: - Texture4DWriter(Texture4D *tex): tex(tex) {} + Texture4DWriter(const Texture4D *tex): tex(tex) {} virtual unsigned int getPixel(int x, int y) override { @@ -228,10 +228,10 @@ public: return tex->getPixel(x, y, z, w).to32BitBGRA(); } private: - Texture4D *tex; + const Texture4D *tex; }; -void Texture4D::saveToFile(const std::string &filepath) +void Texture4D::saveToFile(const std::string &filepath) const { Texture4DWriter writer(this); writer.save(filepath, xsize * wsize, ysize * zsize); diff --git a/src/basics/Texture4D.h b/src/basics/Texture4D.h index 3657951..6b53580 100644 --- a/src/basics/Texture4D.h +++ b/src/basics/Texture4D.h @@ -12,17 +12,17 @@ public: Texture4D(int xsize, int ysize, int zsize, int wsize); ~Texture4D(); - void getSize(int* xsize, int* ysize, int* zsize, int* wsize); + void getSize(int* xsize, int* ysize, int* zsize, int* wsize) const; void setPixel(int x, int y, int z, int w, Color col); - Color getPixel(int x, int y, int z, int w); - Color getNearest(double dx, double dy, double dz, double dw); - Color getLinear(double dx, double dy, double dz, double dw); - Color getCubic(double dx, double dy, double dz, double dw); + Color getPixel(int x, int y, int z, int w) const; + Color getNearest(double dx, double dy, double dz, double dw) const; + Color getLinear(double dx, double dy, double dz, double dw) const; + Color getCubic(double dx, double dy, double dz, double dw) const; void fill(Color col); void add(Texture4D* other); - void save(PackStream* stream); + void save(PackStream* stream) const; void load(PackStream* stream); - void saveToFile(const std::string &filepath); + void saveToFile(const std::string &filepath) const; private: int xsize; diff --git a/src/basics/basics_global.h b/src/basics/basics_global.h index e6830b6..853908d 100644 --- a/src/basics/basics_global.h +++ b/src/basics/basics_global.h @@ -20,6 +20,9 @@ namespace basics { class NoiseGenerator; class Curve; class ColorProfile; + class Texture2D; + class Texture3D; + class Texture4D; } } using namespace paysages::basics; diff --git a/src/definition/CameraDefinition.h b/src/definition/CameraDefinition.h index e5dc512..162e38a 100644 --- a/src/definition/CameraDefinition.h +++ b/src/definition/CameraDefinition.h @@ -36,6 +36,7 @@ public: inline double getRoll() const {return roll;} inline Vector3 getDirection() const {return Vector3(direction);} inline Vector3 getDirectionNormalized() const {return forward;} + inline const Matrix4 &getTransformationMatrix() const {return projector;} inline VectorSpherical getDirectionSpherical() const {return direction;} inline CameraPerspective getPerspective() const {return perspective;} diff --git a/src/definition/Scenery.cpp b/src/definition/Scenery.cpp index 1543e2a..337fea1 100644 --- a/src/definition/Scenery.cpp +++ b/src/definition/Scenery.cpp @@ -199,8 +199,8 @@ void Scenery::getWater(WaterDefinition* water) void Scenery::checkCameraAboveGround() { Vector3 camera_location = camera->getLocation(); - double terrain_height = terrain->getInterpolatedHeight(camera_location.x, camera_location.z, 1, 1) + 0.5; - double water_height = terrain->getWaterHeight() + 0.5; + double terrain_height = terrain->getInterpolatedHeight(camera_location.x, camera_location.z, 1, 1) + 2.0; + double water_height = terrain->getWaterHeight() + 1.5; if (camera_location.y < water_height || camera_location.y < terrain_height) { double diff = ((water_height > terrain_height) ? water_height : terrain_height) - camera_location.y; diff --git a/src/definition/definition_global.h b/src/definition/definition_global.h index 7279f08..78585e3 100644 --- a/src/definition/definition_global.h +++ b/src/definition/definition_global.h @@ -2,6 +2,7 @@ #define DEFINITION_GLOBAL_H #include + #if defined(DEFINITION_LIBRARY) # define DEFINITIONSHARED_EXPORT Q_DECL_EXPORT #else diff --git a/src/render/opengl/BaseExplorerChunk.cpp b/src/render/opengl/BaseExplorerChunk.cpp deleted file mode 100644 index 82f7322..0000000 --- a/src/render/opengl/BaseExplorerChunk.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "BaseExplorerChunk.h" - -#include -#include -#include "ColorProfile.h" - -#ifndef GL_CLAMP_TO_EDGE -#define GL_CLAMP_TO_EDGE 0x812F -#endif - -BaseExplorerChunk::BaseExplorerChunk(SoftwareRenderer* renderer) -{ - _renderer = renderer; - _color_profile = new ColorProfile; - - priority = 0.0; - _reset_needed = false; - - _texture = new QImage(1, 1, QImage::Format_ARGB32); - _texture_id = 0; - _texture_changed = false; - _texture_current_size = 0; - _texture_max_size = 0; -} - -BaseExplorerChunk::~BaseExplorerChunk() -{ - _lock_data.lock(); - delete _color_profile; - delete _texture; - _lock_data.unlock(); -} - -bool BaseExplorerChunk::maintain() -{ - bool subchanged; - - _lock_data.lock(); - if (_reset_needed) - { - _reset_needed = false; - _texture_current_size = 0; - onResetEvent(); - } - _lock_data.unlock(); - - subchanged = onMaintainEvent(); - - // Improve texture resolution - if (_texture_current_size < _texture_max_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, 1.0 - (double)j / (double)new_texture_size); - color = _color_profile->apply(color); - color.normalize(); - new_image->setPixel(i, j, color.to32BitBGRA()); - } - } - } - - _lock_data.lock(); - delete _texture; - _texture = new_image; - _texture_current_size = new_texture_size; - _texture_changed = true; - _lock_data.unlock(); - - /*if (_texture_current_size < 4 && _texture_current_size < _texture_max_size) - { - maintain(); - }*/ - - return true; - } - else - { - return subchanged; - } -} - -void BaseExplorerChunk::updatePriority(CameraDefinition* camera) -{ - if (_reset_needed || (_texture_max_size > 1 && _texture_current_size <= 1)) - { - priority = 1000.0; - } - else if (_texture_current_size == _texture_max_size) - { - priority = -1000.0; - } - else - { - priority = getDisplayedSizeHint(camera) - _texture_current_size; - } - onCameraEvent(camera); -} - -void BaseExplorerChunk::render(QGLWidget* widget) -{ - // Put texture in place - _lock_data.lock(); - if (_texture_changed) - { - _texture_changed = false; - if (_texture_id) - { - widget->deleteTexture(_texture_id); - } - // TODO Only do the scale if not power-of-two textures are unsupported by GPU - _texture_id = widget->bindTexture(_texture->scaled(_texture_current_size, _texture_current_size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - //_texture_id = widget->bindTexture(*_texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - } - else - { - glBindTexture(GL_TEXTURE_2D, _texture_id); - } - _lock_data.unlock(); - - // Delegate poly rendering to subclass - if (!_reset_needed) - { - onRenderEvent(widget); - } -} - -void BaseExplorerChunk::askReset() -{ - _reset_needed = true; -} - -void BaseExplorerChunk::setMaxTextureSize(int size) -{ - _texture_max_size = size; -} - -void BaseExplorerChunk::onCameraEvent(CameraDefinition*) -{ -} - -void BaseExplorerChunk::onResetEvent() -{ -} - -bool BaseExplorerChunk::onMaintainEvent() -{ - return false; -} - -void BaseExplorerChunk::onRenderEvent(QGLWidget*) -{ -} - -double BaseExplorerChunk::getDisplayedSizeHint(CameraDefinition*) -{ - return 0.0; -} - -Color BaseExplorerChunk::getTextureColor(double, double) -{ - return COLOR_TRANSPARENT; -} diff --git a/src/render/opengl/BaseExplorerChunk.h b/src/render/opengl/BaseExplorerChunk.h deleted file mode 100644 index 08c9780..0000000 --- a/src/render/opengl/BaseExplorerChunk.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef BASEEXPLORERCHUNK_H -#define BASEEXPLORERCHUNK_H - -#include "opengl_global.h" - -#include -#include "Color.h" - -class QImage; -class QGLWidget; - -namespace paysages { -namespace opengl { - -class BaseExplorerChunk -{ -public: - virtual ~BaseExplorerChunk(); - - bool maintain(); - void updatePriority(CameraDefinition* camera); - void render(QGLWidget* widget); - - double priority; -protected: - BaseExplorerChunk(SoftwareRenderer* renderer); - - inline SoftwareRenderer* renderer() {return _renderer;} - - void askReset(); - void setMaxTextureSize(int size); - - virtual void onCameraEvent(CameraDefinition* camera); - virtual void onResetEvent(); - virtual bool onMaintainEvent(); - virtual void onRenderEvent(QGLWidget* widget); - virtual double getDisplayedSizeHint(CameraDefinition* camera); - virtual Color getTextureColor(double x, double y); - - QMutex _lock_data; - -private: - SoftwareRenderer* _renderer; - ColorProfile* _color_profile; - - bool _reset_needed; - - QImage* _texture; - unsigned int _texture_id; - bool _texture_changed; - int _texture_current_size; - int _texture_max_size; -}; - -} -} - -#endif // BASEEXPLORERCHUNK_H diff --git a/src/render/opengl/ExplorerChunkSky.cpp b/src/render/opengl/ExplorerChunkSky.cpp deleted file mode 100644 index 3ebd5a9..0000000 --- a/src/render/opengl/ExplorerChunkSky.cpp +++ /dev/null @@ -1,148 +0,0 @@ -#include "ExplorerChunkSky.h" - -#include -#include -#include "CameraDefinition.h" -#include "SoftwareRenderer.h" -#include "AtmosphereRenderer.h" -#include "AtmosphereResult.h" - -ExplorerChunkSky::ExplorerChunkSky(SoftwareRenderer* renderer, double size, SkyboxOrientation orientation) : BaseExplorerChunk(renderer) -{ - _box_size = size; - _orientation = orientation; - _center = VECTOR_ZERO; - - setMaxTextureSize(256); - maintain(); -} - -void ExplorerChunkSky::onCameraEvent(CameraDefinition* camera) -{ - _center = camera->getLocation(); -} - -void ExplorerChunkSky::onRenderEvent(QGLWidget*) -{ - double size = _box_size; - Vector3 camera = _center; - - glBegin(GL_QUADS); - switch (_orientation) - { - case SKYBOX_NORTH: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z - size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z - size); - break; - case SKYBOX_SOUTH: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z + size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z + size); - break; - case SKYBOX_EAST: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z - size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z + size); - break; - case SKYBOX_WEST: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z + size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z - size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z - size); - break; - case SKYBOX_TOP: - glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y + size, camera.z + size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y + size, camera.z - size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y + size, camera.z - size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y + size, camera.z + size); - break; - case SKYBOX_BOTTOM: - /*glTexCoord2d(0.0, 0.0); - glVertex3d(camera.x - size, camera.y - size, camera.z - size); - glTexCoord2d(0.0, 1.0); - glVertex3d(camera.x - size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 1.0); - glVertex3d(camera.x + size, camera.y - size, camera.z + size); - glTexCoord2d(1.0, 0.0); - glVertex3d(camera.x + size, camera.y - size, camera.z - size);*/ - break; - } - glEnd(); -} - -double ExplorerChunkSky::getDisplayedSizeHint(CameraDefinition*) -{ - return 1000.0; -} - -Color ExplorerChunkSky::getTextureColor(double x, double y) -{ - Vector3 location; - - x -= 0.5; - y -= 0.5; - - switch (_orientation) - { - case SKYBOX_NORTH: - location.x = x; - location.y = -y; - location.z = -0.5; - break; - case SKYBOX_SOUTH: - location.x = -x; - location.y = -y; - location.z = 0.5; - break; - case SKYBOX_EAST: - location.x = 0.5; - location.y = -y; - location.z = x; - break; - case SKYBOX_WEST: - location.x = -0.5; - location.y = -y; - location.z = -x; - break; - case SKYBOX_TOP: - location.x = x; - location.y = 0.5; - location.z = -y; - break; - case SKYBOX_BOTTOM: - location.x = x; - location.y = -0.5; - location.z = y; - break; - } - location = location.normalize(); - if (location.y < 0.0) - { - location.y = 0.0; - } - return renderer()->getAtmosphereRenderer()->getSkyColor(location).final; -} diff --git a/src/render/opengl/ExplorerChunkSky.h b/src/render/opengl/ExplorerChunkSky.h deleted file mode 100644 index 7948d79..0000000 --- a/src/render/opengl/ExplorerChunkSky.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef EXPLORERCHUNKSKY_H -#define EXPLORERCHUNKSKY_H - -#include "opengl_global.h" - -#include "BaseExplorerChunk.h" - -#include "Vector3.h" - -namespace paysages { -namespace opengl { - -enum SkyboxOrientation -{ - SKYBOX_NORTH, - SKYBOX_SOUTH, - SKYBOX_WEST, - SKYBOX_EAST, - SKYBOX_TOP, - SKYBOX_BOTTOM -}; - -class OPENGLSHARED_EXPORT ExplorerChunkSky:public BaseExplorerChunk -{ -public: - ExplorerChunkSky(SoftwareRenderer* renderer, double size, SkyboxOrientation orientation); - - void onCameraEvent(CameraDefinition* camera); - void onRenderEvent(QGLWidget* widget); - double getDisplayedSizeHint(CameraDefinition* camera); - Color getTextureColor(double x, double y); - -private: - SkyboxOrientation _orientation; - double _box_size; - Vector3 _center; -}; - -} -} - -#endif // EXPLORERCHUNKSKY_H diff --git a/src/render/opengl/ExplorerChunkTerrain.cpp b/src/render/opengl/ExplorerChunkTerrain.cpp index ed0d595..05edbba 100644 --- a/src/render/opengl/ExplorerChunkTerrain.cpp +++ b/src/render/opengl/ExplorerChunkTerrain.cpp @@ -1,13 +1,28 @@ #include "ExplorerChunkTerrain.h" +#include OPENGL_FUNCTIONS_INCLUDE #include -#include +#include +#include "ColorProfile.h" #include "CameraDefinition.h" -#include "SoftwareRenderer.h" +#include "OpenGLRenderer.h" #include "TerrainRenderer.h" +#include "VertexArray.h" -ExplorerChunkTerrain::ExplorerChunkTerrain(SoftwareRenderer* renderer, double x, double z, double size, int nbchunks, double water_height) : BaseExplorerChunk(renderer) +ExplorerChunkTerrain::ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks, double water_height): + _renderer(renderer) { + _color_profile = new ColorProfile(ColorProfile::TONE_MAPPING_REIHNARD, 2.0); + + priority = 0.0; + _reset_needed = false; + + _texture = new QImage(1, 1, QImage::Format_RGBA8888); + texture_id = 0; + _texture_changed = false; + _texture_current_size = 0; + _texture_max_size = 0; + _startx = x; _startz = z; _size = size; @@ -18,8 +33,11 @@ ExplorerChunkTerrain::ExplorerChunkTerrain(SoftwareRenderer* renderer, double x, _water_height = water_height; _overwater = false; - _tessellation_max_size = 32; - _tessellation = new double[(_tessellation_max_size + 1) * (_tessellation_max_size + 1)]; + tessellation_count = 33; + 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; @@ -31,44 +49,108 @@ ExplorerChunkTerrain::ExplorerChunkTerrain(SoftwareRenderer* renderer, double x, ExplorerChunkTerrain::~ExplorerChunkTerrain() { _lock_data.lock(); - delete [] _tessellation; + delete _color_profile; + delete _texture; + delete tessellated; _lock_data.unlock(); } -void ExplorerChunkTerrain::onResetEvent() +bool ExplorerChunkTerrain::maintain() { - _tessellation_current_size = 0; - _overwater = false; + bool subchanged; + + _lock_data.lock(); + if (_reset_needed) + { + _reset_needed = false; + _texture_current_size = 0; + _tessellation_current_size = 0; + _overwater = false; + } + _lock_data.unlock(); + + subchanged = onMaintainEvent(); + + // Improve texture resolution + if (_texture_current_size < _texture_max_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 = _color_profile->apply(color); + color.normalize(); + new_image->setPixel(i, j, color.to32BitRGBA()); + } + } + } + + _lock_data.lock(); + delete _texture; + _texture = new_image; + _texture_current_size = new_texture_size; + _texture_changed = true; + _lock_data.unlock(); + + /*if (_texture_current_size < 4 && _texture_current_size < _texture_max_size) + { + maintain(); + }*/ + + return true; + } + else + { + return subchanged; + } } bool ExplorerChunkTerrain::onMaintainEvent() { - SoftwareRenderer* renderer = this->renderer(); - // Improve heightmap resolution if (_tessellation_current_size < _tessellation_max_size) { int new_tessellation_size = _tessellation_current_size ? _tessellation_current_size * 4 : 2; 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 height = renderer->getTerrainRenderer()->getHeight(_startx + _tessellation_step * (double) i, _startz + _tessellation_step * (double) j, 1); + double x = _startx + _tessellation_step * (float)i; + double z = _startz + _tessellation_step * (float)j; + + double height = _renderer->getTerrainRenderer()->getHeight(x, z, 1); if (height >= _water_height) { _overwater = true; } - _tessellation[j * (_tessellation_max_size + 1) + i] = 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); } } } _lock_data.lock(); _tessellation_current_size = new_tessellation_size; + tessellated->setAutoGridIndices(tessellation_count, new_tessellation_inc); _lock_data.unlock(); if (_tessellation_current_size < 4 && _tessellation_current_size < _tessellation_max_size) @@ -84,8 +166,21 @@ bool ExplorerChunkTerrain::onMaintainEvent() } } -void ExplorerChunkTerrain::onCameraEvent(CameraDefinition* camera) +void ExplorerChunkTerrain::updatePriority(CameraDefinition* camera) { + if (_reset_needed || (_texture_max_size > 1 && _texture_current_size <= 1)) + { + priority = 1000.0; + } + else if (_texture_current_size == _texture_max_size) + { + priority = -1000.0; + } + else + { + priority = getDisplayedSizeHint(camera) - _texture_current_size; + } + Vector3 camera_location = camera->getLocation(); // Handle position @@ -116,33 +211,66 @@ void ExplorerChunkTerrain::onCameraEvent(CameraDefinition* camera) _lock_data.unlock(); } -void ExplorerChunkTerrain::onRenderEvent(QGLWidget*) +void ExplorerChunkTerrain::render(QOpenGLShaderProgram* program, OpenGLFunctions* functions) { + // Put texture in place _lock_data.lock(); - int tessellation_size = _tessellation_current_size; - double tsize = 1.0 / (double) _tessellation_max_size; + 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(); - if (tessellation_size <= 1 or not _overwater) + // Render tessellated mesh + if (!_reset_needed) { - return; - } + _lock_data.lock(); + int tessellation_size = _tessellation_current_size; + _lock_data.unlock(); - int tessellation_inc = _tessellation_max_size / (double) tessellation_size; - for (int j = 0; j < _tessellation_max_size; j += tessellation_inc) - { - glBegin(GL_QUAD_STRIP); - for (int i = 0; i <= _tessellation_max_size; i += tessellation_inc) + if (tessellation_size <= 1 or not _overwater) { - glTexCoord2d(tsize * (double) i, tsize * (double) j); - glVertex3d(_startx + _tessellation_step * (double) i, _tessellation[j * (_tessellation_max_size + 1) + i], _startz + _tessellation_step * (double) j); - glTexCoord2d(tsize * (double) i, tsize * (double) (j + tessellation_inc)); - glVertex3d(_startx + _tessellation_step * (double) i, _tessellation[(j + tessellation_inc) * (_tessellation_max_size + 1) + i], _startz + _tessellation_step * (double) (j + tessellation_inc)); + return; } - glEnd(); + + _lock_data.lock(); // TEMP + // TEMP + functions->glActiveTexture(GL_TEXTURE0 + 3); + functions->glBindTexture(GL_TEXTURE_2D, texture_id); + program->setUniformValue("groundTexture", 3); + + tessellated->render(program, functions); + _lock_data.unlock(); // TEMP } } +void ExplorerChunkTerrain::askReset() +{ + _reset_needed = true; +} + +void ExplorerChunkTerrain::setMaxTextureSize(int size) +{ + _texture_max_size = size; +} + double ExplorerChunkTerrain::getDisplayedSizeHint(CameraDefinition* camera) { double distance; @@ -170,7 +298,7 @@ double ExplorerChunkTerrain::getDisplayedSizeHint(CameraDefinition* camera) Color ExplorerChunkTerrain::getTextureColor(double x, double y) { Vector3 location = {_startx + x * _size, 0.0, _startz + y * _size}; - return renderer()->getTerrainRenderer()->getFinalColor(location, 0.01); + return _renderer->getTerrainRenderer()->getFinalColor(location, 0.01); } Vector3 ExplorerChunkTerrain::getCenter() diff --git a/src/render/opengl/ExplorerChunkTerrain.h b/src/render/opengl/ExplorerChunkTerrain.h index ce21462..c1ae2ca 100644 --- a/src/render/opengl/ExplorerChunkTerrain.h +++ b/src/render/opengl/ExplorerChunkTerrain.h @@ -1,26 +1,41 @@ #ifndef EXPLORERCHUNKTERRAIN_H #define EXPLORERCHUNKTERRAIN_H -#include "BaseExplorerChunk.h" +#include "opengl_global.h" -#include "Vector3.h" +#include +class QImage; +class QOpenGLShaderProgram; namespace paysages { namespace opengl { -class OPENGLSHARED_EXPORT ExplorerChunkTerrain:public BaseExplorerChunk +class OPENGLSHARED_EXPORT ExplorerChunkTerrain { public: - ExplorerChunkTerrain(SoftwareRenderer* renderer, double x, double z, double size, int nbchunks, double water_height); + typedef struct + { + float location[3]; + float uv[2]; + } TerrainVertex; + +public: + ExplorerChunkTerrain(OpenGLRenderer* renderer, double x, double z, double size, int nbchunks, double water_height); ~ExplorerChunkTerrain(); - void onCameraEvent(CameraDefinition* camera); - void onResetEvent(); + bool maintain(); + void updatePriority(CameraDefinition* camera); + void render(QOpenGLShaderProgram* program, OpenGLFunctions* functions); + + void askReset(); + void setMaxTextureSize(int size); + bool onMaintainEvent(); - void onRenderEvent(QGLWidget* widget); double getDisplayedSizeHint(CameraDefinition* camera); Color getTextureColor(double x, double y); + double priority; + private: Vector3 getCenter(); @@ -33,11 +48,24 @@ private: double _water_height; bool _overwater; - double* _tessellation; + int tessellation_count; + VertexArray *tessellated; int _tessellation_max_size; int _tessellation_current_size; double _tessellation_step; + QMutex _lock_data; + + OpenGLRenderer* _renderer; + ColorProfile* _color_profile; + + bool _reset_needed; + + QImage* _texture; + unsigned int texture_id; + bool _texture_changed; + int _texture_current_size; + int _texture_max_size; }; } diff --git a/src/render/opengl/OpenGLPart.cpp b/src/render/opengl/OpenGLPart.cpp new file mode 100644 index 0000000..807f8cf --- /dev/null +++ b/src/render/opengl/OpenGLPart.cpp @@ -0,0 +1,49 @@ +#include "OpenGLPart.h" + +#include +#include +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "CameraDefinition.h" +#include "AtmosphereDefinition.h" +#include "AtmosphereRenderer.h" +#include "Scenery.h" + +OpenGLPart::OpenGLPart(OpenGLRenderer* renderer): + renderer(renderer) +{ +} + +OpenGLPart::~OpenGLPart() +{ + QMapIterator i(shaders); + while (i.hasNext()) + { + i.next(); + delete i.value(); + } +} + +OpenGLShaderProgram* OpenGLPart::createShader(QString name) +{ + OpenGLShaderProgram* program = new OpenGLShaderProgram(name, renderer); + + if (!shaders.contains(name)) + { + shaders[name] = program; + return program; + } + else + { + return 0; + } +} + +void OpenGLPart::updateScenery(bool onlyCommon) +{ + // Let subclass do its own collecting + if (not onlyCommon) + { + update(); + } +} diff --git a/src/render/opengl/OpenGLPart.h b/src/render/opengl/OpenGLPart.h new file mode 100644 index 0000000..906ad7c --- /dev/null +++ b/src/render/opengl/OpenGLPart.h @@ -0,0 +1,44 @@ +#ifndef OPENGLPART_H +#define OPENGLPART_H + +#include "opengl_global.h" + +#include +#include + +namespace paysages { +namespace opengl { + +// Class that can be inherited by scenery parts, to use OpenGL features + +class OPENGLSHARED_EXPORT OpenGLPart +{ +public: + OpenGLPart(OpenGLRenderer* renderer); + virtual ~OpenGLPart(); + + // Initialize the part rendering (create shaders, prepare static textures...) + virtual void initialize() = 0; + + // Update parameters from scenery + virtual void update() = 0; + + // Do the rendering + virtual void render() = 0; + + void updateScenery(bool onlyCommon=false); + +protected: + // Create a shader program + OpenGLShaderProgram* createShader(QString name); + + // Access to the main scenery renderer + OpenGLRenderer* renderer; + +private: + QMap shaders; +}; +} +} + +#endif // OPENGLPART_H diff --git a/src/render/opengl/OpenGLRenderer.cpp b/src/render/opengl/OpenGLRenderer.cpp index 31bbfc6..e86ef48 100644 --- a/src/render/opengl/OpenGLRenderer.cpp +++ b/src/render/opengl/OpenGLRenderer.cpp @@ -1,62 +1,147 @@ #include "OpenGLRenderer.h" -#include -#include -#include -#include "Scenery.h" +#include OPENGL_FUNCTIONS_INCLUDE #include "CameraDefinition.h" +#include "OpenGLSharedState.h" +#include "OpenGLSkybox.h" +#include "OpenGLWater.h" +#include "OpenGLTerrain.h" +#include "Scenery.h" +#include "LightingManager.h" +#include "Logs.h" + +#include "GL/glu.h" // TEMP OpenGLRenderer::OpenGLRenderer(Scenery* scenery): SoftwareRenderer(scenery) { + ready = false; + + functions = new OpenGLFunctions(); + shared_state = new OpenGLSharedState(); + + shared_state->set("viewDistance", 300.0); + shared_state->set("exposure", 1.6); + + skybox = new OpenGLSkybox(this); + water = new OpenGLWater(this); + terrain = new OpenGLTerrain(this); } OpenGLRenderer::~OpenGLRenderer() { + delete skybox; + delete water; + delete terrain; + + delete functions; + delete shared_state; } void OpenGLRenderer::initialize() { - glClearColor(0.0, 0.0, 0.0, 0.0); + ready = functions->initializeOpenGLFunctions(); - glDisable(GL_LIGHTING); + if (ready) + { + functions->glClearColor(0.0, 0.0, 0.0, 0.0); - glFrontFace(GL_CCW); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); + functions->glDisable(GL_LIGHTING); - glDepthFunc(GL_LESS); - glDepthMask(1); - glEnable(GL_DEPTH_TEST); + functions->glFrontFace(GL_CCW); + functions->glCullFace(GL_BACK); + functions->glEnable(GL_CULL_FACE); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glEnable(GL_LINE_SMOOTH); - glLineWidth(1.0); + functions->glDepthFunc(GL_LESS); + functions->glDepthMask(1); + functions->glEnable(GL_DEPTH_TEST); - glDisable(GL_FOG); + functions->glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + functions->glEnable(GL_LINE_SMOOTH); + functions->glLineWidth(1.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + functions->glDisable(GL_FOG); - prepare(); + functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + prepare(); + + disableClouds(); + getLightingManager()->setSpecularity(false); + + skybox->initialize(); + skybox->updateScenery(); + + water->initialize(); + water->updateScenery(); + + terrain->initialize(); + terrain->updateScenery(); + + cameraChangeEvent(getScenery()->getCamera()); + } + else + { + logError("Failed to initialize OpenGL bindings"); + } } void OpenGLRenderer::resize(int width, int height) { - CameraPerspective perspective; + if (ready) + { + functions->glViewport(0, 0, width, height); + } + getScenery()->getCamera()->setRenderSize(width, height); + render_camera->setRenderSize(width, height); - glViewport(0, 0, width, height); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - perspective = render_camera->getPerspective(); - gluPerspective(perspective.yfov * 180.0 / M_PI, perspective.xratio, perspective.znear, perspective.zfar); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + cameraChangeEvent(getScenery()->getCamera()); } void OpenGLRenderer::paint() { + if (ready) + { + functions->glClearColor(0.0, 0.0, 0.0, 0.0); + functions->glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + functions->glEnable(GL_BLEND); + functions->glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + skybox->render(); + terrain->render(); + water->render(); + + int error_code; + while ((error_code = glGetError()) != GL_NO_ERROR) + { + logWarning("[OpenGL] ERROR : %s", (const char*)gluErrorString(error_code)); + } + } +} + +void OpenGLRenderer::cameraChangeEvent(CameraDefinition *camera) +{ + // Get camera info + Vector3 location = camera->getLocation(); + Vector3 target = camera->getTarget(); + Vector3 up = camera->getUpVector(); + CameraPerspective perspective = camera->getPerspective(); + + // Compute matrix + QMatrix4x4 transform; + transform.setToIdentity(); + transform.lookAt(QVector3D(location.x, location.y, location.z), + QVector3D(target.x, target.y, target.z), + QVector3D(up.x, up.y, up.z)); + + QMatrix4x4 projection; + projection.setToIdentity(); + projection.perspective(perspective.yfov * 180.0 / M_PI, perspective.xratio, perspective.znear, perspective.zfar); + + // Set in shaders + shared_state->set("cameraLocation", location); + shared_state->set("viewMatrix", projection * transform); } double OpenGLRenderer::getPrecision(const Vector3 &) diff --git a/src/render/opengl/OpenGLRenderer.h b/src/render/opengl/OpenGLRenderer.h index 075b3ac..d4daeab 100644 --- a/src/render/opengl/OpenGLRenderer.h +++ b/src/render/opengl/OpenGLRenderer.h @@ -21,8 +21,23 @@ public: void resize(int width, int height); void paint(); + void cameraChangeEvent(CameraDefinition* camera); + + inline OpenGLFunctions* getOpenGlFunctions() const {return functions;} + inline OpenGLSharedState* getSharedState() const {return shared_state;} + virtual double getPrecision(const Vector3 &location) override; virtual Color applyMediumTraversal(Vector3 location, Color color) override; + +private: + bool ready; + + OpenGLFunctions* functions; + OpenGLSharedState* shared_state; + + OpenGLSkybox* skybox; + OpenGLWater* water; + OpenGLTerrain* terrain; }; } diff --git a/src/render/opengl/OpenGLShaderProgram.cpp b/src/render/opengl/OpenGLShaderProgram.cpp new file mode 100644 index 0000000..1b07d7d --- /dev/null +++ b/src/render/opengl/OpenGLShaderProgram.cpp @@ -0,0 +1,115 @@ +#include "OpenGLShaderProgram.h" + +#include OPENGL_FUNCTIONS_INCLUDE +#include +#include +#include "OpenGLRenderer.h" +#include "OpenGLSharedState.h" +#include "Texture2D.h" +#include "Texture3D.h" +#include "Texture4D.h" +#include "Color.h" +#include "Logs.h" + +OpenGLShaderProgram::OpenGLShaderProgram(QString name, OpenGLRenderer* renderer): + renderer(renderer), name(name) +{ + program = new QOpenGLShaderProgram(); + functions = renderer->getOpenGlFunctions(); + compiled = false; +} + +OpenGLShaderProgram::~OpenGLShaderProgram() +{ + delete program; +} + +void OpenGLShaderProgram::addVertexSource(QString path) +{ + QFile file(QString(":/shaders/%1.vert").arg(path)); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + source_vertex += QString(file.readAll()).toStdString(); + } + else + { + logError() << "Can't open vertex file " << file.fileName(); + } +} + +void OpenGLShaderProgram::addFragmentSource(QString path) +{ + QFile file(QString(":/shaders/%1.frag").arg(path)); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + source_fragment += QString(file.readAll()).toStdString(); + } + else + { + logError() << "Can't open fragment file " << file.fileName(); + } +} + +void OpenGLShaderProgram::compile() +{ + program->addShaderFromSourceCode(QOpenGLShader::Vertex, QString::fromStdString(source_vertex)); + program->addShaderFromSourceCode(QOpenGLShader::Fragment, QString::fromStdString(source_fragment)); + + if (not program->link()) + { + qWarning() << "Error while compiling shader " << name << "\n" << program->log() << "\n"; + } + else if (program->log().length() > 0) + { + qDebug() << "Shader " << name << " compilation output:\n" << program->log() << "\n"; + } +} + +void OpenGLShaderProgram::bind() +{ + if (not compiled) + { + compile(); + compiled = true; + } + + program->bind(); + + int texture_unit = 0; + renderer->getSharedState()->apply(this, texture_unit); +} + +void OpenGLShaderProgram::release() +{ + program->release(); +} + +void OpenGLShaderProgram::drawTriangles(float* vertices, int triangle_count) +{ + 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) +{ + 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); + + release(); +} diff --git a/src/render/opengl/OpenGLShaderProgram.h b/src/render/opengl/OpenGLShaderProgram.h new file mode 100644 index 0000000..5caabf5 --- /dev/null +++ b/src/render/opengl/OpenGLShaderProgram.h @@ -0,0 +1,52 @@ +#ifndef OPENGLSHADERPROGRAM_H +#define OPENGLSHADERPROGRAM_H + +#include "opengl_global.h" + +#include + +class QOpenGLShaderProgram; + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLShaderProgram +{ +public: + OpenGLShaderProgram(QString name, OpenGLRenderer* renderer); + ~OpenGLShaderProgram(); + + void addVertexSource(QString path); + void addFragmentSource(QString path); + + void drawTriangles(float* vertices, int triangle_count); + void drawTriangleStrip(float* vertices, int vertex_count); + + void bind(); + void release(); + + inline QOpenGLShaderProgram* getProgram() const {return program;} + inline OpenGLRenderer* getRenderer() const {return renderer;} + +protected: + friend class OpenGLVariable; + +private: + void compile(); + + bool compiled; + + OpenGLRenderer* renderer; + + QString name; + QOpenGLShaderProgram* program; + OpenGLFunctions* functions; + + std::string source_vertex; + std::string source_fragment; +}; + +} +} + +#endif // OPENGLSHADERPROGRAM_H diff --git a/src/render/opengl/OpenGLSharedState.cpp b/src/render/opengl/OpenGLSharedState.cpp new file mode 100644 index 0000000..b59951d --- /dev/null +++ b/src/render/opengl/OpenGLSharedState.cpp @@ -0,0 +1,23 @@ +#include "OpenGLSharedState.h" + +OpenGLSharedState::OpenGLSharedState() +{ +} + +void OpenGLSharedState::apply(OpenGLShaderProgram *program, int &texture_unit) +{ + for (const auto &pair : variables) + { + pair.second->apply(program, texture_unit); + } +} + +OpenGLVariable *OpenGLSharedState::get(const std::string &name) +{ + OpenGLVariable*& var = variables[name]; + if (var == 0) + { + var = new OpenGLVariable(name); + } + return var; +} diff --git a/src/render/opengl/OpenGLSharedState.h b/src/render/opengl/OpenGLSharedState.h new file mode 100644 index 0000000..afe8817 --- /dev/null +++ b/src/render/opengl/OpenGLSharedState.h @@ -0,0 +1,48 @@ +#ifndef OPENGLSHAREDSTATE_H +#define OPENGLSHAREDSTATE_H + +#include "opengl_global.h" + +#include +#include "OpenGLVariable.h" + +namespace paysages { +namespace opengl { + +/*! + * \brief OpenGL variables that can be shared between shaders. + */ +class OPENGLSHARED_EXPORT OpenGLSharedState +{ +public: + OpenGLSharedState(); + + /*! + * \brief Apply the stored variables to the bound program. + */ + void apply(OpenGLShaderProgram* program, int &texture_unit); + + /*! + * \brief Get or create a variable in the state. + */ + OpenGLVariable *get(const std::string &name); + + // Shortcuts + inline void set(const std::string &name, const Texture2D *texture) {get(name)->set(texture);} + inline void set(const std::string &name, const Texture3D *texture) {get(name)->set(texture);} + inline void set(const std::string &name, const Texture4D *texture) {get(name)->set(texture);} + inline void set(const std::string &name, float value) {get(name)->set(value);} + inline void set(const std::string &name, const Vector3 &vector) {get(name)->set(vector);} + inline void set(const std::string &name, const QVector3D &vector) {get(name)->set(vector);} + inline void set(const std::string &name, const Matrix4 &matrix) {get(name)->set(matrix);} + inline void set(const std::string &name, const QMatrix4x4 &matrix) {get(name)->set(matrix);} + inline void set(const std::string &name, const Color &color) {get(name)->set(color);} + +private: + std::map variables; +}; + +} +} + +#endif // OPENGLSHAREDSTATE_H diff --git a/src/render/opengl/OpenGLSkybox.cpp b/src/render/opengl/OpenGLSkybox.cpp new file mode 100644 index 0000000..3f0b2dd --- /dev/null +++ b/src/render/opengl/OpenGLSkybox.cpp @@ -0,0 +1,104 @@ +#include "OpenGLSkybox.h" + +#include +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "OpenGLSharedState.h" +#include "Scenery.h" +#include "AtmosphereDefinition.h" +#include "AtmosphereRenderer.h" +#include "AtmosphereModelBruneton.h" + +OpenGLSkybox::OpenGLSkybox(OpenGLRenderer* renderer): + OpenGLPart(renderer) +{ + vertices = new float[14 * 3]; + daytime = renderer->getScenery()->getAtmosphere()->_daytime; +} + +OpenGLSkybox::~OpenGLSkybox() +{ + delete[] vertices; +} + +void OpenGLSkybox::initialize() +{ + program = createShader("skybox"); + program->addVertexSource("skybox"); + program->addFragmentSource("bruneton"); + program->addFragmentSource("tonemapping"); + program->addFragmentSource("skybox"); + + setVertex(0, 1.0f, 1.0f, 1.0f); + setVertex(12, 1.0f, 1.0f, 1.0f); + + setVertex(1, 1.0f, -1.0f, 1.0f); + setVertex(5, 1.0f, -1.0f, 1.0f); + setVertex(13, 1.0f, -1.0f, 1.0f); + + setVertex(2, -1.0f, 1.0f, 1.0f); + setVertex(10, -1.0f, 1.0f, 1.0f); + + setVertex(3, -1.0f, -1.0f, 1.0f); + + setVertex(4, -1.0f, -1.0f, -1.0f); + setVertex(8, -1.0f, -1.0f, -1.0f); + + setVertex(6, 1.0f, -1.0f, -1.0f); + + setVertex(7, 1.0f, 1.0f, -1.0f); + setVertex(11, 1.0f, 1.0f, -1.0f); + + setVertex(9, -1.0f, 1.0f, -1.0f); +} + +void OpenGLSkybox::update() +{ + Vector3 sun_direction = renderer->getAtmosphereRenderer()->getSunDirection(); + renderer->getSharedState()->set("sunDirection", sun_direction); + + Color sun_color = renderer->getScenery()->getAtmosphere()->sun_color; + renderer->getSharedState()->set("sunColor", sun_color); + + SoftwareBrunetonAtmosphereRenderer* bruneton = (SoftwareBrunetonAtmosphereRenderer*)renderer->getAtmosphereRenderer(); + renderer->getSharedState()->set("transmittanceTexture", bruneton->getModel()->getTextureTransmittance()); + renderer->getSharedState()->set("inscatterTexture", bruneton->getModel()->getTextureInscatter()); +} + +void OpenGLSkybox::render() +{ + program->drawTriangleStrip(vertices, 14); +} + +void OpenGLSkybox::alterDayTime(double delta) +{ +#if 0 + Scenery* scenery = renderer->getScenery(); + AtmosphereDefinition* definition = scenery->getAtmosphere()->definition; + daytime = fmod(daytime + delta * 0.001, 1.0); + // TEMP + if (daytime > 0.8) + { + daytime -= 0.6; + } + if (daytime < 0.2) + { + daytime += 0.6; + } + + definition->hour = (int)(daytime * 24.0); + definition->minute = (int)((daytime - (((double)definition->hour) / 24.0)) * 1440.0); + + AtmosphereDefinitionClass.validate(definition); + + // TODO Update only the sun + updateScenery(scenery, true); +#endif +} + +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 new file mode 100644 index 0000000..e2a72cb --- /dev/null +++ b/src/render/opengl/OpenGLSkybox.h @@ -0,0 +1,35 @@ +#ifndef OPENGLSKYBOX_H +#define OPENGLSKYBOX_H + +#include "opengl_global.h" + +#include "OpenGLPart.h" + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLSkybox: public OpenGLPart +{ +public: + OpenGLSkybox(OpenGLRenderer* renderer); + virtual ~OpenGLSkybox(); + + virtual void initialize() override; + virtual void update() override; + virtual void render() override; + + void alterDayTime(double delta); + +private: + void setVertex(int i, float x, float y, float z); + + OpenGLShaderProgram* program; + float* vertices; + + double daytime; +}; + +} +} + +#endif // OPENGLSKYBOX_H diff --git a/src/render/opengl/OpenGLTerrain.cpp b/src/render/opengl/OpenGLTerrain.cpp new file mode 100644 index 0000000..c251f84 --- /dev/null +++ b/src/render/opengl/OpenGLTerrain.cpp @@ -0,0 +1,128 @@ +#include "OpenGLTerrain.h" + +#include OPENGL_FUNCTIONS_INCLUDE +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "ParallelPool.h" +#include "Thread.h" +#include "ExplorerChunkTerrain.h" +#include "WaterRenderer.h" +#include "CameraDefinition.h" +#include "Scenery.h" + +class ChunkMaintenanceThreads:public ParallelPool +{ +public: + ChunkMaintenanceThreads(OpenGLTerrain* terrain): + terrain(terrain) + { + } + + virtual void work() override + { + while (running) + { + terrain->performChunksMaintenance(); + Thread::timeSleepMs(10); + } + } + +private: + OpenGLTerrain* terrain; +}; + +OpenGLTerrain::OpenGLTerrain(OpenGLRenderer *renderer): + OpenGLPart(renderer) +{ + work = new ChunkMaintenanceThreads(this); +} + +OpenGLTerrain::~OpenGLTerrain() +{ + delete work; + + for (int i = 0; i < _chunks.count(); i++) + { + delete _chunks[i]; + } +} + +void OpenGLTerrain::initialize() +{ + // Prepare shader programs + program = createShader("terrain"); + program->addVertexSource("terrain"); + program->addFragmentSource("bruneton"); + program->addFragmentSource("tonemapping"); + program->addFragmentSource("fadeout"); + program->addFragmentSource("terrain"); + + // Add terrain chunks + int chunks = 25; + double size = 800.0; + double chunksize = size / (double) chunks; + double start = -size / 2.0; + double water_height = renderer->getWaterRenderer()->getHeightInfo().base_height; + for (int i = 0; i < chunks; i++) + { + for (int j = 0; j < chunks; j++) + { + ExplorerChunkTerrain* chunk = new ExplorerChunkTerrain(renderer, start + chunksize * (double) i, start + chunksize * (double) j, chunksize, chunks, water_height); + _chunks.append(chunk); + _updateQueue.append(chunk); + } + } + + // Start chunks maintenance + work->start(); +} + +void OpenGLTerrain::update() +{ +} + +void OpenGLTerrain::render() +{ + program->bind(); + + for (int i = 0; i < _chunks.count(); i++) + { + _chunks[i]->render(program->getProgram(), renderer->getOpenGlFunctions()); + } + + program->release(); +} + +static bool _cmpChunks(const ExplorerChunkTerrain* c1, const ExplorerChunkTerrain* c2) +{ + return c1->priority > c2->priority; +} + +void OpenGLTerrain::performChunksMaintenance() +{ + CameraDefinition* camera = renderer->getScenery()->getCamera(); + ExplorerChunkTerrain* chunk; + + _lock_chunks.lock(); + if (_updateQueue.count() > 0) + { + chunk = _updateQueue.takeFirst(); + _lock_chunks.unlock(); + } + else + { + _lock_chunks.unlock(); + return; + } + + chunk->maintain(); + + _lock_chunks.lock(); + _updateQueue.append(chunk); + for (int i = 0; i < _chunks.count(); i++) + { + _chunks[i]->updatePriority(camera); + } + qSort(_updateQueue.begin(), _updateQueue.end(), _cmpChunks); + _lock_chunks.unlock(); +} diff --git a/src/render/opengl/OpenGLTerrain.h b/src/render/opengl/OpenGLTerrain.h new file mode 100644 index 0000000..d61c657 --- /dev/null +++ b/src/render/opengl/OpenGLTerrain.h @@ -0,0 +1,40 @@ +#ifndef OPENGLTERRAIN_H +#define OPENGLTERRAIN_H + +#include "opengl_global.h" + +#include "OpenGLPart.h" + +#include +#include +#include + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLTerrain:public OpenGLPart +{ +public: + OpenGLTerrain(OpenGLRenderer* renderer); + virtual ~OpenGLTerrain(); + + virtual void initialize() override; + virtual void update() override; + virtual void render() override; + + void performChunksMaintenance(); + +private: + OpenGLShaderProgram* program; + + ParallelPool* work; + + QVector _chunks; + QList _updateQueue; + QMutex _lock_chunks; +}; + +} +} + +#endif // OPENGLTERRAIN_H diff --git a/src/render/opengl/OpenGLVariable.cpp b/src/render/opengl/OpenGLVariable.cpp new file mode 100644 index 0000000..be33aff --- /dev/null +++ b/src/render/opengl/OpenGLVariable.cpp @@ -0,0 +1,231 @@ +#include "OpenGLVariable.h" + +#include OPENGL_FUNCTIONS_INCLUDE +#include +#include +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "Vector3.h" +#include "Matrix4.h" +#include "Color.h" +#include "Texture2D.h" +#include "Texture3D.h" +#include "Texture4D.h" + +OpenGLVariable::OpenGLVariable(const std::string &name): + name(name) +{ + type = TYPE_NONE; + texture_toupload = false; +} + +void OpenGLVariable::apply(OpenGLShaderProgram *program, int &texture_unit) +{ + QOpenGLShaderProgram* pr = program->getProgram(); + OpenGLFunctions* functions = program->getRenderer()->getOpenGlFunctions(); + + if (texture_toupload) + { + uploadTexture(program->getRenderer()); + texture_toupload = false; + } + + switch (type) + { + case TYPE_FLOAT: + pr->setUniformValue(name.c_str(), value_float); + break; + case TYPE_COLOR: + pr->setUniformValue(name.c_str(), value_color); + break; + case TYPE_VECTOR3: + pr->setUniformValue(name.c_str(), value_vector3); + break; + case TYPE_MATRIX4: + pr->setUniformValue(name.c_str(), value_matrix4); + break; + case TYPE_TEXTURE_2D: + functions->glActiveTexture(GL_TEXTURE0 + texture_unit); + functions->glBindTexture(GL_TEXTURE_2D, texture_id); + pr->setUniformValue(name.c_str(), texture_unit); + texture_unit++; + break; + case TYPE_TEXTURE_3D: + case TYPE_TEXTURE_4D: + functions->glActiveTexture(GL_TEXTURE0 + texture_unit); + functions->glBindTexture(GL_TEXTURE_3D, texture_id); + pr->setUniformValue(name.c_str(), texture_unit); + texture_unit++; + break; + case TYPE_NONE: + break; + } +} + +void OpenGLVariable::set(const Texture2D *texture) +{ + assert(type == TYPE_NONE or type == TYPE_TEXTURE_2D); + + type = TYPE_TEXTURE_2D; + value_tex2d = texture; + texture_toupload = true; +} + +void OpenGLVariable::set(const Texture3D *texture) +{ + assert(type == TYPE_NONE or type == TYPE_TEXTURE_3D); + + type = TYPE_TEXTURE_3D; + value_tex3d = texture; + texture_toupload = true; +} + +void OpenGLVariable::set(const Texture4D *texture) +{ + assert(type == TYPE_NONE or type == TYPE_TEXTURE_4D); + + type = TYPE_TEXTURE_4D; + value_tex4d = texture; + texture_toupload = true; +} + +void OpenGLVariable::set(float value) +{ + assert(type == TYPE_NONE or type == TYPE_FLOAT); + + type = TYPE_FLOAT; + value_float = value; +} + +void OpenGLVariable::set(const Vector3 &vector) +{ + set(QVector3D(vector.x, vector.y, vector.z)); +} + +void OpenGLVariable::set(const QVector3D &vector) +{ + assert(type == TYPE_NONE or type == TYPE_VECTOR3); + + type = TYPE_VECTOR3; + value_vector3 = vector; +} + +void OpenGLVariable::set(const Matrix4 &matrix) +{ + set(matrix.toQMatrix()); +} + +void OpenGLVariable::set(const QMatrix4x4 &matrix) +{ + assert(type == TYPE_NONE or type == TYPE_MATRIX4); + + type = TYPE_MATRIX4; + value_matrix4 = matrix; +} + +void OpenGLVariable::set(const Color &color) +{ + assert(type == TYPE_NONE or type == TYPE_COLOR); + + type = TYPE_COLOR; + value_color = QColor(color.r, color.g, color.b); +} + +void OpenGLVariable::uploadTexture(OpenGLRenderer* renderer) +{ + OpenGLFunctions* functions = renderer->getOpenGlFunctions(); + + assert(type == TYPE_TEXTURE_2D or type == TYPE_TEXTURE_3D or type == TYPE_TEXTURE_4D); + + if (texture_id == 0) + { + GLuint texid; + functions->glGenTextures(1, &texid); + texture_id = texid; + } + + GLenum textype = (type == TYPE_TEXTURE_2D) ? GL_TEXTURE_2D : GL_TEXTURE_3D; + + functions->glBindTexture(textype, texture_id); + functions->glTexParameteri(textype, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + functions->glTexParameteri(textype, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + functions->glTexParameteri(textype, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + functions->glTexParameteri(textype, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + if (textype == GL_TEXTURE_3D) + { + functions->glTexParameteri(textype, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + } + + 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, GL_RGBA, 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, GL_RGBA, sx, sy, sz, 0, GL_RGBA, GL_FLOAT, pixels); + delete[] pixels; + } + 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, GL_RGBA, sx, sy, sz * sw, 0, GL_RGBA, GL_FLOAT, pixels); + delete[] pixels; + } +} diff --git a/src/render/opengl/OpenGLVariable.h b/src/render/opengl/OpenGLVariable.h new file mode 100644 index 0000000..07a7f29 --- /dev/null +++ b/src/render/opengl/OpenGLVariable.h @@ -0,0 +1,67 @@ +#ifndef OPENGLVARIABLE_H +#define OPENGLVARIABLE_H + +#include "opengl_global.h" + +#include +#include +#include + +namespace paysages { +namespace opengl { + +/*! + * \brief OpenGL variable that can be bound to a uniform for shaders. + */ +class OpenGLVariable +{ +public: + typedef enum { + TYPE_NONE, + TYPE_TEXTURE_2D, + TYPE_TEXTURE_3D, + TYPE_TEXTURE_4D, + TYPE_FLOAT, + TYPE_VECTOR3, + TYPE_MATRIX4, + TYPE_COLOR + } OpenGLVariableType; + +public: + OpenGLVariable(const std::string &name); + + void apply(OpenGLShaderProgram *program, int &texture_unit); + + void set(const Texture2D *texture); + void set(const Texture3D *texture); + void set(const Texture4D *texture); + void set(float value); + void set(const Vector3 &vector); + void set(const QVector3D &vector); + void set(const Matrix4 &matrix); + void set(const QMatrix4x4 &matrix); + void set(const Color &color); + +protected: + void uploadTexture(OpenGLRenderer* renderer); + +private: + std::string name; + 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; + + bool texture_toupload; + unsigned int texture_id; +}; + +} +} + +#endif // OPENGLVARIABLE_H diff --git a/src/render/opengl/OpenGLWater.cpp b/src/render/opengl/OpenGLWater.cpp new file mode 100644 index 0000000..7de134b --- /dev/null +++ b/src/render/opengl/OpenGLWater.cpp @@ -0,0 +1,56 @@ +#include "OpenGLWater.h" + +#include "OpenGLRenderer.h" +#include "OpenGLShaderProgram.h" +#include "OpenGLSharedState.h" +#include "WaterRenderer.h" +#include "Scenery.h" +#include "WaterDefinition.h" +#include "SurfaceMaterial.h" + +OpenGLWater::OpenGLWater(OpenGLRenderer *renderer): + OpenGLPart(renderer) +{ + vertices = new float[4 * 3]; +} + +OpenGLWater::~OpenGLWater() +{ + delete[] vertices; +} + +void OpenGLWater::initialize() +{ + program = createShader("water"); + program->addVertexSource("water"); + program->addFragmentSource("bruneton"); + program->addFragmentSource("tonemapping"); + program->addFragmentSource("fadeout"); + 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); +} + +void OpenGLWater::update() +{ + double water_height = renderer->getWaterRenderer()->getHeightInfo().max_height; + renderer->getSharedState()->set("waterHeight", water_height); + + Color water_color = renderer->getScenery()->getWater()->material->_rgb; + renderer->getSharedState()->set("waterColor", water_color); +} + +void OpenGLWater::render() +{ + program->drawTriangleStrip(vertices, 4); +} + +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; +} diff --git a/src/render/opengl/OpenGLWater.h b/src/render/opengl/OpenGLWater.h new file mode 100644 index 0000000..a30d4bc --- /dev/null +++ b/src/render/opengl/OpenGLWater.h @@ -0,0 +1,31 @@ +#ifndef OPENGLWATER_H +#define OPENGLWATER_H + +#include "opengl_global.h" + +#include "OpenGLPart.h" + +namespace paysages { +namespace opengl { + +class OPENGLSHARED_EXPORT OpenGLWater: public OpenGLPart +{ +public: + OpenGLWater(OpenGLRenderer* renderer); + virtual ~OpenGLWater(); + + virtual void initialize() override; + virtual void update() override; + virtual void render() override; + +private: + void setVertex(int i, float x, float y, float z); + + OpenGLShaderProgram* program; + float* vertices; +}; + +} +} + +#endif // OPENGLWATER_H diff --git a/src/render/opengl/VertexArray.h b/src/render/opengl/VertexArray.h new file mode 100644 index 0000000..66300d6 --- /dev/null +++ b/src/render/opengl/VertexArray.h @@ -0,0 +1,155 @@ +#ifndef VERTEXARRAY_H +#define VERTEXARRAY_H + +#include "opengl_global.h" + +#include OPENGL_FUNCTIONS_INCLUDE +#include +#include + +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->glDrawElements(GL_TRIANGLES, 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/WidgetExplorer.cpp b/src/render/opengl/WidgetExplorer.cpp index 2337813..0569964 100644 --- a/src/render/opengl/WidgetExplorer.cpp +++ b/src/render/opengl/WidgetExplorer.cpp @@ -11,51 +11,12 @@ #include "WaterDefinition.h" #include "SurfaceMaterial.h" #include "CameraDefinition.h" -#include "ExplorerChunkSky.h" #include "ExplorerChunkTerrain.h" #include "TerrainRenderer.h" #include "WaterRenderer.h" #include "Scenery.h" #include "LightingManager.h" -class ChunkMaintenanceThread : public QThread -{ -public: - - ChunkMaintenanceThread(WidgetExplorer* wanderer) - { - _wanderer = wanderer; - _running = true; - } - - void askStop() - { - _running = false; - } - - static inline void usleep(unsigned long us) - { - QThread::usleep(us); - } - -protected: - - void run() - { - while (_running) - { - _wanderer->performChunksMaintenance(); - QThread::usleep(10000); - } - } - -private: - bool _running; - WidgetExplorer* _wanderer; -}; - -static QVector _threads; - WidgetExplorer::WidgetExplorer(QWidget *parent, CameraDefinition* camera, Scenery* scenery) : QGLWidget(parent) { @@ -67,13 +28,6 @@ QGLWidget(parent) camera->copy(_current_camera); _renderer = new OpenGLRenderer(scenery); - _renderer->prepare(); - _renderer->render_quality = 3; - _renderer->getLightingManager()->setSpecularity(false); - _renderer->disableClouds(); - - _inited = false; - _updated = false; _average_frame_time = 0.05; _quality = 3; @@ -85,111 +39,10 @@ QGLWidget(parent) WidgetExplorer::~WidgetExplorer() { - stopRendering(); - - for (int i = 0; i < _chunks.count(); i++) - { - delete _chunks[i]; - } delete _current_camera; delete _renderer; } -void WidgetExplorer::startRendering() -{ - // Add terrain - int chunks = 20; - double size = 400.0; - double chunksize = size / (double) chunks; - double start = -size / 2.0; - double water_height = _renderer->getWaterRenderer()->getHeightInfo().base_height; - for (int i = 0; i < chunks; i++) - { - for (int j = 0; j < chunks; j++) - { - ExplorerChunkTerrain* chunk = new ExplorerChunkTerrain(_renderer, start + chunksize * (double) i, start + chunksize * (double) j, chunksize, chunks, water_height); - _chunks.append(chunk); - _updateQueue.append(chunk); - } - } - - // Add skybox - for (int orientation = 0; orientation < 5; orientation++) - { - ExplorerChunkSky* chunk = new ExplorerChunkSky(_renderer, 500.0, (SkyboxOrientation) orientation); - _chunks.append(chunk); - _updateQueue.append(chunk); - } - - // Start rendering workers - int nbcore; - _alive = true; - - nbcore = QThread::idealThreadCount(); - if (nbcore < 1) - { - nbcore = 1; - } - - for (int i = 0; i < nbcore; i++) - { - _threads.append(new ChunkMaintenanceThread(this)); - } - for (int i = 0; i < _threads.count(); i++) - { - _threads[i]->start(); - } -} - -void WidgetExplorer::stopRendering() -{ - for (int i = 0; i < _threads.count(); i++) - { - _threads[i]->askStop(); - } - _alive = false; - for (int i = 0; i < _threads.count(); i++) - { - _threads[i]->wait(); - } -} - -bool _cmpChunks(const BaseExplorerChunk* c1, const BaseExplorerChunk* c2) -{ - return c1->priority > c2->priority; -} - -void WidgetExplorer::performChunksMaintenance() -{ - BaseExplorerChunk* chunk; - - _lock_chunks.lock(); - if (_updateQueue.count() > 0) - { - chunk = _updateQueue.takeFirst(); - _lock_chunks.unlock(); - } - else - { - _lock_chunks.unlock(); - return; - } - - if (chunk->maintain()) - { - if (!_alive) - { - return; - } - - _updated = true; - } - - _lock_chunks.lock(); - _updateQueue.append(chunk); - _lock_chunks.unlock(); -} - void WidgetExplorer::resetCamera() { _base_camera->copy(_current_camera); @@ -332,25 +185,7 @@ void WidgetExplorer::wheelEvent(QWheelEvent* event) void WidgetExplorer::timerEvent(QTimerEvent*) { - if (!_inited) - { - _inited = true; - startRendering(); - } - - if (_updated) - { - _updated = false; - updateGL(); - } - - for (int i = 0; i < _chunks.count(); i++) - { - _chunks[i]->updatePriority(_current_camera); - } - _lock_chunks.lock(); - qSort(_updateQueue.begin(), _updateQueue.end(), _cmpChunks); - _lock_chunks.unlock(); + updateGL(); } void WidgetExplorer::initializeGL() @@ -366,46 +201,18 @@ void WidgetExplorer::resizeGL(int w, int h) void WidgetExplorer::paintGL() { - GLenum error_code; QTime start_time; double frame_time; - WaterDefinition* water = _renderer->getScenery()->getWater(); // Don't do this at each frame, only on camera change _renderer->getScenery()->setCamera(_current_camera); _renderer->getScenery()->getCamera(_current_camera); + _renderer->cameraChangeEvent(_current_camera); start_time = QTime::currentTime(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - Vector3 camera_location = _current_camera->getLocation(); - Vector3 camera_target = _current_camera->getTarget(); - Vector3 camera_up = _current_camera->getUpVector(); - gluLookAt(camera_location.x, camera_location.y, camera_location.z, camera_target.x, camera_target.y, camera_target.z, camera_up.x, camera_up.y, camera_up.z); - // Background - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Render water - double water_height = _renderer->getTerrainRenderer()->getWaterHeight(); - glDisable(GL_TEXTURE_2D); - glColor3f(water->material->_rgb.r, water->material->_rgb.g, water->material->_rgb.b); - glBegin(GL_QUADS); - glVertex3f(camera_location.x - 500.0, water_height, camera_location.z - 500.0); - glVertex3f(camera_location.x - 500.0, water_height, camera_location.z + 500.0); - glVertex3f(camera_location.x + 500.0, water_height, camera_location.z + 500.0); - glVertex3f(camera_location.x + 500.0, water_height, camera_location.z - 500.0); - glEnd(); - - // Render chunks - glEnable(GL_TEXTURE_2D); - for (int i = 0; i < _chunks.count(); i++) - { - glColor3f(1.0, 1.0, 1.0); - _chunks[i]->render(this); - } + _renderer->paint(); frame_time = 0.001 * (double) start_time.msecsTo(QTime::currentTime()); @@ -422,16 +229,11 @@ void WidgetExplorer::paintGL() } // Messages - if (!_inited) + /*if (!_inited) { glColor3f(0.0, 0.0, 0.0); renderText(6, height() - 10, tr("Please wait while loading scene...")); glColor3f(1.0, 1.0, 1.0); renderText(5, height() - 9, tr("Please wait while loading scene...")); - } - - while ((error_code = glGetError()) != GL_NO_ERROR) - { - qDebug("[OpenGL] ERROR : %s", (const char*)gluErrorString(error_code)); - } + }*/ } diff --git a/src/render/opengl/WidgetExplorer.h b/src/render/opengl/WidgetExplorer.h index fb7994f..6da061f 100644 --- a/src/render/opengl/WidgetExplorer.h +++ b/src/render/opengl/WidgetExplorer.h @@ -5,8 +5,6 @@ #include -#include - namespace paysages { namespace opengl { @@ -17,8 +15,6 @@ public: WidgetExplorer(QWidget* parent, CameraDefinition* camera, Scenery* scenery); ~WidgetExplorer(); - void performChunksMaintenance(); - public slots: void resetCamera(); void validateCamera(); @@ -35,20 +31,10 @@ protected: void paintGL(); private: - void startRendering(); - void stopRendering(); - CameraDefinition* _current_camera; CameraDefinition* _base_camera; OpenGLRenderer* _renderer; - bool _inited; - bool _updated; - - QVector _chunks; - QList _updateQueue; - bool _alive; - QMutex _lock_chunks; double _average_frame_time; int _quality; diff --git a/src/render/opengl/opengl.pro b/src/render/opengl/opengl.pro index cd7042e..e08d5a8 100644 --- a/src/render/opengl/opengl.pro +++ b/src/render/opengl/opengl.pro @@ -13,20 +13,33 @@ DEFINES += OPENGL_LIBRARY include(../../common.pri) +unix:LIBS += -lGLU + SOURCES += \ OpenGLRenderer.cpp \ - BaseExplorerChunk.cpp \ - ExplorerChunkSky.cpp \ ExplorerChunkTerrain.cpp \ - WidgetExplorer.cpp + WidgetExplorer.cpp \ + OpenGLShaderProgram.cpp \ + OpenGLPart.cpp \ + OpenGLSkybox.cpp \ + OpenGLWater.cpp \ + OpenGLSharedState.cpp \ + OpenGLVariable.cpp \ + OpenGLTerrain.cpp HEADERS +=\ opengl_global.h \ OpenGLRenderer.h \ - BaseExplorerChunk.h \ - ExplorerChunkSky.h \ ExplorerChunkTerrain.h \ - WidgetExplorer.h + WidgetExplorer.h \ + OpenGLShaderProgram.h \ + OpenGLPart.h \ + OpenGLSkybox.h \ + OpenGLWater.h \ + OpenGLSharedState.h \ + OpenGLVariable.h \ + OpenGLTerrain.h \ + VertexArray.h unix:!symbian { maemo5 { @@ -60,3 +73,18 @@ else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../software/debug/ else:unix: LIBS += -L$$OUT_PWD/../software/ -lpaysages_render_software INCLUDEPATH += $$PWD/../software DEPENDPATH += $$PWD/../software + +RESOURCES += \ + shaders/resources.qrc + +OTHER_FILES += \ + shaders/skybox.frag \ + shaders/skybox.vert \ + shaders/water.vert \ + shaders/water.frag \ + shaders/bruneton.frag \ + shaders/bruneton.frag \ + shaders/tonemapping.frag \ + shaders/terrain.frag \ + shaders/terrain.vert \ + shaders/fadeout.frag diff --git a/src/render/opengl/opengl_global.h b/src/render/opengl/opengl_global.h index 19fa590..1150545 100644 --- a/src/render/opengl/opengl_global.h +++ b/src/render/opengl/opengl_global.h @@ -15,9 +15,21 @@ namespace paysages { namespace opengl { class WidgetExplorer; class OpenGLRenderer; - class BaseExplorerChunk; + class OpenGLShaderProgram; + class OpenGLSharedState; + class OpenGLVariable; + class OpenGLSkybox; + class OpenGLWater; + class OpenGLTerrain; + class ExplorerChunkTerrain; + template class VertexArray; } } using namespace paysages::opengl; +//#define OpenGLFunctions QOpenGLFunctions_3_2_Core +#define OpenGLFunctions QOpenGLFunctions_3_0 +#define OPENGL_FUNCTIONS_INCLUDE +class OpenGLFunctions; + #endif // OPENGL_GLOBAL_H diff --git a/src/render/opengl/shaders/bruneton.frag b/src/render/opengl/shaders/bruneton.frag new file mode 100644 index 0000000..acefca0 --- /dev/null +++ b/src/render/opengl/shaders/bruneton.frag @@ -0,0 +1,211 @@ +const float GROUND_OFFSET = 0.5; +const float Rg = 6360.0; +const float Rt = 6420.0; +const float RL = 6421.0; +const float ISun = 100.0; +const float AVERAGE_GROUND_REFLECTANCE = 0.1; +const float HR = 8.0; +const vec3 betaR = vec3(5.8e-3, 1.35e-2, 3.31e-2); +const float HM = 1.2; +const vec3 betaMSca = vec3(4e-3); +const vec3 betaMEx = vec3(4e-3 / 0.9); +const float mieG = 0.8; +const float SPHERE_SIZE = 20000.0; +const float WORLD_SCALING = 0.05; +const float SUN_DISTANCE = 149597870.0; +const float SUN_DISTANCE_SCALED = (SUN_DISTANCE / WORLD_SCALING); +const float SUN_RADIUS = 6.955e5; +const float SUN_RADIUS_SCALED = (SUN_RADIUS / WORLD_SCALING); +const float M_PI = 3.141592657; + +const int RES_MU = 128; +const int RES_MU_S = 32; +const int RES_R = 32; +const int RES_NU = 8; + +uniform float waterHeight; +uniform vec3 cameraLocation; +uniform vec3 sunDirection; +uniform vec4 sunColor; +const float sunRadius = 1.0; // TODO -> uniform + +varying vec3 unprojected; + +uniform sampler2D transmittanceTexture; +uniform sampler3D inscatterTexture; + +vec4 texture4D(sampler3D tex, float r, float mu, float muS, float nu) +{ + if (r < Rg + 0.00000001) r = Rg + 0.00000001; + float H = sqrt(Rt * Rt - Rg * Rg); + float rho = sqrt(r * r - Rg * Rg); + float rmu = r * mu; + float delta = rmu * rmu - r * r + Rg * Rg; + vec4 cst = (rmu < 0.0 && delta > 0.0) ? vec4(1.0, 0.0, 0.0, 0.5 - 0.5 / float(RES_MU)) : vec4(-1.0, H * H, H, 0.5 + 0.5 / float(RES_MU)); + float uR = 0.5 / float(RES_R) + rho / H * (1.0 - 1.0 / float(RES_R)); + float uMu = cst.a + (rmu * cst.r + sqrt(delta + cst.g)) / (rho + cst.b) * (0.5 - 1.0 / float(RES_MU)); + float uMuS = 0.5 / float(RES_MU_S) + (atan(max(muS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / float(RES_MU_S)); + + 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)); + return mix(r1, r2, (uR - float(br) * sr) / sr); +} + +float _limit(float r, float mu) +{ + float dout = -r * mu + sqrt(r * r * (mu * mu - 1.0) + RL * RL); + float delta2 = r * r * (mu * mu - 1.0) + Rg * Rg; + if (delta2 >= 0.0) + { + float din = -r * mu - sqrt(delta2); + if (din >= 0.0) { + dout = min(dout, din); + } + } + return dout; +} + +vec2 _getTransmittanceUV(float r, float mu) +{ + if (r < Rg + 0.00000001) r = Rg + 0.00000001; + float dr = (r - Rg) / (Rt - Rg); + return vec2(atan((mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5, sqrt(dr)); +} + +vec4 _transmittance(float r, float mu) +{ + vec2 uv = _getTransmittanceUV(r, mu); + return texture2D(transmittanceTexture, uv); +} + +vec4 _transmittanceWithShadow(float r, float mu) +{ + return mu < -sqrt(1.0 - (Rg / r) * (Rg / r)) ? vec4(0.0) : _transmittance(r, mu); +} + +vec4 _sunTransmittance(vec3 v, vec3 s, float r, float mu, float radius) +{ + vec4 transmittance = r <= Rt ? _transmittanceWithShadow(r, mu) : vec4(1.0); /* T(x,xo) */ + float d = _limit(r, mu); + radius *= (1.0 + 25.0 * d / Rt); /* Inflating due to lens effect near horizon */ + float isun = step(cos(radius * M_PI / 180.0), dot(v, s)) * ISun; /* Lsun */ + transmittance.r *= isun; + transmittance.g *= isun; + transmittance.b *= isun; + transmittance.a = 1.0; + return transmittance; /* Eq (9) */ +} + +float phaseFunctionR(float mu) { + return (3.0 / (16.0 * M_PI)) * (1.0 + mu * mu); +} + +float phaseFunctionM(float mu) { + return 1.5 * 1.0 / (4.0 * M_PI) * (1.0 - mieG*mieG) * pow(1.0 + (mieG*mieG) - 2.0*mieG*mu, -3.0/2.0) * (1.0 + mu * mu) / (2.0 + mieG*mieG); +} + +float opticalDepth(float H, float r, float mu, float d) { + float a = sqrt((0.5/H)*r); + vec2 a01 = a*vec2(mu, mu + d / r); + vec2 a01s = sign(a01); + vec2 a01sq = a01*a01; + float x = a01s.y > a01s.x ? exp(a01sq.x) : 0.0; + vec2 y = a01s / (2.3193*abs(a01) + sqrt(1.52*a01sq + 4.0)) * vec2(1.0, exp(-d/H*(d/(2.0*r)+mu))); + return sqrt((6.2831*H)*r) * exp((Rg-r)/H) * (x + dot(y, vec2(1.0, -1.0))); +} + +vec3 analyticTransmittance(float r, float mu, float d) { + return exp(- betaR * opticalDepth(HR, r, mu, d) - betaMEx * opticalDepth(HM, r, mu, d)); +} + +vec3 getMie(vec4 rayMie) { // rayMie.rgb=C*, rayMie.w=Cm,r + return rayMie.rgb * rayMie.w / max(rayMie.r, 1e-4) * (betaR.r / betaR); +} + +vec3 _getInscatterColor(inout vec3 x, inout float t, vec3 v, vec3 s, out float r, out float mu, out vec3 attenuation) { + vec3 result; + r = length(x); + mu = dot(x, v) / r; + float d = -r * mu - sqrt(r * r * (mu * mu - 1.0) + Rt * Rt); + if (d > 0.0) { // if x in space and ray intersects atmosphere + // move x to nearest intersection of ray with top atmosphere boundary + x += d * v; + t -= d; + mu = (r * mu + d) / Rt; + r = Rt; + } + if (r <= Rt) { // if ray intersects atmosphere + float nu = dot(v, s); + float muS = dot(x, s) / r; + float phaseR = phaseFunctionR(nu); + float phaseM = phaseFunctionM(nu); + vec4 inscatter = max(texture4D(inscatterTexture, r, mu, muS, nu), 0.0); + if (t > 0.0) { + vec3 x0 = x + t * v; + float r0 = length(x0); + float rMu0 = dot(x0, v); + float mu0 = rMu0 / r0; + float muS0 = dot(x0, s) / r0; + // avoids imprecision problems in transmittance computations based on textures + attenuation = analyticTransmittance(r, mu, t); + if (r0 > Rg + 0.001) { + // computes S[L]-T(x,x0)S[L]|x0 + inscatter = max(inscatter - attenuation.rgbr * texture4D(inscatterTexture, r0, mu0, muS0, nu), 0.0); + // avoids imprecision problems near horizon by interpolating between two points above and below horizon + const float EPS = 0.02; + float muHoriz = -sqrt(1.0 - (Rg / r) * (Rg / r)); + if (abs(mu - muHoriz) < EPS) { + float a = ((mu - muHoriz) + EPS) / (2.0 * EPS); + + mu = muHoriz - EPS; + r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); + mu0 = (r * mu + t) / r0; + vec4 inScatter0 = texture4D(inscatterTexture, r, mu, muS, nu); + vec4 inScatter1 = texture4D(inscatterTexture, r0, mu0, muS0, nu); + vec4 inScatterA = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); + + mu = muHoriz + EPS; + r0 = sqrt(r * r + t * t + 2.0 * r * t * mu); + mu0 = (r * mu + t) / r0; + inScatter0 = texture4D(inscatterTexture, r, mu, muS, nu); + inScatter1 = texture4D(inscatterTexture, r0, mu0, muS0, nu); + vec4 inScatterB = max(inScatter0 - attenuation.rgbr * inScatter1, 0.0); + + inscatter = mix(inScatterA, inScatterB, a); + } + } + } + // avoids imprecision problems in Mie scattering when sun is below horizon + inscatter.w *= smoothstep(0.00, 0.02, muS); + result = max(inscatter.rgb * phaseR + getMie(inscatter) * phaseM, 0.0); + } else { // x in space and ray looking in space + result = vec3(0.0); + } + return result * ISun; +} + +vec4 applyAerialPerspective(vec4 base) +{ + float yoffset = GROUND_OFFSET - waterHeight; + vec3 camera = vec3(cameraLocation.x, max(cameraLocation.y + yoffset, 0.0), cameraLocation.z); + vec3 location = vec3(unprojected.x, max(unprojected.y + yoffset, 0.0), unprojected.z); + vec3 x = vec3(0.0, Rg + camera.y * WORLD_SCALING, 0.0); + vec3 v = normalize(location - camera); + vec3 s = normalize(sunDirection * SUN_DISTANCE_SCALED - x); + + if (v.y == 0.0) + { + v.y = -0.000001; + } + + float r = length(x); + float mu = dot(x, v) / r; + float t = length(location - camera) * WORLD_SCALING; + + vec3 attenuation; + vec3 inscattering = _getInscatterColor(x, t, v, s, r, mu, attenuation); + + return base * vec4(attenuation, 0.0) + vec4(inscattering, 0.0); +} diff --git a/src/render/opengl/shaders/fadeout.frag b/src/render/opengl/shaders/fadeout.frag new file mode 100644 index 0000000..484b005 --- /dev/null +++ b/src/render/opengl/shaders/fadeout.frag @@ -0,0 +1,8 @@ +uniform float viewDistance; + +float distanceFadeout() +{ + vec3 camera = vec3(cameraLocation.x, 0.0, cameraLocation.z); + vec3 location = vec3(unprojected.x, 0.0, unprojected.z); + return mix(1.0, 0.0, clamp((length(location - camera) - viewDistance * 0.8) / (viewDistance * 0.2), 0.0, 1.0)); +} diff --git a/src/render/opengl/shaders/resources.qrc b/src/render/opengl/shaders/resources.qrc new file mode 100644 index 0000000..5dc91be --- /dev/null +++ b/src/render/opengl/shaders/resources.qrc @@ -0,0 +1,13 @@ + + + skybox.frag + skybox.vert + water.frag + water.vert + bruneton.frag + tonemapping.frag + terrain.frag + terrain.vert + fadeout.frag + + diff --git a/src/render/opengl/shaders/skybox.frag b/src/render/opengl/shaders/skybox.frag new file mode 100644 index 0000000..8acc7da --- /dev/null +++ b/src/render/opengl/shaders/skybox.frag @@ -0,0 +1,19 @@ +void main(void) +{ + float yoffset = GROUND_OFFSET - waterHeight; + vec3 camera = vec3(cameraLocation.x, max(cameraLocation.y + yoffset, 0.0), cameraLocation.z); + vec3 location = vec3(unprojected.x, max(unprojected.y + yoffset, 0.0), unprojected.z); + vec3 x = vec3(0.0, Rg + camera.y * WORLD_SCALING, 0.0); + vec3 v = normalize(location - camera); + vec3 s = normalize(sunDirection * SUN_DISTANCE_SCALED - x); + + float r = length(x); + float mu = dot(x, v) / r; + float t = -r * mu - sqrt(r * r * (mu * mu - 1.0) + Rg * Rg); + + vec4 sunTransmittance = _sunTransmittance(v, s, r, mu, sunRadius); + vec3 attenuation; + vec3 inscattering = _getInscatterColor(x, t, v, s, r, mu, attenuation); + + gl_FragColor = applyToneMapping(sunTransmittance + vec4(inscattering, 0.0)); +} diff --git a/src/render/opengl/shaders/skybox.vert b/src/render/opengl/shaders/skybox.vert new file mode 100644 index 0000000..ff142a5 --- /dev/null +++ b/src/render/opengl/shaders/skybox.vert @@ -0,0 +1,10 @@ +attribute highp vec4 vertex; +uniform highp mat4 viewMatrix; +uniform vec3 cameraLocation; +varying vec3 unprojected; + +void main(void) +{ + unprojected = cameraLocation + vertex.xyz * 500.0; + gl_Position = viewMatrix * vec4(unprojected, 1.0); +} diff --git a/src/render/opengl/shaders/terrain.frag b/src/render/opengl/shaders/terrain.frag new file mode 100644 index 0000000..de2f60c --- /dev/null +++ b/src/render/opengl/shaders/terrain.frag @@ -0,0 +1,13 @@ +uniform sampler2D groundTexture; +varying vec2 texcoord; + +void main(void) +{ + gl_FragColor = texture2D(groundTexture, texcoord); + + gl_FragColor = applyAerialPerspective(gl_FragColor); + + gl_FragColor = applyToneMapping(gl_FragColor); + + gl_FragColor.a = distanceFadeout(); +} diff --git a/src/render/opengl/shaders/terrain.vert b/src/render/opengl/shaders/terrain.vert new file mode 100644 index 0000000..c4fd270 --- /dev/null +++ b/src/render/opengl/shaders/terrain.vert @@ -0,0 +1,12 @@ +attribute highp vec4 vertex; +attribute highp vec2 uv; +uniform highp mat4 viewMatrix; +varying vec3 unprojected; +varying vec2 texcoord; + +void main(void) +{ + unprojected = vertex.xyz; + texcoord = uv; + gl_Position = viewMatrix * vertex; +} diff --git a/src/render/opengl/shaders/tonemapping.frag b/src/render/opengl/shaders/tonemapping.frag new file mode 100644 index 0000000..2ea09af --- /dev/null +++ b/src/render/opengl/shaders/tonemapping.frag @@ -0,0 +1,32 @@ +uniform float exposure; + +float _uncharted2Tonemap(float x) +{ + float A = 0.15; + float B = 0.50; + float C = 0.10; + float D = 0.20; + float E = 0.02; + float F = 0.30; + + return ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F; +} + +vec4 applyToneMapping(vec4 color) +{ + return vec4(((color * exposure) / (1.0 + color * exposure)).rgb, 1.0); +} + +/*vec4 applyToneMapping(vec4 color) +{ + float W = 11.2; + float white_scale = 1.0 / _uncharted2Tonemap(W); + vec4 result; + + result.r = pow(_uncharted2Tonemap(color.r * exposure) * white_scale, 1.0 / 2.2); + result.g = pow(_uncharted2Tonemap(color.g * exposure) * white_scale, 1.0 / 2.2); + result.b = pow(_uncharted2Tonemap(color.b * exposure) * white_scale, 1.0 / 2.2); + result.a = 1.0; + + return result; +}*/ diff --git a/src/render/opengl/shaders/water.frag b/src/render/opengl/shaders/water.frag new file mode 100644 index 0000000..06c68b6 --- /dev/null +++ b/src/render/opengl/shaders/water.frag @@ -0,0 +1,12 @@ +uniform vec4 waterColor; + +void main(void) +{ + gl_FragColor = waterColor; + + gl_FragColor = applyAerialPerspective(gl_FragColor); + + gl_FragColor = applyToneMapping(gl_FragColor); + + gl_FragColor.a = distanceFadeout(); +} diff --git a/src/render/opengl/shaders/water.vert b/src/render/opengl/shaders/water.vert new file mode 100644 index 0000000..485fe10 --- /dev/null +++ b/src/render/opengl/shaders/water.vert @@ -0,0 +1,11 @@ +attribute highp vec4 vertex; +uniform highp mat4 viewMatrix; +uniform float waterHeight; +uniform vec3 cameraLocation; +varying vec3 unprojected; + +void main(void) +{ + unprojected = vec3(cameraLocation.x + vertex.x * 500.0, vertex.y + waterHeight, cameraLocation.z + vertex.z * 500.0); + gl_Position = viewMatrix * vec4(unprojected, 1.0); +} diff --git a/src/render/software/AtmosphereModelBruneton.cpp b/src/render/software/AtmosphereModelBruneton.cpp index 9eb28c9..ccaed98 100644 --- a/src/render/software/AtmosphereModelBruneton.cpp +++ b/src/render/software/AtmosphereModelBruneton.cpp @@ -934,7 +934,7 @@ static Color _sunColor(Vector3 v, Vector3 s, double r, double mu, double radius) { Color transmittance = r <= Rt ? _transmittanceWithShadow(r, mu) : COLOR_WHITE; /* T(x,xo) */ double d = _limit(r, mu); - radius *= (1.0 + 10.0 * d / Rt); /* Inflating due to lens effect near horizon */ + radius *= (1.0 + 25.0 * d / Rt); /* Inflating due to lens effect near horizon */ double isun = step(cos(radius * M_PI / 180.0), v.dotProduct(s)) * ISun; /* Lsun */ transmittance.r *= isun; transmittance.g *= isun; @@ -1268,3 +1268,18 @@ void AtmosphereModelBruneton::fillLightingStatus(LightStatus *status, const Vect status->pushComponent(irradiance); } + +Texture2D *AtmosphereModelBruneton::getTextureTransmittance() const +{ + return _transmittanceTexture; +} + +Texture2D *AtmosphereModelBruneton::getTextureIrradiance() const +{ + return _irradianceTexture; +} + +Texture4D *AtmosphereModelBruneton::getTextureInscatter() const +{ + return _inscatterTexture; +} diff --git a/src/render/software/AtmosphereModelBruneton.h b/src/render/software/AtmosphereModelBruneton.h index 71d768c..7d0aa8b 100644 --- a/src/render/software/AtmosphereModelBruneton.h +++ b/src/render/software/AtmosphereModelBruneton.h @@ -17,6 +17,11 @@ public: AtmosphereResult applyAerialPerspective(Vector3 location, const Color &base); void fillLightingStatus(LightStatus *status, const Vector3 &normal, int opaque); + /* Functions to get access to internal textures (for opengl shaders) */ + Texture2D* getTextureTransmittance() const; + Texture2D* getTextureIrradiance() const; + Texture4D* getTextureInscatter() const; + private: SoftwareRenderer* parent; }; diff --git a/src/render/software/AtmosphereRenderer.h b/src/render/software/AtmosphereRenderer.h index fe05468..bdda48f 100644 --- a/src/render/software/AtmosphereRenderer.h +++ b/src/render/software/AtmosphereRenderer.h @@ -39,6 +39,8 @@ public: virtual AtmosphereResult applyAerialPerspective(Vector3 location, Color base) override; virtual AtmosphereResult getSkyColor(Vector3 direction) override; + inline const AtmosphereModelBruneton* getModel() const {return model;} + private: AtmosphereModelBruneton* model; }; diff --git a/src/system/Logs.cpp b/src/system/Logs.cpp new file mode 100644 index 0000000..eca5c5c --- /dev/null +++ b/src/system/Logs.cpp @@ -0,0 +1 @@ +#include "Logs.h" diff --git a/src/system/Logs.h b/src/system/Logs.h new file mode 100644 index 0000000..17d084c --- /dev/null +++ b/src/system/Logs.h @@ -0,0 +1,9 @@ +#ifndef LOGS_H +#define LOGS_H + +#include "system_global.h" + +#define logWarning qWarning +#define logError qCritical + +#endif // LOGS_H diff --git a/src/system/ParallelPool.cpp b/src/system/ParallelPool.cpp new file mode 100644 index 0000000..941868e --- /dev/null +++ b/src/system/ParallelPool.cpp @@ -0,0 +1,55 @@ +#include "ParallelPool.h" + +#include "System.h" +#include "Thread.h" + +static void* _threadFunction(void* data) +{ + ParallelPool* pool = (ParallelPool*)data; + pool->work(); + return NULL; +} + +ParallelPool::ParallelPool() +{ + running = false; +} + +ParallelPool::~ParallelPool() +{ + if (running) + { + interrupt(); + } + for (auto thread : threads) + { + thread->join(); + delete thread; + } + threads.clear(); +} + +void ParallelPool::start(int thread_count) +{ + if (running) + { + qCritical("Starting an already started parallel pool !"); + return; + } + running = true; + if (thread_count < 0) + { + thread_count = System::getCoreCount(); + } + for (int i = 0; i < thread_count; i++) + { + Thread* thread = new Thread(_threadFunction); + thread->start(this); + threads.push_back(thread); + } +} + +void ParallelPool::interrupt() +{ + running = false; +} diff --git a/src/system/ParallelPool.h b/src/system/ParallelPool.h new file mode 100644 index 0000000..350d41e --- /dev/null +++ b/src/system/ParallelPool.h @@ -0,0 +1,43 @@ +#ifndef PARALLELPOOL_H +#define PARALLELPOOL_H + +#include "system_global.h" + +namespace paysages { +namespace system { + +/*! + * Pool to handle a group of threads doing the same task. + */ +class ParallelPool +{ +public: + ParallelPool(); + virtual ~ParallelPool(); + + /*! + * Start the effective work. + */ + void start(int thread_count=-1); + + /*! + * Method called from each thread to do actual work. + */ + virtual void work() = 0; + + /*! + * Method called once to interrupt all threads. + */ + virtual void interrupt(); + +protected: + bool running; + +private: + std::vector threads; +}; + +} +} + +#endif // PARALLELPOOL_H diff --git a/src/system/system.pro b/src/system/system.pro index 2c6bd99..5de52fa 100644 --- a/src/system/system.pro +++ b/src/system/system.pro @@ -23,7 +23,9 @@ SOURCES += \ ParallelWork.cpp \ ParallelQueue.cpp \ CacheFile.cpp \ - PictureWriter.cpp + PictureWriter.cpp \ + Logs.cpp \ + ParallelPool.cpp HEADERS += \ system_global.h \ @@ -36,7 +38,9 @@ HEADERS += \ ParallelWork.h \ ParallelQueue.h \ CacheFile.h \ - PictureWriter.h + PictureWriter.h \ + Logs.h \ + ParallelPool.h unix:!symbian { maemo5 { diff --git a/src/system/system_global.h b/src/system/system_global.h index 5b5dfb2..b557937 100644 --- a/src/system/system_global.h +++ b/src/system/system_global.h @@ -15,6 +15,7 @@ namespace system { class PackStream; class ParallelQueue; class ParallelWork; + class ParallelPool; class Thread; class Mutex; } diff --git a/src/tests/VertexArray_Test.cpp b/src/tests/VertexArray_Test.cpp new file mode 100644 index 0000000..1b29e6d --- /dev/null +++ b/src/tests/VertexArray_Test.cpp @@ -0,0 +1,100 @@ +#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)); +} diff --git a/src/tests/tests.pro b/src/tests/tests.pro index 3fc8679..2e638f3 100644 --- a/src/tests/tests.pro +++ b/src/tests/tests.pro @@ -17,7 +17,8 @@ SOURCES += main.cpp \ Bruneton_Test.cpp \ Camera_Test.cpp \ Clouds_Test.cpp \ - FluidMediumManager_Test.cpp + FluidMediumManager_Test.cpp \ + VertexArray_Test.cpp HEADERS += \ BaseTestCase.h @@ -51,3 +52,9 @@ else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../render/software/ else:unix: LIBS += -L$$OUT_PWD/../render/software/ -lpaysages_render_software INCLUDEPATH += $$PWD/../render/software DEPENDPATH += $$PWD/../render/software + +win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../render/opengl/release/ -lpaysages_render_opengl +else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../render/opengl/debug/ -lpaysages_render_opengl +else:unix: LIBS += -L$$OUT_PWD/../render/opengl/ -lpaysages_render_opengl +INCLUDEPATH += $$PWD/../render/opengl +DEPENDPATH += $$PWD/../render/opengl