paysages : Terrain canvas - Height map painting (WIP).

git-svn-id: https://subversion.assembla.com/svn/thunderk/paysages@390 b1fd45b6-86a6-48da-8261-f70d1f35bdcc
This commit is contained in:
Michaël Lemaire 2012-07-15 14:42:50 +00:00 committed by ThunderK
parent 82181ed5b0
commit 2fc252f59a
4 changed files with 233 additions and 13 deletions

View file

@ -1,5 +1,6 @@
#include "dialogheightmap.h" #include "dialogheightmap.h"
#include <QLabel>
#include <QBoxLayout> #include <QBoxLayout>
#include <QGridLayout> #include <QGridLayout>
#include <QPushButton> #include <QPushButton>
@ -16,6 +17,7 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, HeightMap* heightmap) : Dialog
QWidget* viewer; QWidget* viewer;
QGridLayout* viewer_layout; QGridLayout* viewer_layout;
QLabel* label;
QSlider* slider; QSlider* slider;
QPushButton* button; QPushButton* button;
@ -43,6 +45,7 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, HeightMap* heightmap) : Dialog
panel = new QWidget(mainarea); panel = new QWidget(mainarea);
panel->setLayout(new QVBoxLayout()); panel->setLayout(new QVBoxLayout());
mainarea->layout()->addWidget(panel); mainarea->layout()->addWidget(panel);
mainarea->layout()->setAlignment(panel, Qt::AlignTop);
// Viewer layout (3d display + sliders) // Viewer layout (3d display + sliders)
_3dview = new WidgetHeightMap(viewer, &_value_modified); _3dview = new WidgetHeightMap(viewer, &_value_modified);
@ -57,10 +60,28 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, HeightMap* heightmap) : Dialog
viewer_layout->addWidget(slider, 0, 1); viewer_layout->addWidget(slider, 0, 1);
// Panel layout // 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())); connect(button, SIGNAL(clicked()), _3dview, SLOT(resetToTerrain()));
panel->layout()->addWidget(button); 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 // Buttons layout
button = new QPushButton(tr("Validate"), buttons); button = new QPushButton(tr("Validate"), buttons);
buttons->layout()->addWidget(button); buttons->layout()->addWidget(button);
@ -107,3 +128,13 @@ void DialogHeightMap::angleVChanged(int value)
{ {
_3dview->setVerticalViewAngle(M_PI_2 * ((double)value) / 1000.0); _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);
}

View file

@ -19,6 +19,8 @@ public slots:
private slots: private slots:
void angleHChanged(int value); void angleHChanged(int value);
void angleVChanged(int value); void angleVChanged(int value);
void brushSizeChanged(int value);
void brushSmoothingChanged(int value);
private: private:
HeightMap* _value_original; HeightMap* _value_original;

View file

