From 2fc252f59a5aef98471520b2de07f53e4add6d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Sun, 15 Jul 2012 14:42:50 +0000 Subject: [PATCH] paysages : Terrain canvas - Height map painting (WIP). git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@390 b1fd45b6-86a6-48da-8261-f70d1f35bdcc --- gui_qt/dialogheightmap.cpp | 33 ++++++- gui_qt/dialogheightmap.h | 2 + gui_qt/widgetheightmap.cpp | 191 ++++++++++++++++++++++++++++++++++--- gui_qt/widgetheightmap.h | 20 ++++ 4 files changed, 233 insertions(+), 13 deletions(-) diff --git a/gui_qt/dialogheightmap.cpp b/gui_qt/dialogheightmap.cpp index 7f80f75..9026977 100644 --- a/gui_qt/dialogheightmap.cpp +++ b/gui_qt/dialogheightmap.cpp @@ -1,5 +1,6 @@ #include "dialogheightmap.h" +#include #include #include #include @@ -16,6 +17,7 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, HeightMap* heightmap) : Dialog QWidget* viewer; QGridLayout* viewer_layout; + QLabel* label; QSlider* slider; QPushButton* button; @@ -43,6 +45,7 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, HeightMap* heightmap) : Dialog panel = new QWidget(mainarea); panel->setLayout(new QVBoxLayout()); mainarea->layout()->addWidget(panel); + mainarea->layout()->setAlignment(panel, Qt::AlignTop); // Viewer layout (3d display + sliders) _3dview = new WidgetHeightMap(viewer, &_value_modified); @@ -57,10 +60,28 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, HeightMap* heightmap) : Dialog viewer_layout->addWidget(slider, 0, 1); // Panel layout - button = new QPushButton(tr("Reset to terrain height"), buttons); + button = new QPushButton(tr("Reset to terrain height"), panel); connect(button, SIGNAL(clicked()), _3dview, SLOT(resetToTerrain())); panel->layout()->addWidget(button); + + label = new QLabel(tr("Brush size"), panel); + panel->layout()->addWidget(label); + + slider = new QSlider(Qt::Horizontal, panel); + slider->setRange(6, 150); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(brushSizeChanged(int))); + panel->layout()->addWidget(slider); + slider->setValue(30); + label = new QLabel(tr("Brush smoothing"), panel); + panel->layout()->addWidget(label); + + slider = new QSlider(Qt::Horizontal, panel); + slider->setRange(0, 1000); + connect(slider, SIGNAL(valueChanged(int)), this, SLOT(brushSmoothingChanged(int))); + panel->layout()->addWidget(slider); + slider->setValue(200); + // Buttons layout button = new QPushButton(tr("Validate"), buttons); buttons->layout()->addWidget(button); @@ -107,3 +128,13 @@ void DialogHeightMap::angleVChanged(int value) { _3dview->setVerticalViewAngle(M_PI_2 * ((double)value) / 1000.0); } + +void DialogHeightMap::brushSizeChanged(int value) +{ + _3dview->setBrushSize((double)value / 10.0); +} + +void DialogHeightMap::brushSmoothingChanged(int value) +{ + _3dview->setBrushSmoothing((double)value / 1000.0); +} diff --git a/gui_qt/dialogheightmap.h b/gui_qt/dialogheightmap.h index 56e478e..5562611 100644 --- a/gui_qt/dialogheightmap.h +++ b/gui_qt/dialogheightmap.h @@ -19,6 +19,8 @@ public slots: private slots: void angleHChanged(int value); void angleVChanged(int value); + void brushSizeChanged(int value); + void brushSmoothingChanged(int value); private: HeightMap* _value_original; diff --git a/gui_qt/widgetheightmap.cpp b/gui_qt/widgetheightmap.cpp index 3d89c5c..6a915bd 100644 --- a/gui_qt/widgetheightmap.cpp +++ b/gui_qt/widgetheightmap.cpp @@ -1,6 +1,7 @@ #include "widgetheightmap.h" #include +#include #include #include #include "tools.h" @@ -12,8 +13,12 @@ WidgetHeightMap::WidgetHeightMap(QWidget *parent, HeightMap* heightmap): { setMinimumSize(500, 500); setFocusPolicy(Qt::StrongFocus); + setMouseTracking(true); _heightmap = heightmap; + _vertexes = new _VertexInfo[heightmap->resolution_x * heightmap->resolution_z]; + + _dirty = true; _average_frame_time = 0.0; @@ -22,10 +27,16 @@ WidgetHeightMap::WidgetHeightMap(QWidget *parent, HeightMap* heightmap): _angle_h = 0.0; _angle_v = 0.3; + + _brush_x = 0.0; + _brush_z = 0.0; + _brush_size = 10.0; + _brush_smoothing = 0.5; } WidgetHeightMap::~WidgetHeightMap() { + delete[] _vertexes; } void WidgetHeightMap::setHorizontalViewAngle(double angle_h) @@ -40,6 +51,22 @@ void WidgetHeightMap::setVerticalViewAngle(double angle_v) updateGL(); } +void WidgetHeightMap::setBrushSize(double size) +{ + _brush_size = size; + _brush_x = 0.0; + _brush_z = 0.0; + updateGL(); +} + +void WidgetHeightMap::setBrushSmoothing(double smoothing) +{ + _brush_smoothing = smoothing; + _brush_x = 0.0; + _brush_z = 0.0; + updateGL(); +} + void WidgetHeightMap::resetToTerrain() { TerrainDefinition terrain; @@ -59,6 +86,7 @@ void WidgetHeightMap::resetToTerrain() } terrainDeleteDefinition(&terrain); + _dirty = true; updateGL(); } @@ -72,6 +100,44 @@ void WidgetHeightMap::mousePressEvent(QMouseEvent* event) void WidgetHeightMap::mouseMoveEvent(QMouseEvent* event) { + int move_x = event->x() - _last_mouse_x; + int move_y = event->y() - _last_mouse_y; + + if (event->buttons() & Qt::MiddleButton) + { + // Rotate around the turntable + _angle_h -= (double)move_x * 0.008; + _angle_v += (double)move_y * 0.003; + + updateGL(); + } + else + { + // OpenGL picking using z-buffer + GLint viewport[4]; + GLdouble modelview[16]; + GLdouble projection[16]; + GLfloat winX, winY, winZ; + Vector3 point; + + glGetDoublev(GL_MODELVIEW_MATRIX, modelview); + glGetDoublev(GL_PROJECTION_MATRIX, projection); + glGetIntegerv(GL_VIEWPORT, viewport); + + winX = (float)event->x(); + winY = (float)height() - (float)event->y(); + glReadPixels(event->x(), (int)winY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ); + + gluUnProject(winX, winY, winZ, modelview, projection, viewport, &point.x, &point.y, &point.z); + + _brush_x = point.x; + _brush_z = point.z; + + updateGL(); + } + + _last_mouse_x = event->x(); + _last_mouse_y = event->y(); } void WidgetHeightMap::wheelEvent(QWheelEvent* event) @@ -82,19 +148,28 @@ void WidgetHeightMap::initializeGL() { glClearColor(0.0, 0.0, 0.0, 0.0); - glDisable(GL_LIGHTING); - - glFrontFace(GL_CCW); - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); + GLfloat light_position[] = { 40.0, 40.0, 40.0, 0.0 }; + GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; + GLfloat light_specular[] = { 0.0, 0.0, 0.0, 0.0 }; + glShadeModel(GL_SMOOTH); + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_COLOR_MATERIAL); + + //glFrontFace(GL_CCW); + //glCullFace(GL_BACK); + glDisable(GL_CULL_FACE); glDepthFunc(GL_LESS); glDepthMask(true); glEnable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - glEnable(GL_LINE_SMOOTH); - glLineWidth(1.0); + //glEnable(GL_LINE_SMOOTH); + //glLineWidth(1.0); glDisable(GL_FOG); @@ -121,6 +196,13 @@ void WidgetHeightMap::paintGL() start_time = QTime::currentTime(); + // Update vertex cache + if (_dirty) + { + updateVertexInfo(); + _dirty = false; + } + // Place camera glMatrixMode(GL_MODELVIEW); glLoadIdentity(); @@ -129,18 +211,57 @@ void WidgetHeightMap::paintGL() // Background glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glBegin(GL_POINTS); - glColor3d(1.0, 0.0, 0.0); + // Height map rx = _heightmap->resolution_x; rz = _heightmap->resolution_z; - for (int x = 0; x < rx; x++) + for (int x = 0; x < rx - 1; x++) { + glBegin(GL_QUAD_STRIP); for (int z = 0; z < rz; z++) { - glVertex3d(80.0 * (double)x / (double)(rx - 1) - 40.0, _heightmap->data[z * rx + x], 80.0 * (double)z / (double)(rz - 1) - 40.0); + _VertexInfo* vertex = _vertexes + z * rx + x; + double diff_x, diff_z, diff; + + diff_x = (vertex + 1)->point.x - _brush_x; + diff_z = (vertex + 1)->point.z - _brush_z; + diff = sqrt(diff_x * diff_x + diff_z * diff_z); + if (diff > _brush_size) + { + diff = 0.0; + } + else if (diff > _brush_size * (1.0 - _brush_smoothing)) + { + diff = 1.0 - (diff - _brush_size * (1.0 - _brush_smoothing)) / (_brush_size * _brush_smoothing); + } + else + { + diff = 1.0; + } + glColor3d(1.0, 1.0 - diff, 1.0 - diff); + glNormal3d((vertex + 1)->normal.x, (vertex + 1)->normal.y, (vertex + 1)->normal.z); + glVertex3d((vertex + 1)->point.x, (vertex + 1)->point.y, (vertex + 1)->point.z); + + diff_x = vertex->point.x - _brush_x; + diff_z = vertex->point.z - _brush_z; + diff = sqrt(diff_x * diff_x + diff_z * diff_z); + if (diff > _brush_size) + { + diff = 0.0; + } + else if (diff > _brush_size * (1.0 - _brush_smoothing)) + { + diff = 1.0 - (diff - _brush_size * (1.0 - _brush_smoothing)) / (_brush_size * _brush_smoothing); + } + else + { + diff = 1.0; + } + glColor3d(1.0, 1.0 - diff, 1.0 - diff); + glNormal3d(vertex->normal.x, vertex->normal.y, vertex->normal.z); + glVertex3d(vertex->point.x, vertex->point.y, vertex->point.z); } + glEnd(); } - glEnd(); // Time stats frame_time = 0.001 * (double)start_time.msecsTo(QTime::currentTime()); @@ -152,3 +273,49 @@ void WidgetHeightMap::paintGL() logDebug(QString("[OpenGL] ERROR : ") + (const char*)gluErrorString(error_code)); } } + +void WidgetHeightMap::updateVertexInfo() +{ + int rx = _heightmap->resolution_x; + int rz = _heightmap->resolution_z; + + // Update positions + for (int x = 0; x < rx; x++) + { + for (int z = 0; z < rz; z++) + { + _VertexInfo* vertex = _vertexes + z * rx + x; + + vertex->point.x = 80.0 * (double)x / (double)(rx - 1) - 40.0; + vertex->point.y = _heightmap->data[z * rx + x]; + vertex->point.z = 80.0 * (double)z / (double)(rz - 1) - 40.0; + } + } + + // Update normals + for (int x = 0; x < rx; x++) + { + for (int z = 0; z < rz; z++) + { + _VertexInfo* vertex = _vertexes + z * rx + x; + + if (x == rx - 1) + { + vertex->normal = (vertex - 1)->normal; + } + else if (z == rz - 1) + { + vertex->normal = (vertex - rx)->normal; + } + else + { + Vector3 dx, dz; + + dx = v3Sub((vertex + 1)->point, vertex->point); + dz = v3Sub((vertex + rx)->point, vertex->point); + + vertex->normal = v3Cross(v3Normalize(dz), v3Normalize(dx)); + } + } + } +} diff --git a/gui_qt/widgetheightmap.h b/gui_qt/widgetheightmap.h index 2a96764..3b2d145 100644 --- a/gui_qt/widgetheightmap.h +++ b/gui_qt/widgetheightmap.h @@ -2,8 +2,15 @@ #define _PAYSAGES_QT_WIDGETHEIGHTMAP_H_ #include +#include "../lib_paysages/euclid.h" #include "../lib_paysages/heightmap.h" +typedef struct +{ + Vector3 point; + Vector3 normal; +} _VertexInfo; + class WidgetHeightMap : public QGLWidget { Q_OBJECT @@ -13,6 +20,8 @@ public: void setHorizontalViewAngle(double angle_h); void setVerticalViewAngle(double angle_v); + void setBrushSize(double size); + void setBrushSmoothing(double smoothing); public slots: void resetToTerrain(); @@ -27,8 +36,14 @@ protected: void resizeGL(int w, int h); void paintGL(); +private: + void updateVertexInfo(); + private: HeightMap* _heightmap; + _VertexInfo* _vertexes; + + bool _dirty; double _average_frame_time; @@ -37,6 +52,11 @@ private: double _angle_h; double _angle_v; + + double _brush_x; + double _brush_z; + double _brush_size; + double _brush_smoothing; }; #endif