diff --git a/TODO b/TODO index 9e9fce7..aa22dac 100644 --- a/TODO +++ b/TODO @@ -22,6 +22,7 @@ Technology Preview 2 : => Don't display the water if it's below all ground => Add the sun position => Try to overcome the near frustum cutting + => Disable texture reflection of light (dependant on camera location) - Water and terrain LOD moves with the camera, fix it like in the wanderer. - Pause previews drawing of main window when a dialog is opened. - Interrupt preview chunk renderings that will be discarded at commit, or that are no more visible. diff --git a/gui_qt/wandererchunk.cpp b/gui_qt/wandererchunk.cpp index 3d06160..fa1ff8f 100644 --- a/gui_qt/wandererchunk.cpp +++ b/gui_qt/wandererchunk.cpp @@ -1,7 +1,5 @@ #include "wandererchunk.h" -#include -#include #include #include #include @@ -13,23 +11,29 @@ WandererChunk::WandererChunk(double x, double z, double size) { _startx = x; _startz = z; - _dirty = true; + _dirty = 3; _chunksize = size; - _nbsubchunks = 4; + _nbsubchunks = 8; _subchunksize = size / (double)_nbsubchunks; - _texture = new QImage(4, 4, QImage::Format_ARGB32); + _texture = new QImage(1, 1, QImage::Format_ARGB32); _texture->fill(QColor(0, 200, 0)); _texture_id = 0; _need_texture_upload = true; - _heightmap = (double*)malloc(sizeof(double) * (_nbsubchunks + 1) * (_nbsubchunks + 1)); - //_heightmap[0] = _heightmap[1] = _heightmap[2] = _heightmap[3] = toolsRandom(); + _heightmap = new double[(_nbsubchunks + 1) * (_nbsubchunks + 1)]; + for (int j = 0; j <= _nbsubchunks; j++) + { + for (int i = 0; i <= _nbsubchunks; i++) + { + _heightmap[j * (_nbsubchunks + 1) + i] = -100.0; + } + } } WandererChunk::~WandererChunk() { _lock.lock(); delete _texture; - free(_heightmap); + delete [] _heightmap; _lock.unlock(); } @@ -56,7 +60,6 @@ void WandererChunk::render(QGLWidget* widget) glBegin(GL_QUAD_STRIP); for (int i = 0; i <= _nbsubchunks; i++) { - glColor3f(1.0, 1.0, 1.0); glTexCoord2d(tsize * (double)i, 1.0 - tsize * (double)j); glVertex3d(_startx + _subchunksize * (double)i, _heightmap[j * (_nbsubchunks + 1) + i], _startz + _subchunksize * (double)j); glTexCoord2d(tsize * (double)i, 1.0 - tsize * (double)(j + 1)); @@ -68,37 +71,92 @@ void WandererChunk::render(QGLWidget* widget) _lock.unlock(); } -void WandererChunk::maintain(Renderer* renderer) +bool WandererChunk::maintain(Renderer* renderer) { - _lock.lock(); - + _lock_dirty.lock(); if (_dirty) { - _dirty = false; + int dirty = _dirty--; + _lock_dirty.unlock(); // Compute heightmap - for (int j = 0; j <= _nbsubchunks; j++) + if (dirty == 3) { - for (int i = 0; i <= _nbsubchunks; i++) + double* new_heightmap = new double[(_nbsubchunks + 1) * (_nbsubchunks + 1)]; + for (int j = 0; j <= _nbsubchunks; j++) { - _heightmap[j * (_nbsubchunks + 1) + i] = renderer->getTerrainHeight(renderer, _startx + _subchunksize * (double)i, _startz + _subchunksize * (double)j); + for (int i = 0; i <= _nbsubchunks; i++) + { + new_heightmap[j * (_nbsubchunks + 1) + i] = renderer->getTerrainHeight(renderer, _startx + _subchunksize * (double)i, _startz + _subchunksize * (double)j); + } } + _lock.lock(); + delete [] _heightmap; + _heightmap = new_heightmap; + _lock.unlock(); + } + + // Compute low-res texture + if (dirty == 2) + { + int texture_size = 4; + double step_size = _chunksize / (double)(texture_size - 1); + QImage* new_image = new QImage(texture_size, texture_size, QImage::Format_ARGB32); + for (int j = 0; j < texture_size; j++) + { + for (int i = 0; i < texture_size; i++) + { + Vector3 location = {_startx + step_size * (double)i, 0.0, _startz + step_size * (double)j}; + Color color = renderer->applyTextures(renderer, location, step_size); + new_image->setPixel(i, j, colorTo32BitRGBA(&color)); + } + } + _lock.lock(); + delete _texture; + _texture = new_image; + _need_texture_upload = true; + _lock.unlock(); + } + + // Compute texture + if (dirty == 1) + { + int texture_size = 32; + double step_size = _chunksize / (double)(texture_size - 1); + QImage* new_image = new QImage(texture_size, texture_size, QImage::Format_ARGB32); + for (int j = 0; j < texture_size; j++) + { + for (int i = 0; i < texture_size; i++) + { + Vector3 location = {_startx + step_size * (double)i, 0.0, _startz + step_size * (double)j}; + Color color = renderer->applyTextures(renderer, location, step_size); + new_image->setPixel(i, j, colorTo32BitRGBA(&color)); + } + } + _lock.lock(); + delete _texture; + _texture = new_image; + _need_texture_upload = true; + _lock.unlock(); } - // Compute texture - int texture_size = 4; - double step_size = _chunksize / (double)(texture_size - 1); - for (int j = 0; j < texture_size; j++) - { - for (int i = 0; i < texture_size; i++) - { - Vector3 location = {_startx + step_size * (double)i, 0.0, _startz + step_size * (double)j}; - Color color = renderer->applyTextures(renderer, location, step_size); - _texture->setPixel(i, j, colorTo32BitRGBA(&color)); - } - } - _need_texture_upload = true; + return true; + } + else + { + _lock_dirty.unlock(); + + return false; } - - _lock.unlock(); +} + +Vector3 WandererChunk::getCenter() +{ + Vector3 result; + + result.x = _startz + _chunksize / 2.0; + result.y = 0.0; + result.z = _startz + _chunksize / 2.0; + + return result; } diff --git a/gui_qt/wandererchunk.h b/gui_qt/wandererchunk.h index f071c69..04efa5f 100644 --- a/gui_qt/wandererchunk.h +++ b/gui_qt/wandererchunk.h @@ -12,14 +12,17 @@ public: WandererChunk(double x, double z, double size); ~WandererChunk(); - void maintain(Renderer* renderer); + bool maintain(Renderer* renderer); void render(QGLWidget* widget); + Vector3 getCenter(); + private: QMutex _lock; + QMutex _lock_dirty; double _startx; double _startz; - bool _dirty; + int _dirty; double _chunksize; double _subchunksize; int _nbsubchunks; diff --git a/gui_qt/widgetwanderer.cpp b/gui_qt/widgetwanderer.cpp index 65948ee..79723a8 100644 --- a/gui_qt/widgetwanderer.cpp +++ b/gui_qt/widgetwanderer.cpp @@ -5,7 +5,45 @@ #include #include #include +#include #include "../lib_paysages/scenery.h" +#include "../lib_paysages/euclid.h" + +class ChunkMaintenanceThread:public QThread +{ +public: + ChunkMaintenanceThread(WidgetWanderer* 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; + WidgetWanderer* _wanderer; +}; + +static QVector _threads; WidgetWanderer::WidgetWanderer(QWidget *parent, CameraDefinition* camera): QGLWidget(parent) @@ -18,20 +56,28 @@ WidgetWanderer::WidgetWanderer(QWidget *parent, CameraDefinition* camera): _water = waterCreateDefinition(); sceneryGetWater(&_water); + _sky = skyCreateDefinition(); + sceneryGetSky(&_sky); _renderer = sceneryCreateStandardRenderer(); + _updated = false; int chunks = 16; - double size = 30.0; + double size = 150.0; double chunksize = size / (double)chunks; double start = -size / 2.0; for (int i = 0; i < chunks; i++) { for (int j = 0; j < chunks; j++) { - _chunks.append(new WandererChunk(start + chunksize * (double)i, start + chunksize * (double)j, chunksize)); + WandererChunk* chunk = new WandererChunk(start + chunksize * (double)i, start + chunksize * (double)j, chunksize); + _chunks.append(chunk); + _updateQueue.append(chunk); } } + + startThreads(); + startTimer(1000); _average_frame_time = 0.05; _quality = 3; @@ -41,6 +87,8 @@ WidgetWanderer::WidgetWanderer(QWidget *parent, CameraDefinition* camera): WidgetWanderer::~WidgetWanderer() { + stopThreads(); + for (int i = 0; i < _chunks.count(); i++) { delete _chunks[i]; @@ -48,6 +96,72 @@ WidgetWanderer::~WidgetWanderer() waterDeleteDefinition(&_water); } +void WidgetWanderer::startThreads() +{ + int nbcore; + + _alive = true; + + nbcore = QThread::idealThreadCount() - 1; + 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 WidgetWanderer::stopThreads() +{ + for (int i = 0; i < _threads.count(); i++) + { + _threads[i]->askStop(); + } + _alive = false; + for (int i = 0; i < _threads.count(); i++) + { + _threads[i]->wait(); + } +} + +void WidgetWanderer::performChunksMaintenance() +{ + WandererChunk* chunk; + + _lock_chunks.lock(); + if (_updateQueue.count() > 0) + { + chunk = _updateQueue.takeFirst(); + _lock_chunks.unlock(); + } + else + { + _lock_chunks.unlock(); + return; + } + + if (chunk->maintain(&_renderer)) + { + if (!_alive) + { + return; + } + + _updated = true; + } + + _lock_chunks.lock(); + _updateQueue.append(chunk); + _lock_chunks.unlock(); +} + void WidgetWanderer::resetCamera() { cameraCopyDefinition(_base_camera, &_current_camera); @@ -189,9 +303,18 @@ void WidgetWanderer::wheelEvent(QWheelEvent* event) event->accept(); } +void WidgetWanderer::timerEvent(QTimerEvent *event) +{ + if (_updated) + { + _updated = false; + updateGL(); + } +} + void WidgetWanderer::initializeGL() { - glClearColor(0.4, 0.7, 0.8, 0.0); + glClearColor(0.0, 0.0, 0.0, 0.0); glDisable(GL_LIGHTING); @@ -208,7 +331,7 @@ void WidgetWanderer::initializeGL() glLineWidth(1.0); glDisable(GL_FOG); - + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } @@ -230,6 +353,11 @@ void WidgetWanderer::paintGL() QTime start_time; double frame_time; + if (_current_camera.location.y > 30.0) + { + _current_camera.location.y = 30.0; + } + cameraValidateDefinition(&_current_camera, 1); start_time = QTime::currentTime(); @@ -238,13 +366,27 @@ void WidgetWanderer::paintGL() glLoadIdentity(); gluLookAt(_current_camera.location.x, _current_camera.location.y, _current_camera.location.z, _current_camera.target.x, _current_camera.target.y, _current_camera.target.z, _current_camera.up.x, _current_camera.up.y, _current_camera.up.z); + // Background + Color zenith_color = skyGetZenithColor(&_sky); + glClearColor(zenith_color.r, zenith_color.g, zenith_color.b, 0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // Render sun + Vector3 sun_location = v3Add(_current_camera.location, v3Scale(skyGetSunDirection(&_sky), 500.0)); + Color sun_color = skyGetSunColor(&_sky); + glDisable(GL_TEXTURE); + glDisable(GL_TEXTURE_2D); + glColor3f(sun_color.r, sun_color.g, sun_color.b); + glPointSize(15.0 * _sky.sun_radius / 0.02); + glEnable(GL_POINT_SMOOTH); + glBegin(GL_POINTS); + glVertex3f(sun_location.x, sun_location.y, sun_location.z); + glEnd(); // Render water glDisable(GL_TEXTURE); glDisable(GL_TEXTURE_2D); - glColor3f(0.0, 0.0, 1.0); + glColor3f(_water.material.base.r, _water.material.base.g, _water.material.base.b); glBegin(GL_QUADS); glVertex3f(_current_camera.location.x - 500.0, _water.height, _current_camera.location.z - 500.0); glVertex3f(_current_camera.location.x - 500.0, _water.height, _current_camera.location.z + 500.0); @@ -257,8 +399,7 @@ void WidgetWanderer::paintGL() glEnable(GL_TEXTURE_2D); for (int i = 0; i < _chunks.count(); i++) { - _chunks[i]->maintain(&_renderer); - + glColor3f(1.0, 1.0, 1.0); _chunks[i]->render(this); } diff --git a/gui_qt/widgetwanderer.h b/gui_qt/widgetwanderer.h index 556f48b..7e29ec7 100644 --- a/gui_qt/widgetwanderer.h +++ b/gui_qt/widgetwanderer.h @@ -3,11 +3,10 @@ #include #include "wandererchunk.h" -#include "../lib_paysages/atmosphere.h" #include "../lib_paysages/camera.h" -#include "../lib_paysages/terrain.h" #include "../lib_paysages/water.h" #include "../lib_paysages/renderer.h" +#include "../lib_paysages/sky.h" class WidgetWanderer : public QGLWidget { @@ -15,6 +14,8 @@ class WidgetWanderer : public QGLWidget public: WidgetWanderer(QWidget* parent, CameraDefinition* camera); ~WidgetWanderer(); + + void performChunksMaintenance(); public slots: void resetCamera(); @@ -25,20 +26,29 @@ protected: void mousePressEvent(QMouseEvent* event); void mouseMoveEvent(QMouseEvent* event); void wheelEvent(QWheelEvent* event); + void timerEvent(QTimerEvent *event); void initializeGL(); void resizeGL(int w, int h); void paintGL(); - + private: + void startThreads(); + void stopThreads(); + CameraDefinition _current_camera; CameraDefinition* _base_camera; Renderer _renderer; + bool _updated; QVector _chunks; + QList _updateQueue; + bool _alive; + QMutex _lock_chunks; WaterDefinition _water; + SkyDefinition _sky; double _average_frame_time; int _quality; diff --git a/lib_paysages/sky.c b/lib_paysages/sky.c index 7f1c140..b46f77d 100644 --- a/lib_paysages/sky.c +++ b/lib_paysages/sky.c @@ -143,14 +143,11 @@ int skyGetLights(SkyDefinition* sky, LightDefinition* lights, int max_lights) Color skyGetColor(SkyDefinition* definition, Renderer* renderer, Vector3 eye, Vector3 look) { - double sun_angle, dist; + double dist; Vector3 sun_position; Color sun_color, sky_color; - sun_angle = (definition->daytime + 0.75) * M_PI * 2.0; - sun_position.x = cos(sun_angle); - sun_position.y = sin(sun_angle); - sun_position.z = 0.0; + sun_position = skyGetSunDirection(definition); look = v3Normalize(look); dist = v3Norm(v3Sub(look, sun_position)); @@ -269,3 +266,23 @@ void skyRender(SkyDefinition* definition, Renderer* renderer) } } } + +Vector3 skyGetSunDirection(SkyDefinition* definition) +{ + Vector3 result; + double sun_angle = (definition->daytime + 0.75) * M_PI * 2.0; + result.x = cos(sun_angle); + result.y = sin(sun_angle); + result.z = 0.0; + return result; +} + +Color skyGetSunColor(SkyDefinition* definition) +{ + return colorGradationGet(definition->sun_color, definition->daytime); +} + +Color skyGetZenithColor(SkyDefinition* definition) +{ + return colorGradationGet(definition->zenith_color, definition->daytime); +} diff --git a/lib_paysages/sky.h b/lib_paysages/sky.h index 299f3b3..e966a79 100644 --- a/lib_paysages/sky.h +++ b/lib_paysages/sky.h @@ -35,6 +35,9 @@ void skyValidateDefinition(SkyDefinition* definition); int skyGetLights(SkyDefinition* sky, LightDefinition* lights, int max_lights); Color skyGetColor(SkyDefinition* definition, Renderer* renderer, Vector3 eye, Vector3 look); void skyRender(SkyDefinition* definition, Renderer* renderer); +Vector3 skyGetSunDirection(SkyDefinition* definition); +Color skyGetSunColor(SkyDefinition* definition); +Color skyGetZenithColor(SkyDefinition* definition); #ifdef __cplusplus }