diff --git a/TODO b/TODO index 038debc..04ff7e9 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,5 @@ Technology Preview 2 : +- Implement perspective correction for coordinate mapping of rasterized polygons. - Finalize terrain editor. => Add a generation dialog for base noise (overwriting changes). - Get rid of noise dialogs, for simpler settings. @@ -15,6 +16,7 @@ Technology Preview 2 : Technlogy Preview 3 : - Start an undo/redo system ? +- Alter aerial perspective using estimation of the amount of light left after cloud layers traversal. - Add a map preview to terrain editor. - Better time selection widget for atmosphere. - Clouds should keep distance to ground. diff --git a/data/ui_pictures.qrc b/data/ui_pictures.qrc index b16e299..a3fa8d0 100644 --- a/data/ui_pictures.qrc +++ b/data/ui_pictures.qrc @@ -6,6 +6,9 @@ images/explore.png images/render.png images/about.png + images/load.png + images/new.png + images/save.png images/tab_atmosphere.png @@ -15,4 +18,10 @@ images/tab_textures.png images/tab_water.png + + images/logo_16.png + images/logo_32.png + images/logo_64.png + images/logo_256.png + diff --git a/src/editing/common/freeformhelper.cpp b/src/editing/common/freeformhelper.cpp index 9ab7c67..36967fd 100644 --- a/src/editing/common/freeformhelper.cpp +++ b/src/editing/common/freeformhelper.cpp @@ -2,11 +2,16 @@ #include #include +#include +#include +#include #include +#include #include "dialogrender.h" #include "dialogexplorer.h" #include "rendering/scenery.h" #include "rendering/renderer.h" +#include "tools.h" Q_DECLARE_METATYPE(double*) @@ -24,8 +29,43 @@ FreeFormHelper::~FreeFormHelper() { } +bool FreeFormHelper::eventFilter(QObject* object, QEvent* event) +{ + if (event->type() == QEvent::Resize && object == _form_widget) + { + QSize form_size = ((QResizeEvent*) event)->size(); + QSize preview_size; + + if (form_size.width() > 1400 && form_size.height() > 900) + { + preview_size = QSize(300, 300); + } + else if (form_size.width() < 1000 || form_size.height() < 700) + { + preview_size = QSize(160, 160); + } + else + { + preview_size = QSize(220, 220); + } + + for (int i = 0; i < _previews.size(); i++) + { + if (_previews[i]->size() != preview_size) + { + _previews[i]->setMinimumSize(preview_size); + _previews[i]->setMaximumSize(preview_size); + _previews[i]->resize(preview_size); + } + } + } + return false; +} + void FreeFormHelper::startManaging() { + _form_widget->installEventFilter(this); + connect(this, SIGNAL(needLocalRefreshing()), _form_widget, SLOT(refreshFromLocalData())); connect(this, SIGNAL(needGlobalRefreshing()), _form_widget, SLOT(refreshFromFellowData())); connect(this, SIGNAL(needReverting()), _form_widget, SLOT(updateLocalDataFromScenery())); diff --git a/src/editing/common/freeformhelper.h b/src/editing/common/freeformhelper.h index c245fcb..5592d3b 100644 --- a/src/editing/common/freeformhelper.h +++ b/src/editing/common/freeformhelper.h @@ -1,11 +1,12 @@ #ifndef FREEFORMHELPER_H #define FREEFORMHELPER_H -#include -#include #include "widgetsliderdecimal.h" #include "../basepreview.h" +class QSlider; +class QPushButton; + class FreeFormHelper:public QObject { Q_OBJECT @@ -57,6 +58,9 @@ public slots: void processRenderClicked(); void processDecimalChange(double value); +protected: + bool eventFilter(QObject* object, QEvent* event); + private: QWidget* _form_widget; diff --git a/src/editing/mainwindow.cpp b/src/editing/common/mainwindow.cpp similarity index 76% rename from src/editing/mainwindow.cpp rename to src/editing/common/mainwindow.cpp index b568c73..6597607 100644 --- a/src/editing/mainwindow.cpp +++ b/src/editing/common/mainwindow.cpp @@ -1,10 +1,10 @@ #include "mainwindow.h" +#include "ui_mainwindow.h" #include #include #include #include -#include #include #include #include @@ -64,6 +64,7 @@ int main(int argc, char** argv) window = new MainWindow(); window->show(); + //window->showMaximized(); splash->finish(window); delete splash; @@ -79,68 +80,60 @@ int main(int argc, char** argv) } MainWindow::MainWindow(QWidget *parent) : -QMainWindow(parent) + QMainWindow(parent), + ui(new Ui::MainWindow) { + ui->setupUi(this); + BaseForm* form; - QTabWidget* tabs; - QToolBar* toolbar; - tabs = new QTabWidget(this); - tabs->setIconSize(QSize(32, 32)); + connect(ui->action_explore, SIGNAL(triggered()), this, SLOT(explore3D())); + connect(ui->action_quick_render, SIGNAL(triggered()), this, SLOT(quickPreview())); + connect(ui->action_final_render, SIGNAL(triggered()), this, SLOT(finalRender())); + connect(ui->action_file_new, SIGNAL(triggered()), this, SLOT(fileNew())); + connect(ui->action_file_save, SIGNAL(triggered()), this, SLOT(fileSave())); + connect(ui->action_file_load, SIGNAL(triggered()), this, SLOT(fileLoad())); + connect(ui->action_about, SIGNAL(triggered()), this, SLOT(showAboutDialog())); - tabs->addTab(new MainTerrainForm(tabs), QIcon(getDataPath("images/tab_terrain.png")), tr("Landscape shape")); - - form = new FormTextures(tabs); - tabs->addTab(form, QIcon(getDataPath("images/tab_textures.png")), tr("Textures")); + form = new FormTextures(ui->tabs); + ui->tabs->addTab(form, QIcon(getDataPath("images/tab_textures.png")), tr("Textures")); QObject::connect(form, SIGNAL(configApplied()), this, SLOT(refreshAll()), Qt::QueuedConnection); _forms.append(form); - form = new FormWater(tabs); - tabs->addTab(form, QIcon(getDataPath("images/tab_water.png")), tr("Water")); + form = new FormWater(ui->tabs); + ui->tabs->addTab(form, QIcon(getDataPath("images/tab_water.png")), tr("Water")); QObject::connect(form, SIGNAL(configApplied()), this, SLOT(refreshAll()), Qt::QueuedConnection); _forms.append(form); - form = new FormAtmosphere(tabs); - tabs->addTab(form, QIcon(getDataPath("images/tab_atmosphere.png")), tr("Atmosphere")); + form = new FormAtmosphere(ui->tabs); + ui->tabs->addTab(form, QIcon(getDataPath("images/tab_atmosphere.png")), tr("Atmosphere")); QObject::connect(form, SIGNAL(configApplied()), this, SLOT(refreshAll()), Qt::QueuedConnection); _forms.append(form); - form = new FormClouds(tabs); - tabs->addTab(form, QIcon(getDataPath("images/tab_clouds.png")), tr("Clouds")); + form = new FormClouds(ui->tabs); + ui->tabs->addTab(form, QIcon(getDataPath("images/tab_clouds.png")), tr("Clouds")); QObject::connect(form, SIGNAL(configApplied()), this, SLOT(refreshAll()), Qt::QueuedConnection); _forms.append(form); - _form_render = new FormRender(tabs); - tabs->addTab(_form_render, QIcon(getDataPath("images/tab_render.png")), tr("Render")); + _form_render = new FormRender(ui->tabs); + ui->tabs->addTab(_form_render, QIcon(getDataPath("images/tab_render.png")), tr("Render")); _forms.append(_form_render); - toolbar = new QToolBar(this); - toolbar->setOrientation(Qt::Vertical); - toolbar->setAllowedAreas(Qt::LeftToolBarArea); - toolbar->setMovable(false); - toolbar->setFloatable(false); - toolbar->setToolButtonStyle(Qt::ToolButtonTextUnderIcon); - toolbar->toggleViewAction()->setEnabled(false); - toolbar->setIconSize(QSize(32, 32)); - addToolBar(Qt::LeftToolBarArea, toolbar); - - toolbar->addAction(QIcon(getDataPath("images/new.png")), tr("&New"), this, SLOT(fileNew()))->setShortcut(QKeySequence(tr("Crtl+N"))); - toolbar->addAction(QIcon(getDataPath("images/save.png")), tr("&Save"), this, SLOT(fileSave()))->setShortcut(QKeySequence(tr("Crtl+S"))); - toolbar->addAction(QIcon(getDataPath("images/load.png")), tr("&Load"), this, SLOT(fileLoad()))->setShortcut(QKeySequence(tr("Crtl+L"))); - toolbar->addAction(QIcon(getDataPath("images/explore.png")), tr("&Explore (F2)"), this, SLOT(explore3D()))->setShortcut(QKeySequence(tr("F2"))); - toolbar->addAction(QIcon(getDataPath("images/render.png")), tr("&Quick\nrender (F5)"), this, SLOT(quickPreview()))->setShortcut(QKeySequence(tr("F5"))); - toolbar->addAction(QIcon(getDataPath("images/about.png")), tr("&About"), this, SLOT(showAboutDialog())); - - setCentralWidget(tabs); - - setWindowTitle("Paysages 3D"); - setWindowIcon(QIcon(getDataPath("images/logo_32.png"))); + // TODO Decide this according to platform / screen size + //ui->toolBar->hide(); + ui->tool_panel->hide(); + ui->menuBar->hide(); scenerySetCustomDataCallback(MainWindow::guiSaveCallback, MainWindow::guiLoadCallback, this); refreshAll(); } +MainWindow::~MainWindow() +{ + delete ui; +} + bool MainWindow::event(QEvent* event) { if (event->type() == QEvent::WindowActivate) @@ -160,6 +153,7 @@ void MainWindow::refreshAll() { _forms[i]->revertConfig(); } + // TODO Refresh free forms // Refresh preview OSD CameraDefinition* camera = cameraCreateDefinition(); @@ -244,6 +238,16 @@ void MainWindow::quickPreview() _form_render->startQuickRender(); } +void MainWindow::finalRender() +{ + _form_render->startRender(); +} + +void MainWindow::showLastRender() +{ + _form_render->showRender(); +} + void MainWindow::explore3D() { CameraDefinition* camera; diff --git a/src/editing/mainwindow.h b/src/editing/common/mainwindow.h similarity index 72% rename from src/editing/mainwindow.h rename to src/editing/common/mainwindow.h index 0a00ae8..64b73de 100644 --- a/src/editing/mainwindow.h +++ b/src/editing/common/mainwindow.h @@ -1,16 +1,25 @@ -#ifndef _PAYSAGES_QT_MAINWINDOW_H_ -#define _PAYSAGES_QT_MAINWINDOW_H_ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H #include -#include "formrender.h" +#include #include "rendering/tools/pack.h" +class BaseForm; +class FormRender; + +namespace Ui { +class MainWindow; +} + class MainWindow : public QMainWindow { Q_OBJECT - + public: explicit MainWindow(QWidget *parent = 0); + ~MainWindow(); + virtual bool event(QEvent* event); static void guiSaveCallback(PackStream* stream, void* data); @@ -26,9 +35,14 @@ public slots: void showAboutDialog(); void quickPreview(); + void finalRender(); + void showLastRender(); + void explore3D(); private: + Ui::MainWindow *ui; + void guiSave(PackStream* stream); void guiLoad(PackStream* stream); @@ -36,4 +50,4 @@ private: FormRender* _form_render; }; -#endif +#endif // MAINWINDOW_H diff --git a/src/editing/common/mainwindow.ui b/src/editing/common/mainwindow.ui new file mode 100644 index 0000000..b6744c8 --- /dev/null +++ b/src/editing/common/mainwindow.ui @@ -0,0 +1,572 @@ + + + MainWindow + + + + 0 + 0 + 946 + 651 + + + + Paysages 3D + + + + :/logo/images/logo_32.png:/logo/images/logo_32.png + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + <html><head/><body><p><span style=" font-size:14pt; font-weight:600;">Paysages 3D</span></p><p><img src=":/logo/images/logo_32.png"/></p></body></html> + + + Qt::RichText + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Preview + + + true + + + + + + + 3D + + + + + + + Top + + + + + + + + + QFrame::Panel + + + QFrame::Raised + + + 3 + + + + 0 + + + QLayout::SetMinimumSize + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 200 + 150 + + + + + 400 + 300 + + + + + + widget + widget + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Actions + + + + + + Explore in 3D + + + + :/buttons/logo/images/explore.png:/buttons/logo/images/explore.png + + + F2 + + + + + + + Render + + + + :/buttons/logo/images/render.png:/buttons/logo/images/render.png + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + File + + + + + + New + + + + :/buttons/logo/images/new.png:/buttons/logo/images/new.png + + + + + + + Save + + + + :/buttons/logo/images/save.png:/buttons/logo/images/save.png + + + + + + + Load + + + + :/buttons/logo/images/load.png:/buttons/logo/images/load.png + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + + + + 32 + 32 + + + + + + :/tabs/images/tab_terrain.png:/tabs/images/tab_terrain.png + + + Lanscape shape + + + + + + + + + toolBar + + + false + + + Qt::LeftToolBarArea + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + false + + + LeftToolBarArea + + + false + + + + + + + + + + + + + + + 0 + 0 + 946 + 25 + + + + + File + + + + + + + + Actions + + + + + + + + + Help + + + + + + + + + + + :/buttons/logo/images/explore.png:/buttons/logo/images/explore.png + + + Explore in 3D + + + Start exploring your scenery in real-time 3D + + + F2 + + + + + + :/buttons/logo/images/new.png:/buttons/logo/images/new.png + + + New scene + + + Create a virgin scene + + + Ctrl+N + + + + + + :/buttons/logo/images/load.png:/buttons/logo/images/load.png + + + Load scene + + + Load a scenery from a file + + + Ctrl+O + + + + + + :/buttons/logo/images/save.png:/buttons/logo/images/save.png + + + Save scene + + + Save a scenery to a file + + + Ctrl+S + + + + + + :/buttons/logo/images/render.png:/buttons/logo/images/render.png + + + Quick render + + + Start a quick render + + + F5 + + + + + + :/buttons/logo/images/render.png:/buttons/logo/images/render.png + + + Final render + + + Start the final rendering processs + + + Ctrl+F5 + + + + + + :/buttons/logo/images/about.png:/buttons/logo/images/about.png + + + About + + + Display information on this software + + + F1 + + + + + + :/buttons/logo/images/render.png:/buttons/logo/images/render.png + + + Show last render + + + Display the last render done + + + F6 + + + + + + MainTerrainForm + QWidget +
terrain/mainterrainform.h
+ 1 +
+
+ + + + + + button_explore + clicked() + action_explore + trigger() + + + 127 + 380 + + + -1 + -1 + + + + + button_render + clicked() + action_final_render + trigger() + + + 213 + 425 + + + 213 + 425 + + + + + button_file_new + clicked() + action_file_new + trigger() + + + 213 + 522 + + + -1 + -1 + + + + + button_file_load + clicked() + action_file_load + trigger() + + + 213 + 588 + + + -1 + -1 + + + + + button_file_save + clicked() + action_file_save + trigger() + + + 213 + 555 + + + -1 + -1 + + + + +
diff --git a/src/editing/dialogrender.cpp b/src/editing/dialogrender.cpp index 09844b9..d8fe4fa 100644 --- a/src/editing/dialogrender.cpp +++ b/src/editing/dialogrender.cpp @@ -133,7 +133,7 @@ DialogRender::DialogRender(QWidget *parent, Renderer* renderer): _actions->layout()->addWidget(_save_button); // Connections - connect(this, SIGNAL(renderSizeChanged(int, int)), this, SLOT(applyRenderSize(int, int))); + //connect(this, SIGNAL(renderSizeChanged(int, int)), this, SLOT(applyRenderSize(int, int))); connect(this, SIGNAL(progressChanged(double)), this, SLOT(applyProgress(double))); connect(this, SIGNAL(renderEnded()), this, SLOT(applyRenderEnded())); connect(_save_button, SIGNAL(clicked()), this, SLOT(saveRender())); @@ -173,7 +173,7 @@ void DialogRender::startRender(RenderParams params) { _started = time(NULL); - //applyRenderSize(params.width, params.height); + applyRenderSize(params.width, params.height); rendererSetPreviewCallbacks(_renderer, _renderStart, _renderDraw, _renderUpdate); _render_thread = new RenderThread(this, _renderer, params); @@ -217,9 +217,10 @@ void DialogRender::toneMappingChanged() void DialogRender::loadLastRender() { - //applyRenderSize(_renderer->render_width, _renderer->render_height); + applyRenderSize(_renderer->render_width, _renderer->render_height); rendererSetPreviewCallbacks(_renderer, _renderStart, _renderDraw, _renderUpdate); renderEnded(); + toneMappingChanged(); exec(); } diff --git a/src/editing/formclouds.cpp b/src/editing/formclouds.cpp index 885e73b..4a47163 100644 --- a/src/editing/formclouds.cpp +++ b/src/editing/formclouds.cpp @@ -1,5 +1,6 @@ #include "formclouds.h" +#include "rendering/clouds/clo_preview.h" #include "rendering/tools/color.h" #include "rendering/tools/euclid.h" #include "rendering/scenery.h" @@ -12,24 +13,22 @@ class PreviewCloudsCoverage:public BasePreview public: PreviewCloudsCoverage(QWidget* parent, CloudsLayerDefinition* layer):BasePreview(parent) { - _renderer = cloudsCreatePreviewCoverageRenderer(); + _renderer = cloudsPreviewCoverageCreateRenderer(); _3d = true; _original_layer = layer; - _preview_definition = (CloudsDefinition*)CloudsDefinitionClass.create(); addToggle("3d", tr("Perspective"), true); configScaling(100.0, 1000.0, 20.0, 200.0); } ~PreviewCloudsCoverage() { - CloudsDefinitionClass.destroy(_preview_definition); rendererDelete(_renderer); } protected: Color getColor(double x, double y) { - return cloudsGetPreviewCoverage(_renderer, x, y, scaling, _3d); + return cloudsPreviewCoverageGetPixel(_renderer, x, y, scaling, _3d); } virtual void toggleChangeEvent(QString key, bool value) { @@ -41,15 +40,12 @@ protected: } void updateData() { - layersDeleteLayer(_preview_definition->layers, 0); - layersAddLayer(_preview_definition->layers, _original_layer); - CloudsRendererClass.bind(_renderer, _preview_definition); + cloudsPreviewCoverageBindLayer(_renderer, _original_layer); } private: Renderer* _renderer; CloudsLayerDefinition* _original_layer; - CloudsDefinition* _preview_definition; bool _3d; }; @@ -59,27 +55,28 @@ public: PreviewCloudsColor(QWidget* parent, CloudsLayerDefinition* layer):BasePreview(parent) { _original_layer = layer; - _preview_definition = (CloudsDefinition*)CloudsDefinitionClass.create(); - _renderer = cloudsCreatePreviewColorRenderer(); + _renderer = cloudsPreviewMaterialCreateRenderer(); configScaling(0.5, 2.0, 0.1, 2.0); } + + ~PreviewCloudsColor() + { + rendererDelete(_renderer); + } protected: Color getColor(double x, double y) { - return cloudsGetPreviewColor(_renderer, x, y); + return cloudsPreviewMaterialGetPixel(_renderer, x, y); } void updateData() { - layersDeleteLayer(_preview_definition->layers, 0); - layersAddLayer(_preview_definition->layers, _original_layer); - CloudsRendererClass.bind(_renderer, _preview_definition); + cloudsPreviewMaterialBindLayer(_renderer, _original_layer); } private: Renderer* _renderer; CloudsLayerDefinition* _original_layer; - CloudsDefinition* _preview_definition; }; /**************** Form ****************/ diff --git a/src/editing/formrender.h b/src/editing/formrender.h index c7629ea..d0b18e6 100644 --- a/src/editing/formrender.h +++ b/src/editing/formrender.h @@ -21,14 +21,12 @@ public slots: virtual void revertConfig(); virtual void applyConfig(); void startQuickRender(); + void startRender(); + void showRender(); protected slots: virtual void configChangeEvent(); -private slots: - void startRender(); - void showRender(); - private: RenderParams _params; CameraDefinition* _camera; diff --git a/src/editing/paysages-qt.pro b/src/editing/paysages-qt.pro index 290b851..1169b91 100644 --- a/src/editing/paysages-qt.pro +++ b/src/editing/paysages-qt.pro @@ -24,14 +24,13 @@ win32:LIBS += ../libpaysages.a ../libpaysages_exploring.a -lDevIL -lILU -lILUT - TRANSLATIONS = $$PROJECT_PATH/data/i18n/paysages_fr.ts HEADERS += \ - widgetheightmap.h \ + terrain/widgetheightmap.h \ widgetexplorer.h \ widgetcurveeditor.h \ tools.h \ previewosd.h \ previewmaterial.h \ previewcolorgradation.h \ - mainwindow.h \ inputnoise.h \ inputmaterial.h \ inputlayers.h \ @@ -70,17 +69,19 @@ HEADERS += \ common/freeformhelper.h \ terrain/previewterrainshape.h \ common/widgetsliderdecimal.h \ - common/previewrenderer.h + common/previewrenderer.h \ + terrain/widgetterrainbasenoisepreview.h \ + common/mainwindow.h \ + terrain/dialogbaseterrainnoise.h SOURCES += \ - widgetheightmap.cpp \ + terrain/widgetheightmap.cpp \ widgetexplorer.cpp \ widgetcurveeditor.cpp \ tools.cpp \ previewosd.cpp \ previewmaterial.cpp \ previewcolorgradation.cpp \ - mainwindow.cpp \ inputnoise.cpp \ inputmaterial.cpp \ inputlayers.cpp \ @@ -119,12 +120,17 @@ SOURCES += \ common/freeformhelper.cpp \ terrain/previewterrainshape.cpp \ common/widgetsliderdecimal.cpp \ - common/previewrenderer.cpp + common/previewrenderer.cpp \ + terrain/widgetterrainbasenoisepreview.cpp \ + common/mainwindow.cpp \ + terrain/dialogbaseterrainnoise.cpp FORMS += \ terrain/dialogterrainpainting.ui \ common/widgetglobalformbuttons.ui \ - terrain/mainterrainform.ui + terrain/mainterrainform.ui \ + common/mainwindow.ui \ + terrain/dialogbaseterrainnoise.ui RESOURCES += \ ../../data/ui_pictures.qrc diff --git a/src/editing/terrain/dialogbaseterrainnoise.cpp b/src/editing/terrain/dialogbaseterrainnoise.cpp new file mode 100644 index 0000000..5b139c8 --- /dev/null +++ b/src/editing/terrain/dialogbaseterrainnoise.cpp @@ -0,0 +1,31 @@ +#include "dialogbaseterrainnoise.h" +#include "ui_dialogbaseterrainnoise.h" + +DialogBaseTerrainNoise::DialogBaseTerrainNoise(QWidget *parent) : + QDialog(parent), + ui(new Ui::DialogBaseTerrainNoise) +{ + ui->setupUi(this); + + _original = 0; + _modified = noiseCreateGenerator(); +} + +DialogBaseTerrainNoise::~DialogBaseTerrainNoise() +{ + delete ui; + noiseDeleteGenerator(_modified); +} + +void DialogBaseTerrainNoise::setNoise(NoiseGenerator* noise) +{ + _original = noise; + noiseCopy(noise, _modified); +} + +int DialogBaseTerrainNoise::editNoise(QWidget* parent, NoiseGenerator* noise) +{ + DialogBaseTerrainNoise dialog(parent); + dialog.setNoise(noise); + return dialog.exec(); +} diff --git a/src/editing/terrain/dialogbaseterrainnoise.h b/src/editing/terrain/dialogbaseterrainnoise.h new file mode 100644 index 0000000..a38292c --- /dev/null +++ b/src/editing/terrain/dialogbaseterrainnoise.h @@ -0,0 +1,29 @@ +#ifndef DIALOGBASETERRAINNOISE_H +#define DIALOGBASETERRAINNOISE_H + +#include +#include "rendering/noise.h" + +namespace Ui { +class DialogBaseTerrainNoise; +} + +class DialogBaseTerrainNoise : public QDialog +{ + Q_OBJECT + +public: + explicit DialogBaseTerrainNoise(QWidget *parent = 0); + ~DialogBaseTerrainNoise(); + + void setNoise(NoiseGenerator* noise); + + static int editNoise(QWidget* parent, NoiseGenerator* noise); + +private: + Ui::DialogBaseTerrainNoise *ui; + NoiseGenerator* _original; + NoiseGenerator* _modified; +}; + +#endif // DIALOGBASETERRAINNOISE_H diff --git a/src/editing/terrain/dialogbaseterrainnoise.ui b/src/editing/terrain/dialogbaseterrainnoise.ui new file mode 100644 index 0000000..57b1add --- /dev/null +++ b/src/editing/terrain/dialogbaseterrainnoise.ui @@ -0,0 +1,43 @@ + + + DialogBaseTerrainNoise + + + Qt::ApplicationModal + + + + 0 + 0 + 400 + 300 + + + + Paysages 3D - Base terrain noise + + + true + + + + + + + + + + WidgetGlobalFormButtons + QWidget +
common/widgetglobalformbuttons.h
+ 1 + + okClicked() + revertClicked() + cancelClicked() + +
+
+ + +
diff --git a/src/editing/terrain/dialogterrainpainting.ui b/src/editing/terrain/dialogterrainpainting.ui index 72d405f..fb7a98c 100644 --- a/src/editing/terrain/dialogterrainpainting.ui +++ b/src/editing/terrain/dialogterrainpainting.ui @@ -789,7 +789,7 @@ WidgetHeightMap QWidget -
widgetheightmap.h
+
terrain/widgetheightmap.h
1 heightmapChanged() diff --git a/src/editing/terrain/mainterrainform.cpp b/src/editing/terrain/mainterrainform.cpp index 04ad6c7..0c659ed 100644 --- a/src/editing/terrain/mainterrainform.cpp +++ b/src/editing/terrain/mainterrainform.cpp @@ -1,6 +1,8 @@ #include "mainterrainform.h" #include "ui_mainterrainform.h" +#include +#include "dialogbaseterrainnoise.h" #include "dialogterrainpainting.h" #include "previewterrainshape.h" #include "tools.h" @@ -20,15 +22,16 @@ MainTerrainForm::MainTerrainForm(QWidget *parent) : _form_helper->addPreview(ui->preview_shape, _renderer_shape); _form_helper->addDoubleInputSlider(ui->input_scaling, &_terrain->scaling, 0.1, 3.0, 0.03, 0.3); - _form_helper->addDoubleInputSlider(ui->input_height, &_terrain->height, 1.0, 45.0, 0.3, 3.0); + _form_helper->addDoubleInputSlider(ui->input_height, &_terrain->height, 1.0, 90.0, 0.5, 5.0); _form_helper->addDoubleInputSlider(ui->input_shadow_smoothing, &_terrain->shadow_smoothing, 0.0, 0.3, 0.003, 0.03); - _form_helper->addDoubleInputSlider(ui->input_water_height, &_terrain->water_height, -2.0, 2.0, 0.01, 0.1); + _form_helper->addDoubleInputSlider(ui->input_water_height, &_terrain->water_height, -1.0, 1.0, 0.01, 0.1); _form_helper->setApplyButton(ui->button_apply); _form_helper->setRevertButton(ui->button_revert); _form_helper->setExploreButton(ui->button_explore); _form_helper->setRenderButton(ui->button_render); + connect(ui->button_dialog_basenoise, SIGNAL(clicked()), this, SLOT(buttonBaseNoisePressed())); connect(ui->button_dialog_painting, SIGNAL(clicked()), this, SLOT(buttonPaintingPressed())); connect(ui->button_goto_textures, SIGNAL(clicked()), this, SLOT(buttonTexturesPressed())); @@ -55,6 +58,8 @@ void MainTerrainForm::refreshFromLocalData() { _form_helper->setLabelText("label_painting_info", tr("No manual scuplting done")); } + + ui->widget_base_noise_preview->setNoise(_terrain->_height_noise); } void MainTerrainForm::refreshFromFellowData() @@ -77,6 +82,27 @@ void MainTerrainForm::alterRenderer(Renderer* renderer) TerrainRendererClass.bind(renderer, _terrain); } +void MainTerrainForm::buttonBaseNoisePressed() +{ + int erase; + if (terrainGetMemoryStats(_terrain) > 0) + { + erase = QMessageBox::question(this, tr("Paysages 3D - Base noise edition"), tr("You have manual modifications on this terrain, regenerating base noise may produce weird results."), tr("Keep my changes anyway"), tr("Erase my changes")); + } + else + { + erase = 0; + } + + if (DialogBaseTerrainNoise::editNoise(this, _terrain->_height_noise)) + { + if (erase) + { + terrainClearPainting(_terrain->height_map); + } + } +} + void MainTerrainForm::buttonPaintingPressed() { DialogTerrainPainting dialog(this, _terrain); diff --git a/src/editing/terrain/mainterrainform.h b/src/editing/terrain/mainterrainform.h index 712197a..5cf5bb9 100644 --- a/src/editing/terrain/mainterrainform.h +++ b/src/editing/terrain/mainterrainform.h @@ -26,6 +26,7 @@ public slots: void commitLocalDataToScenery(); void alterRenderer(Renderer* renderer); + void buttonBaseNoisePressed(); void buttonPaintingPressed(); void buttonTexturesPressed(); diff --git a/src/editing/terrain/mainterrainform.ui b/src/editing/terrain/mainterrainform.ui index 3e08c61..dac4f2f 100644 --- a/src/editing/terrain/mainterrainform.ui +++ b/src/editing/terrain/mainterrainform.ui @@ -44,7 +44,7 @@ - + Generate base noise @@ -54,7 +54,7 @@ - + 1 @@ -447,6 +447,12 @@ QSlider
common/widgetsliderdecimal.h
+ + WidgetTerrainBaseNoisePreview + QWidget +
terrain/widgetterrainbasenoisepreview.h
+ 1 +
diff --git a/src/editing/terrain/previewterrainshape.cpp b/src/editing/terrain/previewterrainshape.cpp index 5582f9d..0919370 100644 --- a/src/editing/terrain/previewterrainshape.cpp +++ b/src/editing/terrain/previewterrainshape.cpp @@ -5,6 +5,7 @@ PreviewTerrainShape::PreviewTerrainShape(TerrainDefinition* terrain) { _terrain = terrain; + _highlight_enabled = true; // TODO Don't delete the base renderer, just alter it rendererDelete(renderer); @@ -14,6 +15,7 @@ PreviewTerrainShape::PreviewTerrainShape(TerrainDefinition* terrain) void PreviewTerrainShape::bindEvent(BasePreview* preview) { preview->addOsd(QString("geolocation")); + //preview->addToggle("highlight", tr("Coverage highlight"), true); preview->configScaling(20.0, 1000.0, 20.0, 50.0); preview->configScrolling(-1000.0, 1000.0, 0.0, -1000.0, 1000.0, 0.0); @@ -26,5 +28,13 @@ void PreviewTerrainShape::updateEvent() Color PreviewTerrainShape::getColor2D(double x, double y, double scaling) { - return terrainGetPreviewColor(renderer, x, y, scaling); + return waterGetPreviewCoverage(renderer, x, y, scaling, _highlight_enabled ? 1 : 0); +} + +void PreviewTerrainShape::toggleChangeEvent(QString key, bool value) +{ + if (key == "highlight") + { + _highlight_enabled = value; + } } diff --git a/src/editing/terrain/previewterrainshape.h b/src/editing/terrain/previewterrainshape.h index ae54091..c99141e 100644 --- a/src/editing/terrain/previewterrainshape.h +++ b/src/editing/terrain/previewterrainshape.h @@ -15,9 +15,12 @@ protected: virtual void bindEvent(BasePreview* preview); virtual void updateEvent(); virtual Color getColor2D(double x, double y, double scaling); + virtual void toggleChangeEvent(QString key, bool value); private: TerrainDefinition* _terrain; + bool _highlight_enabled; + double _water_height; }; #endif // PREVIEWTERRAINSHAPE_H diff --git a/src/editing/widgetheightmap.cpp b/src/editing/terrain/widgetheightmap.cpp similarity index 99% rename from src/editing/widgetheightmap.cpp rename to src/editing/terrain/widgetheightmap.cpp index cd13d71..f6cde1e 100644 --- a/src/editing/widgetheightmap.cpp +++ b/src/editing/terrain/widgetheightmap.cpp @@ -35,8 +35,8 @@ QGLWidget(parent) _average_frame_time = 0.0; _last_brush_action = 0; - _last_mouse_x = 0; - _last_mouse_y = 0; + _last_mouse_x = 250; + _last_mouse_y = 250; _last_time = QDateTime::currentDateTime(); _mouse_moved = false; diff --git a/src/editing/widgetheightmap.h b/src/editing/terrain/widgetheightmap.h similarity index 100% rename from src/editing/widgetheightmap.h rename to src/editing/terrain/widgetheightmap.h diff --git a/src/editing/terrain/widgetterrainbasenoisepreview.cpp b/src/editing/terrain/widgetterrainbasenoisepreview.cpp new file mode 100644 index 0000000..aacdc99 --- /dev/null +++ b/src/editing/terrain/widgetterrainbasenoisepreview.cpp @@ -0,0 +1,45 @@ +#include "widgetterrainbasenoisepreview.h" + +#include +#include +#include "tools.h" + +WidgetTerrainBaseNoisePreview::WidgetTerrainBaseNoisePreview(QWidget* parent) : + QWidget(parent) +{ + _noise = NULL; +} + +void WidgetTerrainBaseNoisePreview::setNoise(NoiseGenerator* noise) +{ + _noise = noise; + update(); +} + +void WidgetTerrainBaseNoisePreview::paintEvent(QPaintEvent* event) +{ + QPainter painter(this); + + painter.setBrush(Qt::SolidPattern); + painter.drawRect(rect()); + + int height = this->height(); + + if (_noise) + { + QRect boundaries = event->region().boundingRect(); + double value, factor; + double minvalue, maxvalue; + + noiseGetRange(_noise, &minvalue, &maxvalue); + factor = ((double)height) / (maxvalue - minvalue); + + for (int x = boundaries.left(); x <= boundaries.right(); x++) + { + value = noiseGet1DTotal(_noise, 100.0 * ((double)x) / factor); + + painter.setPen(Qt::white); + painter.drawLine(x, height - 1 - (value - minvalue) * factor, x, height - 1); + } + } +} diff --git a/src/editing/terrain/widgetterrainbasenoisepreview.h b/src/editing/terrain/widgetterrainbasenoisepreview.h new file mode 100644 index 0000000..f3e12a5 --- /dev/null +++ b/src/editing/terrain/widgetterrainbasenoisepreview.h @@ -0,0 +1,26 @@ +#ifndef _PAYSAGES_EDITING_TERRAIN_WIDGETTERRAINBASENOISEPREVIEW_H_ +#define _PAYSAGES_EDITING_TERRAIN_WIDGETTERRAINBASENOISEPREVIEW_H_ + +#include +#include "rendering/noise.h" + +class WidgetTerrainBaseNoisePreview : public QWidget +{ + Q_OBJECT +public: + explicit WidgetTerrainBaseNoisePreview(QWidget* parent = 0); + + void setNoise(NoiseGenerator* noise); + +protected: + virtual void paintEvent(QPaintEvent* event); + +signals: + +public slots: + +private: + NoiseGenerator* _noise; +}; + +#endif diff --git a/src/rendering/atmosphere/atm_bruneton.c b/src/rendering/atmosphere/atm_bruneton.c index 64f93ab..1549948 100644 --- a/src/rendering/atmosphere/atm_bruneton.c +++ b/src/rendering/atmosphere/atm_bruneton.c @@ -922,6 +922,8 @@ static Color _getInscatterColor(Vector3* _x, double* _t, Vector3 v, Vector3 s, d static Color _sunColor(Vector3 v, Vector3 s, double r, double mu, double radius) { Color transmittance = r <= Rt ? _transmittanceWithShadow(r, mu) : COLOR_WHITE; /* T(x,xo) */ + double d = _limit(r, mu); + radius *= (1.0 + 10.0 * d / Rt); /* Inflating due to lens effect near horizon */ double isun = step(cos(radius * M_PI / 180.0), v3Dot(v, s)) * ISun; /* Lsun */ transmittance.r *= isun; transmittance.g *= isun; diff --git a/src/rendering/clouds/clo_definition.c b/src/rendering/clouds/clo_definition.c index 1bdf822..845c3d9 100644 --- a/src/rendering/clouds/clo_definition.c +++ b/src/rendering/clouds/clo_definition.c @@ -112,7 +112,7 @@ void cloudsLayerValidateDefinition(CloudsLayerDefinition* definition) break; } - noiseNormalizeAmplitude(definition->_coverage_noise, 0.0, 1.0, 0); + noiseNormalizeAmplitude(definition->_coverage_noise, -1.0, 3.0, 0); noiseNormalizeAmplitude(definition->_shape_noise, -0.5, 0.5, 0); } diff --git a/src/rendering/clouds/clo_preview.c b/src/rendering/clouds/clo_preview.c index df39f87..5a0afa6 100644 --- a/src/rendering/clouds/clo_preview.c +++ b/src/rendering/clouds/clo_preview.c @@ -18,7 +18,7 @@ Color _fakeApplyLightingToSurface(Renderer* renderer, Vector3 location, Vector3 return COLOR_WHITE; } -Renderer* cloudsCreatePreviewCoverageRenderer() +Renderer* cloudsPreviewCoverageCreateRenderer() { Renderer* result = rendererCreate(); result->render_quality = 5; @@ -26,7 +26,15 @@ Renderer* cloudsCreatePreviewCoverageRenderer() return result; } -Color cloudsGetPreviewCoverage(Renderer* renderer, double x, double y, double scaling, int perspective) +void cloudsPreviewCoverageBindLayer(Renderer* renderer, CloudsLayerDefinition* layer) +{ + CloudsDefinition* definition = (CloudsDefinition*)CloudsDefinitionClass.create(); + layersAddLayer(definition->layers, layer); + CloudsRendererClass.bind(renderer, definition); + CloudsDefinitionClass.destroy(definition); +} + +Color cloudsPreviewCoverageGetPixel(Renderer* renderer, double x, double y, double scaling, int perspective) { if (perspective) { @@ -55,25 +63,91 @@ Color cloudsGetPreviewCoverage(Renderer* renderer, double x, double y, double sc } } -Renderer* cloudsCreatePreviewColorRenderer() +static void _getLightingStatus(Renderer* renderer, LightStatus* status, Vector3 normal, int opaque) +{ + LightDefinition light; + + UNUSED(renderer); + UNUSED(normal); + UNUSED(opaque); + + light.color.r = 1.0; + light.color.g = 1.0; + light.color.b = 1.0; + light.direction.x = -1.0; + light.direction.y = -0.5; + light.direction.z = 1.0; + light.direction = v3Normalize(light.direction); + light.altered = 1; + light.reflection = 0.0; + lightingPushLight(status, &light); + + light.color.r = 0.2; + light.color.g = 0.2; + light.color.b = 0.2; + light.direction.x = 1.0; + light.direction.y = -0.5; + light.direction.z = -1.0; + light.direction = v3Normalize(light.direction); + light.altered = 0; + light.reflection = 0.0; + lightingPushLight(status, &light); +} + +Renderer* cloudsPreviewMaterialCreateRenderer() { Renderer* result = rendererCreate(); result->render_quality = 8; + result->atmosphere->getLightingStatus = _getLightingStatus; return result; } -Color cloudsGetPreviewColor(Renderer* renderer, double x, double y) +static double _getDensity(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 location) +{ + UNUSED(renderer); + UNUSED(layer); + + double distance = v3Norm(location); + if (distance > 1.0) + { + return 0.0; + } + else if (distance < 0.8) + { + return 1.0; + } + else + { + return (1.0 - distance) / 0.2; + } +} + +void cloudsPreviewMaterialBindLayer(Renderer* renderer, CloudsLayerDefinition* layer) +{ + CloudsDefinition* definition = (CloudsDefinition*)CloudsDefinitionClass.create(); + layersAddLayer(definition->layers, layer); + CloudsRendererClass.bind(renderer, definition); + CloudsDefinitionClass.destroy(definition); + + layer = layersGetLayer(renderer->clouds->definition->layers, 0); + layer->lower_altitude = -1.0; + layer->thickness = 2.0; + + renderer->clouds->getLayerDensity = _getDensity; +} + +Color cloudsPreviewMaterialGetPixel(Renderer* renderer, double x, double y) { Vector3 start, end; - double thickness = 0.5; + double thickness = 2.0; start.x = x * thickness * 0.5; - start.y = -y * thickness * 0.5; - start.z = thickness * 0.5; + start.z = y * thickness * 0.5; + start.y = thickness * 0.5; end.x = start.x; - end.y = start.y; - end.z = -start.z; + end.z = start.z; + end.y = -start.y; return renderer->clouds->getColor(renderer, COLOR_BLUE, start, end); } diff --git a/src/rendering/clouds/clo_preview.h b/src/rendering/clouds/clo_preview.h new file mode 100644 index 0000000..c1bfe35 --- /dev/null +++ b/src/rendering/clouds/clo_preview.h @@ -0,0 +1,28 @@ +#ifndef _PAYSAGES_CLOUDS_PREVIEW_H_ +#define _PAYSAGES_CLOUDS_PREVIEW_H_ + +#include "public.h" +#include "../tools/euclid.h" + +/** + * Cloud preview helpers. + */ + +#ifdef __cplusplus +extern "C" +{ +#endif + +Renderer* cloudsPreviewCoverageCreateRenderer(); +void cloudsPreviewCoverageBindLayer(Renderer* renderer, CloudsLayerDefinition* layer); +Color cloudsPreviewCoverageGetPixel(Renderer* renderer, double x, double y, double scaling, int perspective); + +Renderer* cloudsPreviewMaterialCreateRenderer(); +void cloudsPreviewMaterialBindLayer(Renderer* renderer, CloudsLayerDefinition* layer); +Color cloudsPreviewMaterialGetPixel(Renderer* renderer, double x, double y); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/rendering/clouds/clo_rendering.c b/src/rendering/clouds/clo_rendering.c index e5e8ada..1d6abd4 100644 --- a/src/rendering/clouds/clo_rendering.c +++ b/src/rendering/clouds/clo_rendering.c @@ -1,9 +1,11 @@ #include "private.h" +#include #include #include "../tools.h" #include "../renderer.h" #include "clo_density.h" +#include "clo_walking.h" /******************** Fake ********************/ static int _fakeAlterLight(Renderer* renderer, LightDefinition* light, Vector3 location) @@ -25,24 +27,117 @@ static Color _fakeGetColor(Renderer* renderer, Color base, Vector3 start, Vector } /******************** Real ********************/ -/*static int _cmpLayer(const void* layer1, const void* layer2) +typedef struct { - return (((CloudsLayerDefinition*)layer1)->lower_altitude > ((CloudsLayerDefinition*)layer2)->lower_altitude) ? -1 : 1; -}*/ + double light_power; + double out_scattering; /* Amount of light scattered away by heavy particles */ +} AccumulatedLightData; + +static void _walkerFilterCallback(CloudsWalker* walker) +{ + CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker); + AccumulatedLightData* data = (AccumulatedLightData*)segment->data; + + assert(data != NULL); + + double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0; + + data->out_scattering += 0.3 * density_integral; + + if (data->out_scattering > data->light_power) + { + cloudsWalkerOrderStop(walker); + } +} static int _alterLight(Renderer* renderer, LightDefinition* light, Vector3 location) { CloudsDefinition* definition = renderer->clouds->definition; int i, n; + AccumulatedLightData data; + data.out_scattering = 0.0; + data.light_power = colorGetPower(&light->color); + /* TODO Iter layers in sorted order */ n = layersCount(definition->layers); for (i = 0; i < n; i++) { - light->color = cloudsLayerFilterLight(layersGetLayer(definition->layers, i), renderer, light->color, location, v3Add(location, v3Scale(light->direction, -10000.0)), v3Scale(light->direction, -1.0)); - /* TODO Reduce light->reflection too */ + CloudsLayerDefinition* layer = (CloudsLayerDefinition*)layersGetLayer(renderer->clouds->definition->layers, i); + Vector3 ostart, oend; + + ostart = location; + oend = v3Add(location, v3Scale(light->direction, -10000.0)); + if (!cloudsOptimizeWalkingBounds(layer, &ostart, &oend)) + { + continue; + } + else + { + CloudsWalker* walker; + + walker = cloudsCreateWalker(renderer, layer, ostart, oend); + cloudsWalkerSetStepSize(walker, -1.0); + cloudsStartWalking(walker, _walkerFilterCallback, &data); + cloudsDeleteWalker(walker); + } } - return n > 0; + + double max_power = colorGetPower(&light->color) - data.out_scattering; + if (max_power < 0.0) + { + light->color = COLOR_BLACK; + } + else + { + colorLimitPower(&light->color, max_power); + } + + return data.out_scattering > 0.0; +} + +typedef struct +{ + double out_scattering; /* Amount of light scattered away by heavy particles */ + Color in_scattering; /* Amount of light redirected toward the viewer */ +} AccumulatedMaterialData; + +static inline void _applyOutScattering(Color* col, double out_scattering) +{ + if (out_scattering >= 1.0) + { + col->r = col->g = col->b = 0.0; + } + else + { + col->r *= (1.0 - out_scattering); + col->g *= (1.0 - out_scattering); + col->b *= (1.0 - out_scattering); + } +} + +static void _walkerMaterialCallback(CloudsWalker* walker) +{ + CloudWalkerStepInfo* segment = cloudsWalkerGetLastSegment(walker); + AccumulatedMaterialData* data = (AccumulatedMaterialData*)segment->data; + Renderer* renderer = segment->renderer; + CloudsLayerDefinition* layer = segment->layer; + + assert(data != NULL); + + double density_integral = segment->length * (segment->start.global_density + segment->end.global_density) / 2.0; + + data->out_scattering += 0.5 * density_integral; + + Color in_scattering = renderer->applyLightingToSurface(renderer, segment->start.location, VECTOR_UP, &layer->material); + in_scattering.r *= density_integral * 5.0; + in_scattering.g *= density_integral * 5.0; + in_scattering.b *= density_integral * 5.0; + _applyOutScattering(&in_scattering, data->out_scattering); + + data->in_scattering.r += in_scattering.r; + data->in_scattering.g += in_scattering.g; + data->in_scattering.b += in_scattering.b; } static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 end) @@ -59,7 +154,39 @@ static Color _getColor(Renderer* renderer, Color base, Vector3 start, Vector3 en /* TODO Iter layers in sorted order */ for (i = 0; i < n; i++) { - base = cloudsApplyLayer(layersGetLayer(definition->layers, i), base, renderer, start, end); + CloudsLayerDefinition* layer = (CloudsLayerDefinition*)layersGetLayer(renderer->clouds->definition->layers, i); + Vector3 ostart, oend; + + ostart = start; + oend = end; + if (!cloudsOptimizeWalkingBounds(layer, &ostart, &oend)) + { + continue; + } + else + { + CloudsWalker* walker; + AccumulatedMaterialData data; + data.out_scattering = 0.0; + data.in_scattering = COLOR_BLACK; + + walker = cloudsCreateWalker(renderer, layer, ostart, oend); + cloudsWalkerSetStepSize(walker, -1.0); + cloudsStartWalking(walker, _walkerMaterialCallback, &data); + cloudsDeleteWalker(walker); + + /* Apply final out_scattering to base */ + _applyOutScattering(&base, data.out_scattering); + + /* Apply in_scattering */ + base.r += data.in_scattering.r; + base.g += data.in_scattering.g; + base.b += data.in_scattering.b; + + /* Apply aerial perspective approximation */ + /* TODO This should be done at cloud entry */ + base = renderer->atmosphere->applyAerialPerspective(renderer, ostart, base).final; + } } return base; diff --git a/src/rendering/clouds/clo_tools.c b/src/rendering/clouds/clo_tools.c deleted file mode 100644 index 5337e54..0000000 --- a/src/rendering/clouds/clo_tools.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "private.h" - -/* - * Clouds tools. - */ - -#include "clo_walking.h" -#include "../renderer.h" -#include "../tools.h" - -static inline Vector3 _getNormal(CloudsLayerDefinition* layer, Vector3 position, double detail) -{ - Vector3 result = {0.0, 0.0, 0.0}; - /*Vector3 dposition; - double val, dval; - - val = _getDistanceToBorder(layer, position); - - dposition.x = position.x + detail; - dposition.y = position.y; - dposition.z = position.z; - dval = val - _getDistanceToBorder(layer, dposition); - result.x += dval; - - dposition.x = position.x - detail; - dval = val - _getDistanceToBorder(layer, dposition); - result.x -= dval; - - dposition.x = position.x; - dposition.y = position.y + detail; - dval = val - _getDistanceToBorder(layer, dposition); - result.y += dval; - - dposition.y = position.y - detail; - dval = val - _getDistanceToBorder(layer, dposition); - result.y -= dval; - - dposition.y = position.y; - dposition.z = position.z + detail; - dval = val - _getDistanceToBorder(layer, dposition); - result.z += dval; - - dposition.z = position.z - detail; - dval = val - _getDistanceToBorder(layer, dposition); - result.z -= dval;*/ - - return v3Normalize(result); -} - -static Color _applyLayerLighting(CloudsLayerDefinition* definition, Renderer* renderer, Vector3 location, double detail) -{ - Vector3 normal; - Color col1, col2; - LightStatus* lighting; - - normal = _getNormal(definition, location, 3.0); - if (renderer->render_quality > 3) - { - normal = v3Add(normal, _getNormal(definition, location, 2.0)); - normal = v3Add(normal, _getNormal(definition, location, 1.0)); - } - if (renderer->render_quality > 5) - { - normal = v3Add(normal, _getNormal(definition, location, 0.5)); - } - if (renderer->render_quality > 8) - { - normal = v3Add(normal, _getNormal(definition, location, 0.75)); - normal = v3Add(normal, _getNormal(definition, location, 1.25)); - normal = v3Add(normal, _getNormal(definition, location, 2.5)); - } - normal = v3Scale(v3Normalize(normal), definition->hardness); - - return renderer->applyLightingToSurface(renderer, location, normal, &definition->material); - - lighting = lightingCreateStatus(renderer->lighting, location, renderer->getCameraLocation(renderer, location)); - renderer->atmosphere->getLightingStatus(renderer, lighting, normal, 0); - col1 = lightingApplyStatus(lighting, normal, &definition->material); - col2 = lightingApplyStatus(lighting, v3Scale(normal, -1.0), &definition->material); - lightingDeleteStatus(lighting); - - col1.r = (col1.r + col2.r) / 2.0; - col1.g = (col1.g + col2.g) / 2.0; - col1.b = (col1.b + col2.b) / 2.0; - col1.a = (col1.a + col2.a) / 2.0; - - return col1; -} - -Color cloudsApplyLayer(CloudsLayerDefinition* definition, Color base, Renderer* renderer, Vector3 start, Vector3 end) -{ - int segment_count; - Color col; - CloudPrimarySegment segments[MAX_SEGMENT_COUNT]; - - segment_count = cloudsGetLayerPrimarySegments(renderer, definition, start, end, MAX_SEGMENT_COUNT, segments); - /* TODO Crawl in segments for render */ - - col = definition->material.base; - /*if (definition->transparencydepth == 0 || inside_length >= definition->transparencydepth) - { - col.a = 1.0; - } - else - { - col.a = inside_length / definition->transparencydepth; - }*/ - - col = renderer->atmosphere->applyAerialPerspective(renderer, start, col).final; - col.a = 0.0; - - colorMask(&base, &col); - - return base; -} - -Color cloudsLayerFilterLight(CloudsLayerDefinition* definition, Renderer* renderer, Color light, Vector3 location, Vector3 light_location, Vector3 direction_to_light) -{ - /*double inside_depth, total_depth, factor; - CloudSegment segments[MAX_SEGMENT_COUNT]; - - if (!cloudsOptimizeWalkingBounds(definition, &location, &light_location)) - { - return light; - } - - _getPrimarySegments(definition, renderer, location, direction_to_light, MAX_SEGMENT_COUNT, definition->lighttraversal, v3Norm(v3Sub(light_location, location)), &inside_depth, &total_depth, segments); - - if (definition->lighttraversal < 0.0001) - { - factor = 0.0; - } - else - { - factor = inside_depth / definition->lighttraversal; - if (factor > 1.0) - { - factor = 1.0; - } - } - - factor = 1.0 - (1.0 - definition->minimumlight) * factor; - - light.r = light.r * factor; - light.g = light.g * factor; - light.b = light.b * factor;*/ - - return light; -} diff --git a/src/rendering/clouds/clo_walking.c b/src/rendering/clouds/clo_walking.c index 5efb929..5a84b4c 100644 --- a/src/rendering/clouds/clo_walking.c +++ b/src/rendering/clouds/clo_walking.c @@ -2,6 +2,47 @@ #include "../renderer.h" +/** + * Control of the next walking order. + */ +typedef enum +{ + CLOUD_WALKING_CONTINUE, + CLOUD_WALKING_STOP, + CLOUD_WALKING_REFINE, + CLOUD_WALKING_SUBDIVIDE +} CloudWalkingOrder; + +/** + * Additional info for walking orders. + */ +typedef struct +{ + CloudWalkingOrder order; + double precision; + int max_segments; +} CloudWalkingNextAction; + +/* + * Private structure for the walker. + */ +struct CloudsWalker +{ + Vector3 start; + Vector3 end; + Vector3 diff; + + double cursor; + double max_length; + double step_size; + + int started; + CloudWalkerStepInfo last_segment; + + CloudWalkingNextAction next_action; +}; + + int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Vector3* end) { Vector3 diff; @@ -51,107 +92,176 @@ int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Ve } } - /* TODO Limit the search length */ return 1; } -int cloudsGetLayerPrimarySegments(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end, int max_segments, CloudPrimarySegment* out_segments) +CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end) { - int inside, segment_count; - double step_length, segment_length; - Vector3 diff, walker, segment_start; - double render_precision, density; - double diff_length, progress; + CloudsWalker* result; - if (max_segments <= 0) - { - return 0; - } + result = (CloudsWalker*)malloc(sizeof (CloudsWalker)); - if (!cloudsOptimizeWalkingBounds(layer, &start, &end)) - { - return 0; - } + result->start = start; + result->end = end; + result->diff = v3Sub(end, start); + result->max_length = v3Norm(result->diff); + result->cursor = 0.0; + result->step_size = 1.0; - diff = v3Sub(end, start); - diff_length = v3Norm(diff); + result->started = 0; + result->last_segment.renderer = renderer; + result->last_segment.layer = layer; - if (diff_length < 0.000001) - { - return 0; - } + result->next_action.order = CLOUD_WALKING_CONTINUE; - render_precision = 1.005 - 0.01 * (double)(renderer->render_quality * renderer->render_quality); - /*if (render_precision > max_total_length / 10.0) - { - render_precision = max_total_length / 10.0; - } - else if (render_precision < max_total_length / 10000.0) - { - render_precision = max_total_length / 10000.0; - }*/ - - segment_count = 0; - segment_length = 0.0; - density = renderer->clouds->getLayerDensity(renderer, layer, start); - progress = 0.0; - step_length = render_precision; - inside = (density > 0.0); - - do - { - progress += step_length; - walker = v3Add(start, v3Scale(diff, progress / diff_length)); - - if (progress >= diff_length) - { - density = 0.0; - } - else - { - density = renderer->clouds->getLayerDensity(renderer, layer, walker); - } - - if (density > 0.0) - { - if (inside) - { - /* inside the cloud */ - segment_length += step_length; - } - else - { - /* entering the cloud */ - segment_length = step_length; - segment_start = v3Add(start, v3Scale(diff, (progress - step_length) / diff_length)); - /* TODO Refine entry position */ - - inside = 1; - } - } - else - { - if (inside) - { - /* exiting the cloud */ - segment_length += step_length; - - out_segments->enter = segment_start; - out_segments->exit = walker; - out_segments->length = segment_length; - out_segments++; - if (++segment_count >= max_segments) - { - break; - } - /* TODO Refine exit position */ - - inside = 0; - } - } - /* step = v3Scale(direction, (info.distance_to_edge < render_precision) ? render_precision : info.distance_to_edge); */ - } - while (inside || (walker.y <= layer->lower_altitude + layer->thickness + 0.001 && walker.y >= layer->lower_altitude - 0.001 && progress < diff_length)); - - return segment_count; + return result; +} + +void cloudsDeleteWalker(CloudsWalker* walker) +{ + free(walker); +} + +void cloudsWalkerSetStepSize(CloudsWalker* walker, double step) +{ + if (step > 0.0) + { + walker->step_size = step; + } + else + { + /* TODO Automatic settings (using rendering quality and cloud feature size) */ + walker->step_size = 1.0; + } +} + +static void _getPoint(CloudsWalker* walker, double cursor, CloudWalkerPoint* out_point) +{ + out_point->distance_from_start = cursor; + out_point->location = v3Add(walker->start, v3Scale(walker->diff, out_point->distance_from_start / walker->max_length)); + + Renderer* renderer = walker->last_segment.renderer; + CloudsLayerDefinition* layer = walker->last_segment.layer; + out_point->global_density = renderer->clouds->getLayerDensity(renderer, layer, out_point->location); +} + +static void _refineSegment(CloudsWalker* walker, double start_cursor, double start_density, double end_cursor, double end_density, double precision, CloudWalkerPoint* result) +{ + CloudWalkerPoint middle; + + _getPoint(walker, (start_cursor + end_cursor) / 2.0, &middle); + + if (start_density == 0.0) + { + /* Looking for entry */ + if (middle.distance_from_start - start_cursor < precision) + { + *result = middle; + } + else if (middle.global_density == 0.0) + { + _refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result); + } + else + { + _refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result); + } + } + else + { + /* Looking for exit */ + if (end_cursor - middle.distance_from_start < precision) + { + *result = middle; + } + else if (middle.global_density == 0.0) + { + _refineSegment(walker, start_cursor, start_density, middle.distance_from_start, middle.global_density, precision, result); + } + else + { + _refineSegment(walker, middle.distance_from_start, middle.global_density, end_cursor, end_density, precision, result); + } + } +} + +int cloudsWalkerPerformStep(CloudsWalker* walker) +{ + if (!walker->started) + { + _getPoint(walker, 0.0, &walker->last_segment.end); + walker->started = 1; + } + + if (walker->next_action.order == CLOUD_WALKING_STOP || walker->cursor >= walker->max_length) + { + walker->next_action.order = CLOUD_WALKING_STOP; + return 0; + } + else if (walker->next_action.order == CLOUD_WALKING_CONTINUE) + { + /* TODO Limit to end */ + walker->last_segment.start = walker->last_segment.end; + + walker->cursor += walker->step_size; + + _getPoint(walker, walker->cursor, &walker->last_segment.end); + walker->last_segment.length = walker->step_size; + walker->last_segment.refined = 0; + + return 1; + } + else if (walker->next_action.order == CLOUD_WALKING_REFINE) + { + /* Refine segment with dichotomy */ + _refineSegment(walker, + walker->last_segment.start.distance_from_start, + walker->last_segment.start.global_density, + walker->last_segment.end.distance_from_start, + walker->last_segment.end.global_density, + walker->next_action.precision, + (walker->last_segment.start.global_density == 0.0) ? (&walker->last_segment.start) : (&walker->last_segment.end)); + walker->last_segment.length = walker->last_segment.end.distance_from_start - walker->last_segment.start.distance_from_start; + walker->last_segment.refined = 1; + + walker->next_action.order = CLOUD_WALKING_CONTINUE; + + return 1; + } + else + { + /* TODO */ + return 0; + } +} + +void cloudsWalkerOrderStop(CloudsWalker* walker) +{ + walker->next_action.order = CLOUD_WALKING_STOP; +} + +void cloudsWalkerOrderRefine(CloudsWalker* walker, double precision) +{ + walker->next_action.order = CLOUD_WALKING_REFINE; + walker->next_action.precision = precision; +} + +void cloudsWalkerOrderSubdivide(CloudsWalker* walker, double max_segments) +{ + walker->next_action.order = CLOUD_WALKING_SUBDIVIDE; + walker->next_action.max_segments = max_segments; +} + +CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker) +{ + return &walker->last_segment; +} + +void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data) +{ + walker->last_segment.data = data; + while (cloudsWalkerPerformStep(walker)) + { + callback(walker); + } } diff --git a/src/rendering/clouds/clo_walking.h b/src/rendering/clouds/clo_walking.h index 2ad19b3..1bd31fc 100644 --- a/src/rendering/clouds/clo_walking.h +++ b/src/rendering/clouds/clo_walking.h @@ -15,10 +15,33 @@ extern "C" typedef struct { - Vector3 enter; - Vector3 exit; + double distance_from_start; + Vector3 location; + double global_density; +} CloudWalkerPoint; + +/** + * Information on a segment yielded by walking. + */ +typedef struct +{ + Renderer* renderer; + CloudsLayerDefinition* layer; + + CloudWalkerPoint start; + CloudWalkerPoint end; double length; -} CloudPrimarySegment; + + int refined; + /*int subdivision_level; + double precision_asked;*/ + + void* data; +} CloudWalkerStepInfo; + +typedef struct CloudsWalker CloudsWalker; + +typedef void (*FuncCloudsWalkingCallback)(CloudsWalker* walker); /** * Optimize the search limits in a layer. @@ -31,17 +54,81 @@ typedef struct int cloudsOptimizeWalkingBounds(CloudsLayerDefinition* layer, Vector3* start, Vector3* end); /** - * Go through the cloud layer to find segments (parts of the lookup that are likely to contain cloud). + * Create a cloud walker. * - * @param renderer The renderer environment - * @param layer The cloud layer - * @param start Start position of the lookup - * @param end End position of the lookup - * @param max_segments Maximum number of segments to collect - * @param out_segments Allocated space to fill found segments - * @return Number of segments found + * For better performance, the segment should by optimized using cloudsOptimizeWalkingBounds. + * @param renderer Renderer context + * @param layer The cloud layer to traverse + * @param start Start of the walk + * @param end End of the walk */ -int cloudsGetLayerPrimarySegments(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end, int max_segments, CloudPrimarySegment* out_segments); +CloudsWalker* cloudsCreateWalker(Renderer* renderer, CloudsLayerDefinition* layer, Vector3 start, Vector3 end); + +/** + * Delete a cloud walker. + * + * @param walker The walker to free + */ +void cloudsDeleteWalker(CloudsWalker* walker); + +/** + * Define the segment size for next steps. + * + * @param walker The walker to configure + * @param step The step length, negative for automatic + */ +void cloudsWalkerSetStepSize(CloudsWalker* walker, double step); + +/** + * Perform a single step. + * + * @param walker The walker to use + * @return 1 to continue the loop, 0 to stop + */ +int cloudsWalkerPerformStep(CloudsWalker* walker); + +/** + * Order the walker to stop. + * + * @param walker The walker to use + */ +void cloudsWalkerOrderStop(CloudsWalker* walker); + +/** + * Order the walker to refine the search for cloud entry or exit. + * + * The refinement will next yield a shorter version of the segment, containing only the cloud-inside portion, with a + * tolerance fixed by precision. For an entry point, this will discard the part before cloud entry. For en exit point, + * the portion after this point will be part of the next step, as normal walking resumes. + * @param walker The walker to use + * @param precision Precision wanted for the refinement + */ +void cloudsWalkerOrderRefine(CloudsWalker* walker, double precision); + +/** + * Order the walker to subdivide the previous segment in smaller segments. + * + * @param walker The walker to use + * @param max_segments Maximal number of segments + */ +void cloudsWalkerOrderSubdivide(CloudsWalker* walker, double max_segments); + +/** + * Get the last segment information. + * + * @param walker The walker to use + */ +CloudWalkerStepInfo* cloudsWalkerGetLastSegment(CloudsWalker* walker); + +/** + * Start walking automatically through a segment. + * + * The callback will be called with each segment found, giving info and asking for desired alteration on walking. + * @param walker The walker to use + * @param callback Callback to be called with each found segment + * @param data User data that will be passed back in the callback + */ +void cloudsStartWalking(CloudsWalker* walker, FuncCloudsWalkingCallback callback, void* data); #ifdef __cplusplus } diff --git a/src/rendering/clouds/public.h b/src/rendering/clouds/public.h index beded50..283ac27 100644 --- a/src/rendering/clouds/public.h +++ b/src/rendering/clouds/public.h @@ -80,12 +80,6 @@ LayerType cloudsGetLayerType(); void cloudsAutoPreset(CloudsDefinition* definition, CloudsPreset preset); void cloudsLayerAutoPreset(CloudsLayerDefinition* definition, CloudsLayerPreset preset); -Renderer* cloudsCreatePreviewCoverageRenderer(); -Color cloudsGetPreviewCoverage(Renderer* renderer, double x, double y, double scaling, int perspective); - -Renderer* cloudsCreatePreviewColorRenderer(); -Color cloudsGetPreviewColor(Renderer* renderer, double x, double y); - #ifdef __cplusplus } #endif diff --git a/src/rendering/main.c b/src/rendering/main.c index 970e351..3006430 100644 --- a/src/rendering/main.c +++ b/src/rendering/main.c @@ -2,6 +2,7 @@ #include #include "auto.h" +#include "tools/data.h" #include "system.h" #include "scenery.h" #include "render.h" @@ -13,6 +14,12 @@ void paysagesInit() { systemInit(); + if (!dataInit()) + { + /* TODO Add error callback (for interface) */ + fprintf(stderr, "ERROR : Can't locate data files.\n"); + exit(1); + } openclInit(); sceneryInit(); diff --git a/src/rendering/noise.c b/src/rendering/noise.c index 5525cc9..60b652f 100644 --- a/src/rendering/noise.c +++ b/src/rendering/noise.c @@ -202,17 +202,8 @@ void noiseValidate(NoiseGenerator* generator) generator->_max_value = generator->height_offset; for (x = 0; x < generator->level_count; x++) { - double min_value = generator->levels[x].minvalue; - double max_value = min_value + generator->levels[x].amplitude; - - if (min_value < generator->_min_value) - { - generator->_min_value = min_value; - } - if (max_value > generator->_max_value) - { - generator->_max_value = max_value; - } + generator->_min_value += generator->levels[x].minvalue; + generator->_max_value += generator->levels[x].minvalue + generator->levels[x].amplitude; } } @@ -394,14 +385,14 @@ void noiseNormalizeAmplitude(NoiseGenerator* generator, double minvalue, double for (level = 0; level < generator->level_count; level++) { - generator->levels[level].minvalue = minvalue + (generator->levels[level].minvalue - current_minvalue) * factor; + generator->levels[level].minvalue *= factor; generator->levels[level].amplitude *= factor; if (adjust_scaling) { generator->levels[level].wavelength *= factor; } } - /*generator->height_offset = minvalue + (generator->height_offset - current_minvalue) * factor;*/ + generator->height_offset = minvalue + (generator->height_offset - current_minvalue) * factor; noiseValidate(generator); } diff --git a/src/rendering/terrain/public.h b/src/rendering/terrain/public.h index ca83821..905e626 100644 --- a/src/rendering/terrain/public.h +++ b/src/rendering/terrain/public.h @@ -81,6 +81,7 @@ typedef struct /* Heightmap manipulation */ int terrainIsPainted(TerrainHeightMap* heightmap, int x, int z); +void terrainClearPainting(TerrainHeightMap* heightmap); void terrainBrushElevation(TerrainHeightMap* heightmap, TerrainBrush* brush, double value); void terrainBrushSmooth(TerrainHeightMap* heightmap, TerrainBrush* brush, double value); void terrainBrushAddNoise(TerrainHeightMap* heightmap, TerrainBrush* brush, NoiseGenerator* generator, double value); diff --git a/src/rendering/terrain/ter_definition.c b/src/rendering/terrain/ter_definition.c index b61c74a..be387c2 100644 --- a/src/rendering/terrain/ter_definition.c +++ b/src/rendering/terrain/ter_definition.c @@ -27,13 +27,13 @@ static TerrainDefinition* _createDefinition() { TerrainDefinition* definition = malloc(sizeof(TerrainDefinition)); - definition->height = 0.0; + definition->height = 1.0; definition->scaling = 1.0; definition->shadow_smoothing = 0.0; definition->height_map = terrainHeightMapCreate(definition); - definition->water_height = -0.8; + definition->water_height = -0.3; definition->_height_noise = noiseCreateGenerator(); diff --git a/src/rendering/terrain/ter_painting.c b/src/rendering/terrain/ter_painting.c index 3d60890..b94ab59 100644 --- a/src/rendering/terrain/ter_painting.c +++ b/src/rendering/terrain/ter_painting.c @@ -454,6 +454,12 @@ int terrainIsPainted(TerrainHeightMap* heightmap, int x, int z) return _getDataPointer(&heightmap->brush_data, x, z, NULL, NULL, 0) || _getDataPointer(&heightmap->merged_data, x, z, NULL, NULL, 0); } +void terrainClearPainting(TerrainHeightMap* heightmap) +{ + _clearData(&heightmap->merged_data); + _clearData(&heightmap->brush_data); +} + typedef double (*BrushCallback)(TerrainHeightMap* heightmap, TerrainBrush* brush, double x, double z, double basevalue, double influence, double force, void* data); static inline void _applyBrush(TerrainHeightMap* heightmap, TerrainBrush* brush, double force, void* data, BrushCallback callback) diff --git a/src/rendering/terrain/ter_presets.c b/src/rendering/terrain/ter_presets.c index e7236c0..4fd52e0 100644 --- a/src/rendering/terrain/ter_presets.c +++ b/src/rendering/terrain/ter_presets.c @@ -19,7 +19,7 @@ void terrainAutoPreset(TerrainDefinition* definition, TerrainPreset preset) noiseNormalizeAmplitude(definition->_height_noise, -1.0, 1.0, 0); noiseSetFunctionParams(definition->_height_noise, NOISE_FUNCTION_SIMPLEX, 0.0, 0.0); definition->scaling = 1.0; - definition->height = 15.0; + definition->height = 30.0; definition->shadow_smoothing = 0.03; break; default: diff --git a/src/rendering/tools/data.c b/src/rendering/tools/data.c new file mode 100644 index 0000000..859abe7 --- /dev/null +++ b/src/rendering/tools/data.c @@ -0,0 +1,34 @@ +#include "data.h" + +#include +#include +#include + +static const char* _datapath = NULL; + +static int _tryDataPath(const char* path) +{ + char* buffer; + + buffer = malloc(sizeof (char) * (strlen(path) + 30)); + strcpy(buffer, path); + strcat(buffer, "/.paysages_data"); + + FILE* f = fopen(buffer, "r"); + free(buffer); + if (f) + { + _datapath = path; + fclose(f); + return 1; + } + else + { + return 0; + } +} + +int dataInit() +{ + return _tryDataPath("./data"); +} diff --git a/src/rendering/tools/data.h b/src/rendering/tools/data.h new file mode 100644 index 0000000..f5c77b1 --- /dev/null +++ b/src/rendering/tools/data.h @@ -0,0 +1,10 @@ +#ifndef _PAYSAGES_TOOLS_DATA_H_ +#define _PAYSAGES_TOOLS_DATA_H_ + +/* + * Data directory management. + */ + +int dataInit(); + +#endif diff --git a/src/testing/common.h b/src/testing/common.h index e06d1ad..1992adb 100644 --- a/src/testing/common.h +++ b/src/testing/common.h @@ -29,6 +29,11 @@ static inline void _add_methods_to_case(TCase* tc, ...) suite_add_tcase(s, tc); \ } +/***** Boolean assertions *****/ +#define ck_assert_true(_X_) ck_assert_int_ne((_X_), 0) +#define ck_assert_false(_X_) ck_assert_int_eq((_X_), 0) + +/***** Floating point assertions *****/ static inline int _double_equals(double x, double y) { return fabs(x - y) < 0.00000000001; @@ -39,7 +44,7 @@ static inline int _double_not_equals(double x, double y) } static inline int _double_greater(double x, double y) { - return _double_not_equals(x, y) || (x > y); + return _double_not_equals(x, y) && (x > y); } static inline int _double_greater_or_equal(double x, double y) { @@ -47,7 +52,7 @@ static inline int _double_greater_or_equal(double x, double y) } static inline int _double_less(double x, double y) { - return _double_not_equals(x, y) || (x < y); + return _double_not_equals(x, y) && (x < y); } static inline int _double_less_or_equal(double x, double y) { @@ -58,8 +63,8 @@ static inline int _double_less_or_equal(double x, double 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) #define ck_assert_double_gt(X, Y) _ck_assert_double(_double_greater, X, >, Y) -#define ck_assert_double_lt(X, Y) _ck_assert_double(_double_greater_or_equal, X, >=, Y) -#define ck_assert_double_gte(X, Y) _ck_assert_double(_double_less, X, <, Y) +#define ck_assert_double_lt(X, Y) _ck_assert_double(_double_less, X, <, Y) +#define ck_assert_double_gte(X, Y) _ck_assert_double(_double_greater_or_equal, X, >=, Y) #define ck_assert_double_lte(X, Y) _ck_assert_double(_double_less_or_equal, X, <=, Y) diff --git a/src/testing/main.c b/src/testing/main.c index aebbe85..df8b459 100644 --- a/src/testing/main.c +++ b/src/testing/main.c @@ -9,6 +9,7 @@ extern void test_euclid_case(Suite* s); extern void test_camera_case(Suite* s); extern void test_clouds_case(Suite* s); extern void test_render_case(Suite* s); +extern void test_noise_case(Suite* s); int main(int argc, char** argv) { @@ -24,6 +25,7 @@ int main(int argc, char** argv) test_camera_case(s); test_clouds_case(s); test_render_case(s); + test_noise_case(s); SRunner *sr = srunner_create(s); srunner_run_all(sr, CK_NORMAL); diff --git a/src/testing/test_clouds.c b/src/testing/test_clouds.c index 7ab715e..4d255c3 100644 --- a/src/testing/test_clouds.c +++ b/src/testing/test_clouds.c @@ -200,11 +200,9 @@ static double _getLayerDensitySinX(Renderer* renderer, CloudsLayerDefinition* la return (density > 0.0) ? density : 0.0; } -START_TEST(test_clouds_primary_segments) +START_TEST(test_clouds_walking) { - int segment_count, i; - CloudPrimarySegment segments[10]; - + /* Init */ CloudsLayerDefinition* layer; layer = cloudsGetLayerType().callback_create(); layer->lower_altitude = -1.0; @@ -217,36 +215,88 @@ START_TEST(test_clouds_primary_segments) renderer->render_quality = 8; renderer->clouds->getLayerDensity = _getLayerDensitySinX; - segment_count = cloudsGetLayerPrimarySegments(renderer, layer, v3(-0.4, 0.0, 0.0), v3(1.9, 0.0, 0.0), 10, segments); - ck_assert_int_eq(segment_count, 2); - for (i = 0; i < segment_count; i++) - { - ck_assert_double_eq(segments[i].enter.y, 0.0); - ck_assert_double_eq(segments[i].enter.z, 0.0); - ck_assert_double_eq(segments[i].exit.y, 0.0); - ck_assert_double_eq(segments[i].exit.z, 0.0); - } - ck_assert_double_in_range(segments[0].enter.x, -0.5, 0.0); - ck_assert_double_in_range(segments[0].exit.x, 0.5, 1.0); - ck_assert_double_in_range(segments[0].length, 0.5, 1.5); - ck_assert_double_gte(segments[1].enter.x, segments[0].exit.x); - ck_assert_double_in_range(segments[1].enter.x, 0.5, 1.0); - ck_assert_double_in_range(segments[1].exit.x, 1.5, 2.0); - ck_assert_double_in_range(segments[1].length, 0.5, 1.5); + CloudsWalker* walker = cloudsCreateWalker(renderer, layer, v3(-0.4, 0.0, 0.0), v3(10.0, 0.0, 0.0)); + CloudWalkerStepInfo* segment; + int result; + + /* First step */ + cloudsWalkerSetStepSize(walker, 0.3); + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_false(segment->refined); + ck_assert_double_eq(segment->length, 0.3); + ck_assert_double_eq(segment->start.distance_from_start, 0.0); + ck_assert_vector_values(segment->start.location, -0.4, 0.0, 0.0); + ck_assert_double_eq(segment->start.global_density, 0.0); + ck_assert_double_eq(segment->end.distance_from_start, 0.3); + ck_assert_vector_values(segment->end.location, -0.1, 0.0, 0.0); + ck_assert_double_eq(segment->end.global_density, 0.0); + + /* Second step */ + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_false(segment->refined); + ck_assert_double_eq(segment->length, 0.3); + ck_assert_double_eq(segment->start.distance_from_start, 0.3); + ck_assert_vector_values(segment->start.location, -0.1, 0.0, 0.0); + ck_assert_double_eq(segment->start.global_density, 0.0); + ck_assert_double_eq(segment->end.distance_from_start, 0.6); + ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0); + ck_assert_double_gt(segment->end.global_density, 0.9); + + /* Order to refine second step around the entry point */ + cloudsWalkerOrderRefine(walker, 0.01); + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_true(segment->refined); + ck_assert_double_in_range(segment->length, 0.19, 0.20); + ck_assert_double_in_range(segment->start.distance_from_start, 0.40, 0.41); + ck_assert_double_in_range(segment->start.location.x, 0.0, 0.01); + ck_assert_double_gt(segment->start.global_density, 0.0); + ck_assert_double_eq(segment->end.distance_from_start, 0.6); + ck_assert_vector_values(segment->end.location, 0.2, 0.0, 0.0); + ck_assert_double_gt(segment->end.global_density, 0.9); + + /* Third step, change step size */ + cloudsWalkerSetStepSize(walker, 0.4); + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_false(segment->refined); + ck_assert_double_eq(segment->length, 0.4); + ck_assert_double_eq(segment->start.distance_from_start, 0.6); + ck_assert_vector_values(segment->start.location, 0.2, 0.0, 0.0); + ck_assert_double_gt(segment->start.global_density, 0.9); + ck_assert_double_eq(segment->end.distance_from_start, 1.0); + ck_assert_vector_values(segment->end.location, 0.6, 0.0, 0.0); + ck_assert_double_eq(segment->end.global_density, 0.0); + + /* Refine exit point */ + cloudsWalkerOrderRefine(walker, 0.001); + result = cloudsWalkerPerformStep(walker); + segment = cloudsWalkerGetLastSegment(walker); + ck_assert_int_eq(result, 1); + ck_assert_true(segment->refined); + ck_assert_double_in_range(segment->length, 0.3, 0.301); + ck_assert_double_eq(segment->start.distance_from_start, 0.6); + ck_assert_vector_values(segment->start.location, 0.2, 0.0, 0.0); + ck_assert_double_gt(segment->start.global_density, 0.9); + ck_assert_double_in_range(segment->end.distance_from_start, 0.9, 0.901); + ck_assert_double_in_range(segment->end.location.x, 0.5, 0.501); + ck_assert_double_lt(segment->end.global_density, 0.1); + + /* Clean up */ + cloudsDeleteWalker(walker); cloudsGetLayerType().callback_delete(layer); rendererDelete(renderer); } END_TEST -START_TEST(test_clouds_preview_color) -{ - Renderer* renderer = cloudsCreatePreviewColorRenderer(); - - /* TODO Test the density overriding */ - - rendererDelete(renderer); -} -END_TEST - -TEST_CASE(clouds, test_clouds_density, test_clouds_walking_boundaries, test_clouds_primary_segments, test_clouds_preview_color) +TEST_CASE(clouds, + test_clouds_density, + test_clouds_walking_boundaries, + test_clouds_walking) diff --git a/src/testing/test_noise.c b/src/testing/test_noise.c new file mode 100644 index 0000000..5ab413e --- /dev/null +++ b/src/testing/test_noise.c @@ -0,0 +1,78 @@ +#include "testing/common.h" + +#include "rendering/noise.h" + +START_TEST(test_noise_range) +{ + NoiseGenerator* noise; + double minvalue, maxvalue; + + noise = noiseCreateGenerator(); + + noiseAddLevelSimple(noise, 0.1, -1.0, 1.0); + noiseAddLevelSimple(noise, 0.2, -0.5, 0.2); + noiseAddLevelSimple(noise, 0.4, -0.3, 1.2); + noiseValidate(noise); + noiseGetRange(noise, &minvalue, &maxvalue); + + ck_assert_double_eq(minvalue, -1.8); + ck_assert_double_eq(maxvalue, 2.4); +} +END_TEST + +START_TEST(test_noise_normalize) +{ + int x; + NoiseGenerator* noise; + + noise = noiseCreateGenerator(); + + /* Symmetric case */ + noiseAddLevelsSimple(noise, 10, 1.0, -4.0, 4.0, 0.5); + noiseValidate(noise); + noiseNormalizeAmplitude(noise, -1.0, 1.0, 0); + for (x = 0; x < 1000; x++) + { + double value = noiseGet1DTotal(noise, 0.01 * (double)x); + ck_assert_double_in_range(value, -1.0, 1.0); + } + + /* Target center differs from current center */ + noiseClearLevels(noise); + noiseAddLevelsSimple(noise, 10, 1.0, -5.0, 5.0, 0.5); + noiseValidate(noise); + noiseNormalizeAmplitude(noise, 0.0, 1.0, 0); + for (x = 0; x < 1000; x++) + { + double value = noiseGet1DTotal(noise, 0.01 * (double)x); + ck_assert_double_in_range(value, 0.0, 1.0); + } + + /* Asymmetric range */ + noiseClearLevels(noise); + noiseAddLevelsSimple(noise, 10, 1.0, 0.0, 10.0, 0.0); + noiseValidate(noise); + noiseNormalizeAmplitude(noise, 0.0, 1.0, 0); + for (x = 0; x < 1000; x++) + { + double value = noiseGet1DTotal(noise, 0.01 * (double)x); + ck_assert_double_in_range(value, 0.0, 1.0); + } + + /* Complex asymmetric case */ + noiseClearLevels(noise); + noiseAddLevelsSimple(noise, 3, 1.0, -2.0, 8.0, 0.3); + noiseValidate(noise); + noiseNormalizeAmplitude(noise, -2.0, 4.0, 0.0); + for (x = 0; x < 1000; x++) + { + double value = noiseGet1DTotal(noise, 0.01 * (double)x); + ck_assert_double_in_range(value, -2.0, 4.0); + } + + noiseDeleteGenerator(noise); +} +END_TEST + +TEST_CASE(noise, test_noise_range, test_noise_normalize) +