diff --git a/TODO b/TODO index 55a1aa5..9e9fce7 100644 --- a/TODO +++ b/TODO @@ -11,11 +11,17 @@ Technology Preview 2 : - Add a zone editor dialog for localized textures. - Add a terrain modifier dialog with zones. - Add a noise filler (and maybe noise intervals ?). -- Fix the sun appearance. +- Fix the distorted sun appearance. - Improve curve editor. => Add curve modes => Improve curve rendering => Add axis labels +- Improve 3d explorer + => Store computed vertices in memory, using chunks + => Compute the ground texture by chunk + => Don't display the water if it's below all ground + => Add the sun position + => Try to overcome the near frustum cutting - 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 new file mode 100644 index 0000000..3d06160 --- /dev/null +++ b/gui_qt/wandererchunk.cpp @@ -0,0 +1,104 @@ +#include "wandererchunk.h" + +#include +#include +#include +#include +#include +#include +#include "../lib_paysages/color.h" +#include "../lib_paysages/tools.h" + +WandererChunk::WandererChunk(double x, double z, double size) +{ + _startx = x; + _startz = z; + _dirty = true; + _chunksize = size; + _nbsubchunks = 4; + _subchunksize = size / (double)_nbsubchunks; + _texture = new QImage(4, 4, 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(); +} + +WandererChunk::~WandererChunk() +{ + _lock.lock(); + delete _texture; + free(_heightmap); + _lock.unlock(); +} + +void WandererChunk::render(QGLWidget* widget) +{ + _lock.lock(); + + if (_need_texture_upload) + { + _need_texture_upload = false; + if (_texture_id) + { + widget->deleteTexture(_texture_id); + } + _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); + } + glBindTexture(GL_TEXTURE_2D, _texture_id); + + double tsize = 1.0 / (double)_nbsubchunks; + for (int j = 0; j < _nbsubchunks; j++) + { + 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)); + glVertex3d(_startx + _subchunksize * (double)i, _heightmap[(j + 1) * (_nbsubchunks + 1) + i], _startz + _subchunksize * (double)(j + 1)); + } + glEnd(); + } + + _lock.unlock(); +} + +void WandererChunk::maintain(Renderer* renderer) +{ + _lock.lock(); + + if (_dirty) + { + _dirty = false; + + // Compute heightmap + for (int j = 0; j <= _nbsubchunks; j++) + { + for (int i = 0; i <= _nbsubchunks; i++) + { + _heightmap[j * (_nbsubchunks + 1) + i] = renderer->getTerrainHeight(renderer, _startx + _subchunksize * (double)i, _startz + _subchunksize * (double)j); + } + } + + // 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; + } + + _lock.unlock(); +} diff --git a/gui_qt/wandererchunk.h b/gui_qt/wandererchunk.h new file mode 100644 index 0000000..f071c69 --- /dev/null +++ b/gui_qt/wandererchunk.h @@ -0,0 +1,32 @@ +#ifndef _PAYSAGES_QT_WANDERERCHUNK_H_ +#define _PAYSAGES_QT_WANDERERCHUNK_H_ + +#include +#include +#include +#include "../lib_paysages/renderer.h" + +class WandererChunk +{ +public: + WandererChunk(double x, double z, double size); + ~WandererChunk(); + + void maintain(Renderer* renderer); + void render(QGLWidget* widget); + +private: + QMutex _lock; + double _startx; + double _startz; + bool _dirty; + double _chunksize; + double _subchunksize; + int _nbsubchunks; + QImage* _texture; + GLuint _texture_id; + bool _need_texture_upload; + double* _heightmap; +}; + +#endif diff --git a/gui_qt/widgetwanderer.cpp b/gui_qt/widgetwanderer.cpp index e41d891..65948ee 100644 --- a/gui_qt/widgetwanderer.cpp +++ b/gui_qt/widgetwanderer.cpp @@ -16,16 +16,36 @@ WidgetWanderer::WidgetWanderer(QWidget *parent, CameraDefinition* camera): _base_camera = camera; cameraCopyDefinition(camera, &_current_camera); - this->terrain = terrainCreateDefinition(); - sceneryGetTerrain(&terrain); - - this->water = waterCreateDefinition(); - sceneryGetWater(&water); + _water = waterCreateDefinition(); + sceneryGetWater(&_water); - average_frame_time = 0.05; - quality = 3; - last_mouse_x = 0; - last_mouse_y = 0; + _renderer = sceneryCreateStandardRenderer(); + + int chunks = 16; + double size = 30.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)); + } + } + + _average_frame_time = 0.05; + _quality = 3; + _last_mouse_x = 0; + _last_mouse_y = 0; +} + +WidgetWanderer::~WidgetWanderer() +{ + for (int i = 0; i < _chunks.count(); i++) + { + delete _chunks[i]; + } + waterDeleteDefinition(&_water); } void WidgetWanderer::resetCamera() @@ -95,8 +115,8 @@ void WidgetWanderer::keyPressEvent(QKeyEvent* event) void WidgetWanderer::mousePressEvent(QMouseEvent* event) { - last_mouse_x = event->x(); - last_mouse_y = event->y(); + _last_mouse_x = event->x(); + _last_mouse_y = event->y(); event->ignore(); } @@ -121,15 +141,15 @@ void WidgetWanderer::mouseMoveEvent(QMouseEvent* event) if (event->buttons() & Qt::LeftButton) { - cameraRotateYaw(&_current_camera, (double)(event->x() - last_mouse_x) * factor * 0.1); - cameraRotatePitch(&_current_camera, (double)(event->y() - last_mouse_y) * factor * 0.1); + cameraRotateYaw(&_current_camera, (double)(event->x() - _last_mouse_x) * factor * 0.1); + cameraRotatePitch(&_current_camera, (double)(event->y() - _last_mouse_y) * factor * 0.1); updateGL(); event->accept(); } else if (event->buttons() & Qt::RightButton) { - cameraStrafeRight(&_current_camera, (double)(last_mouse_x - event->x()) * factor); - cameraStrafeUp(&_current_camera, (double)(event->y() - last_mouse_y) * factor); + cameraStrafeRight(&_current_camera, (double)(_last_mouse_x - event->x()) * factor); + cameraStrafeUp(&_current_camera, (double)(event->y() - _last_mouse_y) * factor); updateGL(); event->accept(); } @@ -138,8 +158,8 @@ void WidgetWanderer::mouseMoveEvent(QMouseEvent* event) event->ignore(); } - last_mouse_x = event->x(); - last_mouse_y = event->y(); + _last_mouse_x = event->x(); + _last_mouse_y = event->y(); } void WidgetWanderer::wheelEvent(QWheelEvent* event) @@ -174,7 +194,6 @@ void WidgetWanderer::initializeGL() glClearColor(0.4, 0.7, 0.8, 0.0); glDisable(GL_LIGHTING); - glDisable(GL_TEXTURE); glFrontFace(GL_CCW); glCullFace(GL_BACK); @@ -187,6 +206,10 @@ void WidgetWanderer::initializeGL() glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_LINE_SMOOTH); glLineWidth(1.0); + + glDisable(GL_FOG); + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void WidgetWanderer::resizeGL(int w, int h) @@ -198,74 +221,8 @@ void WidgetWanderer::resizeGL(int w, int h) glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(_current_camera.yfov * 180.0 / M_PI, _current_camera.xratio, _current_camera.znear, _current_camera.zfar); -} - -static inline void _renderTerrainQuad(TerrainDefinition* terrain, double x, double z, double size) -{ - double y1, y2, y3, y4; - - y1 = terrainGetHeight(terrain, x, z); - y2 = terrainGetHeight(terrain, x, z + size); - y3 = terrainGetHeight(terrain, x + size, z + size); - y4 = terrainGetHeight(terrain, x + size, z); - - glColor3f(0.0, 1.0, 0.0); - glBegin(GL_LINE_LOOP); - glVertex3f(x, y1, z); - glVertex3f(x, y2, z + size); - glVertex3f(x + size, y3, z + size); - glVertex3f(x + size, y4, z); - glEnd(); - - glColor3f(0.0, 0.5, 0.0); - glBegin(GL_QUADS); - glVertex3f(x, y1, z); - glVertex3f(x, y2, z + size); - glVertex3f(x + size, y3, z + size); - glVertex3f(x + size, y4, z); - glEnd(); -} - -static void _renderTerrain(TerrainDefinition* terrain, CameraDefinition* camera, int quality) -{ - int chunk_factor, chunk_count, i; - double min_chunk_size, visible_chunk_size; - double radius_int, radius_ext, chunk_size; - double cx, cz; - - min_chunk_size = 1.0 / (double)quality; - visible_chunk_size = 1.0 / (double)quality; - - cx = camera->location.x - fmod(camera->location.x, min_chunk_size * 16.0); - cz = camera->location.z - fmod(camera->location.z, min_chunk_size * 16.0); - - chunk_factor = 1; - chunk_count = 2; - radius_int = 0.0; - radius_ext = min_chunk_size; - chunk_size = min_chunk_size; - - while (radius_ext < 100.0) - { - for (i = 0; i < chunk_count - 1; i++) - { - _renderTerrainQuad(terrain, cx - radius_ext + chunk_size * i, cz - radius_ext, chunk_size); - _renderTerrainQuad(terrain, cx + radius_int, cz - radius_ext + chunk_size * i, chunk_size); - _renderTerrainQuad(terrain, cx + radius_int - chunk_size * i, cz + radius_int, chunk_size); - _renderTerrainQuad(terrain, cx - radius_ext, cz + radius_int - chunk_size * i, chunk_size); - } - - if (chunk_count % 32 == 0 && chunk_size / radius_int < visible_chunk_size) - { - chunk_count /= 2; - chunk_factor *= 2; - /* TODO Fill in gaps with triangles */ - } - chunk_count += 2; - chunk_size = min_chunk_size * chunk_factor; - radius_int = radius_ext; - radius_ext += chunk_size; - } + + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } void WidgetWanderer::paintGL() @@ -282,29 +239,40 @@ void WidgetWanderer::paintGL() 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); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // Render water + glDisable(GL_TEXTURE); + glDisable(GL_TEXTURE_2D); glColor3f(0.0, 0.0, 1.0); 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); - 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); + 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); + 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); glEnd(); - _renderTerrain(&terrain, &_current_camera, quality); + // Render terrain chunks + glEnable(GL_TEXTURE); + glEnable(GL_TEXTURE_2D); + for (int i = 0; i < _chunks.count(); i++) + { + _chunks[i]->maintain(&_renderer); + + _chunks[i]->render(this); + } frame_time = 0.001 * (double)start_time.msecsTo(QTime::currentTime()); - average_frame_time = average_frame_time * 0.8 + frame_time * 0.2; + _average_frame_time = _average_frame_time * 0.8 + frame_time * 0.2; //printf("%d %f\n", quality, average_frame_time); - if (average_frame_time > 0.1 && quality > 1) + if (_average_frame_time > 0.1 && _quality > 1) { - quality--; + _quality--; } - if (average_frame_time < 0.04 && quality < 10) + if (_average_frame_time < 0.04 && _quality < 10) { - quality++; + _quality++; } } diff --git a/gui_qt/widgetwanderer.h b/gui_qt/widgetwanderer.h index 135440f..556f48b 100644 --- a/gui_qt/widgetwanderer.h +++ b/gui_qt/widgetwanderer.h @@ -2,15 +2,19 @@ #define _PAYSAGES_QT_WIDGETWANDERER_H_ #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" class WidgetWanderer : public QGLWidget { Q_OBJECT public: WidgetWanderer(QWidget* parent, CameraDefinition* camera); + ~WidgetWanderer(); public slots: void resetCamera(); @@ -29,15 +33,18 @@ protected: private: CameraDefinition _current_camera; CameraDefinition* _base_camera; - - TerrainDefinition terrain; - WaterDefinition water; - double average_frame_time; - int quality; + Renderer _renderer; + + QVector _chunks; - int last_mouse_x; - int last_mouse_y; + WaterDefinition _water; + + double _average_frame_time; + int _quality; + + int _last_mouse_x; + int _last_mouse_y; }; #endif