@ -1,6 +1,7 @@
#include "widgetheightmap.h" #include "widgetheightmap.h"
#include <QTime> #include <QTime>
#include <QMouseEvent>
#include <math.h> #include <math.h>
#include <GL/glu.h> #include <GL/glu.h>
#include "tools.h" #include "tools.h"
@ -12,8 +13,12 @@ WidgetHeightMap::WidgetHeightMap(QWidget *parent, HeightMap* heightmap):
{ {
setMinimumSize(500, 500); setMinimumSize(500, 500);
setFocusPolicy(Qt::StrongFocus); setFocusPolicy(Qt::StrongFocus);
setMouseTracking(true);
_heightmap = heightmap; _heightmap = heightmap;
_vertexes = new _VertexInfo[heightmap->resolution_x * heightmap->resolution_z];
_dirty = true;
_average_frame_time = 0.0; _average_frame_time = 0.0;
@ -22,10 +27,16 @@ WidgetHeightMap::WidgetHeightMap(QWidget *parent, HeightMap* heightmap):
_angle_h = 0.0; _angle_h = 0.0;
_angle_v = 0.3; _angle_v = 0.3;
_brush_x = 0.0;
_brush_z = 0.0;
_brush_size = 10.0;
_brush_smoothing = 0.5;
} }
WidgetHeightMap::~WidgetHeightMap() WidgetHeightMap::~WidgetHeightMap()
{ {
delete[] _vertexes;
} }
void WidgetHeightMap::setHorizontalViewAngle(double angle_h) void WidgetHeightMap::setHorizontalViewAngle(double angle_h)
@ -40,6 +51,22 @@ void WidgetHeightMap::setVerticalViewAngle(double angle_v)
updateGL(); 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() void WidgetHeightMap::resetToTerrain()
{ {
TerrainDefinition terrain; TerrainDefinition terrain;
@ -59,6 +86,7 @@ void WidgetHeightMap::resetToTerrain()
} }
terrainDeleteDefinition(&terrain); terrainDeleteDefinition(&terrain);
_dirty = true;
updateGL(); updateGL();
} }
@ -72,6 +100,44 @@ void WidgetHeightMap::mousePressEvent(QMouseEvent* event)
void WidgetHeightMap::mouseMoveEvent(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) void WidgetHeightMap::wheelEvent(QWheelEvent* event)
@ -82,19 +148,28 @@ void WidgetHeightMap::initializeGL()
{ {
glClearColor(0.0, 0.0, 0.0, 0.0); glClearColor(0.0, 0.0, 0.0, 0.0);
glDisable(GL_LIGHTING); 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); //glFrontFace(GL_CCW);
glCullFace(GL_BACK); //glCullFace(GL_BACK);
glEnable(GL_CULL_FACE); glDisable(GL_CULL_FACE);
glDepthFunc(GL_LESS); glDepthFunc(GL_LESS);
glDepthMask(true); glDepthMask(true);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_LINE_SMOOTH); //glEnable(GL_LINE_SMOOTH);
glLineWidth(1.0); //glLineWidth(1.0);
glDisable(GL_FOG); glDisable(GL_FOG);
@ -121,6 +196,13 @@ void WidgetHeightMap::paintGL()
start_time = QTime::currentTime(); start_time = QTime::currentTime();
// Update vertex cache
if (_dirty)
{
updateVertexInfo();
_dirty = false;
}
// Place camera // Place camera
glMatrixMode(GL_MODELVIEW); glMatrixMode(GL_MODELVIEW);
glLoadIdentity(); glLoadIdentity();
@ -129,18 +211,57 @@ void WidgetHeightMap::paintGL()
// Background // Background
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBegin(GL_POINTS); // Height map
glColor3d(1.0, 0.0, 0.0);
rx = _heightmap->resolution_x; rx = _heightmap->resolution_x;
rz = _heightmap->resolution_z; 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++) 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 // Time stats
frame_time = 0.001 * (double)start_time.msecsTo(QTime::currentTime()); 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)); 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));
}
}
}
}

View file

@ -2,8 +2,15 @@
#define _PAYSAGES_QT_WIDGETHEIGHTMAP_H_ #define _PAYSAGES_QT_WIDGETHEIGHTMAP_H_
#include <QGLWidget> #include <QGLWidget>
#include "../lib_paysages/euclid.h"
#include "../lib_paysages/heightmap.h" #include "../lib_paysages/heightmap.h"
typedef struct
{
Vector3 point;
Vector3 normal;
} _VertexInfo;
class WidgetHeightMap : public QGLWidget class WidgetHeightMap : public QGLWidget
{ {
Q_OBJECT Q_OBJECT
@ -13,6 +20,8 @@ public:
void setHorizontalViewAngle(double angle_h); void setHorizontalViewAngle(double angle_h);
void setVerticalViewAngle(double angle_v); void setVerticalViewAngle(double angle_v);
void setBrushSize(double size);
void setBrushSmoothing(double smoothing);
public slots: public slots:
void resetToTerrain(); void resetToTerrain();
@ -27,8 +36,14 @@ protected:
void resizeGL(int w, int h); void resizeGL(int w, int h);
void paintGL(); void paintGL();
private:
void updateVertexInfo();
private: private:
HeightMap* _heightmap; HeightMap* _heightmap;
_VertexInfo* _vertexes;
bool _dirty;
double _average_frame_time; double _average_frame_time;
@ -37,6 +52,11 @@ private:
double _angle_h; double _angle_h;
double _angle_v; double _angle_v;
double _brush_x;
double _brush_z;
double _brush_size;
double _brush_smoothing;
}; };
#endif #endif