diff --git a/Makefile b/Makefile index 687ce03..229de20 100644 --- a/Makefile +++ b/Makefile @@ -6,24 +6,30 @@ all: @+cd src/exploring && make BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} @+cd src/controlling && make BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} @+cd src/editing && qmake "BUILDMODE=${BUILDMODE}" "PROJECT_PATH=${CURDIR}" && make + @+cd src/testing && make BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} clean: cd src/rendering && make clean BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} cd src/exploring && make clean BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} cd src/controlling && make clean BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} cd src/editing && qmake "BUILDMODE=${BUILDMODE}" "PROJECT_PATH=${CURDIR}" && make clean + cd src/testing && make clean BUILDMODE=${BUILDMODE} PROJECT_PATH=${CURDIR} rm -f ${BUILDPATH}/paysages-cli rm -f ${BUILDPATH}/paysages-qt + rm -f ${BUILDPATH}/paysages-tests rm -f ${BUILDPATH}/libpaysages_exploring.so rm -f ${BUILDPATH}/libpaysages_rendering.so release: make BUILDMODE=release all -run_cli: +tests: all + LD_LIBRARY_PATH=${BUILDPATH} ${BUILDPATH}/paysages-tests + +run_cli: all LD_LIBRARY_PATH=${BUILDPATH} ${BUILDPATH}/paysages-cli -run_qt: +run_qt: all LD_LIBRARY_PATH=${BUILDPATH} ${BUILDPATH}/paysages-qt profile: diff --git a/src/editing/dialogheightmap.cpp b/src/editing/dialogheightmap.cpp index 19ef0e5..81fd5ab 100644 --- a/src/editing/dialogheightmap.cpp +++ b/src/editing/dialogheightmap.cpp @@ -18,8 +18,6 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, TerrainDefinition* terrain) : QWidget* mainarea; QWidget* buttons; QWidget* panel; - QWidget* viewer; - QGridLayout* viewer_layout; QLabel* label; QSlider* slider; @@ -42,30 +40,15 @@ DialogHeightMap::DialogHeightMap(QWidget* parent, TerrainDefinition* terrain) : this->layout()->addWidget(buttons); // Main area layout (viewer + panel) - viewer = new QWidget(mainarea); - viewer_layout = new QGridLayout(); - viewer->setLayout(viewer_layout); - mainarea->layout()->addWidget(viewer); + _3dview = new WidgetHeightMap(mainarea, _value_modified); + connect(_3dview, SIGNAL(heightmapChanged()), this, SLOT(heightmapChanged())); + mainarea->layout()->addWidget(_3dview); 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); - viewer_layout->addWidget(_3dview, 0, 0); - connect(_3dview, SIGNAL(heightmapChanged()), this, SLOT(heightmapChanged())); - - slider = new QSlider(Qt::Horizontal, viewer); - slider->setRange(0, 1000); - connect(slider, SIGNAL(valueChanged(int)), this, SLOT(angleHChanged(int))); - viewer_layout->addWidget(slider, 1, 0); - slider = new QSlider(Qt::Vertical, viewer); - slider->setRange(-300, 700); - connect(slider, SIGNAL(valueChanged(int)), this, SLOT(angleVChanged(int))); - viewer_layout->addWidget(slider, 0, 1); - // Panel layout _info_memory = new QLabel(panel); panel->layout()->addWidget(_info_memory); @@ -151,16 +134,6 @@ void DialogHeightMap::revert() _3dview->revert(); } -void DialogHeightMap::angleHChanged(int value) -{ - _3dview->setHorizontalViewAngle(M_PI * ((double)value) / 500.0); -} - -void DialogHeightMap::angleVChanged(int value) -{ - _3dview->setVerticalViewAngle(M_PI_2 * ((double)value) / 1000.0); -} - void DialogHeightMap::brushModeChanged(int value) { _3dview->setBrushMode((HeightMapBrushMode)value); diff --git a/src/editing/dialogheightmap.h b/src/editing/dialogheightmap.h index 2847f23..23c0c47 100644 --- a/src/editing/dialogheightmap.h +++ b/src/editing/dialogheightmap.h @@ -18,8 +18,6 @@ public slots: void revert(); private slots: - void angleHChanged(int value); - void angleVChanged(int value); void brushModeChanged(int value); void brushSizeChanged(int value); void brushSmoothingChanged(int value); diff --git a/src/editing/widgetheightmap.cpp b/src/editing/widgetheightmap.cpp index 8491d54..5497be5 100644 --- a/src/editing/widgetheightmap.cpp +++ b/src/editing/widgetheightmap.cpp @@ -35,11 +35,13 @@ WidgetHeightMap::WidgetHeightMap(QWidget *parent, TerrainDefinition* terrain): _last_time = QDateTime::currentDateTime(); _mouse_moved = false; - _position_x = 0; - _position_z = 0; - _angle_h = 0.0; - _angle_v = 1.4; - _distance = 100.0; + _current_camera = cameraCreateDefinition(); + _top_camera = cameraCreateDefinition(); + _temp_camera = cameraCreateDefinition(); + + cameraSetLocation(&_current_camera, 0.0, 80.0, 20.0); + cameraSetTarget(&_current_camera, 0.0, 0.0, 0.0); + cameraCopyDefinition(&_current_camera, &_top_camera); _brush_x = 0.0; _brush_z = 0.0; @@ -58,18 +60,6 @@ WidgetHeightMap::~WidgetHeightMap() delete[] _vertices; } -void WidgetHeightMap::setHorizontalViewAngle(double angle_h) -{ - _angle_h = angle_h; - updateGL(); -} - -void WidgetHeightMap::setVerticalViewAngle(double angle_v) -{ - _angle_v = angle_v; - updateGL(); -} - void WidgetHeightMap::setBrushMode(HeightMapBrushMode mode) { _brush_mode = mode; @@ -166,8 +156,9 @@ void WidgetHeightMap::mouseMoveEvent(QMouseEvent* event) int move_x = event->x() - _last_mouse_x; int move_y = event->y() - _last_mouse_y; - _angle_h -= (double)move_x * 0.008; - _angle_v += (double)move_y * 0.003; + // TODO + /*_angle_h -= (double)move_x * 0.008; + _angle_v += (double)move_y * 0.003;*/ } _last_mouse_x = event->x(); @@ -193,8 +184,8 @@ void WidgetHeightMap::timerEvent(QTimerEvent*) double brush_strength; TerrainBrush brush; - brush.relative_x = _brush_x + (double)_position_x; - brush.relative_z = _brush_z + (double)_position_z; + brush.relative_x = _brush_x + _current_camera.target.x; + brush.relative_z = _brush_z + _current_camera.target.z; brush.hard_radius = _brush_size * (1.0 - _brush_smoothing); brush.smoothed_size = _brush_size * _brush_smoothing; brush.total_radius = brush.hard_radius + brush.smoothed_size; @@ -230,7 +221,7 @@ void WidgetHeightMap::timerEvent(QTimerEvent*) // Edge scrolling // TODO Apply scrolling to vertex info and dirty only needed area - double edge_length = 10.0; + /*double edge_length = 10.0; if (_brush_x > HEIGHTMAP_RESOLUTION / 2.0 - edge_length) { double dx = HEIGHTMAP_RESOLUTION / 2.0 - edge_length - _brush_x; @@ -262,7 +253,7 @@ void WidgetHeightMap::timerEvent(QTimerEvent*) _dirty = true; updateGL(); - } + }*/ } void WidgetHeightMap::initializeGL() @@ -365,7 +356,7 @@ void WidgetHeightMap::paintGL() // Place camera glMatrixMode(GL_MODELVIEW); glLoadIdentity(); - gluLookAt(_distance * cos(_angle_h) * cos(_angle_v), _distance * sin(_angle_v), -_distance * sin(_angle_h) * cos(_angle_v), 0.0, 0.0, 0.0, -cos(_angle_h) * sin(_angle_v), cos(_angle_v), sin(_angle_h) * sin(_angle_v)); + 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); // Place lights GLfloat light_position[] = { 40.0, 40.0, 40.0, 0.0 }; @@ -388,8 +379,8 @@ void WidgetHeightMap::paintGL() _VertexInfo* vertex = _vertices + z * rx + x + dx; double diff_x, diff_z, diff; - diff_x = vertex->point.x - (double)_position_x - _brush_x; - diff_z = vertex->point.z - (double)_position_z - _brush_z; + diff_x = vertex->point.x - _current_camera.target.x - _brush_x; + diff_z = vertex->point.z - _current_camera.target.z - _brush_z; diff = sqrt(diff_x * diff_x + diff_z * diff_z); if (diff > _brush_size) { @@ -405,7 +396,7 @@ void WidgetHeightMap::paintGL() } glColor3f(0.8 + diff, vertex->painted ? 1.0 : 0.8, 0.8); glNormal3f(vertex->normal.x, vertex->normal.y, vertex->normal.z); - glVertex3f(vertex->point.x - (double)_position_x, vertex->point.y, vertex->point.z - (double)_position_z); + glVertex3f(vertex->point.x - _current_camera.target.x, vertex->point.y, vertex->point.z - _current_camera.target.z); } } glEnd(); @@ -441,8 +432,8 @@ void WidgetHeightMap::updateVertexInfo() { _VertexInfo* vertex = _vertices + z * rx + x; - dx = _position_x + x - rx / 2; - dz = _position_z + z - rz / 2; + dx = _current_camera.target.x + x - rx / 2; + dz = _current_camera.target.z + z - rz / 2; vertex->point.x = (double)dx; vertex->point.z = (double)dz; diff --git a/src/editing/widgetheightmap.h b/src/editing/widgetheightmap.h index dcfe6cd..325f538 100644 --- a/src/editing/widgetheightmap.h +++ b/src/editing/widgetheightmap.h @@ -3,6 +3,7 @@ #include #include +#include "rendering/camera.h" #include "rendering/tools/euclid.h" #include "rendering/renderer.h" #include "rendering/terrain/public.h" @@ -28,8 +29,6 @@ public: WidgetHeightMap(QWidget* parent, TerrainDefinition* terrain); ~WidgetHeightMap(); - void setHorizontalViewAngle(double angle_h); - void setVerticalViewAngle(double angle_v); void setBrushMode(HeightMapBrushMode mode); void setBrushSize(double size); void setBrushSmoothing(double smoothing); @@ -73,11 +72,9 @@ private: QDateTime _last_time; bool _mouse_moved; - int _position_x; - int _position_z; - double _angle_h; - double _angle_v; - double _distance; + CameraDefinition _top_camera; + CameraDefinition _temp_camera; + CameraDefinition _current_camera; double _brush_x; double _brush_z; diff --git a/src/rendering/camera.c b/src/rendering/camera.c index 846e5b6..7a3cba8 100644 --- a/src/rendering/camera.c +++ b/src/rendering/camera.c @@ -155,7 +155,8 @@ void cameraSetTarget(CameraDefinition* camera, double x, double y, double z) } else { - /* TODO Guess angles */ + /* Guess angles */ + v3ToEuler(forward, &camera->yaw, &camera->pitch); } cameraValidateDefinition(camera, 0); diff --git a/src/rendering/tools/euclid.c b/src/rendering/tools/euclid.c index f64f1e4..4ff94a1 100644 --- a/src/rendering/tools/euclid.c +++ b/src/rendering/tools/euclid.c @@ -102,6 +102,12 @@ Vector3 v3Cross(Vector3 v1, Vector3 v2) return result; } +void v3ToEuler(Vector3 v, double* heading, double* attitude) +{ + *heading = euclidGet2DAngle(v.x, v.z); + *attitude = euclidGet2DAngle(sqrt(v.x * v.x + v.z * v.z), v.y); +} + void m4Save(PackStream* stream, Matrix4* m) { packWriteDouble(stream, &m->a); @@ -405,6 +411,41 @@ Matrix4 m4Inverse(Matrix4 m) } } +double euclidGet2DAngle(double x, double y) +{ + double nx, ny, d, ret; + + if (x == 0.0) + { + if (y == 0.0) + { + return 0.0; + } + else + { + if (y < 0.0) + { + return 3.0 * M_PI_2; + } + else + { + return M_PI_2; + } + } + } + + d = sqrt(x * x + y * y); + nx = x / d; + ny = y / d; + + ret = asin(ny); + if (nx < 0.0) + { + ret = M_PI - ret; + } + return fmod(ret, 2.0 * M_PI); +} + Vector3 euclidGetNormalFromTriangle(Vector3 center, Vector3 bottom, Vector3 right) { Vector3 dx = v3Sub(right, center); diff --git a/src/rendering/tools/euclid.h b/src/rendering/tools/euclid.h index a732d6e..8bb0072 100644 --- a/src/rendering/tools/euclid.h +++ b/src/rendering/tools/euclid.h @@ -53,6 +53,7 @@ double v3Norm(Vector3 v); Vector3 v3Normalize(Vector3 v); double v3Dot(Vector3 v1, Vector3 v2); Vector3 v3Cross(Vector3 v1, Vector3 v2); +void v3ToEuler(Vector3 v, double* heading, double* attitude); void m4Save(PackStream* stream, Matrix4* m); void m4Load(PackStream* stream, Matrix4* m); @@ -74,6 +75,7 @@ Matrix4 m4NewPerspective(double fov_y, double aspect, double near, double far); double m4Determinant(Matrix4 m); Matrix4 m4Inverse(Matrix4 m); +double euclidGet2DAngle(double x, double y); Vector3 euclidGetNormalFromTriangle(Vector3 center, Vector3 bottom, Vector3 right); double euclidGetDistance2D(double x1, double y1, double x2, double y2); int euclidRayIntersectSphere(Vector3 ray_point, Vector3 ray_direction, Vector3 sphere_center, double sphere_radius, Vector3* hit1, Vector3* hit2); diff --git a/src/testing/Makefile b/src/testing/Makefile new file mode 100644 index 0000000..522be61 --- /dev/null +++ b/src/testing/Makefile @@ -0,0 +1,11 @@ +include ../common_pre.mk + +OBJPATH = ${BUILDPATH}/testing +RESULT = ${BUILDPATH}/paysages-tests +SOURCES += $(wildcard *.c) +HEADERS += $(wildcard *.h) + +CC_LDFLAGS += -L${BUILDPATH} -lpaysages_rendering +LIBS += check + +include ../common_post.mk diff --git a/src/testing/common.h b/src/testing/common.h new file mode 100644 index 0000000..cd205bd --- /dev/null +++ b/src/testing/common.h @@ -0,0 +1,40 @@ +#ifndef _PAYSAGES_TESTING_COMMON_H_ +#define _PAYSAGES_TESTING_COMMON_H_ + +#include +#include +#include + +static inline void _add_methods_to_case(TCase* tc, ...) +{ + void* method; + va_list argp; + + va_start(argp, tc); + while ((method = va_arg(argp, void*)) != NULL) + { + tcase_add_test(tc, method); + } + va_end(argp); +} + +#define TEST_CASE(_name_,...) void test_ ## _name_ ## _case(Suite* s) { \ + TCase *tc = tcase_create(#_name_); \ + _add_methods_to_case(tc, __VA_ARGS__, NULL); \ + suite_add_tcase(s, tc); \ +} + +static inline int _double_equals(double x, double y) +{ + return fabs(x - y) < 0.00000000001; +} +static inline int _double_not_equals(double x, double y) +{ + return fabs(x - y) >= 0.00000000001; +} + +#define _ck_assert_double(F, X, O, Y) ck_assert_msg(F(X, Y), "Assertion '"#X#O#Y"' failed: "#X"==%f, "#Y"==%f", X, Y) +#define ck_assert_double_eq(X, Y) _ck_assert_double(_double_equals, X, ==, Y) +#define ck_assert_double_ne(X, Y) _ck_assert_double(_double_not_equals, X, !=, Y) + +#endif diff --git a/src/testing/euclid.c b/src/testing/euclid.c new file mode 100644 index 0000000..0da69d6 --- /dev/null +++ b/src/testing/euclid.c @@ -0,0 +1,31 @@ +#include "testing/common.h" +#include "rendering/tools/euclid.h" + +START_TEST(test_euclid_angles) +{ + ck_assert_double_eq(euclidGet2DAngle(0.0, 0.0), 0.0); + + ck_assert_double_eq(euclidGet2DAngle(0.1, 0.0), 0.0); + ck_assert_double_eq(euclidGet2DAngle(1.0, 0.0), 0.0); + ck_assert_double_eq(euclidGet2DAngle(2.0, 0.0), 0.0); + + ck_assert_double_eq(euclidGet2DAngle(0.0, 0.1), M_PI_2); + ck_assert_double_eq(euclidGet2DAngle(0.0, 1.0), M_PI_2); + ck_assert_double_eq(euclidGet2DAngle(0.0, 2.0), M_PI_2); + + ck_assert_double_eq(euclidGet2DAngle(-0.1, 0.0), M_PI); + ck_assert_double_eq(euclidGet2DAngle(-1.0, 0.0), M_PI); + ck_assert_double_eq(euclidGet2DAngle(-2.0, 0.0), M_PI); + + ck_assert_double_eq(euclidGet2DAngle(0.0, -0.1), 3.0 * M_PI_2); + ck_assert_double_eq(euclidGet2DAngle(0.0, -1.0), 3.0 * M_PI_2); + ck_assert_double_eq(euclidGet2DAngle(0.0, -2.0), 3.0 * M_PI_2); + + ck_assert_double_eq(euclidGet2DAngle(0.5, 0.5), M_PI_4); + ck_assert_double_eq(euclidGet2DAngle(0.5, -0.5), 7.0 * M_PI_4); + ck_assert_double_eq(euclidGet2DAngle(-0.5, 0.5), 3.0 * M_PI_4); + ck_assert_double_eq(euclidGet2DAngle(-0.5, -0.5), 5.0 * M_PI_4); +} +END_TEST + +TEST_CASE(euclid, test_euclid_angles) diff --git a/src/testing/main.c b/src/testing/main.c new file mode 100644 index 0000000..5e71eda --- /dev/null +++ b/src/testing/main.c @@ -0,0 +1,19 @@ +#include +#include + +extern void test_euclid_case(Suite* s); + +int main(int argc, char** argv) +{ + int number_failed; + Suite *s = suite_create("rendering"); + + /* TODO Find a way to automate this */ + test_euclid_case(s); + + SRunner *sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + number_failed = srunner_ntests_failed(sr); + srunner_free(sr); + return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +}