├── .gitignore ├── GlaciersFeatures.ipynb ├── GlaciersRender.pro ├── GlaciersSim.pro ├── LICENSE ├── README.md ├── code ├── appRender │ ├── featurespainter.cpp │ ├── featurespainter.h │ ├── glacierswidget.cpp │ ├── glacierswidget.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── mainwindow.ui ├── appSimulation │ ├── glacierswidget.cpp │ ├── glacierswidget.h │ ├── main.cpp │ ├── mainwindow.cpp │ ├── mainwindow.h │ └── mainwindow.ui ├── core │ ├── core.cpp │ ├── core.h │ ├── scalarfield2.cpp │ ├── scalarfield2.h │ ├── shader-utils.cpp │ ├── shader-utils.h │ ├── vectorfield2.cpp │ └── vectorfield2.h ├── glacierterrain.cpp └── glacierterrain.h ├── data ├── arrow.png ├── maps │ ├── aiguestortes │ │ ├── dem-hires.png │ │ ├── dem.png │ │ ├── ela.png │ │ └── ice.png │ └── aiguestortesCrop │ │ ├── crevassesLong.png │ │ ├── crevassesMargin.png │ │ ├── crevassesTrans.png │ │ ├── dem-hires.png │ │ ├── dem.png │ │ ├── ela.png │ │ ├── ice.png │ │ ├── icefalls.png │ │ ├── icefallsRaw.png │ │ ├── ogivesDistCtr.png │ │ ├── ogivesDistSrc.png │ │ ├── ogivesId.png │ │ ├── rimayes.png │ │ ├── segmentation.png │ │ ├── seracs.png │ │ ├── valleydist.png │ │ └── valleywidth.png ├── stamps │ ├── crevasses_1_mask.png │ ├── crevasses_2_mask.png │ ├── crevasses_3_mask.png │ ├── crevasses_4_mask.png │ ├── crevasses_5_mask.png │ ├── crevasses_6_mask.png │ ├── crevasses_7_mask.png │ ├── crevasses_8_mask.png │ └── crevasses_9_mask.png └── terrains │ ├── aiguestortes_20m.png │ ├── aiguestortes_precipitation.png │ ├── aiguestortes_sun.png │ ├── canyon_10m.png │ ├── canyon_precipitation.png │ ├── canyon_sun.png │ ├── chartreuse_20m.png │ ├── chartreuse_edit_20m.png │ ├── chartreuse_sun.png │ ├── ecrins_30m.png │ ├── ecrins_sun.png │ ├── kungsleden_25m.png │ ├── kungsleden_sun.png │ ├── mtperdu_20m.png │ ├── mtperdu_sun.png │ ├── ruapehu_25m.png │ ├── ruapehu_sun.png │ ├── smokies_10m.png │ ├── smokies_sun.png │ ├── sthelens_10m.png │ └── sthelens_sun.png ├── img ├── Crevasses-Rimayes.jpg ├── Crevasses_Longitudinal-Marginal.jpg ├── Crevasses_Transverse.jpg ├── Icefalls-Seracs.jpg ├── Moraines-Transverse.jpg ├── Ogives-Moraines.jpg ├── scene-aiguestortes.jpg ├── scene-ecrins.jpg └── teaser.jpg └── shaders ├── instanced-cube.glsl ├── sia.glsl ├── skybox.glsl └── tex-mesh.glsl /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /GlaciersRender.pro: -------------------------------------------------------------------------------- 1 | QT += core gui opengl 2 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 3 | greaterThan(QT_MAJOR_VERSION, 5): QT += openglwidgets 4 | 5 | CONFIG += c++11 6 | 7 | INCLUDEPATH += code 8 | INCLUDEPATH += code/core 9 | INCLUDEPATH += code/appRender 10 | INCLUDEPATH += $$(GLEW_DIR)/include/GL 11 | 12 | VPATH += code 13 | 14 | SOURCES += \ 15 | glacierterrain.cpp \ 16 | appRender/main.cpp \ 17 | appRender/mainwindow.cpp \ 18 | appRender/glacierswidget.cpp \ 19 | appRender/featurespainter.cpp \ 20 | core/core.cpp \ 21 | core/scalarfield2.cpp \ 22 | core/vectorfield2.cpp \ 23 | core/shader-utils.cpp 24 | 25 | HEADERS += \ 26 | glacierterrain.h \ 27 | appRender/mainwindow.h \ 28 | appRender/glacierswidget.h \ 29 | appRender/featurespainter.h \ 30 | core/core.h \ 31 | core/scalarfield2.h \ 32 | core/vectorfield2.h \ 33 | core/shader-utils.h 34 | 35 | FORMS += \ 36 | appRender/mainwindow.ui 37 | 38 | LIBS += -L$$(GLEW_DIR)/lib/Release/x64 -lglew32 39 | LIBS += -lopengl32 -lglu32 40 | 41 | # Default rules for deployment. 42 | qnx: target.path = /tmp/$${TARGET}/bin 43 | else: unix:!android: target.path = /opt/$${TARGET}/bin 44 | !isEmpty(target.path): INSTALLS += target 45 | -------------------------------------------------------------------------------- /GlaciersSim.pro: -------------------------------------------------------------------------------- 1 | QT += core gui opengl 2 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 3 | greaterThan(QT_MAJOR_VERSION, 5): QT += openglwidgets 4 | 5 | CONFIG += c++11 6 | 7 | INCLUDEPATH += code 8 | INCLUDEPATH += code/core 9 | INCLUDEPATH += code/appRender 10 | INCLUDEPATH += $$(GLEW_DIR)/include/GL 11 | 12 | VPATH += code 13 | 14 | SOURCES += \ 15 | glacierterrain.cpp \ 16 | appSimulation/main.cpp \ 17 | appSimulation/mainwindow.cpp \ 18 | appSimulation/glacierswidget.cpp \ 19 | core/core.cpp \ 20 | core/scalarfield2.cpp \ 21 | core/vectorfield2.cpp \ 22 | core/shader-utils.cpp 23 | 24 | HEADERS += \ 25 | glacierterrain.h \ 26 | appSimulation/mainwindow.h \ 27 | appSimulation/glacierswidget.h \ 28 | core/core.h \ 29 | core/scalarfield2.h \ 30 | core/vectorfield2.h \ 31 | core/shader-utils.h 32 | 33 | FORMS += \ 34 | appSimulation/mainwindow.ui 35 | 36 | LIBS += -L$$(GLEW_DIR)/lib/Release/x64 -lglew32 37 | LIBS += -lopengl32 -lglu32 38 | 39 | # Default rules for deployment. 40 | qnx: target.path = /tmp/$${TARGET}/bin 41 | else: unix:!android: target.path = /opt/$${TARGET}/bin 42 | !isEmpty(target.path): INSTALLS += target 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Oscar Argudo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simulation, Modeling and Authoring of Glaciers 2 | ![Teaser image](./img/teaser.jpg) 3 | 4 | Interactive simulation of glaciers on a terrain and procedural modeling of several glacier features. 5 | 6 | 7 | ## Repository contents 8 | 9 | This repository contains our implementation of the paper, needed data, and examples. 10 | 11 | | Directory | Description | 12 | | ---: | :--- | 13 | | **[code](./code)** | Source code of the two OpenGL applications: simulation and feature placement. | 14 | | **[data](./data)** | Input terrains, application data, intermediate results and maps. | 15 | | **[img](./img)** | Teaser image and other beautiful pictures of our glaciers rendered using E-On software's VUE. | 16 | | **[shaders](./shaders)** | Shaders for rendering and ice simulation. | 17 | 18 | 19 | ## Requirements 20 | 21 | We have tested this implementation on a Windows 10 machine equipped with an NVIDIA GeForce GTX 1060. 22 | 23 | For the simulation and rendering applications, we used Qt Creator and tested the projects using both the ``MinGW`` and the ``MSVC2019`` kits (64 bit). Importing the .pro files into Visual Studio 2019 with Qt Visual Studio Tools 2.6.0 also created working solutions directly. 24 | 25 | C++ libraries: 26 | ``` 27 | Qt 5.15, OpenGL 4.3, GLEW 2.1.0 28 | ``` 29 | You will need to define a ``GLEW_DIR`` environment variable pointing to the location of the headers and library files. 30 | 31 | For the features notebook, we used a miniconda Python 3.7 environment installing the following packages: 32 | ``` 33 | numpy, matplotlib, jupyter, pypng, opencv, scikit-image 34 | ``` 35 | 36 | 37 | ## Running the code 38 | 39 | The workflow consists of three sequential steps. 40 | 41 | ### 1. Glacier simulation 42 | 43 | Compile and run [GlaciersSim.pro](./GlaciersSim.pro). 44 | 45 | The ice simulation is executed in an interactive terrain editor using OpenGL and a Compute Shader. 46 | 47 | The input is an elevation map, we assume height to be encoded in a 16 bit png as decimeters (0.1m). Additionally, a solar radiance map, a precipitation map or an initial ice layer can also be provided. See [data/terrains](./data/terrains) for examples. We included preset scenes configurations to replicate some of the results shown in the paper. 48 | 49 | The core of the glacier simulation implementation is the SIA compute shader [sia.glsl](./shaders/sia.glsl), which is used from the [GlacierTerrain class](./code/glacierterrain.h). 50 | 51 | 52 | ### 2. Feature placement 53 | 54 | Run the [GlaciersFeatures.ipynb](/GlaciersFeatures.ipynb) jupyter notebook. It contains all the functions, instructions and comments explaining the main ideas, so you only need to follow it in order. 55 | 56 | This notebook will read the elevation map of the bedrock, the simulated ice thickness, and the mask of ice cells above ELA, and output all the necessary extra maps that will later be used to prepare the final rendering. 57 | 58 | 59 | ### 3. Render 60 | 61 | Compile and run [GlaciersRender.pro](./GlaciersRender.pro). 62 | 63 | We include a simple Qt OpenGL app that we used to test the feature placement and generate the necessary maps that would be streamed to the offline renders using ``E-On software's VUE``. Note that the goal of this application is to demonstrate the placement, not to be photorealistic. 64 | 65 | 66 | ## Remarks and to-do 67 | 68 | The code we provide here is a cleaned version of our research code for demonstration purposes and is provided *as is*. 69 | 70 | The C++ application relied on many classes from the team's internal libraries. We have extracted and refactored the relevant functions for this code release, so don't expect these classes to be fully reusable out of this context, commonly needed functionality might be missing. 71 | 72 | It would also be interesting to integrate the features computation into the OpenGL app editor directly. This part was faster to experiment using Python and we haven't ported the final code into the apps. 73 | 74 | 75 | ### Known issues 76 | 77 | GlacierSim: brush edits on a map added for demonstration purposes. They do not propagate to other levels of the multiresolution, but this should be easy to fix. 78 | 79 | 80 | ## Windows binaries 81 | 82 | For convenience, if you just want to try and play with the two applications, we provide Windows binaries with sample data. 83 | You can download them from this [release](https://github.com/oargudo/glaciers/releases/tag/v1.0). 84 | 85 | 86 | ## Article 87 | 88 | The article is published in [ACM Transactions on Graphics](https://doi.org/10.1145/3414685.3417855). The authors' version can also be read [here](https://hal.archives-ouvertes.fr/hal-02929531/document). 89 | 90 | If you use this code for your research, please cite our paper: 91 | ``` 92 | @article{Argudo2020glaciers, 93 | title = {Simulation, Modeling and Authoring of Glaciers}, 94 | author = {Argudo,Oscar and Galin,Eric and Peytavie,Adrien and Paris,Axel and Gu\'{e}rin,Eric}, 95 | journal = {ACM Transactions on Graphics (SIGGRAPH Asia 2020)}, 96 | year = {2020}, 97 | volume = {39}, 98 | number = {6} 99 | } 100 | ``` 101 | 102 | 103 | ## Acknowledgements 104 | 105 | * Alex Jarosch for sharing a Python implementation of their paper "Restoring mass conservation to shallow ice flow models over complex terrain", which was used as a starting point in our experiments. Check out [his repository](https://github.com/alexjarosch/sia-fluxlim) for their code and a link to their paper. 106 | 107 | * [Jordi Camins](http://www.gelicehielo.com/) for his advice on modeling the glacier features. 108 | 109 | * Jürg Alean and Michael Hambrey from Glaciers online. Their [photoglossary](https://www.swisseduc.ch/glaciers/glossary/index-en.html) was very useful for understanding and modeling the glacier features. 110 | -------------------------------------------------------------------------------- /code/appRender/featurespainter.h: -------------------------------------------------------------------------------- 1 | #ifndef FEATURESPAINTER_H 2 | #define FEATURESPAINTER_H 3 | 4 | #include 5 | #include 6 | #include "core.h" 7 | #include "scalarfield2.h" 8 | #include "vectorfield2.h" 9 | 10 | 11 | class FeaturesPainter { 12 | 13 | public: 14 | QImage baseTexture(const ScalarField2& hf, const ScalarField2& ice, const ScalarField2& ela) const; 15 | 16 | QImage crevasses(const ScalarField2& crevasseField, int crevasseType, double scale, const ScalarField2& alphaCrevasses, QImage& texture); 17 | QImage rimayes(const ScalarField2& rimayeField, QImage& texture); 18 | QImage ogives(const ScalarField2& ogiveDistSrc, const ScalarField2& ogiveDistCtr, const QImage& ogiveId, double ogiveLenScale, double ogiveFreq, 19 | QImage& texture, ScalarField2& ogiveStrength); 20 | QImage moraines(const Vector3& moraineColor, double moraineScale, QImage& texture); 21 | ScalarField2 seracs(const ScalarField2& seracs, double displacement); 22 | 23 | public: 24 | Box2 terrainBox; 25 | 26 | ScalarField2 bedLow, iceLow, hfLow; 27 | ScalarField2 bedHi, iceHi, hfHi; 28 | ScalarField2 accumArea; 29 | 30 | ScalarField2 slopeField; 31 | VectorField2 flowDirField; 32 | ScalarField2 valleyDist; 33 | QImage glacierIds; 34 | 35 | QImage imgBase; 36 | double TEXTURE_RES = 1.25; // m 37 | 38 | std::minstd_rand rne; 39 | std::uniform_real_distribution uniformDist = std::uniform_real_distribution(0.0, 1.0); 40 | 41 | protected: 42 | double randUniform(double a = 0.0, double b = 1.0); 43 | 44 | }; 45 | 46 | #endif // FEATURESPAINTER_H 47 | -------------------------------------------------------------------------------- /code/appRender/glacierswidget.cpp: -------------------------------------------------------------------------------- 1 | #include "glacierswidget.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "shader-utils.h" 9 | 10 | void checkGLError() 11 | { 12 | GLenum err; 13 | while ((err = glGetError()) != GL_NO_ERROR) { 14 | std::cerr << err << std::endl; 15 | } 16 | } 17 | 18 | GlaciersWidget::GlaciersWidget(QWidget* parent) : QOpenGLWidget(parent) 19 | { 20 | setMouseTracking(true); 21 | setFocusPolicy(Qt::StrongFocus); 22 | } 23 | 24 | GlaciersWidget::~GlaciersWidget() 25 | { 26 | } 27 | 28 | 29 | void GlaciersWidget::initializeGL() 30 | { 31 | glewExperimental = true; 32 | GLenum err = glewInit(); 33 | if (err != GLEW_OK) { 34 | std::cout << "Error : " << glewGetErrorString(err) << std::endl; 35 | exit(-1); 36 | } 37 | 38 | glEnable(GL_DEPTH_TEST); 39 | glEnable(GL_BLEND); 40 | 41 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 42 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 43 | 44 | shader = read_program("./shaders/tex-mesh.glsl"); 45 | skyboxShader = read_program("./shaders/skybox.glsl"); 46 | 47 | glGenVertexArrays(1, &skyboxVAO); 48 | 49 | unsigned char foo = 255; 50 | glGenTextures(1, &texId); 51 | glBindTexture(GL_TEXTURE_2D, texId); 52 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, (void*)(&foo)); 53 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 54 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 55 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 56 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 57 | glBindTexture(GL_TEXTURE_2D, 0); 58 | } 59 | 60 | void GlaciersWidget::resizeGL(int w, int h) 61 | { 62 | glViewport(0, 0, (GLint)w, (GLint)h); 63 | } 64 | 65 | void GlaciersWidget::paintGL() 66 | { 67 | double camDist = Norm(camera.getEye() - terrainBBox.center()); 68 | double tradius = terrainBBox.radius(); 69 | if (camDist < terrainBBox.radius()) 70 | camera.setPlanes(100, 2*tradius); 71 | else 72 | camera.setPlanes(camDist - tradius, 2 * tradius + camDist); 73 | 74 | // Clear 75 | glClearColor(0.62f, 0.74f, 0.85f, 1.f); 76 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 77 | 78 | // Sky 79 | glEnable(GL_DEPTH_TEST); 80 | glDepthFunc(GL_LEQUAL); 81 | glUseProgram(skyboxShader); 82 | glBindVertexArray(skyboxVAO); 83 | glUniform3f(0, camera.getEye()[0], camera.getEye()[1], camera.getEye()[2]); 84 | glUniform3f(1, camera.getAt()[0], camera.getAt()[1], camera.getAt()[2]); 85 | glUniform3f(2, camera.getUp()[0], camera.getUp()[1], camera.getUp()[2]); 86 | glUniform2f(3, width(), height()); 87 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 88 | 89 | // Terrain 90 | if (meshVAO > 0) { 91 | glUseProgram(shader); 92 | 93 | QMatrix4x4 matPerspective; 94 | matPerspective.perspective(Math::RadianToDegree(camera.getAngleOfViewV(width(), height())), 95 | (GLdouble)width() / (GLdouble)height(), 96 | camera.getNearPlane(), camera.getFarPlane()); 97 | glUniformMatrix4fv(glGetUniformLocation(shader, "ProjectionMatrix"), 1, GL_FALSE, matPerspective.data()); 98 | 99 | QMatrix4x4 matView; 100 | matView.lookAt(QVector3D(camera.getEye()[0], camera.getEye()[1], camera.getEye()[2]), 101 | QVector3D(camera.getAt()[0], camera.getAt()[1], camera.getAt()[2]), 102 | QVector3D(camera.getUp()[0], camera.getUp()[1], camera.getUp()[2])); 103 | glUniformMatrix4fv(glGetUniformLocation(shader, "ModelViewMatrix"), 1, GL_FALSE, matView.data()); 104 | 105 | glUniform2f(glGetUniformLocation(shader, "u_worldMin"), terrainBBox.getMin()[0], terrainBBox.getMin()[1]); 106 | glUniform2f(glGetUniformLocation(shader, "u_worldSize"), terrainBBox.width(), terrainBBox.height()); 107 | 108 | glActiveTexture(GL_TEXTURE0); 109 | glBindTexture(GL_TEXTURE_2D, texId); 110 | glUniform1i(glGetUniformLocation(shader, "u_texture"), 0); 111 | 112 | glBindVertexArray(meshVAO); 113 | glDrawElements(GL_TRIANGLES, numTriangles*3, GL_UNSIGNED_INT, 0); 114 | glBindVertexArray(0); 115 | 116 | glUseProgram(0); 117 | } 118 | 119 | // Schedule next draw 120 | //update(); 121 | } 122 | 123 | 124 | void GlaciersWidget::setHeightfield(const ScalarField2& hf, bool centerCamera) 125 | { 126 | makeCurrent(); 127 | 128 | int nx = hf.getSizeX(); 129 | int ny = hf.getSizeY(); 130 | 131 | // bbox 132 | Box2 bbox = hf.getDomain(); 133 | double a, b; 134 | hf.getRange(a, b); 135 | terrainBBox = Box3( 136 | Vector3(bbox.getMin()[0], bbox.getMin()[1], a), 137 | Vector3(bbox.getMax()[0], bbox.getMax()[1], b)); 138 | 139 | // camera 140 | if (centerCamera) camera = Camera::View(terrainBBox); 141 | 142 | // verts 143 | int idx = 0; 144 | std::vector verts(3*nx*ny); 145 | for (int i = 0; i < nx; i++) { 146 | for (int j = 0; j < ny; j++) { 147 | Vector3 p = hf.vertex(i, j); 148 | verts[idx++] = GLfloat(p[0]); 149 | verts[idx++] = GLfloat(p[1]); 150 | verts[idx++] = GLfloat(p[2]); 151 | } 152 | } 153 | 154 | // tris 155 | numTriangles = (nx - 1) * (ny - 1) * 2; 156 | idx = 0; 157 | std::vector indices(numTriangles * 3); 158 | for (int i = 1; i < nx; i++) { 159 | for (int j = 1; j < ny; j++) { 160 | GLuint v00 = (i - 1) * ny + j - 1; 161 | GLuint v01 = (i - 1) * ny + j; 162 | GLuint v10 = i * ny + j - 1; 163 | GLuint v11 = i * ny + j; 164 | 165 | indices[idx++] = v00; 166 | indices[idx++] = v01; 167 | indices[idx++] = v10; 168 | 169 | indices[idx++] = v10; 170 | indices[idx++] = v01; 171 | indices[idx++] = v11; 172 | } 173 | } 174 | 175 | // update buffers 176 | if (bufferVerts > 0) glDeleteBuffers(1, &bufferVerts); 177 | if (bufferIndices > 0) glDeleteBuffers(1, &bufferIndices); 178 | if (meshVAO > 0) glDeleteVertexArrays(1, &meshVAO); 179 | 180 | glGenVertexArrays(1, &meshVAO); 181 | glBindVertexArray(meshVAO); 182 | 183 | glUseProgram(shader); 184 | GLuint attribVertexLoc = glGetAttribLocation(shader, "a_position"); 185 | 186 | glGenBuffers(1, &bufferVerts); 187 | glBindBuffer(GL_ARRAY_BUFFER, bufferVerts); 188 | glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * verts.size(), &verts[0], GL_STATIC_DRAW); 189 | glVertexAttribPointer(attribVertexLoc, 3, GL_FLOAT, GL_FALSE, 0, 0); 190 | glEnableVertexAttribArray(attribVertexLoc); 191 | 192 | glGenBuffers(1, &bufferIndices); 193 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndices); 194 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices[0], GL_STATIC_DRAW); 195 | 196 | glBindVertexArray(0); 197 | glUseProgram(0); 198 | } 199 | 200 | 201 | void GlaciersWidget::setTexture(const QImage& img) 202 | { 203 | makeCurrent(); 204 | 205 | glBindTexture(GL_TEXTURE_2D, texId); 206 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img.width(), img.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, img.bits()); 207 | glGenerateMipmap(GL_TEXTURE_2D); 208 | } 209 | 210 | 211 | void GlaciersWidget::mousePressEvent(QMouseEvent * e) 212 | { 213 | x0 = e->globalPosition().x(); 214 | y0 = e->globalPosition().y(); 215 | 216 | update(); 217 | } 218 | 219 | void GlaciersWidget::mouseMoveEvent(QMouseEvent * e) 220 | { 221 | int x = e->globalPosition().x(); 222 | int y = e->globalPosition().y(); 223 | 224 | if (e->buttons() & Qt::LeftButton) { 225 | camera.leftRightRound((x0 - x) * 0.01); 226 | camera.upDownRound((y0 - y) * 0.005); 227 | } 228 | else if (e->buttons() & Qt::RightButton) { 229 | Vector3 previousAt = camera.getAt(); 230 | Vector3 direction = camera.getAt() - camera.getEye(); 231 | double currentDist = 0.002f * Norm(direction); 232 | camera.backForth((y - y0) * currentDist); 233 | camera.setAt(previousAt); 234 | } 235 | else if (e->buttons() & Qt::MiddleButton) { 236 | camera.leftRightPlane((x - x0) * 1.0); 237 | camera.upDownPlane((y - y0) * 1.0); 238 | } 239 | 240 | x0 = e->globalPosition().x(); 241 | y0 = e->globalPosition().y(); 242 | 243 | update(); 244 | } 245 | 246 | void GlaciersWidget::mouseReleaseEvent(QMouseEvent*) 247 | { 248 | update(); 249 | } 250 | 251 | void GlaciersWidget::wheelEvent(QWheelEvent* e) 252 | { 253 | int numDegrees = e->angleDelta().y() / 8; 254 | int numSteps = numDegrees / 15; 255 | 256 | if (!(e->modifiers()&Qt::ShiftModifier)) { 257 | Vector3 direction = camera.getAt() - camera.getEye(); 258 | double currentDist = Norm(direction); 259 | direction = Normalized(direction); 260 | double speed = 0.1*currentDist; 261 | 262 | camera.setEye(camera.getEye() + direction * speed * numSteps); 263 | } 264 | 265 | update(); 266 | } 267 | -------------------------------------------------------------------------------- /code/appRender/glacierswidget.h: -------------------------------------------------------------------------------- 1 | #ifndef GLACIERSWIDGET_H 2 | #define GLACIERSWIDGET_H 3 | 4 | #include "glew.h" 5 | #include 6 | #include 7 | #include 8 | #include "scalarfield2.h" 9 | 10 | 11 | class GlaciersWidget : public QOpenGLWidget 12 | { 13 | Q_OBJECT 14 | 15 | public: 16 | GlaciersWidget(QWidget* = nullptr); 17 | ~GlaciersWidget(); 18 | 19 | void SetCamera(const Camera& cam) { camera = cam; }; 20 | Camera GetCamera() { return camera; } 21 | 22 | void setHeightfield(const ScalarField2& hf, bool centerCamera = true); 23 | void setTexture(const QImage& img); 24 | 25 | public slots: 26 | virtual void mousePressEvent(QMouseEvent*); 27 | virtual void mouseMoveEvent(QMouseEvent*); 28 | virtual void mouseReleaseEvent(QMouseEvent*); 29 | virtual void wheelEvent(QWheelEvent* event); 30 | 31 | signals: 32 | void _signalEditSceneLeft(const Vector3&); 33 | void _signalEditSceneRight(const Vector3&); 34 | 35 | protected: 36 | virtual void initializeGL(); 37 | virtual void resizeGL(int, int); 38 | virtual void paintGL(); 39 | 40 | protected: 41 | 42 | // OpenGL render 43 | Box3 terrainBBox; 44 | GLuint shader = 0; 45 | GLuint meshVAO = 0; 46 | GLuint bufferVerts = 0, bufferIndices = 0; 47 | GLuint numTriangles = 0; 48 | GLuint texId = 0; 49 | GLuint skyboxShader = 0; 50 | GLuint skyboxVAO = 0; 51 | 52 | // Camera 53 | Camera camera; 54 | bool MoveAt = false; 55 | int x0 = 0, y0 = 0; 56 | Vector3 currentAt = Vector3(0); 57 | Vector3 toAt = Vector3(0); 58 | int stepAt = 0; 59 | 60 | }; 61 | 62 | #endif // GLACIERSWIDGET_H 63 | -------------------------------------------------------------------------------- /code/appRender/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QApplication a(argc, argv); 9 | 10 | QSurfaceFormat f; 11 | f.setVersion(4, 3); 12 | f.setProfile(QSurfaceFormat::CoreProfile); 13 | QSurfaceFormat::setDefaultFormat(f); 14 | 15 | MainWindow w; 16 | w.show(); 17 | return a.exec(); 18 | } 19 | -------------------------------------------------------------------------------- /code/appRender/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include "vectorfield2.h" 4 | #include 5 | #include 6 | 7 | 8 | MainWindow::MainWindow(QWidget *parent) 9 | : QMainWindow(parent) 10 | , ui(new Ui::MainWindow) 11 | { 12 | ui->setupUi(this); 13 | 14 | glaciersWidget = new GlaciersWidget(); 15 | QGridLayout* GLlayout = new QGridLayout; 16 | GLlayout->addWidget(glaciersWidget, 0, 0); 17 | GLlayout->setContentsMargins(0, 0, 0, 0); 18 | ui->glWidget->setLayout(GLlayout); 19 | 20 | glaciersWidget->SetCamera(Camera(Vector3(-10.0, -10.0, 10.0), Vector3(0.0, 0.0, 0.0))); 21 | features = nullptr; 22 | 23 | createActions(); 24 | } 25 | 26 | MainWindow::~MainWindow() 27 | { 28 | if (features) delete features; 29 | delete ui; 30 | } 31 | 32 | void MainWindow::createActions() 33 | { 34 | connect(ui->btnOpenFolder, SIGNAL(clicked()), this, SLOT(openFolder())); 35 | connect(ui->btnLoadTerrain, SIGNAL(clicked()), this, SLOT(loadTerrain())); 36 | connect(ui->btnUpdateFeatures, SIGNAL(clicked()), this, SLOT(updateFeatures())); 37 | 38 | connect(&featuresThread, SIGNAL(textureUpdate(const QImage&)), this, SLOT(updateTexture(const QImage&))); 39 | connect(&featuresThread, SIGNAL(statusUpdate(const QString&)), this, SLOT(setStatusText(const QString&))); 40 | connect(&featuresThread, SIGNAL(finished()), this, SLOT(finishedFeatures())); 41 | } 42 | 43 | void MainWindow::openFolder() 44 | { 45 | QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), 46 | "", QFileDialog::ShowDirsOnly); 47 | if (!dir.isEmpty()) { 48 | ui->mapsFolder->setText(dir); 49 | } 50 | } 51 | 52 | void MainWindow::loadTerrain() 53 | { 54 | QString dir = QDir::cleanPath(ui->mapsFolder->text()) + QDir::separator(); 55 | QString demFile = dir + "dem.png"; 56 | QString hiFile = dir + "dem-hires.png"; 57 | QString iceFile = dir + "ice.png"; 58 | QString elaFile = dir + "ela.png"; 59 | 60 | if (QFile::exists(demFile) && QFile::exists(iceFile) && QFile::exists(elaFile)) { 61 | 62 | statusBar()->showMessage("Loading terrain..."); 63 | 64 | if (features) delete features; 65 | features = new FeaturesPainter(); 66 | features->TEXTURE_RES = ui->sbTexResolution->value(); 67 | 68 | double tw = ui->sbTerrainWidth->value(); 69 | double th = ui->sbTerrainHeight->value(); 70 | features->terrainBox = Box2(Vector2(0), Vector2(tw*1000.0, th*1000.0)); 71 | 72 | QImage imgDemLow(demFile); 73 | QImage imgDemHi; 74 | if (QFile::exists(hiFile)) 75 | imgDemHi.load(hiFile); 76 | else 77 | imgDemHi.load(demFile); 78 | 79 | // low res 80 | features->bedLow = ScalarField2(features->terrainBox, imgDemLow.mirrored(false, true), 0, 0.1*(256*256-1), true); 81 | features->iceLow = ScalarField2(features->terrainBox, QImage(iceFile), 0.0, (256 * 256) - 1.0, false); 82 | GlacierTerrain terrainLow(features->bedLow, features->iceLow); 83 | features->hfLow = features->bedLow + features->iceLow; 84 | 85 | // hi res 86 | features->bedHi = ScalarField2(features->terrainBox, imgDemHi.mirrored(false, true), 0, 0.1 * (256 * 256 - 1), true); 87 | features->iceHi = terrainLow.remapIceSurface(features->bedHi); 88 | features->hfHi = features->bedHi + features->iceHi; 89 | 90 | // above ELA texture 91 | features->accumArea = ScalarField2(features->terrainBox, QImage(elaFile), 0.0, 1.0, true); 92 | features->accumArea.smooth(1); 93 | features->accumArea = features->accumArea.setResolution(features->hfHi.getSizeX(), features->hfHi.getSizeY()); 94 | 95 | // create base texture 96 | features->imgBase = features->baseTexture(features->hfHi, features->iceHi, features->accumArea); 97 | 98 | // update viewer 99 | glaciersWidget->setHeightfield(features->hfHi); 100 | glaciersWidget->setTexture(features->imgBase); 101 | glaciersWidget->update(); 102 | 103 | statusBar()->showMessage("Terrain loaded"); 104 | } 105 | else { 106 | statusBar()->showMessage("ERROR: Incorrect folder or missing map files"); 107 | } 108 | } 109 | 110 | void MainWindow::updateFeatures() 111 | { 112 | ui->btnUpdateFeatures->setEnabled(false); 113 | ui->btnUpdateFeatures->setText("Placing features..."); 114 | statusBar()->showMessage("Placing features..."); 115 | 116 | featuresThread.setUI(ui); 117 | featuresThread.setPainter(features); 118 | featuresThread.start(); 119 | } 120 | 121 | void MainWindow::finishedFeatures() 122 | { 123 | ui->btnUpdateFeatures->setText("Update features"); 124 | ui->btnUpdateFeatures->setEnabled(true); 125 | statusBar()->showMessage("Done!", 5000); 126 | } 127 | 128 | void MainWindow::updateTexture(const QImage &img) 129 | { 130 | // update viewer 131 | glaciersWidget->setTexture(img.mirrored(false, true)); 132 | glaciersWidget->update(); 133 | } 134 | 135 | void MainWindow::setStatusText(const QString& s) 136 | { 137 | statusBar()->showMessage(s); 138 | } 139 | 140 | void FeaturesWorker::run() 141 | { 142 | emit statusUpdate("Placing features..."); 143 | 144 | // Final texture 145 | QImage imgFeatures(features->imgBase.mirrored(false, true)); 146 | 147 | // declarations 148 | QString dir = QDir::cleanPath(ui->mapsFolder->text()) + QDir::separator(); 149 | 150 | // replicability 151 | features->rne = std::minstd_rand(133742); 152 | 153 | // maps derived from heightfield, only need to compute once unless terrain reloaded 154 | if (features->flowDirField.getSizeX()*features->flowDirField.getSizeY() <= 0) { 155 | // load and compute additional maps that will be used during feature placement 156 | features->flowDirField = features->hfLow.gradientField(); 157 | features->slopeField = ScalarField2(features->terrainBox, features->flowDirField.getSizeX(), features->flowDirField.getSizeY(), 0); 158 | for (int i = 0; i < features->flowDirField.getSizeX(); i++) { 159 | for (int j = 0; j < features->flowDirField.getSizeY(); j++) { 160 | double n = Norm(features->flowDirField.at(i, j)); 161 | features->slopeField(i, j) = n; 162 | if (n > 1e-6) features->flowDirField(i, j) = -features->flowDirField.at(i, j) / n; 163 | else features->flowDirField(i, j) = Vector2(0, 0); 164 | } 165 | } 166 | } 167 | features->valleyDist = ScalarField2(features->terrainBox, QImage(dir + "valleydist.png"), 0.0, 256 * 256 - 1.0, false); 168 | features->glacierIds = QImage(dir + "segmentation.png"); 169 | 170 | // alpha map for crevasses 171 | ScalarField2 alphaCrevasses(features->iceHi); 172 | alphaCrevasses.step(0, 2.0); 173 | alphaCrevasses = alphaCrevasses.setResolution(imgFeatures.width(), imgFeatures.height()); 174 | 175 | // SERACS AND ICEFALLS: perturb heightfield 176 | if (ui->renderSeracs->isChecked()) { 177 | emit statusUpdate("Seracs..."); 178 | 179 | ScalarField2 seracsField(features->terrainBox, QImage(dir + "seracs.png"), 0.0, 256 * 256 - 1.0, true); 180 | ScalarField2 icefalls = ScalarField2(features->terrainBox, QImage(dir + "icefallsRaw.png"), 0.0, 256 * 256 - 1.0, true); 181 | double seracDisp = ui->sbSeracDisp->value(); 182 | 183 | for (int i = 0; i < seracsField.getSizeX() * seracsField.getSizeY(); i++) { 184 | seracsField[i] = std::min(1.0, seracsField[i] + icefalls[i]); 185 | } 186 | ScalarField2 iceSeracs = features->seracs(seracsField, seracDisp); 187 | 188 | ScalarField2 hfSeracs = features->bedHi + iceSeracs; 189 | //glaciersWidget->setHeightfield(hfSeracs, false); 190 | imgFeatures = features->baseTexture(hfSeracs, features->iceHi, features->accumArea).mirrored(false, true); 191 | emit textureUpdate(imgFeatures); 192 | } 193 | 194 | // OGIVES: ondulations, need to draw before moraines as they will cover its margins (which have interpolation artifacts on distance field) 195 | if (ui->renderOgives->isChecked()) { 196 | emit statusUpdate("Ogives..."); 197 | 198 | ScalarField2 ogiveDistSrc = ScalarField2(features->terrainBox, QImage(dir + "ogivesDistSrc.png"), 0.0, (256. * 256.) - 1.0, false); 199 | ScalarField2 ogiveDistCtr = ScalarField2(features->terrainBox, QImage(dir + "ogivesDistCtr.png"), 0.0, (256. * 256.) - 1.0, false); 200 | QImage ogiveId = QImage(dir + "ogivesId.png"); 201 | double scaleLen = ui->sbOgiveLength->value(); 202 | double scaleFreq = ui->sbOgiveFreq->value(); 203 | 204 | ScalarField2 ogiveStrength(features->terrainBox, imgFeatures.width(), imgFeatures.height(), 0); 205 | QImage ogiveMap = features->ogives(ogiveDistSrc, ogiveDistCtr, ogiveId, scaleLen, scaleFreq, imgFeatures, ogiveStrength); 206 | emit textureUpdate(imgFeatures); 207 | 208 | // prevent crevasses from forming on top of ogives (for better illustration) 209 | ogiveStrength = ogiveStrength.setResolution(features->iceLow.getSizeX(), features->iceLow.getSizeY()); 210 | ogiveStrength.smooth(1); 211 | ogiveStrength.step(0.1, 0.4); 212 | for (int i = 0; i < alphaCrevasses.getSizeX(); i++) { 213 | for (int j = 0; j < alphaCrevasses.getSizeY(); j++) { 214 | Vector2 p = alphaCrevasses.domainCoords(i, j); 215 | alphaCrevasses(i, j) = std::max(0.0, std::min(1.0, alphaCrevasses.at(i, j) - ogiveStrength.value(p))); 216 | } 217 | } 218 | } 219 | 220 | // MORAINES: draw moraine paths 221 | if (ui->renderMoraines->isChecked()) { 222 | emit statusUpdate("Moraines..."); 223 | 224 | //const Vector3 moraineColor(164 / 255.0, 151 / 255.0, 125 / 255.0); 225 | const Vector3 moraineColor(0.9 * 171 / 255.0, 0.9 * 167 / 255.0, 0.9 * 164 / 255.0); 226 | double scale = ui->sbMoraineScale->value(); 227 | QImage moraineMap = features->moraines(moraineColor, scale, imgFeatures); 228 | emit textureUpdate(imgFeatures); 229 | 230 | // prevent crevasses from forming on top of moraines (for better illustration) 231 | for (int i = 0; i < alphaCrevasses.getSizeX(); i++) { 232 | for (int j = 0; j < alphaCrevasses.getSizeY(); j++) { 233 | alphaCrevasses(i, j) = std::max(0.0, std::min(1.0, alphaCrevasses.at(i, j) 234 | - double(moraineMap.pixelColor(i, moraineMap.height() - 1 - j).red()) / 255.0)); 235 | } 236 | } 237 | } 238 | 239 | // CREVASSES: texture bombing 240 | if (ui->renderCrevasses->isChecked()) { 241 | double modDensityCT = ui->crevDensityT->value() / 10.0; 242 | double modDensityCL = ui->crevDensityL->value() / 10.0; 243 | double modDensityCM = ui->crevDensityT->value() / 10.0; 244 | double crevasseScale = ui->sbCrevScale->value(); 245 | ScalarField2 crevasseField; 246 | 247 | emit statusUpdate("Transverse crevasses..."); 248 | crevasseField = ScalarField2(features->terrainBox, QImage(dir + "crevassesTrans.png"), 0.0, 256 * 256 - 1.0, true); 249 | crevasseField.normalize(); 250 | crevasseField *= modDensityCT; 251 | for (int i = 0; i < crevasseField.getSizeX() * crevasseField.getSizeY(); i++) { 252 | // fewer crevasses where no much ice above ELA 253 | crevasseField[i] *= std::min(1.0, features->iceLow[i] / 10.0) * features->accumArea[i] + (1 - features->accumArea[i]); 254 | } 255 | QImage crevTransMap = features->crevasses(crevasseField, 0, crevasseScale, alphaCrevasses, imgFeatures); 256 | emit textureUpdate(imgFeatures); 257 | 258 | emit statusUpdate("Longitudinal crevasses..."); 259 | crevasseField = ScalarField2(features->terrainBox, QImage(dir + "crevassesLong.png"), 0.0, 256 * 256 - 1.0, true); 260 | crevasseField.normalize(); 261 | crevasseField *= modDensityCL; 262 | QImage crevLongMap = features->crevasses(crevasseField, 1, crevasseScale, alphaCrevasses, imgFeatures); 263 | emit textureUpdate(imgFeatures); 264 | 265 | emit statusUpdate("Marginal crevasses..."); 266 | crevasseField = ScalarField2(features->terrainBox, QImage(dir + "crevassesMargin.png"), 0.0, 256 * 256 - 1.0, true); 267 | crevasseField.normalize(); 268 | crevasseField *= modDensityCM; 269 | QImage crevMarginMap = features->crevasses(crevasseField, 2, crevasseScale, alphaCrevasses, imgFeatures); 270 | emit textureUpdate(imgFeatures); 271 | } 272 | 273 | // ICEFALLS: as crevasses 274 | if (ui->renderIcefalls->isChecked()) { 275 | emit statusUpdate("Icefalls..."); 276 | double modDensityCI = ui->crevDensityI->value() / 10.0; 277 | ScalarField2 icefalls = ScalarField2(features->terrainBox, QImage(dir + "icefallsRaw.png"), 0.0, 256 * 256 - 1.0, true); 278 | icefalls.normalize(); 279 | icefalls *= modDensityCI; 280 | QImage crevIcefallsMap = features->crevasses(icefalls, 3, 1.0, alphaCrevasses, imgFeatures); 281 | emit textureUpdate(imgFeatures); 282 | } 283 | 284 | // RIMAYES: fixed positions 285 | if (ui->renderRimayes->isChecked()) { 286 | emit statusUpdate("Rimayes..."); 287 | ScalarField2 rimayeField = ScalarField2(features->terrainBox, QImage(dir + "rimayes.png"), 0.0, 256 * 256 - 1.0, true); 288 | QImage rimayeMap = features->rimayes(rimayeField, imgFeatures); 289 | emit textureUpdate(imgFeatures); 290 | } 291 | 292 | emit finished(); 293 | } 294 | -------------------------------------------------------------------------------- /code/appRender/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include "glacierswidget.h" 7 | #include "glacierterrain.h" 8 | #include "featurespainter.h" 9 | 10 | QT_BEGIN_NAMESPACE 11 | namespace Ui { class MainWindow; } 12 | QT_END_NAMESPACE 13 | 14 | 15 | class FeaturesWorker : public QThread 16 | { 17 | Q_OBJECT 18 | void run() override; 19 | signals: 20 | void textureUpdate(const QImage& img); 21 | void statusUpdate(const QString& s); 22 | void finished(); 23 | public: 24 | void setUI(Ui::MainWindow* ui) { this->ui = ui; } 25 | void setPainter(FeaturesPainter* painter) { features = painter; } 26 | private: 27 | Ui::MainWindow* ui; 28 | FeaturesPainter* features; 29 | }; 30 | 31 | 32 | class MainWindow : public QMainWindow 33 | { 34 | Q_OBJECT 35 | 36 | public: 37 | MainWindow(QWidget *parent = nullptr); 38 | ~MainWindow(); 39 | 40 | public slots: 41 | void openFolder(); 42 | void loadTerrain(); 43 | void updateFeatures(); 44 | void finishedFeatures(); 45 | 46 | void updateTexture(const QImage& img); 47 | void setStatusText(const QString& s); 48 | 49 | private: 50 | void createActions(); 51 | 52 | private: 53 | Ui::MainWindow *ui; 54 | GlaciersWidget *glaciersWidget; 55 | FeaturesWorker featuresThread; 56 | 57 | FeaturesPainter *features; 58 | }; 59 | #endif // MAINWINDOW_H 60 | -------------------------------------------------------------------------------- /code/appRender/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 1618 10 | 1137 11 | 12 | 13 | 14 | Glacier features demo 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 425 23 | 16777215 24 | 25 | 26 | 27 | QFrame::StyledPanel 28 | 29 | 30 | QFrame::Raised 31 | 32 | 33 | 34 | 35 | 36 | 37 | 0 38 | 0 39 | 40 | 41 | 42 | 43 | 400 44 | 0 45 | 46 | 47 | 48 | 49 | 400 50 | 16777215 51 | 52 | 53 | 54 | Input maps 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | data/maps/aiguestortesCrop 63 | 64 | 65 | true 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 28 74 | 28 75 | 76 | 77 | 78 | ... 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | DEM size (km) 90 | 91 | 92 | 93 | 94 | 95 | 96 | 1000.000000000000000 97 | 98 | 99 | 12.000000000000000 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 0 108 | 0 109 | 110 | 111 | 112 | x 113 | 114 | 115 | 116 | 117 | 118 | 119 | 1000.000000000000000 120 | 121 | 122 | 12.000000000000000 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 0 133 | 32 134 | 135 | 136 | 137 | Load 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | Output 148 | 149 | 150 | 151 | 152 | 153 | Texture resolution (m/pixel) 154 | 155 | 156 | 157 | 158 | 159 | 160 | 20.000000000000000 161 | 162 | 163 | 0.250000000000000 164 | 165 | 166 | 1.250000000000000 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Features 177 | 178 | 179 | 180 | 181 | 182 | Qt::Vertical 183 | 184 | 185 | QSizePolicy::Fixed 186 | 187 | 188 | 189 | 20 190 | 20 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | Render features 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | Rimayes 208 | 209 | 210 | true 211 | 212 | 213 | 214 | 215 | 216 | 217 | Crevasses 218 | 219 | 220 | true 221 | 222 | 223 | 224 | 225 | 226 | 227 | Ogives 228 | 229 | 230 | true 231 | 232 | 233 | 234 | 235 | 236 | 237 | Moraines 238 | 239 | 240 | true 241 | 242 | 243 | 244 | 245 | 246 | 247 | Icefalls 248 | 249 | 250 | true 251 | 252 | 253 | 254 | 255 | 256 | 257 | Seracs 258 | 259 | 260 | true 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | Qt::Vertical 270 | 271 | 272 | QSizePolicy::Fixed 273 | 274 | 275 | 276 | 20 277 | 15 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | Crevasse scale 286 | 287 | 288 | 289 | 290 | 291 | 292 | 10.000000000000000 293 | 294 | 295 | 0.100000000000000 296 | 297 | 298 | 1.000000000000000 299 | 300 | 301 | 302 | 303 | 304 | 305 | - transverse density 306 | 307 | 308 | 309 | 310 | 311 | 312 | 20 313 | 314 | 315 | 6 316 | 317 | 318 | Qt::Horizontal 319 | 320 | 321 | QSlider::NoTicks 322 | 323 | 324 | 325 | 326 | 327 | 328 | - longitudinal density 329 | 330 | 331 | 332 | 333 | 334 | 335 | 20 336 | 337 | 338 | 10 339 | 340 | 341 | Qt::Horizontal 342 | 343 | 344 | QSlider::NoTicks 345 | 346 | 347 | 348 | 349 | 350 | 351 | - marginal density 352 | 353 | 354 | 355 | 356 | 357 | 358 | 20 359 | 360 | 361 | 14 362 | 363 | 364 | Qt::Horizontal 365 | 366 | 367 | QSlider::NoTicks 368 | 369 | 370 | 371 | 372 | 373 | 374 | - icefalls 375 | 376 | 377 | 378 | 379 | 380 | 381 | 20 382 | 383 | 384 | 8 385 | 386 | 387 | Qt::Horizontal 388 | 389 | 390 | QSlider::NoTicks 391 | 392 | 393 | 394 | 395 | 396 | 397 | Qt::Vertical 398 | 399 | 400 | QSizePolicy::Fixed 401 | 402 | 403 | 404 | 20 405 | 15 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | Serac displacement 414 | 415 | 416 | 417 | 418 | 419 | 420 | 10.000000000000000 421 | 422 | 423 | 0.100000000000000 424 | 425 | 426 | 1.000000000000000 427 | 428 | 429 | 430 | 431 | 432 | 433 | Qt::Vertical 434 | 435 | 436 | QSizePolicy::Fixed 437 | 438 | 439 | 440 | 20 441 | 15 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | Moraines width 450 | 451 | 452 | 453 | 454 | 455 | 456 | 4.000000000000000 457 | 458 | 459 | 0.100000000000000 460 | 461 | 462 | 1.000000000000000 463 | 464 | 465 | 466 | 467 | 468 | 469 | Qt::Vertical 470 | 471 | 472 | QSizePolicy::Fixed 473 | 474 | 475 | 476 | 20 477 | 15 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | Ogives length 486 | 487 | 488 | 489 | 490 | 491 | 492 | 10.000000000000000 493 | 494 | 495 | 0.100000000000000 496 | 497 | 498 | 1.000000000000000 499 | 500 | 501 | 502 | 503 | 504 | 505 | Ogives frequency 506 | 507 | 508 | 509 | 510 | 511 | 512 | 4.000000000000000 513 | 514 | 515 | 0.100000000000000 516 | 517 | 518 | 1.000000000000000 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 0 530 | 32 531 | 532 | 533 | 534 | Update features 535 | 536 | 537 | 538 | 539 | 540 | 541 | Qt::Vertical 542 | 543 | 544 | 545 | 20 546 | 622 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 0 563 | 0 564 | 1618 565 | 26 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | -------------------------------------------------------------------------------- /code/appSimulation/glacierswidget.h: -------------------------------------------------------------------------------- 1 | #ifndef GLACIERSWIDGET_H 2 | #define GLACIERSWIDGET_H 3 | 4 | #include "glew.h" 5 | #include 6 | #include 7 | #include 8 | #include "scalarfield2.h" 9 | #include "glacierterrain.h" 10 | 11 | 12 | class GlaciersWidget : public QOpenGLWidget 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | 18 | enum class GlacierTextureType { 19 | NONE, 20 | ELA, 21 | THICKNESS, 22 | GRADIENT, 23 | STRESS, 24 | DIFFUSIVITY, 25 | DIFFUSIVITY_RAW, 26 | SPEED_DEFORM, 27 | SPEED_SLIP, 28 | SPEED_DOMINANT_TYPE, 29 | VELOCITY, 30 | ICE_DIFFERENCE, 31 | FEAT_SHADER 32 | }; 33 | 34 | 35 | GlaciersWidget(QWidget* = nullptr); 36 | ~GlaciersWidget(); 37 | 38 | void setGlacierTerrain(GlacierTerrain* glacier); 39 | 40 | void SetCamera(const Camera& cam) { camera = cam; }; 41 | Camera GetCamera() { return camera; } 42 | 43 | void setRenderType(bool voxels, bool bedrock, bool ice); 44 | void setTextureType(GlacierTextureType texType) { activeTexType = texType; updateTexture(); } 45 | void updateBedrockTexture(); 46 | void updateTexture(); 47 | const QImage& getTexture() { return texImg; }; 48 | void setTexture(const QImage& img); 49 | 50 | void setSteadyCondition(double d) { dIceSteadyCondition = d; } 51 | double getLastdIce() { return simuldIce; } 52 | void setMinimumSimulationYears(double y) { minSimulYears = y; } 53 | 54 | void runSimulation(bool pauseStationary); // remember to config parameters before this call, directly on the glacier heightfield 55 | void pauseSimulation(); 56 | void resetSimulation(); 57 | 58 | QImage shadedBedrock(); 59 | 60 | 61 | public slots: 62 | virtual void mousePressEvent(QMouseEvent*); 63 | virtual void mouseMoveEvent(QMouseEvent*); 64 | virtual void mouseReleaseEvent(QMouseEvent*); 65 | virtual void wheelEvent(QWheelEvent* event); 66 | 67 | void simulationTask(); 68 | 69 | void updateGeometry(); 70 | void reloadShader(); 71 | void updateBrushRadius(double d) { brushRadius = d; }; 72 | 73 | signals: 74 | void _signalEditSceneLeft(const Vector3&); 75 | void _signalEditSceneRight(const Vector3&); 76 | 77 | protected: 78 | 79 | struct CubeInstanceData { 80 | float x, y, z; 81 | float height; 82 | }; 83 | 84 | virtual void initializeGL(); 85 | virtual void resizeGL(int, int); 86 | virtual void paintGL(); 87 | 88 | void initializeCubeVAO(); 89 | 90 | void fillCubeInstances(const ScalarField2& fieldBottom, const ScalarField2& fieldTop, std::vector& cubeInstances); 91 | 92 | void drawVoxels(); 93 | 94 | 95 | protected: 96 | 97 | // Glacier simulator 98 | GlacierTerrain* glacierTerrain = nullptr; 99 | Box3 terrainBBox; 100 | int nx = 0, ny = 0; 101 | ScalarField2 icePrev; 102 | const unsigned int SIMUL_STEPS_BATCH = 16; 103 | QTimer* simulQtimer = nullptr; 104 | 105 | // OpenGL render 106 | GLuint shaderCubes = 0; 107 | GLuint vaoCube = 0; 108 | GLuint vboInstanceData = 0; 109 | GLuint texBed = 0; 110 | GLuint texId = 0; 111 | GLuint texArrow = 0; 112 | GLuint skyboxShader = 0; 113 | GLuint skyboxVAO = 0; 114 | std::vector cubeInstancesBedrock; 115 | std::vector cubeInstancesIce; 116 | 117 | bool renderAsVoxels, showBedrock, showIce; 118 | ScalarField2 texValues; 119 | QImage texImg; 120 | GlacierTextureType activeTexType; 121 | 122 | // Custom shader data 123 | std::vector datatexBedVals; 124 | std::vector datatexIceVals; 125 | GLuint datatexBedrock = 0; 126 | GLuint datatexIce = 0; 127 | double bedMin, bedMax; 128 | double iceMin, iceMax; 129 | 130 | 131 | // Camera 132 | Camera camera; 133 | bool MoveAt = false; 134 | int x0 = 0, y0 = 0; 135 | Vector3 currentAt = Vector3(0); 136 | Vector3 toAt = Vector3(0); 137 | int stepAt = 0; 138 | 139 | // Timer 140 | std::chrono::time_point start; 141 | int nbframes; 142 | int fps = 0; 143 | 144 | // Simul 145 | double maxSimulYears = 1e5; 146 | double simulYear = 0; 147 | double simulComputeTime = 0; 148 | double simulPerf = 0; 149 | double simuldIce = 0; 150 | double simulCurrMaxIce = 0; 151 | int simulSteps = 0; 152 | bool steadyState = false; 153 | bool simulationRunning = true; 154 | bool pauseWhenSteady = true; 155 | double dIceSteadyCondition = 0.001; 156 | double minSimulYears = 0; 157 | double lastIceVol = 0; 158 | double yearlyIce = 0; 159 | 160 | // Editor 161 | bool validAnchor; 162 | Vector3 anchor; 163 | double brushRadius = 750.0; 164 | }; 165 | 166 | #endif // GLACIERSWIDGET_H 167 | -------------------------------------------------------------------------------- /code/appSimulation/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | 3 | #include 4 | #include 5 | 6 | int main(int argc, char *argv[]) 7 | { 8 | QApplication a(argc, argv); 9 | 10 | QSurfaceFormat f; 11 | f.setVersion(4, 3); 12 | f.setProfile(QSurfaceFormat::CoreProfile); 13 | QSurfaceFormat::setDefaultFormat(f); 14 | 15 | MainWindow w; 16 | w.show(); 17 | return a.exec(); 18 | } 19 | -------------------------------------------------------------------------------- /code/appSimulation/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include 4 | #include 5 | #include 6 | 7 | MainWindow::MainWindow(QWidget *parent) 8 | : QMainWindow(parent) 9 | , ui(new Ui::MainWindow) 10 | { 11 | ui->setupUi(this); 12 | 13 | glaciersWidget = new GlaciersWidget(); 14 | QGridLayout* GLlayout = new QGridLayout; 15 | GLlayout->addWidget(glaciersWidget, 0, 0); 16 | GLlayout->setContentsMargins(0, 0, 0, 0); 17 | ui->glWidget->setLayout(GLlayout); 18 | glaciersWidget->SetCamera(Camera(Vector3(-10.0, -10.0, 10.0), Vector3(0.0, 0.0, 0.0))); 19 | 20 | createActions(); 21 | } 22 | 23 | MainWindow::~MainWindow() 24 | { 25 | delete ui; 26 | } 27 | 28 | void MainWindow::createActions() 29 | { 30 | connect(glaciersWidget, SIGNAL(_signalEditSceneLeft(const Vector3&)), this, SLOT(editingSceneLeft(const Vector3&))); 31 | connect(glaciersWidget, SIGNAL(_signalEditSceneRight(const Vector3&)), this, SLOT(editingSceneRight(const Vector3&))); 32 | 33 | connect(ui->btnLoadScene1, SIGNAL(clicked()), this, SLOT(presetScene1())); 34 | connect(ui->btnLoadScene2, SIGNAL(clicked()), this, SLOT(presetScene2())); 35 | connect(ui->btnLoadScene3, SIGNAL(clicked()), this, SLOT(presetScene3())); 36 | connect(ui->btnLoadScene4, SIGNAL(clicked()), this, SLOT(presetScene4())); 37 | connect(ui->btnLoadScene5, SIGNAL(clicked()), this, SLOT(presetScene5())); 38 | connect(ui->btnLoadScene6, SIGNAL(clicked()), this, SLOT(presetScene6())); 39 | connect(ui->btnLoadScene7, SIGNAL(clicked()), this, SLOT(presetScene7())); 40 | connect(ui->btnLoadScene8, SIGNAL(clicked()), this, SLOT(presetScene8())); 41 | connect(ui->btnLoadScene9, SIGNAL(clicked()), this, SLOT(presetScene9())); 42 | 43 | connect(ui->btnSelectDEM, SIGNAL(clicked()), this, SLOT(selectDEM())); 44 | connect(ui->btnLoadDEM, SIGNAL(clicked()), this, SLOT(loadDEM())); 45 | connect(ui->btnLoadELA, SIGNAL(clicked()), this, SLOT(loadELA())); 46 | connect(ui->btnLoadPrec, SIGNAL(clicked()), this, SLOT(loadPrecipitation())); 47 | connect(ui->btnLoadInitialIce, SIGNAL(clicked()), this, SLOT(loadInitialIce())); 48 | 49 | connect(ui->btnGlacierSimRun, SIGNAL(clicked()), this, SLOT(runGlacierSimulation())); 50 | connect(ui->btnGlacierSimPause, SIGNAL(clicked()), this, SLOT(pauseGlacierSimulation())); 51 | connect(ui->btnGlacierSimReset, SIGNAL(clicked()), this, SLOT(resetGlacierSimulation())); 52 | connect(ui->btnGlacierUpsample, SIGNAL(clicked()), this, SLOT(upscaleGlacier())); 53 | connect(ui->sb_dIce, SIGNAL(valueChanged(double)), this, SLOT(updateSteadyCondition(double))); 54 | 55 | connect(ui->sb_brushRadius, SIGNAL(valueChanged(double)), this, SLOT(updateBrushRadius(double))); 56 | 57 | connect(ui->btnSaveScene, SIGNAL(clicked()), this, SLOT(saveScene())); 58 | 59 | connect(ui->radioButton_Voxels, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 60 | connect(ui->radioButton_Mesh, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 61 | 62 | connect(ui->checkBox_RenderBedrock, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 63 | connect(ui->checkBox_RenderIce, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 64 | 65 | connect(ui->radioButton_ShadingIceNeutral, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 66 | connect(ui->radioButton_ShadingIceELA, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 67 | connect(ui->radioButton_ShadingIceThick, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 68 | connect(ui->radioButton_ShadingIceGradient, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 69 | connect(ui->radioButton_ShadingIceStress, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 70 | connect(ui->radioButton_ShadingIceDiffusivity, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 71 | connect(ui->radioButton_ShadingIceDiffusivityRaw, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 72 | connect(ui->radioButton_ShadingIceUDeform, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 73 | connect(ui->radioButton_ShadingIceUSlip, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 74 | connect(ui->radioButton_ShadingIceUDominant, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 75 | connect(ui->radioButton_ShadingIceDifference, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 76 | connect(ui->radioButton_ShadingFeatShader, SIGNAL(toggled(bool)), this, SLOT(RenderUpdate())); 77 | 78 | connect(ui->btnReloadShader, SIGNAL(clicked()), glaciersWidget, SLOT(reloadShader())); 79 | } 80 | 81 | void MainWindow::presetScene1() 82 | { 83 | loadBedrocks("data/terrains/aiguestortes_20m.png", { 1, 2, 3, 6 }, 30); 84 | loadMapsELA("data/terrains/aiguestortes_sun.png"); 85 | //loadMapsPrecipitation("data/terrains/aiguestortes_precipitation.png"); 86 | ui->checkUseELAMap->setChecked(true); 87 | ui->checkUsePrecMap->setChecked(false); 88 | ui->sb_ELA->setValue(2500); 89 | ui->sb_ELAdev->setValue(200); 90 | ui->sb_AccumRate->setValue(0.002); 91 | ui->sb_AblateRate->setValue(0.001); 92 | } 93 | 94 | void MainWindow::presetScene2() 95 | { 96 | loadBedrocks(QString("data/terrains/ecrins_30m.png"), { 1, 2, 4, 8 }, 84); 97 | loadMapsELA("data/terrains/ecrins_sun.png"); 98 | factorBetaMaps.clear(); 99 | ui->checkUseELAMap->setChecked(true); 100 | ui->checkUsePrecMap->setChecked(false); 101 | ui->sb_ELA->setValue(2600.0); 102 | ui->sb_ELAdev->setValue(200.0); 103 | ui->sb_AccumRate->setValue(0.001); 104 | ui->sb_AblateRate->setValue(0.001); 105 | } 106 | 107 | void MainWindow::presetScene3() 108 | { 109 | loadBedrocks(QString("data/terrains/canyon_10m.png"), { 1, 2, 4, 6, 12 }, 30); 110 | loadMapsELA("data/terrains/canyon_sun.png"); 111 | loadMapsPrecipitation("data/terrains/canyon_precipitation.png"); 112 | ui->checkUseELAMap->setChecked(true); 113 | ui->checkUsePrecMap->setChecked(true); 114 | ui->sb_ELA->setValue(2000.0); 115 | ui->sb_ELAdev->setValue(300.0); 116 | ui->sb_AccumRate->setValue(0.002); 117 | ui->sb_AblateRate->setValue(0.001); 118 | } 119 | 120 | void MainWindow::presetScene4() 121 | { 122 | loadBedrocks("data/terrains/mtperdu_20m.png", { 1, 2, 3, 6 }, 30); 123 | loadMapsELA("data/terrains/mtperdu_sun.png"); 124 | factorBetaMaps.clear(); 125 | ui->checkUseELAMap->setChecked(true); 126 | ui->checkUsePrecMap->setChecked(false); 127 | ui->sb_ELA->setValue(2500); 128 | ui->sb_ELAdev->setValue(300); 129 | ui->sb_AccumRate->setValue(0.002); 130 | ui->sb_AblateRate->setValue(0.001); 131 | } 132 | 133 | void MainWindow::presetScene5() 134 | { 135 | loadBedrocks("data/terrains/sthelens_10m.png", { 2, 4, 8 }, 20); 136 | loadMapsELA("data/terrains/sthelens_sun.png"); 137 | factorBetaMaps.clear(); 138 | ui->checkUseELAMap->setChecked(true); 139 | ui->checkUsePrecMap->setChecked(false); 140 | ui->sb_ELA->setValue(2000); 141 | ui->sb_ELAdev->setValue(200); 142 | ui->sb_AccumRate->setValue(0.005); 143 | ui->sb_AblateRate->setValue(0.001); 144 | } 145 | 146 | void MainWindow::presetScene6() 147 | { 148 | loadBedrocks(QString("data/terrains/ruapehu_25m.png"), { 1, 2, 3, 6 }, 30); 149 | loadMapsELA("data/terrains/ruapehu_sun.png"); 150 | factorBetaMaps.clear(); 151 | ui->checkUseELAMap->setChecked(true); 152 | ui->checkUsePrecMap->setChecked(false); 153 | ui->sb_ELA->setValue(2000); 154 | ui->sb_ELAdev->setValue(300); 155 | ui->sb_AccumRate->setValue(0.003); 156 | ui->sb_AblateRate->setValue(0.001); 157 | } 158 | 159 | void MainWindow::presetScene7() 160 | { 161 | loadBedrocks(QString("data/terrains/smokies_10m.png"), { 1, 2, 4, 6, 12 }, 30); 162 | loadMapsELA("data/terrains/smokies_sun.png"); 163 | factorBetaMaps.clear(); 164 | ui->checkUseELAMap->setChecked(true); 165 | ui->checkUsePrecMap->setChecked(false); 166 | ui->sb_ELA->setValue(1500); 167 | ui->sb_ELAdev->setValue(0); 168 | ui->sb_AccumRate->setValue(0.001); 169 | ui->sb_AblateRate->setValue(0.001); 170 | } 171 | 172 | void MainWindow::presetScene8() 173 | { 174 | loadBedrocks(QString("data/terrains/kungsleden_25m.png"), { 1, 3, 6, 12 }, 60); 175 | loadMapsELA("data/terrains/kungsleden_sun.png"); 176 | factorBetaMaps.clear(); 177 | ui->checkUseELAMap->setChecked(true); 178 | ui->checkUsePrecMap->setChecked(false); 179 | ui->sb_ELA->setValue(1400); 180 | ui->sb_ELAdev->setValue(0); 181 | ui->sb_AccumRate->setValue(0.005); 182 | ui->sb_AblateRate->setValue(0.001); 183 | } 184 | 185 | void MainWindow::presetScene9() 186 | { 187 | loadBedrocks(QString("data/terrains/chartreuse_edit_20m.png"), { 1, 2, 5, 10 }, 50); 188 | loadMapsELA("data/terrains/chartreuse_sun.png"); 189 | factorBetaMaps.clear(); 190 | ui->checkUseELAMap->setChecked(true); 191 | ui->checkUsePrecMap->setChecked(false); 192 | ui->sb_ELA->setValue(1400); 193 | ui->sb_ELAdev->setValue(100); 194 | ui->sb_AccumRate->setValue(0.003); 195 | ui->sb_AblateRate->setValue(0.001); 196 | } 197 | 198 | void MainWindow::selectDEM() 199 | { 200 | QString filename = QFileDialog::getOpenFileName(nullptr, 201 | "Choose a filename to load", "", "png file (*.png)"); 202 | if (!filename.isNull()) { 203 | demFilename = filename; 204 | ui->btnSelectDEM->setText(QFile(filename).fileName()); 205 | } 206 | } 207 | 208 | void MainWindow::loadDEM() 209 | { 210 | if (demFilename.length() < 1) return; 211 | 212 | double demKM = ui->sb_DEM_km->value(); 213 | double hmin = ui->sb_DEM_hmin->value(); 214 | double hmax = ui->sb_DEM_hmax->value(); 215 | std::vector scales; 216 | QStringList ds = ui->le_DEM_scales->text().split(","); 217 | for (QString ss : ds) { 218 | bool ok = false; 219 | int s = ss.toInt(&ok); 220 | if (!ok) s = 1; 221 | scales.push_back(s); 222 | } 223 | 224 | loadBedrocks(demFilename, scales, demKM, { hmin, hmax }); 225 | } 226 | 227 | void MainWindow::loadELA() 228 | { 229 | QString filename = QFileDialog::getOpenFileName(nullptr, 230 | "Choose a filename to load", "", "png file (*.png)"); 231 | if (!filename.isNull()) { 232 | loadMapsELA(filename); 233 | } 234 | } 235 | 236 | void MainWindow::loadPrecipitation() 237 | { 238 | QString filename = QFileDialog::getOpenFileName(nullptr, 239 | "Choose a filename to load", "", "png file (*.png)"); 240 | if (!filename.isNull()) { 241 | loadMapsPrecipitation(filename); 242 | } 243 | } 244 | 245 | void MainWindow::loadInitialIce() 246 | { 247 | QString filename = QFileDialog::getOpenFileName(nullptr, 248 | "Choose a filename to load", "", "png file (*.png)"); 249 | if (!filename.isNull()) { 250 | loadMapsIce(filename); 251 | } 252 | } 253 | 254 | void MainWindow::Render(bool resetCamera) 255 | { 256 | if (glacierTerrain.isEmpty()) 257 | return; 258 | 259 | if (resetCamera) { 260 | Box3 tbox = glacierTerrain.getBoundingBox(); 261 | Camera cc = Camera::View(tbox); 262 | glaciersWidget->SetCamera(cc); 263 | } 264 | 265 | // Geometry 266 | bool b = ui->checkBox_RenderBedrock->isChecked(); 267 | bool i = ui->checkBox_RenderIce->isChecked(); 268 | if (ui->radioButton_Voxels->isChecked()) { 269 | glaciersWidget->setRenderType(true, b, i); 270 | } 271 | else { 272 | glaciersWidget->setRenderType(false, b, i); 273 | } 274 | 275 | // Texture 276 | if (ui->radioButton_ShadingIceNeutral->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::NONE); 277 | if (ui->radioButton_ShadingIceELA->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::ELA); 278 | if (ui->radioButton_ShadingIceThick->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::THICKNESS); 279 | if (ui->radioButton_ShadingIceGradient->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::GRADIENT); 280 | if (ui->radioButton_ShadingIceStress->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::STRESS); 281 | if (ui->radioButton_ShadingIceDiffusivity->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::DIFFUSIVITY); 282 | if (ui->radioButton_ShadingIceDiffusivityRaw->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::DIFFUSIVITY_RAW); 283 | if (ui->radioButton_ShadingIceUDeform->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::SPEED_DEFORM); 284 | if (ui->radioButton_ShadingIceUSlip->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::SPEED_SLIP); 285 | if (ui->radioButton_ShadingIceUDominant->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::SPEED_DOMINANT_TYPE); 286 | if (ui->radioButton_ShadingIceDifference->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::ICE_DIFFERENCE); 287 | if (ui->radioButton_ShadingFeatShader->isChecked()) glaciersWidget->setTextureType(GlaciersWidget::GlacierTextureType::FEAT_SHADER); 288 | 289 | glaciersWidget->update(); 290 | } 291 | 292 | void MainWindow::RenderUpdate() 293 | { 294 | Render(false); 295 | } 296 | 297 | void MainWindow::editingSceneLeft(const Vector3& anchor) 298 | { 299 | Vector2 ctr = Vector2(anchor[0], anchor[1]); 300 | if (ui->radioButton_brushBed->isChecked()) { 301 | glacierTerrain.GetBedrock().addGaussian(ctr, brushRadius, rockStrength); 302 | glacierTerrain.updateBedrockGPU(); 303 | glaciersWidget->updateGeometry(); 304 | } 305 | else if (ui->radioButton_brushIce->isChecked()) { 306 | glacierTerrain.GetIce().addGaussian(ctr, brushRadius, iceStrength); 307 | glacierTerrain.updateIceGPU(); 308 | glaciersWidget->updateGeometry(); 309 | } 310 | else if (ui->radioButton_brushELA->isChecked()) { 311 | glacierTerrain.GetELA().addGaussian(ctr, brushRadius, elaStrength); 312 | glacierTerrain.updateELAGPU(); 313 | updatePreviewELA(glacierTerrain.GetELA()); 314 | } 315 | else if (ui->radioButton_brushPrec->isChecked()) { 316 | glacierTerrain.GetAccumRate().addGaussian(ctr, brushRadius, precStrenth); 317 | glacierTerrain.updateAccumRateGPU(); 318 | updatePreviewAccum(glacierTerrain.GetAccumRate()); 319 | } 320 | 321 | Render(false); 322 | } 323 | 324 | void MainWindow::editingSceneRight(const Vector3& anchor) 325 | { 326 | Vector2 ctr = Vector2(anchor[0], anchor[1]); 327 | if (ui->radioButton_brushBed->isChecked()) { 328 | glacierTerrain.GetBedrock().addGaussian(ctr, brushRadius, -rockStrength); 329 | glacierTerrain.updateBedrockGPU(); 330 | glaciersWidget->updateGeometry(); 331 | } 332 | else if (ui->radioButton_brushIce->isChecked()) { 333 | glacierTerrain.GetIce().addGaussian(ctr, brushRadius, -iceStrength); 334 | glacierTerrain.updateIceGPU(); 335 | glaciersWidget->updateGeometry(); 336 | } 337 | else if (ui->radioButton_brushELA->isChecked()) { 338 | glacierTerrain.GetELA().addGaussian(ctr, brushRadius, -elaStrength); 339 | glacierTerrain.updateELAGPU(); 340 | updatePreviewELA(glacierTerrain.GetELA()); 341 | } 342 | else if (ui->radioButton_brushPrec->isChecked()) { 343 | glacierTerrain.GetAccumRate().addGaussian(ctr, brushRadius, -precStrenth); 344 | glacierTerrain.updateAccumRateGPU(); 345 | updatePreviewAccum(glacierTerrain.GetAccumRate()); 346 | } 347 | 348 | Render(false); 349 | } 350 | 351 | void MainWindow::updateBrushRadius(double d) 352 | { 353 | brushRadius = d; 354 | glaciersWidget->updateBrushRadius(d); 355 | } 356 | 357 | void MainWindow::configSimulation() 358 | { 359 | double factorUdeform = std::min(2.0 - 0.1 * double(ui->sliderFactorU->value()), 1.0); 360 | double factorUslide = std::min(0.1 * double(ui->sliderFactorU->value()), 1.0); 361 | 362 | ScalarField2 configELA = ui->checkUseELAMap->isChecked() ? 363 | getVariableELA(ui->sb_ELA->value(), ui->sb_ELAdev->value()) : 364 | ScalarField2(glacierTerrain.getDomain(), glacierTerrain.numCellsX(), glacierTerrain.numCellsY(), ui->sb_ELA->value()); 365 | 366 | ScalarField2 configBeta = ui->checkUsePrecMap->isChecked() ? 367 | factorBetaMaps[multiresIndex] : 368 | ScalarField2(glacierTerrain.getDomain(), glacierTerrain.numCellsX(), glacierTerrain.numCellsY(), 1.0); 369 | 370 | glacierTerrain.configSimulation(configELA, configBeta, 371 | ui->sb_AccumRate->value(), ui->sb_AblateRate->value(), 372 | factorUdeform, factorUslide); 373 | 374 | double dIceSteady = ui->sb_dIce->value(); 375 | glaciersWidget->setSteadyCondition(dIceSteady); 376 | glaciersWidget->setMinimumSimulationYears(1); 377 | } 378 | 379 | void MainWindow::runGlacierSimulation() 380 | { 381 | configSimulation(); 382 | glaciersWidget->runSimulation(ui->checkPauseOnSteady->isChecked()); 383 | } 384 | 385 | void MainWindow::pauseGlacierSimulation() 386 | { 387 | glaciersWidget->pauseSimulation(); 388 | } 389 | 390 | void MainWindow::resetGlacierSimulation() 391 | { 392 | glacierTerrain.resetSimulation(); 393 | if (ui->checkUseInitialIce->isChecked() && int(initialIceMaps.size()) > multiresIndex) { 394 | glacierTerrain.setIceMap(initialIceMaps[multiresIndex]); 395 | } 396 | glaciersWidget->pauseSimulation(); 397 | glaciersWidget->resetSimulation(); 398 | glaciersWidget->update(); 399 | } 400 | 401 | void MainWindow::upscaleGlacier() 402 | { 403 | if (multiresIndex <= 0) { 404 | return; 405 | } 406 | 407 | multiresIndex--; 408 | ScalarField2 hiresIce = glacierTerrain.remapIceSurface(bedrocksMultires[multiresIndex]); 409 | glacierTerrain = GlacierTerrain(bedrocksMultires[multiresIndex], hiresIce); 410 | 411 | double dIce = glaciersWidget->getLastdIce(); 412 | if (bedrocksMultires.size() > 0) { 413 | double r = double(bedrocksMultires[multiresIndex].getSizeX()) / double(bedrocksMultires[multiresIndex+1].getSizeX()); 414 | dIce *= r; 415 | } 416 | ui->sb_dIce->setValue(dIce); 417 | 418 | glaciersWidget->setGlacierTerrain(&glacierTerrain); 419 | configSimulation(); 420 | 421 | ui->labelResolution->setText(QString::number(hiresIce.getSizeX()) + "x" + QString::number(hiresIce.getSizeY()) + (multiresIndex > 0 ? "" : " (max)")); 422 | 423 | Render(false); 424 | } 425 | 426 | void MainWindow::updateSteadyCondition(double d) 427 | { 428 | glaciersWidget->setSteadyCondition(d); 429 | } 430 | 431 | void MainWindow::saveScene() 432 | { 433 | QString filename = QFileDialog::getSaveFileName(nullptr, 434 | "Choose a filename to save", 435 | "", 436 | "png file (*.png)"); 437 | 438 | if (!filename.isNull()) { 439 | 440 | double iceMin, iceMax; 441 | glacierTerrain.GetIce().getRange(iceMin, iceMax); 442 | QImage imageIce = glacierTerrain.GetIce().CreateImage(0.0, 256.0 * 256.0, false); 443 | imageIce.save(QString(filename).replace(".png", "_ice_" + QString::number(iceMax) + ".png")); 444 | 445 | double hmin, hmax; 446 | ScalarField2 hf = glacierTerrain.GetHeightfield(); 447 | hf.getRange(hmin, hmax); 448 | hf.CreateImage(0.0, hmax, false).mirrored(false, true).save(QString(filename).replace(".png", "_heights_" + QString::number(hmax) + ".png")); 449 | 450 | QImage aboveELA(imageIce.width(), imageIce.height(), QImage::Format::Format_ARGB32); 451 | for (int i = 0; i < aboveELA.width(); i++) { 452 | for (int j = 0; j < aboveELA.height(); j++) { 453 | aboveELA.setPixel(i, j, glacierTerrain.aboveELA(i,j) ? QColor(255,255,255).rgba() : QColor(0,0,0,255).rgba() ); 454 | } 455 | } 456 | aboveELA.save(QString(filename).replace(".png", "_ela.png")); 457 | } 458 | } 459 | 460 | void MainWindow::loadBedrocks(const QString& filename, const std::vector& downscales, double terrainKM, const std::vector& elevRange) 461 | { 462 | QImage bedImg = QImage(filename).mirrored(false, true); 463 | 464 | int w = bedImg.width(); 465 | int h = bedImg.height(); 466 | double terrainWidth = w >= h ? terrainKM : terrainKM * (double(w) / double(h)); 467 | double terrainHeight = h >= w ? terrainKM : terrainKM * (double(h) / double(w)); 468 | Box2 terrainSize(Vector2(0), Vector2(terrainWidth*1000.0, terrainHeight*1000.0)); 469 | 470 | ScalarField2 bedrock(terrainSize, bedImg, 0, 0.1 * (256 * 256 - 1), true); 471 | double hmin, hmax; 472 | bedrock.getRange(hmin, hmax); 473 | if (elevRange.size() == 1) { 474 | bedrock *= elevRange[0]; 475 | } 476 | else if (elevRange.size() == 2) { 477 | for (int i = 0; i < bedrock.getSizeX() * bedrock.getSizeY(); i++) { 478 | bedrock[i] = elevRange[0] + (elevRange[1] - elevRange[0]) * (bedrock[i] - hmin) / (hmax - hmin); 479 | } 480 | hmin = elevRange[0]; 481 | hmax = elevRange[1]; 482 | } 483 | 484 | bedrocksMultires.clear(); 485 | for (int s : downscales) { 486 | bedrocksMultires.push_back(bedrock.setResolution(bedrock.getSizeX() / s, bedrock.getSizeY() / s)); 487 | } 488 | multiresIndex = int(bedrocksMultires.size()) - 1; 489 | 490 | ui->sb_dIce->setValue(0.001); 491 | 492 | glacierTerrain = GlacierTerrain(bedrocksMultires.back()); 493 | glaciersWidget->setGlacierTerrain(&glacierTerrain); 494 | 495 | Render(true); 496 | 497 | demFilename = filename; 498 | ui->btnSelectDEM->setText(QFileInfo(filename).fileName()); 499 | ui->labelResolution->setText(QString::number(bedrocksMultires.back().getSizeX()) 500 | + "x" + QString::number(bedrocksMultires.back().getSizeY())); 501 | ui->sb_DEM_km->setValue(terrainKM); 502 | ui->sb_DEM_hmin->setValue(hmin); 503 | ui->sb_DEM_hmax->setValue(hmax); 504 | QString ss = QString::number(downscales[0]); 505 | for (unsigned int i = 1; i < downscales.size(); i++) ss += "," + QString::number(downscales[i]); 506 | ui->le_DEM_scales->setText(ss); 507 | 508 | int s = std::min(ui->imglabelDEM->width(), ui->imglabelDEM->height()); 509 | ui->imglabelDEM->setPixmap(QPixmap::fromImage(bedImg.mirrored(false, true)).scaled(s, s, Qt::KeepAspectRatio)); 510 | } 511 | 512 | void MainWindow::loadMapsELA(const QString& filename) 513 | { 514 | QImage sunImg = QImage(filename).mirrored(false, true); 515 | double sunavg = 0.0; 516 | for (int i = 0; i < sunImg.width(); i++) { 517 | for (int j = 0; j < sunImg.height(); j++) { 518 | sunavg += double(qRed(sunImg.pixel(i, j))) / 255.0; 519 | } 520 | } 521 | sunavg /= sunImg.width() * sunImg.height(); 522 | 523 | for (unsigned int i = 0; i < bedrocksMultires.size(); i++) { 524 | const ScalarField2& bedrock = bedrocksMultires[i]; 525 | int nx = bedrock.getSizeX(); 526 | int ny = bedrock.getSizeY(); 527 | QImage sunlight = sunImg.scaled(QSize(nx, ny), Qt::KeepAspectRatio, Qt::SmoothTransformation); 528 | ScalarField2 elaFactor(bedrock.getDomain(), nx, ny, 1.0); 529 | for (int i = 0; i < nx; i++) { 530 | for (int j = 0; j < ny; j++) { 531 | elaFactor(i, j) = double(qRed(sunlight.pixel(i, j))) / 255.0 - sunavg; 532 | } 533 | } 534 | factorElaDeviation.push_back(elaFactor); 535 | } 536 | 537 | updatePreviewELA(getVariableELA(ui->sb_ELA->value(), ui->sb_ELAdev->value())); 538 | } 539 | 540 | void MainWindow::loadMapsPrecipitation(const QString& filename) 541 | { 542 | QImage precImg = QImage(filename).mirrored(false, true); 543 | 544 | for (unsigned int i = 0; i < bedrocksMultires.size(); i++) { 545 | const ScalarField2& bedrock = bedrocksMultires[i]; 546 | int nx = bedrock.getSizeX(); 547 | int ny = bedrock.getSizeY(); 548 | QImage precipitation = precImg.scaled(QSize(nx, ny), Qt::KeepAspectRatio, Qt::SmoothTransformation); 549 | ScalarField2 precFactor(bedrock.getDomain(), ny, ny, 1.0); 550 | for (int i = 0; i < nx; i++) { 551 | for (int j = 0; j < ny; j++) { 552 | precFactor(i, j) = double(qRed(precipitation.pixel(i, j))) / 255.0; 553 | } 554 | } 555 | factorBetaMaps.push_back(precFactor); 556 | } 557 | 558 | updatePreviewAccum(factorBetaMaps.back()); 559 | } 560 | 561 | void MainWindow::loadMapsIce(const QString& filename) 562 | { 563 | QImage iceImg = QImage(filename); // .mirrored(false, true); 564 | 565 | for (unsigned int i = 0; i < bedrocksMultires.size(); i++) { 566 | const ScalarField2& bedrock = bedrocksMultires[i]; 567 | int nx = bedrock.getSizeX(); 568 | int ny = bedrock.getSizeY(); 569 | QImage ice = iceImg.scaled(QSize(nx, ny), Qt::KeepAspectRatio, Qt::SmoothTransformation); 570 | ScalarField2 iceMap(bedrock.getDomain(), ice, 0, 256.0 * 256.0 - 1, false); 571 | initialIceMaps.push_back(iceMap); 572 | } 573 | 574 | ui->imglabelInitialIce->setPixmap(QPixmap::fromImage(iceImg.mirrored(false, true)) 575 | .scaled(ui->imglabelELA->width(), ui->imglabelELA->height(), Qt::KeepAspectRatio)); 576 | } 577 | 578 | ScalarField2 MainWindow::getVariableELA(double avgELA, double devELA) 579 | { 580 | ScalarField2 elamap(factorElaDeviation[multiresIndex]); 581 | elamap *= devELA; 582 | elamap += avgELA; 583 | return elamap; 584 | } 585 | 586 | void MainWindow::updatePreviewELA(const ScalarField2& ela) 587 | { 588 | ui->imglabelELA->setPixmap(QPixmap::fromImage(ela.CreateImage(ColorPalette::CoolWarm()).mirrored(false, true)) 589 | .scaled(ui->imglabelELA->width(), ui->imglabelELA->height(), Qt::KeepAspectRatio)); 590 | } 591 | 592 | void MainWindow::updatePreviewAccum(const ScalarField2& accum) 593 | { 594 | ui->imglabelPrec->setPixmap(QPixmap::fromImage(accum.CreateImage(ColorPalette::CoolWarm()).mirrored(false, true)) 595 | .scaled(ui->imglabelPrec->width(), ui->imglabelPrec->height(), Qt::KeepAspectRatio)); 596 | } 597 | -------------------------------------------------------------------------------- /code/appSimulation/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include "glacierswidget.h" 6 | #include "glacierterrain.h" 7 | 8 | QT_BEGIN_NAMESPACE 9 | namespace Ui { class MainWindow; } 10 | QT_END_NAMESPACE 11 | 12 | class MainWindow : public QMainWindow 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | MainWindow(QWidget *parent = nullptr); 18 | ~MainWindow(); 19 | 20 | public slots: 21 | 22 | void presetScene1(); 23 | void presetScene2(); 24 | void presetScene3(); 25 | void presetScene4(); 26 | void presetScene5(); 27 | void presetScene6(); 28 | void presetScene7(); 29 | void presetScene8(); 30 | void presetScene9(); 31 | 32 | void selectDEM(); 33 | void loadDEM(); 34 | void loadELA(); 35 | void loadPrecipitation(); 36 | void loadInitialIce(); 37 | 38 | void Render(bool resetCamera = true); 39 | void RenderUpdate(); 40 | 41 | void editingSceneLeft(const Vector3& p); 42 | void editingSceneRight(const Vector3& p); 43 | void updateBrushRadius(double d); 44 | 45 | void runGlacierSimulation(); 46 | void pauseGlacierSimulation(); 47 | void resetGlacierSimulation(); 48 | void upscaleGlacier(); 49 | void updateSteadyCondition(double); 50 | 51 | void saveScene(); 52 | 53 | private: 54 | void createActions(); 55 | 56 | void configSimulation(); 57 | 58 | void loadBedrocks(const QString& filename, const std::vector& downscales, double terrainKM, const std::vector& elevRange = {}); 59 | void loadMapsELA(const QString& filename); 60 | void loadMapsPrecipitation(const QString& filename); 61 | void loadMapsIce(const QString& filename); 62 | 63 | ScalarField2 getVariableELA(double avgELA, double devELA); 64 | void updatePreviewELA(const ScalarField2& ela); 65 | void updatePreviewAccum(const ScalarField2& accum); 66 | 67 | private: 68 | Ui::MainWindow *ui; 69 | 70 | GlaciersWidget *glaciersWidget; 71 | GlacierTerrain glacierTerrain; 72 | 73 | // multiresolution bedrocks and control maps 74 | QString demFilename = ""; 75 | std::vector bedrocksMultires; 76 | std::vector factorElaDeviation; 77 | std::vector factorBetaMaps; 78 | std::vector initialIceMaps; 79 | int multiresIndex; 80 | 81 | // brushes 82 | double brushRadius = 750; 83 | const double rockStrength = 20; 84 | const double iceStrength = 10; 85 | const double elaStrength = 20; 86 | const double precStrenth = 0.1; 87 | 88 | }; 89 | #endif // MAINWINDOW_H 90 | -------------------------------------------------------------------------------- /code/core/core.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | 3 | const int SimplexNoise::perm[512] = { 4 | 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 5 | 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 6 | 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 7 | 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 8 | 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 9 | 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 10 | 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 11 | 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 12 | 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 13 | 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 14 | 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 15 | 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180, 16 | 17 | 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30, 69, 142, 18 | 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 19 | 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 20 | 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 21 | 55, 46, 245, 40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 22 | 18, 169, 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 23 | 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 24 | 189, 28, 42, 223, 183, 170, 213, 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 25 | 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 26 | 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 81, 51, 145, 235, 249, 14, 239, 27 | 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 28 | 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 29 | }; 30 | const int SimplexNoise::grad2[8][2] = { 31 | { 1, 1 }, { -1, 1 }, { 1, -1 }, { -1, -1 }, 32 | { 1, 0 }, { -1, 0 }, { 0, 1 }, { 0, -1 } 33 | }; 34 | const double SimplexNoise::F2 = 0.5 * (sqrt(3.0) - 1.0); 35 | const double SimplexNoise::G2 = (3.0 - sqrt(3.0)) / 6.0; 36 | 37 | double SimplexNoise::at(double x, double y) const 38 | { 39 | // Noise contributions from the three corners 40 | double n[3] = { 0.0, 0.0, 0.0 }; 41 | 42 | // Skew the input space to determine which simplex cell we are in 43 | 44 | // Hairy factor for 2D 45 | double s = (x + y) * F2; 46 | 47 | int i = Math::Integer(x + s); 48 | int j = Math::Integer(y + s); 49 | 50 | double t = (i + j) * G2; 51 | 52 | // Unskew the cell origin back to (x,y) space 53 | double X0 = i - t; 54 | double Y0 = j - t; 55 | 56 | // The x,y distances from the cell origin 57 | double x0 = x - X0; 58 | double y0 = y - Y0; 59 | 60 | // For the 2D case, the simplex shape is an equilateral triangle. 61 | // Determine which simplex we are in. 62 | 63 | int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 64 | 65 | if (x0 > y0) { i1 = 1; j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1) 66 | else { i1 = 0; j1 = 1; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) 67 | 68 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 69 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where c = (3-sqrt(3))/6 70 | 71 | double x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 72 | double y1 = y0 - j1 + G2; 73 | double x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords 74 | double y2 = y0 - 1.0 + 2.0 * G2; 75 | 76 | // Work out the hashed gradient indices of the three simplex corners 77 | int ii = i & 255; 78 | int jj = j & 255; 79 | 80 | int gi0 = perm[ii + perm[jj]] % 8; 81 | int gi1 = perm[ii + i1 + perm[jj + j1]] % 8; 82 | int gi2 = perm[ii + 1 + perm[jj + 1]] % 8; 83 | 84 | // Calculate the contribution from the three corners 85 | 86 | double t0 = 0.5 - x0 * x0 - y0 * y0; 87 | if (t0 >= 0) 88 | { 89 | t0 *= t0; 90 | n[0] = t0 * t0 * dot(grad2[gi0], x0, y0); 91 | } 92 | 93 | double t1 = 0.5 - x1 * x1 - y1 * y1; 94 | if (t1 >= 0) 95 | { 96 | t1 *= t1; 97 | n[1] = t1 * t1 * dot(grad2[gi1], x1, y1); 98 | } 99 | 100 | double t2 = 0.5 - x2 * x2 - y2 * y2; 101 | if (t2 >= 0) 102 | { 103 | t2 *= t2; 104 | n[2] = t2 * t2 * dot(grad2[gi2], x2, y2); 105 | } 106 | 107 | // Add contributions from each corner to get the final noise value. 108 | // The result is scaled to return values in the interval [-1,1]. 109 | return 70.0 * (n[0] + n[1] + n[2]); 110 | } 111 | 112 | 113 | bool Box2::Intersect(const Vector2 &s0, const Vector2 &s1, double &tmin, double &tmax) 114 | { 115 | const double epsilon = 1.0e-5; 116 | 117 | tmin = -1e16; 118 | tmax = 1e16; 119 | 120 | const Vector2& a = bmin; 121 | const Vector2& b = bmax; 122 | Vector2 p = s0; 123 | Vector2 d = s1 - s0; 124 | 125 | double t; 126 | // Ox 127 | if (d[0] < -epsilon) { 128 | t = (a[0] - p[0]) / d[0]; 129 | if (t < tmin) 130 | return false; 131 | if (t <= tmax) 132 | tmax = t; 133 | t = (b[0] - p[0]) / d[0]; 134 | if (t >= tmin) { 135 | if (t > tmax) 136 | return false; 137 | tmin = t; 138 | } 139 | } 140 | else if (d[0] > epsilon) { 141 | t = (b[0] - p[0]) / d[0]; 142 | if (t < tmin) 143 | return false; 144 | if (t <= tmax) 145 | tmax = t; 146 | t = (a[0] - p[0]) / d[0]; 147 | if (t >= tmin) { 148 | if (t > tmax) 149 | return false; 150 | tmin = t; 151 | } 152 | } 153 | else if (p[0]b[0]) 154 | return false; 155 | 156 | // Oy 157 | if (d[1] < -epsilon) { 158 | t = (a[1] - p[1]) / d[1]; 159 | if (t < tmin) 160 | return false; 161 | if (t <= tmax) 162 | tmax = t; 163 | t = (b[1] - p[1]) / d[1]; 164 | if (t >= tmin) { 165 | if (t > tmax) 166 | return false; 167 | tmin = t; 168 | } 169 | } 170 | else if (d[1] > epsilon) { 171 | t = (b[1] - p[1]) / d[1]; 172 | if (t < tmin) 173 | return false; 174 | if (t <= tmax) 175 | tmax = t; 176 | t = (a[1] - p[1]) / d[1]; 177 | if (t >= tmin) { 178 | if (t > tmax) 179 | return false; 180 | tmin = t; 181 | } 182 | } 183 | else if (p[1]b[1]) 184 | return false; 185 | 186 | return true; 187 | } 188 | 189 | Camera::Camera() 190 | { 191 | Camera::eye = Vector3(0.0); 192 | Camera::at = Vector3(0.0, 1.0, 0.0); 193 | Camera::up = Vector3(0.0, 0.0, 1.0); 194 | 195 | // Near and far planes 196 | Camera::nearplane = 1.0; 197 | Camera::farplane = 1000.0; 198 | 199 | // Aperture 200 | Camera::cah = 0.980; 201 | Camera::cav = 0.735; 202 | Camera::fl = 35.0; 203 | } 204 | 205 | Camera::Camera(const Vector3& eye, const Vector3& at, const Vector3& up, double near, double far) 206 | { 207 | Camera::eye = eye; 208 | Camera::at = at; 209 | Camera::up = up; 210 | 211 | // Near and far planes 212 | Camera::nearplane = near; 213 | Camera::farplane = far; 214 | 215 | // Aperture 216 | Camera::cah = 0.980; 217 | Camera::cav = 0.735; 218 | Camera::fl = 35.0; 219 | } 220 | 221 | double Camera::getAngleOfViewH(double, double) const 222 | { 223 | return 2.0 * atan(cah * 25.4 * 0.5 / fl); 224 | } 225 | 226 | double Camera::getAngleOfViewV(double w, double h) const 227 | { 228 | double avh = getAngleOfViewH(w, h); 229 | return 2.0 * atan(tan(avh / 2.0) * double(h) / double(w)); 230 | } 231 | 232 | void Camera::upDownRound(double a) 233 | { 234 | Vector3 z = at - eye; 235 | double length = Norm(z); 236 | z = z/length; 237 | Vector3 left = Normalized(cross(up, z)); 238 | 239 | // Rotate 240 | z = z * cos(a) + up * sin(a); 241 | 242 | // Update Vector 243 | up = cross(z, left); 244 | eye = at - z * length; 245 | } 246 | 247 | void Camera::leftRightRound(double a) 248 | { 249 | Vector3 e = eye - at; 250 | Vector3 left = cross(up, e); 251 | e = Vector3(e[0] * cos(a) - e[1] * sin(a), e[0] * sin(a) + e[1] * cos(a), e[2]); 252 | left = Vector3(left[0] * cos(a) - left[1] * sin(a), left[0] * sin(a) + left[1] * cos(a), 0.0); 253 | up = Normalized(cross(left, -e)); 254 | eye = at + e; 255 | } 256 | 257 | void Camera::backForth(double a, bool moveAt) 258 | { 259 | Vector3 z = at - eye; 260 | double length = Norm(z); 261 | z = z/length; 262 | eye = eye + a * z; 263 | if (moveAt) { 264 | at = at + a * z; 265 | } 266 | } 267 | 268 | void Camera::upDownPlane(double a) 269 | { 270 | Vector3 z = at - eye; 271 | double length = Norm(z); 272 | z = z/length; 273 | Vector3 left = Normalized(cross(Vector3(0, 0, 1), z)); 274 | 275 | eye = eye + a * cross(z, left); 276 | at = at + a * cross(z, left); 277 | } 278 | 279 | void Camera::leftRightPlane(double a) 280 | { 281 | Vector3 z = at - eye; 282 | z[2] = 0.0; 283 | double length = Norm(z); 284 | z = z/length; 285 | Vector3 left = Normalized(cross(Vector3(0, 0, 1), z)); 286 | 287 | eye = eye + a * left; 288 | at = at + a * left; 289 | } 290 | 291 | Ray Camera::pixelToRay(int px, int py, int w, int h) const 292 | { 293 | // Get coordinates 294 | Vector3 view = getViewDir(); 295 | Vector3 horizontal = Normalized(cross(view, up)); 296 | Vector3 vertical = Normalized(cross(horizontal, view)); 297 | 298 | double length = 1.0; 299 | 300 | // Convert to radians 301 | double rad = getAngleOfViewV(w, h); // fov 302 | 303 | double vLength = tan(rad / 2.0) * length; 304 | double hLength = vLength * (double(w) / double(h)); 305 | vertical = vertical*vLength; 306 | horizontal = horizontal*hLength; 307 | 308 | // Translate mouse coordinates so that the origin lies in the center of the view port 309 | double x = px - w / 2.0; 310 | double y = h / 2.0 - py; 311 | 312 | // Scale mouse coordinates so that half the view port width and height becomes 1.0 313 | x /= w / 2.0; 314 | y /= h / 2.0; 315 | 316 | // Direction is a linear combination to compute intersection of picking ray with view port plane 317 | return Ray(eye, Normalized(view * length + horizontal * x + vertical * y)); 318 | } 319 | 320 | Camera Camera::View(const Box3& box) 321 | { 322 | Vector3 v = 0.5*(box.getMax() - box.getMin()); 323 | v[2] = 0; 324 | double r = Norm(v); 325 | v = 2*v; 326 | v[2] = -r; 327 | return Camera(box.center() - v, box.center(), Vector3(0.0, 0.0, 1.0), r, 3*r); 328 | } 329 | 330 | 331 | Vector3 ColorPalette::getColor(double t) const { 332 | if (colors.size() == 0) return Vector3(1); 333 | if (colors.size() == 1) return colors[0]; 334 | 335 | if (t < anchors.front()) return colors.front(); 336 | if (t > anchors.back()) return colors.back(); 337 | for (int i = 0; i < int(colors.size() - 1); i++) { 338 | if (t < anchors[i+1]) { 339 | double s = Math::LinearStep(t, anchors[i], anchors[i+1]); 340 | return (1-s)*colors[i] + s*colors[i+1]; 341 | } 342 | } 343 | 344 | return Vector3(1); 345 | } 346 | -------------------------------------------------------------------------------- /code/core/core.h: -------------------------------------------------------------------------------- 1 | #ifndef _CORE_H_ 2 | #define _CORE_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Math { 9 | const double Pi = 3.14159265358979323846; 10 | 11 | inline double min(double a, double b) { 12 | return (a < b ? a : b); 13 | } 14 | 15 | inline double max(double a, double b) { 16 | return (a > b ? a : b); 17 | } 18 | 19 | inline double DegreeToRadian(double a) { 20 | return a * Math::Pi / 180.0; 21 | } 22 | 23 | inline double RadianToDegree(double a) { 24 | return a * 180.0 / Math::Pi; 25 | } 26 | 27 | inline double LinearStep(double x, double a, double b) { 28 | if (x < a) return 0; 29 | if (x > b) return 1; 30 | return (x - a) / (b - a); 31 | } 32 | 33 | inline double Clamp(double x, double a = 0, double b = 1) { 34 | return (x < a ? a : (x > b ? b : x)); 35 | } 36 | 37 | inline double Lerp(double a, double b, double t) { 38 | return a + t * (b - a); 39 | } 40 | 41 | inline double Bilinear(double a00, double a10, double a11, double a01, double u, double v) { 42 | return (1 - u)*(1 - v)*a00 + (1 - u)*(v)*a01 + (u)*(1 - v)*a10 + (u)*(v)*a11; 43 | } 44 | 45 | inline double CubicSmooth(double x, double r) { 46 | return (1.0 - x / r)*(1.0 - x / r)*(1.0 - x / r); 47 | } 48 | 49 | inline int Integer(double x) { 50 | return x > 0.0 ? int(x) : int(x) - 1; 51 | } 52 | 53 | inline double Ridge(const double& z, const double& r) { 54 | if (z < r) return z; 55 | else return 2.0 * r - z; 56 | } 57 | } 58 | 59 | class SimplexNoise { 60 | public: 61 | SimplexNoise() {} 62 | ~SimplexNoise() {} 63 | double at(double x, double y) const; 64 | protected: 65 | double dot(const int* g, const double& x, const double& y) const { 66 | return g[0] * x + g[1] * y; 67 | } 68 | static const int perm[512]; //!< Permutation table, 256 entries duplicated once to avoid modulo computations. 69 | static const int grad2[8][2]; //!< Array of gradients for 2D noise. 70 | static const double F2, G2; //!< Unskew factors for 2D case. 71 | }; 72 | 73 | 74 | class Vector2 { 75 | protected: 76 | double c[2]; 77 | 78 | public: 79 | Vector2() { 80 | c[0] = c[1] = 0; 81 | } 82 | explicit Vector2(double d) { 83 | c[0] = c[1] = d; 84 | } 85 | explicit Vector2(double d0, double d1) { 86 | c[0] = d0; 87 | c[1] = d1; 88 | } 89 | 90 | double& operator[] (int i) { return c[i]; }; 91 | double operator[] (int i) const { return c[i]; }; 92 | 93 | Vector2 operator- () const { return Vector2(-c[0], -c[1]); }; 94 | friend Vector2 operator+(const Vector2& u, const Vector2& v) { return Vector2(u[0]+v[0], u[1]+v[1]); }; 95 | friend Vector2 operator-(const Vector2& u, const Vector2& v) { return Vector2(u[0]-v[0], u[1]-v[1]); }; 96 | friend Vector2 operator*(const Vector2& u, double a) { return Vector2(u[0]*a, u[1]*a); } 97 | friend Vector2 operator*(double a, const Vector2& v) { return v * a; } 98 | friend Vector2 operator/(const Vector2& u, double a) { return Vector2(u[0]/a, u[1]/a); } 99 | 100 | friend double Norm(const Vector2& u) { return sqrt(u[0]*u[0] + u[1]*u[1]); } 101 | friend double SquaredNorm(const Vector2& u) { return u[0]*u[0] + u[1]*u[1]; } 102 | friend Vector2 Normalized(const Vector2& u) { return u/Norm(u); } 103 | }; 104 | 105 | 106 | class Vector3 { 107 | protected: 108 | double c[3]; 109 | 110 | public: 111 | Vector3() { 112 | c[0] = c[1] = c[2] = 0; 113 | } 114 | explicit Vector3(double d) { 115 | c[0] = c[1] = c[2] = d; 116 | }; 117 | explicit Vector3(double d0, double d1, double d2) { 118 | c[0] = d0; 119 | c[1] = d1; 120 | c[2] = d2; 121 | }; 122 | explicit Vector3(const Vector2& u) { 123 | c[0] = u[0]; 124 | c[1] = u[1]; 125 | c[2] = 0; 126 | } 127 | 128 | double& operator[] (int i) { return c[i]; }; 129 | double operator[] (int i) const { return c[i]; }; 130 | Vector3 operator-() const { return Vector3(-c[0], -c[1], -c[2]); } 131 | 132 | friend Vector3 operator+(const Vector3& u, const Vector3& v) { return Vector3(u[0]+v[0], u[1]+v[1], u[2]+v[2]); }; 133 | friend Vector3 operator-(const Vector3& u, const Vector3& v) { return Vector3(u[0]-v[0], u[1]-v[1], u[2]-v[2]); }; 134 | friend Vector3 operator*(const Vector3& u, double a) { return Vector3(u[0]*a, u[1]*a, u[2]*a); } 135 | friend Vector3 operator*(double a, const Vector3& v) { return v * a; } 136 | friend Vector3 operator/(const Vector3& u, double a) { return Vector3(u[0]/a, u[1]/a, u[2]/a); } 137 | 138 | friend double Norm(const Vector3& u) { return sqrt(u[0]*u[0] + u[1]*u[1] + u[2]*u[2]); } 139 | friend double SquaredNorm(const Vector3& u) { return u[0]*u[0] + u[1]*u[1] + u[2]*u[2]; } 140 | friend Vector3 Normalized(const Vector3& u) { return u/Norm(u); } 141 | 142 | friend double dot(const Vector3& u, const Vector3& v) { return u[0]*v[0] + u[1]*v[1] + u[2]*v[2]; } 143 | friend Vector3 cross(const Vector3& u, const Vector3& v) { 144 | return Vector3(u[1]*v[2] - u[2]*v[1], u[2]*v[0] - u[0]*v[2], u[0]*v[1] - u[1]*v[0]); 145 | } 146 | 147 | static Vector3 fromQColor(const QColor& c) { 148 | return Vector3(c.red() / 255.0, c.green() / 255.0, c.blue() / 255.0); 149 | } 150 | QColor toQColor() const { 151 | return QColor(int(255.0 * Math::Clamp(c[0])), 152 | int(255.0 * Math::Clamp(c[1])), 153 | int(255.0 * Math::Clamp(c[2])), 154 | 255); 155 | } 156 | }; 157 | 158 | 159 | class Box2 { 160 | protected: 161 | Vector2 bmin; // min 162 | Vector2 bmax; // max 163 | 164 | public: 165 | Box2() : bmin(0), bmax(0) {}; 166 | Box2(const Vector2& pmin, const Vector2& pmax) : bmin(pmin), bmax(pmax) {} 167 | Box2(const Vector2& c, double r) : bmin(c - Vector2(r)), bmax(c + Vector2(r)) {} 168 | 169 | Vector2 getMin() const { return bmin; } 170 | Vector2 getMax() const { return bmax; } 171 | Vector2 center() const { return 0.5*(bmin + bmax); } 172 | double radius() const { return 0.5 * Norm(bmax - bmin); } 173 | double width() const { return bmax[0] - bmin[0]; } 174 | double height() const { return bmax[1] - bmin[1]; } 175 | 176 | bool Intersect(const Vector2& s0, const Vector2& s1, double& tmin, double& tmax); 177 | }; 178 | 179 | 180 | class Box3 { 181 | protected: 182 | Vector3 bmin; // min 183 | Vector3 bmax; // max 184 | 185 | public: 186 | Box3() : bmin(0), bmax(0) {}; 187 | Box3(const Vector3& pmin, const Vector3& pmax) : bmin(pmin), bmax(pmax) {} 188 | 189 | Vector3 getMin() const { return bmin; } 190 | Vector3 getMax() const { return bmax; } 191 | Vector3 center() const { return 0.5*(bmin + bmax); } 192 | double radius() const { return 0.5 * Norm(bmax - bmin); } 193 | double width() const { return bmax[0] - bmin[0]; } 194 | double height() const { return bmax[1] - bmin[1]; } 195 | double depth() const { return bmax[2] - bmin[2]; } 196 | }; 197 | 198 | 199 | class Ray 200 | { 201 | protected: 202 | Vector3 p; // Origin of the ray. 203 | Vector3 d; // Direction. 204 | 205 | public: 206 | Ray() {} 207 | explicit Ray(const Vector3& p, const Vector3& d) : p(p), d(d) {} 208 | 209 | Vector3 origin() const { return p; } 210 | Vector3 direction() const { return d; } 211 | 212 | Vector3 operator()(double t) const { return p + t * d; } 213 | 214 | Ray reflect(const Vector3& p, const Vector3& n) { return Ray(p, n - 2 * n * dot(d, n)); } 215 | }; 216 | 217 | 218 | class Camera { 219 | protected: 220 | Vector3 eye; // Eye 221 | Vector3 at; // Look at point 222 | Vector3 up; // Up vector 223 | double cah; // Camera aperture horizontal 224 | double cav; // Camera aperture vertical 225 | double nearplane; // Near plane 226 | double farplane; // Far plane 227 | double fl; // Focal length 228 | 229 | public: 230 | Camera(); 231 | Camera(const Vector3& eye, const Vector3& at, const Vector3& up = Vector3(0,0,1), double near = 1.0, double far = 100000.0); 232 | 233 | Vector3 getEye() const { return eye; } 234 | Vector3 getAt() const { return at; } 235 | Vector3 getUp() const { return up; } 236 | Vector3 getViewDir() const { return Normalized(at - eye); } 237 | double getNearPlane() const { return nearplane; } 238 | double getFarPlane() const { return farplane; } 239 | double getAngleOfViewH(double, double) const; 240 | double getAngleOfViewV(double, double) const; 241 | 242 | void setAt(const Vector3& p) { at = p; up = Vector3(0,0,1); } 243 | void setEye(const Vector3& p) { eye = p; } 244 | void setPlanes(double n, double f) { nearplane = n; farplane = f;} 245 | 246 | // Move camera around eye 247 | void upDownRound(double a); 248 | void leftRightRound(double a); 249 | void backForth(double a, bool moveAt = false); 250 | 251 | // Move camera in a plane 252 | void upDownPlane(double); 253 | void leftRightPlane(double); 254 | 255 | Ray pixelToRay(int px, int py, int w, int h) const; 256 | 257 | static Camera View(const Box3& box); 258 | }; 259 | 260 | 261 | class ColorPalette { 262 | 263 | protected: 264 | std::vector colors; 265 | std::vector anchors; 266 | 267 | public: 268 | ColorPalette() : colors({Vector3(1)}), anchors({0}) {} 269 | ColorPalette(const std::vector& c, const std::vector& a) : colors(c), anchors(a) {} 270 | 271 | Vector3 getColor(double u) const; 272 | 273 | static ColorPalette CoolWarm() { 274 | const Vector3 Cool = Vector3(97, 130, 234) / 255.0; 275 | const Vector3 White = Vector3(221, 220, 219) / 255.0; 276 | const Vector3 Warm = Vector3(220, 94, 75) / 255.0; 277 | return ColorPalette({Cool, White, Warm}, {0, 0.5, 1}); 278 | } 279 | static ColorPalette Relief() { 280 | const std::vector c = { Vector3(160, 220, 105) / 255.0, 281 | Vector3(1.0, 0.9, 0.45), 282 | Vector3(168/ 255.0, 155 / 255.0, 138 / 255.0), 283 | Vector3(0.95, 0.95, 0.95) }; 284 | return ColorPalette(c, {0, 150, 250, 400}); 285 | } 286 | }; 287 | 288 | 289 | #endif 290 | -------------------------------------------------------------------------------- /code/core/scalarfield2.cpp: -------------------------------------------------------------------------------- 1 | #include "scalarfield2.h" 2 | #include 3 | 4 | ScalarField2::ScalarField2() : nx(0), ny(0), domain(Box2(Vector2(0), Vector2(1))) 5 | { 6 | } 7 | 8 | ScalarField2::ScalarField2(const Box2 &domain, int nx, int ny, double v) : nx(nx), ny(ny), domain(domain) 9 | { 10 | field.fill(v, nx*ny); 11 | cellSize = Vector2(domain.width()/(nx-1), domain.height()/(ny-1)); 12 | } 13 | 14 | ScalarField2::ScalarField2(const Box2& box, const QImage& image, const double& a, const double& b, bool grayscale) 15 | { 16 | domain = box; 17 | nx = image.width(); 18 | ny = image.height(); 19 | field.resize(nx*ny); 20 | cellSize = Vector2(domain.width()/(nx - 1), domain.height()/(ny - 1)); 21 | 22 | // Write Heightmap 23 | for (int i = 0; i < image.width(); i++) { 24 | for (int j = 0; j < image.height(); j++) { 25 | double t = 0.0; 26 | 27 | // Grayscale 28 | if (grayscale) { 29 | // Grayscale 16 bits 30 | if (image.format() == QImage::Format_Grayscale16) { 31 | QColor thecolor = image.pixelColor(i, j); 32 | t = thecolor.blueF(); 33 | } 34 | // Grayscale 8 bits 35 | else { 36 | QRgb color = image.pixel(i, j); 37 | t = double(qGray(color)) / 255.0; 38 | } 39 | } 40 | // Color 41 | else { 42 | QRgb color = image.pixel(i, j); 43 | // Maximum value is 256^3-1 44 | t = double(qRed(color) << 16 | qGreen(color) << 8 | qBlue(color)) / (16777216.0 - 1.0); 45 | } 46 | 47 | field[vertexIndex(i, j)] = Math::Lerp(a, b, t); 48 | } 49 | } 50 | } 51 | 52 | void ScalarField2::getRange(double &vmin, double &vmax) const 53 | { 54 | vmin = vmax = field.at(0); 55 | for (int i = 1; i < field.size(); i++) { 56 | double x = field.at(i); 57 | if (x < vmin) vmin = x; 58 | if (x > vmax) vmax = x; 59 | } 60 | } 61 | 62 | void ScalarField2::cellCoords(const Vector2& p, int& i, int& j, double& u, double& v) const 63 | { 64 | Vector2 q = p - domain.getMin(); 65 | u = q[0]/cellSize[0]; 66 | v = q[1]/cellSize[1]; 67 | 68 | // Integer coordinates 69 | i = int(u); 70 | j = int(v); 71 | 72 | // Local coordinates within cell 73 | u -= i; 74 | v -= j; 75 | } 76 | 77 | void ScalarField2::cellIntegerCoords(const Vector2 &p, int &i, int &j) const 78 | { 79 | Vector2 q = p - domain.getMin(); 80 | i = int(q[0]/cellSize[0]); 81 | j = int(q[1]/cellSize[1]); 82 | } 83 | 84 | double ScalarField2::value(const Vector2& p) const 85 | { 86 | double u, v; 87 | int i, j; 88 | cellCoords(p, i, j, u, v); 89 | 90 | if (!validIndex(i, j)) return 0.0; 91 | return Math::Bilinear(at(i, j), at(i + 1, j), at(i + 1, j + 1), at(i, j + 1), u, v); 92 | } 93 | 94 | Vector3 ScalarField2::vertex(int i, int j) const 95 | { 96 | return Vector3(domain.getMin()[0] + i*cellSize[0], domain.getMin()[1] + j*cellSize[1], at(i, j)); 97 | } 98 | 99 | inline Vector3 triangleAreaNormal(const Vector3& p0, const Vector3& p1, const Vector3& p2) { 100 | return 0.5*cross(p1 - p0, p2 - p0); 101 | } 102 | 103 | Vector3 ScalarField2::normal(int i, int j) const 104 | { 105 | Vector3 n; 106 | if (i == 0) { 107 | if (j == 0) { 108 | // Corner: 0/1 109 | n = triangleAreaNormal(vertex(i, j), vertex(i + 1, j), vertex(i + 1, j + 1)) 110 | + triangleAreaNormal(vertex(i, j), vertex(i + 1, j + 1), vertex(i, j + 1)); 111 | } 112 | else if (j == ny - 1) { 113 | // Corner: 5 114 | n = triangleAreaNormal(vertex(i, j), vertex(i, j - 1), vertex(i + 1, j)); 115 | } 116 | else { 117 | // Edge: 0/1/5 118 | n = triangleAreaNormal(vertex(i, j), vertex(i + 1, j), vertex(i + 1, j + 1)) 119 | + triangleAreaNormal(vertex(i, j), vertex(i + 1, j + 1), vertex(i, j + 1)) 120 | + triangleAreaNormal(vertex(i, j), vertex(i, j - 1), vertex(i + 1, j)); 121 | } 122 | } 123 | else if (i == nx - 1) { 124 | if (j == 0) { 125 | // Corner: 2 126 | n = triangleAreaNormal(vertex(i, j), vertex(i, j + 1), vertex(i - 1, j)); 127 | } 128 | else if (j == ny - 1) { 129 | // Corner: 3/4 130 | n = triangleAreaNormal(vertex(i, j), vertex(i - 1, j - 1), vertex(i, j - 1)) 131 | + triangleAreaNormal(vertex(i, j), vertex(i - 1, j), vertex(i - 1, j - 1)); 132 | } else { 133 | // Edge: 2/3/4 134 | n = triangleAreaNormal(vertex(i, j), vertex(i, j + 1), vertex(i - 1, j)) 135 | + triangleAreaNormal(vertex(i, j), vertex(i - 1, j), vertex(i - 1, j - 1)) 136 | + triangleAreaNormal(vertex(i, j), vertex(i - 1, j - 1), vertex(i, j - 1)); 137 | } 138 | } 139 | else { 140 | if (j == 0) { 141 | // Edge: 0/1/2 142 | n = triangleAreaNormal(vertex(i, j), vertex(i + 1, j), vertex(i + 1, j + 1)) 143 | + triangleAreaNormal(vertex(i, j), vertex(i + 1, j + 1), vertex(i, j + 1)) 144 | + triangleAreaNormal(vertex(i, j), vertex(i, j + 1), vertex(i - 1, j)); 145 | } 146 | else if (j == ny - 1) { 147 | // Edge: 3/4/5 148 | n = triangleAreaNormal(vertex(i, j), vertex(i - 1, j), vertex(i - 1, j - 1)) 149 | + triangleAreaNormal(vertex(i, j), vertex(i - 1, j - 1), vertex(i, j - 1)) 150 | + triangleAreaNormal(vertex(i, j), vertex(i, j - 1), vertex(i + 1, j)); 151 | } 152 | else { 153 | // Face: 0/1/2/3/4/5 154 | n = triangleAreaNormal(vertex(i, j), vertex(i + 1, j), vertex(i + 1, j + 1)) 155 | + triangleAreaNormal(vertex(i, j), vertex(i + 1, j + 1), vertex(i, j + 1)) 156 | + triangleAreaNormal(vertex(i, j), vertex(i, j + 1), vertex(i - 1, j)) 157 | + triangleAreaNormal(vertex(i, j), vertex(i - 1, j), vertex(i - 1, j - 1)) 158 | + triangleAreaNormal(vertex(i, j), vertex(i - 1, j - 1), vertex(i, j - 1)) 159 | + triangleAreaNormal(vertex(i, j), vertex(i, j - 1), vertex(i + 1, j)); 160 | } 161 | } 162 | 163 | return Normalized(n); 164 | } 165 | 166 | Vector2 ScalarField2::gradient(int i, int j) const 167 | { 168 | Vector2 n; 169 | 170 | // Gradient along x axis 171 | if (i == 0) 172 | n[0] = (at(i + 1, j) - at(i, j)) / cellSize[0]; 173 | else if (i == nx - 1) 174 | n[0] = (at(i, j) - at(i - 1, j)) / cellSize[0]; 175 | else 176 | n[0] = (at(i + 1, j) - at(i - 1, j)) / (2.0 * cellSize[0]); 177 | 178 | // Gradient along y axis 179 | if (j == 0) 180 | n[1] = (at(i, j + 1) - at(i, j)) / cellSize[1]; 181 | else if (j == ny - 1) 182 | n[1] = (at(i, j) - at(i, j - 1)) / cellSize[1]; 183 | else 184 | n[1] = (at(i, j + 1) - at(i, j - 1)) / (2.0 * cellSize[1]); 185 | 186 | return n; 187 | } 188 | 189 | VectorField2 ScalarField2::gradientField() const 190 | { 191 | VectorField2 v(getDomain(), nx, ny); 192 | for (int i = 0; i < nx; i++) { 193 | for (int j = 0; j < ny; j++) { 194 | v(i, j) = gradient(i, j); 195 | } 196 | } 197 | return v; 198 | } 199 | 200 | void ScalarField2::fill(double d) 201 | { 202 | field.fill(d, nx*ny); 203 | } 204 | 205 | void ScalarField2::smooth(int n) 206 | { 207 | // Smooth the scalar field using a discrete gaussian kernel. 208 | // The function uses a 3^2 approximation of the Gaussian kernel. 209 | QVector smoothed; 210 | smoothed.resize(nx * ny); 211 | int k; 212 | 213 | for (int iter = 0; iter < n; iter++) { 214 | // Smooth center 215 | for (int i = 1; i < nx - 1; i++) { 216 | for (int j = 1; j < ny - 1; j++) { 217 | k = vertexIndex(i, j); 218 | smoothed[k] = (4.0*at(k) + 2.0*at(k - 1) + 2.0*at(k + 1) + 2.0*at(k - nx) + 2.0*at(k + nx) + at(k - 1 - nx) + at(k + 1 - nx) + at(k - 1 + nx) + at(k + 1 + nx)) / 16.0; 219 | } 220 | } 221 | 222 | // Smooth edges 223 | for (int i = 1; i < nx - 1; i++) { 224 | k = vertexIndex(i, 0); 225 | smoothed[k] = (4.0*at(k) + 2.0*at(k - 1) + 2.0*at(k + 1) + 2.0*at(k + nx) + at(k - 1 + nx) + at(k + 1 + nx)) / 12.0; 226 | k = vertexIndex(i, ny - 1); 227 | smoothed[k] = (4.0*at(k) + 2.0*at(k - 1) + 2.0*at(k + 1) + 2.0*at(k - nx) + at(k - 1 - nx) + at(k + 1 - nx)) / 12.0; 228 | } 229 | for (int j = 1; j < ny - 1; j++) { 230 | k = vertexIndex(0, j); 231 | smoothed[k] = (2.0*at(k - nx) + 4.0*at(k) + 2.0*at(k + nx) + at(k + 1 - nx) + 2.0*at(k + 1) + at(k + 1 + nx)) / 12.0; 232 | k = vertexIndex(nx - 1, j); 233 | smoothed[k] = (2.0*at(k - nx) + 4.0*at(k) + 2.0*at(k + nx) + at(k - 1 - nx) + 2.0*at(k - 1) + at(k - 1 + nx)) / 12.0; 234 | } 235 | 236 | // Corners 237 | k = vertexIndex(0, 0); 238 | smoothed[k] = (4.0*at(k) + 2.0*at(k + 1) + 2.0*at(k + nx) + 1.0*at(k + nx + 1)) / 9.0; 239 | k = vertexIndex(nx - 1, 0); 240 | smoothed[k] = (4.0*at(k) + 2.0*at(k - 1) + 2.0*at(k + nx) + 1.0*at(k + nx - 1)) / 9.0; 241 | k = vertexIndex(0, ny - 1); 242 | smoothed[k] = (4.0*at(k) + 2.0*at(k + 1) + 2.0*at(k - nx) + 1.0*at(k - nx + 1)) / 9.0; 243 | k = vertexIndex(nx - 1, ny - 1); 244 | smoothed[k] = (4.0*at(k) + 2.0*at(k - 1) + 2.0*at(k - nx) + 1.0*at(k - nx - 1)) / 9.0; 245 | } 246 | field = smoothed; 247 | } 248 | 249 | void ScalarField2::gaussianBlur() 250 | { 251 | const int kernelSize = 9; 252 | double kernel[kernelSize] = { 1, 8, 28, 56, 70, 56, 28, 8, 1 }; 253 | 254 | ScalarField2 temp(*this); 255 | for (int i = 0; i < nx; i++) { 256 | for (int j = 0; j < ny; j++) { 257 | double v = 0; 258 | double w = 0; 259 | for (int dj = -kernelSize / 2; dj <= kernelSize / 2; dj++) { 260 | if (j + dj >= 0 && j + dj < ny - 1) { 261 | v += kernel[dj + kernelSize / 2] * at(i, j + dj); 262 | w += kernel[dj + kernelSize / 2]; 263 | } 264 | } 265 | temp(i, j) = v / w; 266 | } 267 | } 268 | for (int i = 0; i < nx; i++) { 269 | for (int j = 0; j < ny; j++) { 270 | double v = 0; 271 | double w = 0; 272 | for (int di = -kernelSize / 2; di <= kernelSize / 2; di++) { 273 | if (i + di >= 0 && i + di < nx - 1) { 274 | v += kernel[di + kernelSize / 2] * temp.at(i + di, j); 275 | w += kernel[di + kernelSize / 2]; 276 | } 277 | } 278 | (*this)(i, j) = v / w; 279 | } 280 | } 281 | } 282 | 283 | void ScalarField2::step(const double& a, const double& b) 284 | { 285 | for (int i = 0; i < field.size(); i++) { 286 | field[i] = Math::LinearStep(field.at(i), a, b); 287 | } 288 | } 289 | 290 | void ScalarField2::normalize() 291 | { 292 | double a, b; 293 | getRange(a, b); 294 | if (a == b) { 295 | field.fill(1.0); 296 | } 297 | else { 298 | for (int i = 0; i < field.size(); i++) { 299 | field[i] = (field[i] - a) / (b - a); 300 | } 301 | } 302 | } 303 | 304 | void ScalarField2::addGaussian(const Vector2& center, const double& radius, const double& height) 305 | { 306 | 307 | Box2 box (center, radius); 308 | 309 | int ia,ib,ja,jb; 310 | cellIntegerCoords(box.getMin(), ia, ja); 311 | cellIntegerCoords(box.getMax(), ib, jb); 312 | 313 | QPoint pa(ia, ja); 314 | QPoint pb(ib, jb); 315 | 316 | // Rectangle 317 | QRect area(pa.x(), pa.y(), pb.x() - pa.x() + 1, pb.y() - pa.y() + 1); 318 | 319 | // Limit to domain 320 | QRect mask(0, 0, nx - 1, ny - 1); 321 | area = area.intersected(mask); 322 | 323 | // Add to field 324 | for (int y = area.y(); y <= area.y() + area.height(); y++) { 325 | for (int x = area.x(); x <= area.x() + area.width(); x++) { 326 | // Distance between central point and current point 327 | double u = SquaredNorm(center - domainCoords(x, y)); 328 | 329 | if (u < radius * radius) { 330 | field[vertexIndex(x, y)] += height * Math::CubicSmooth(u, radius * radius); 331 | } 332 | } 333 | } 334 | } 335 | 336 | ScalarField2 ScalarField2::setResolution(int x, int y) const 337 | { 338 | // Sampled scalar field 339 | ScalarField2 sampled(domain, x, y); 340 | 341 | // Corners 342 | sampled(0, 0) = at(0, 0); 343 | sampled(0, y - 1) = at(0, ny - 1); 344 | sampled(x - 1, 0) = at(nx - 1, 0); 345 | sampled(x - 1, y - 1) = at(nx - 1, ny - 1); 346 | 347 | // Borders (use linear interpolation) 348 | for (int i = 1; i < x - 1; i++) { 349 | double tx = (nx - 1) * (i / double(x - 1)); 350 | int x0 = int(floor(tx)); 351 | int x1 = int(ceil(tx)); 352 | 353 | sampled(i, 0) = Math::Lerp(at(x0, 0), at(x1, 0), tx - x0); 354 | sampled(i, y - 1) = Math::Lerp(at(x0, ny - 1), at(x1, ny - 1), tx - x0); 355 | } 356 | for (int j = 1; j < y - 1; j++) { 357 | double ty = (ny - 1) * (j / double(y - 1)); 358 | int y0 = int(floor(ty)); 359 | int y1 = int(ceil(ty)); 360 | 361 | sampled(0, j) = Math::Lerp(at(0, y0), at(0, y1), ty - y0); 362 | sampled(x - 1, j) = Math::Lerp(at(nx - 1, y0), at(nx - 1, y1), ty - y0); 363 | } 364 | 365 | // Interior 366 | for (int i = 1; i < x - 1; i++) { 367 | for (int j = 1; j < y - 1; j++) { 368 | sampled(i, j) = value(sampled.domainCoords(i, j)); 369 | } 370 | } 371 | 372 | return sampled; 373 | } 374 | 375 | QImage ScalarField2::CreateImage(bool grayscale) const 376 | { 377 | double a, b; 378 | this->getRange(a, b); 379 | if (a == b) { 380 | b = a + 1.0; 381 | } 382 | return CreateImage(a, b, grayscale); 383 | } 384 | 385 | QImage ScalarField2::CreateImage(double a, double b, bool grayscale) const 386 | { 387 | QImage image(nx, ny, QImage::Format_ARGB32); 388 | for (int i = 0; i < image.width(); i++) { 389 | for (int j = 0; j < image.height(); j++) { 390 | double x = field.at(vertexIndex(i, j)); 391 | double y = Math::LinearStep(x, a, b); 392 | 393 | QColor color; 394 | if (grayscale) { 395 | int c = int(y * 255.0); 396 | color = QColor(c, c, c); 397 | } 398 | else { 399 | int c = int(y * (256.0 * 256.0 * 256.0 - 1.0)); 400 | int cr = (c >> 16) & 255; 401 | int cv = (c >> 8) & 255; 402 | int cb = c & 255; 403 | color = QColor(cr, cv, cb); 404 | } 405 | image.setPixel(i, j, color.rgb()); 406 | } 407 | } 408 | return image; 409 | } 410 | 411 | QImage ScalarField2::CreateImage(const ColorPalette& palette) const 412 | { 413 | double a, b; 414 | getRange(a, b); 415 | if (a == b) { 416 | b = a + 1.0; 417 | } 418 | return CreateImage(a, b, palette); 419 | } 420 | 421 | QImage ScalarField2::CreateImage(double a, double b, const ColorPalette& palette) const 422 | { 423 | QImage image(nx, ny, QImage::Format_ARGB32); 424 | for (int i = 0; i < image.width(); i++) { 425 | for (int j = 0; j < image.height(); j++) { 426 | double x = field.at(vertexIndex(i, j)); 427 | double y = Math::LinearStep(x, a, b); 428 | QColor color = palette.getColor(y).toQColor(); 429 | image.setPixel(i, j, color.rgb()); 430 | } 431 | } 432 | return image; 433 | } 434 | 435 | ScalarField2& ScalarField2::operator+=(const ScalarField2& s) 436 | { 437 | for (int i = 0; i < field.size(); i++) { 438 | field[i] += s.at(i); 439 | } 440 | return *this; 441 | } 442 | 443 | ScalarField2 &ScalarField2::operator+=(const double &d) 444 | { 445 | for (int i = 0; i < field.size(); i++) { 446 | field[i] += d; 447 | } 448 | return *this; 449 | } 450 | 451 | ScalarField2& ScalarField2::operator*=(const double &d) 452 | { 453 | for (int i = 0; i < field.size(); i++) { 454 | field[i] *= d; 455 | } 456 | return *this; 457 | } 458 | 459 | ScalarField2 operator+(const ScalarField2& s1, const ScalarField2& s2) 460 | { 461 | ScalarField2 r(s1); 462 | for (int i = 0; i < r.field.size(); i++) { 463 | r.field[i] += s2.at(i); 464 | } 465 | return r; 466 | } 467 | -------------------------------------------------------------------------------- /code/core/scalarfield2.h: -------------------------------------------------------------------------------- 1 | #ifndef SCALARFIELD2_H 2 | #define SCALARFIELD2_H 3 | 4 | #include 5 | #include 6 | #include "core.h" 7 | #include "vectorfield2.h" 8 | 9 | 10 | class ScalarField2 11 | { 12 | public: 13 | ScalarField2(); 14 | ScalarField2(const Box2& domain, int nx, int ny, double v = 0.0); 15 | ScalarField2(const Box2& domain, const QImage& image, const double& a, const double& b, bool grayscale); 16 | 17 | int getSizeX() const { return nx; } 18 | int getSizeY() const { return ny; } 19 | Vector2 getCellSize() const { return cellSize; } 20 | 21 | Box2 getDomain() const { return domain; }; 22 | void getRange(double& vmin, double& vmax) const; 23 | 24 | int vertexIndex(int i, int j) const { return i + nx*j; } 25 | bool validIndex(int i, int j) const { return (i >= 0) && (i < nx - 1) && (j >= 0) && (j < ny - 1); } 26 | 27 | void cellCoords(const Vector2& p, int& i, int& j, double& u, double& v) const; 28 | void cellIntegerCoords(const Vector2& p, int& i, int& j) const; 29 | Vector2 domainCoords(int i, int j) const { return domain.getMin() + Vector2(i * cellSize[0], j * cellSize[1]); } 30 | 31 | double at(int i, int j) const { return field.at(vertexIndex(i, j)); } 32 | double& operator()(int i, int j) { return field[vertexIndex(i, j)]; } 33 | double at(int i) const { return field.at(i); } 34 | double& operator[](int i) { return field[i]; } 35 | double value(const Vector2& p) const; 36 | 37 | Vector3 vertex(int i, int j) const; 38 | Vector3 normal(int i, int j) const; 39 | Vector2 gradient(int i, int j) const; 40 | 41 | VectorField2 gradientField() const; 42 | 43 | void fill(double d); 44 | void smooth(int n); 45 | void gaussianBlur(); 46 | void step(const double& a, const double& b); 47 | void normalize(); 48 | void addGaussian(const Vector2& center, const double& radius, const double& height); 49 | 50 | ScalarField2 setResolution(int nx, int ny) const; 51 | 52 | 53 | QImage CreateImage(bool grayscale) const; 54 | QImage CreateImage(double a, double b, bool grayscale) const; 55 | QImage CreateImage(const ColorPalette& palette) const; 56 | QImage CreateImage(double a, double b, const ColorPalette& palette) const; 57 | 58 | ScalarField2& operator+=(const ScalarField2& s); 59 | ScalarField2& operator+=(const double& d); 60 | ScalarField2& operator*=(const double& d); 61 | friend ScalarField2 operator+(const ScalarField2& s1, const ScalarField2& s2); 62 | 63 | protected: 64 | 65 | QVector field; 66 | int nx, ny; 67 | Box2 domain; 68 | Vector2 cellSize; 69 | 70 | }; 71 | 72 | 73 | 74 | #endif // SCALARFIELD2_H 75 | -------------------------------------------------------------------------------- /code/core/shader-utils.cpp: -------------------------------------------------------------------------------- 1 | #include "shader-utils.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | static 10 | std::string read(const char* filename) 11 | { 12 | std::stringbuf source; 13 | std::ifstream in(filename); 14 | if (in.good() == false) 15 | printf("[error] loading program '%s'...\n", filename); 16 | else 17 | printf("loading program '%s'...\n", filename); 18 | 19 | in.get(source, 0); 20 | return source.str(); 21 | } 22 | 23 | static 24 | std::string prepare_source(std::string file, const std::string& definitions) 25 | { 26 | if (file.empty()) 27 | return std::string(); 28 | 29 | std::string source; 30 | 31 | std::string version; 32 | size_t b = file.find("#version"); 33 | if (b != std::string::npos) 34 | { 35 | size_t e = file.find('\n', b); 36 | if (e != std::string::npos) 37 | { 38 | version = file.substr(0, e + 1); 39 | file.erase(0, e + 1); 40 | 41 | if (file.find("#version") != std::string::npos) 42 | { 43 | printf("[error] found several #version directives. failed.\n"); 44 | return std::string(); 45 | } 46 | } 47 | } 48 | else 49 | { 50 | printf("[error] no #version directive found. failed.\n"); 51 | return std::string(); 52 | } 53 | 54 | if (definitions.empty() == false) 55 | { 56 | source.append(version); 57 | source.append(definitions).append("\n"); 58 | source.append(file); 59 | } 60 | else 61 | { 62 | source.append(version); 63 | source.assign(file); 64 | } 65 | 66 | return source; 67 | } 68 | 69 | 70 | static 71 | const char* shader_string(const GLenum type) 72 | { 73 | switch (type) 74 | { 75 | case GL_VERTEX_SHADER: return "vertex shader"; 76 | case GL_FRAGMENT_SHADER: return "fragment shader"; 77 | case GL_GEOMETRY_SHADER: return "geometry shader"; 78 | #ifdef GL_VERSION_4_0 79 | case GL_TESS_CONTROL_SHADER: return "control shader"; 80 | case GL_TESS_EVALUATION_SHADER: return "evaluation shader"; 81 | #endif 82 | #ifdef GL_VERSION_4_3 83 | case GL_COMPUTE_SHADER: return "compute shader"; 84 | #endif 85 | default: return "shader"; 86 | } 87 | } 88 | 89 | static 90 | const char* shader_keys[] = 91 | { 92 | "VERTEX_SHADER", 93 | "FRAGMENT_SHADER", 94 | "GEOMETRY_SHADER", 95 | "TESSELATION_CONTROL", 96 | "EVALUATION_CONTROL", 97 | "COMPUTE_SHADER" 98 | }; 99 | const int shader_keys_max = 6; 100 | 101 | static 102 | GLenum shader_types[] = 103 | { 104 | GL_VERTEX_SHADER, 105 | GL_FRAGMENT_SHADER, 106 | GL_GEOMETRY_SHADER, 107 | #ifdef GL_VERSION_4_0 108 | GL_TESS_CONTROL_SHADER, 109 | GL_TESS_EVALUATION_SHADER, 110 | #else 111 | 0, 112 | 0, 113 | #endif 114 | #ifdef GL_VERSION_4_3 115 | GL_COMPUTE_SHADER 116 | #else 117 | 0 118 | #endif 119 | }; 120 | 121 | static 122 | GLuint compile_shader(const GLuint program, const GLenum shader_type, const std::string& source) 123 | { 124 | if (source.size() == 0 || shader_type == 0) 125 | return 0; 126 | 127 | GLuint shader = glCreateShader(shader_type); 128 | glAttachShader(program, shader); 129 | 130 | const char* sources = source.c_str(); 131 | glShaderSource(shader, 1, &sources, NULL); 132 | glCompileShader(shader); 133 | 134 | GLint status; 135 | glGetShaderiv(shader, GL_COMPILE_STATUS, &status); 136 | return (status == GL_TRUE) ? shader : 0; 137 | } 138 | 139 | 140 | int reload_program(GLuint program, const char* filename, const char* definitions) 141 | { 142 | if (program == 0) 143 | return -1; 144 | 145 | int shaders_max = 0; 146 | glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max); 147 | if (shaders_max > 0) 148 | { 149 | std::vector shaders(shaders_max, 0); 150 | glGetAttachedShaders(program, shaders_max, NULL, &shaders.front()); 151 | for (int i = 0; i < shaders_max; i++) 152 | { 153 | glDetachShader(program, shaders[i]); 154 | glDeleteShader(shaders[i]); 155 | } 156 | } 157 | 158 | #ifdef GL_VERSION_4_3 159 | glObjectLabel(GL_PROGRAM, program, -1, filename); 160 | #endif 161 | 162 | std::string common_source = read(filename); 163 | for (int i = 0; i < shader_keys_max; i++) 164 | { 165 | if (common_source.find(shader_keys[i]) != std::string::npos) 166 | { 167 | std::string source = prepare_source(common_source, std::string(definitions).append("#define ").append(shader_keys[i]).append("\n")); 168 | GLuint shader = compile_shader(program, shader_types[i], source); 169 | if (shader == 0) 170 | printf("[error] compiling %s...\n%s\n", shader_string(shader_types[i]), definitions); 171 | } 172 | } 173 | 174 | glLinkProgram(program); 175 | 176 | GLint status; 177 | glGetProgramiv(program, GL_LINK_STATUS, &status); 178 | if (status == GL_FALSE) 179 | { 180 | printf("[error] linking program %u '%s'...\n", program, filename); 181 | return -1; 182 | } 183 | 184 | glUseProgram(program); 185 | return 0; 186 | } 187 | 188 | GLuint read_program(const char* filename, const char* definitions) 189 | { 190 | GLuint program = glCreateProgram(); 191 | reload_program(program, filename, definitions); 192 | program_print_errors(program); 193 | return program; 194 | } 195 | 196 | int release_program(const GLuint program) 197 | { 198 | if (program == 0) 199 | return -1; 200 | 201 | int shaders_max = 0; 202 | glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max); 203 | 204 | if (shaders_max > 0) 205 | { 206 | std::vector shaders(shaders_max, 0); 207 | glGetAttachedShaders(program, shaders_max, NULL, &shaders.front()); 208 | for (int i = 0; i < shaders_max; i++) 209 | { 210 | glDetachShader(program, shaders[i]); 211 | glDeleteShader(shaders[i]); 212 | } 213 | } 214 | 215 | glDeleteProgram(program); 216 | return 0; 217 | } 218 | 219 | 220 | static 221 | void print_line(std::string& errors, const char* source, const int begin_id, const int line_id) 222 | { 223 | int line = 0; 224 | char last = '\n'; 225 | for (unsigned int i = 0; source[i] != 0; i++) 226 | { 227 | if (line > line_id) 228 | break; 229 | 230 | if (last == '\n') 231 | { 232 | line++; 233 | if (line >= begin_id && line <= line_id) 234 | { 235 | errors.append(" "); 236 | errors.push_back('0' + (line / 1000) % 10); 237 | errors.push_back('0' + (line / 100) % 10); 238 | errors.push_back('0' + (line / 10) % 10); 239 | errors.push_back('0' + (line / 1) % 10); 240 | errors.append(" "); 241 | } 242 | } 243 | 244 | if (line >= begin_id && line <= line_id) 245 | { 246 | if (source[i] == '\t') 247 | errors.append(" "); 248 | else 249 | errors.push_back(source[i]); 250 | } 251 | last = source[i]; 252 | } 253 | } 254 | 255 | static 256 | int print_errors(std::string& errors, const char* log, const char* source) 257 | { 258 | printf("[error log]\n%s\n", log); 259 | 260 | int first_error = INT_MAX; 261 | int last_string = -1; 262 | int last_line = -1; 263 | for (int i = 0; log[i] != 0; i++) 264 | { 265 | int string_id = 0, line_id = 0, position = 0; 266 | if (sscanf_s(&log[i], "%d ( %d ) : %n", &string_id, &line_id, &position) == 2 // nvidia syntax 267 | || sscanf_s(&log[i], "%d : %d (%*d) : %n", &string_id, &line_id, &position) == 2 // mesa syntax 268 | || sscanf_s(&log[i], "ERROR : %d : %d : %n", &string_id, &line_id, &position) == 2 // ati syntax 269 | || sscanf_s(&log[i], "WARNING : %d : %d : %n", &string_id, &line_id, &position) == 2) // ati syntax 270 | { 271 | if (string_id != last_string || line_id != last_line) 272 | { 273 | first_error = std::min(first_error, line_id); 274 | 275 | errors.append("\n"); 276 | print_line(errors, source, last_line + 1, line_id); 277 | errors.append("\n"); 278 | } 279 | } 280 | for (i += position; log[i] != 0; i++) 281 | { 282 | errors.push_back(log[i]); 283 | if (log[i] == '\n') 284 | break; 285 | } 286 | 287 | last_string = string_id; 288 | last_line = line_id; 289 | } 290 | errors.append("\n"); 291 | print_line(errors, source, last_line + 1, 1000); 292 | errors.append("\n"); 293 | 294 | return first_error; 295 | } 296 | 297 | int program_format_errors(const GLuint program, std::string& errors) 298 | { 299 | errors.clear(); 300 | 301 | if (program == 0) 302 | { 303 | errors.append("[error] no program...\n"); 304 | return -1; 305 | } 306 | 307 | GLint status; 308 | glGetProgramiv(program, GL_LINK_STATUS, &status); 309 | if (status == GL_TRUE) 310 | return 0; 311 | 312 | int first_error = INT_MAX; 313 | 314 | int shaders_max = 0; 315 | glGetProgramiv(program, GL_ATTACHED_SHADERS, &shaders_max); 316 | if (shaders_max == 0) 317 | { 318 | errors.append("[error] no shaders...\n"); 319 | return 0; 320 | } 321 | 322 | std::vector shaders(shaders_max, 0); 323 | glGetAttachedShaders(program, shaders_max, NULL, &shaders.front()); 324 | for (int i = 0; i < shaders_max; i++) 325 | { 326 | GLint value; 327 | glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &value); 328 | if (value == GL_FALSE) 329 | { 330 | glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &value); 331 | std::vectorlog(value + 1, 0); 332 | glGetShaderInfoLog(shaders[i], (GLsizei)log.size(), NULL, &log.front()); 333 | 334 | // recupere le source 335 | glGetShaderiv(shaders[i], GL_SHADER_SOURCE_LENGTH, &value); 336 | std::vector source(value + 1, 0); 337 | glGetShaderSource(shaders[i], (GLsizei)source.size(), NULL, &source.front()); 338 | 339 | glGetShaderiv(shaders[i], GL_SHADER_TYPE, &value); 340 | errors.append("[error] compiling ").append(shader_string(value)).append("...\n"); 341 | 342 | // formatte les erreurs 343 | int last_error = print_errors(errors, &log.front(), &source.front()); 344 | first_error = std::min(first_error, last_error); 345 | } 346 | } 347 | 348 | // recupere les erreurs de link du program 349 | { 350 | GLint value = 0; 351 | glGetProgramiv(program, GL_INFO_LOG_LENGTH, &value); 352 | 353 | std::vectorlog(value + 1, 0); 354 | glGetProgramInfoLog(program, (GLsizei)log.size(), NULL, &log.front()); 355 | 356 | errors.append("[error] linking program...\n").append(log.begin(), log.end()); 357 | } 358 | 359 | return first_error; 360 | } 361 | 362 | int program_print_errors(const GLuint program) 363 | { 364 | std::string errors; 365 | int code = program_format_errors(program, errors); 366 | if (errors.size() > 0) 367 | printf("%s\n", errors.c_str()); 368 | return code; 369 | } 370 | -------------------------------------------------------------------------------- /code/core/shader-utils.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_UTILS_H 2 | #define SHADER_UTILS_H 3 | 4 | #include "glew.h" 5 | #include 6 | 7 | // Shader API 8 | GLuint read_program(const char* filename, const char* definitions = ""); 9 | int release_program(const GLuint program); 10 | int reload_program(const GLuint program, const char* filename, const char* definitions = ""); 11 | int program_format_errors(const GLuint program, std::string& errors); 12 | int program_print_errors(const GLuint program); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /code/core/vectorfield2.cpp: -------------------------------------------------------------------------------- 1 | #include "vectorfield2.h" 2 | #include 3 | 4 | VectorField2::VectorField2() : nx(0), ny(0), domain(Box2(Vector2(0), Vector2(1))) 5 | { 6 | } 7 | 8 | VectorField2::VectorField2(const Box2 &domain, int nx, int ny, const Vector2& v) : nx(nx), ny(ny), domain(domain) 9 | { 10 | field.fill(v, nx*ny); 11 | cellSize = Vector2(domain.width()/(nx-1), domain.height()/(ny-1)); 12 | } 13 | 14 | void VectorField2::getRange(Vector2& vmin, Vector2& vmax) const 15 | { 16 | vmin = vmax = field.at(0); 17 | for (int i = 1; i < field.size(); i++) { 18 | double x = field.at(i)[0]; 19 | if (x < vmin[0]) vmin[0] = x; 20 | if (x > vmax[0]) vmax[0] = x; 21 | double y = field.at(i)[1]; 22 | if (y < vmin[1]) vmin[1] = y; 23 | if (y > vmax[1]) vmax[1] = y; 24 | } 25 | } 26 | 27 | void VectorField2::cellCoords(const Vector2& p, int& i, int& j, double& u, double& v) const 28 | { 29 | Vector2 q = p - domain.getMin(); 30 | u = q[0] / cellSize[0]; 31 | v = q[1] / cellSize[1]; 32 | 33 | // Integer coordinates 34 | i = int(u); 35 | j = int(v); 36 | 37 | // Local coordinates within cell 38 | u -= i; 39 | v -= j; 40 | } 41 | 42 | Vector2 VectorField2::value(const Vector2& p) const 43 | { 44 | double u, v; 45 | int i, j; 46 | cellCoords(p, i, j, u, v); 47 | 48 | if (!validIndex(i, j)) return Vector2(0.0); 49 | 50 | Vector2 a00 = at(i, j); 51 | Vector2 a01 = at(i, j + 1); 52 | Vector2 a10 = at(i + 1, j); 53 | Vector2 a11 = at(i + 1, j + 1); 54 | double x = Math::Bilinear(a00[0], a10[0], a11[0], a01[0], u, v); 55 | double y = Math::Bilinear(a00[1], a10[1], a11[1], a01[1], u, v); 56 | return Vector2(x, y); 57 | } 58 | -------------------------------------------------------------------------------- /code/core/vectorfield2.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTORFIELD2_H 2 | #define VECTORFIELD2_H 3 | 4 | #include 5 | #include 6 | #include "core.h" 7 | 8 | 9 | class VectorField2 10 | { 11 | public: 12 | VectorField2(); 13 | VectorField2(const Box2& domain, int nx, int ny, const Vector2& v = Vector2(0)); 14 | 15 | int getSizeX() const { return nx; } 16 | int getSizeY() const { return ny; } 17 | Vector2 getCellSize() const { return cellSize; } 18 | 19 | Box2 getDomain() const { return domain; }; 20 | void getRange(Vector2& vmin, Vector2& vmax) const; 21 | 22 | int vertexIndex(int i, int j) const { return i + nx*j; } 23 | bool validIndex(int i, int j) const { return (i >= 0) && (i < nx - 1) && (j >= 0) && (j < ny - 1); } 24 | 25 | void cellCoords(const Vector2& p, int& i, int& j, double& u, double& v) const; 26 | Vector2 domainCoords(int i, int j) const { return domain.getMin() + Vector2(i * cellSize[0], j * cellSize[1]); } 27 | 28 | Vector2 at(int i, int j) const { return field.at(vertexIndex(i, j)); } 29 | Vector2& operator()(int i, int j) { return field[vertexIndex(i, j)]; } 30 | Vector2 at(int i) const { return field.at(i); } 31 | Vector2& operator[](int i) { return field[i]; } 32 | Vector2 value(const Vector2& p) const; 33 | 34 | protected: 35 | 36 | QVector field; 37 | int nx, ny; 38 | Box2 domain; 39 | Vector2 cellSize; 40 | 41 | }; 42 | 43 | #endif // VECTORFIELD2_H 44 | -------------------------------------------------------------------------------- /code/glacierterrain.cpp: -------------------------------------------------------------------------------- 1 | #include "glacierterrain.h" 2 | #include "shader-utils.h" 3 | #include 4 | #include 5 | 6 | 7 | GlacierTerrain::GlacierTerrain() 8 | { 9 | initializedBuffers = false; 10 | nx = ny = 0; 11 | } 12 | 13 | GlacierTerrain::GlacierTerrain(int nx, int ny) : nx(nx), ny(ny), 14 | bedrock(Box2(Vector2(0), Vector2(1)), nx, ny), 15 | ice(Box2(Vector2(0), Vector2(1)), nx, ny), 16 | ela(Box2(Vector2(0), Vector2(1)), nx, ny), 17 | beta(Box2(Vector2(0), Vector2(1)), nx, ny), 18 | diffusivity(Box2(Vector2(0), Vector2(1)), nx, ny) 19 | 20 | { 21 | initializedBuffers = false; 22 | terrainSize = Vector2(1, 1); 23 | } 24 | 25 | GlacierTerrain::GlacierTerrain(const ScalarField2& b) : nx(b.getSizeX()), ny(b.getSizeY()), bedrock(b), 26 | ice(b.getDomain(), b.getSizeX(), b.getSizeY()), 27 | ela(b.getDomain(), b.getSizeX(), b.getSizeY()), 28 | beta(b.getDomain(), b.getSizeX(), b.getSizeY(), 1.0), 29 | diffusivity(b.getDomain(), b.getSizeX(), b.getSizeY()) 30 | 31 | { 32 | initializedBuffers = false; 33 | terrainSize = Vector2(b.getDomain().width(), b.getDomain().height()); 34 | } 35 | 36 | GlacierTerrain::GlacierTerrain(const ScalarField2& b, const ScalarField2& i) : nx(b.getSizeX()), ny(b.getSizeY()), bedrock(b), ice(i), 37 | ela(b.getDomain(), b.getSizeX(), b.getSizeY()), 38 | beta(b.getDomain(), b.getSizeX(), b.getSizeY(), 1.0), 39 | diffusivity(b.getDomain(), b.getSizeX(), b.getSizeY()) 40 | { 41 | initializedBuffers = false; 42 | terrainSize = Vector2(b.getDomain().width(), b.getDomain().height()); 43 | } 44 | 45 | 46 | 47 | GlacierTerrain::~GlacierTerrain() { 48 | if (initializedBuffers) { 49 | glDeleteBuffers(1, &glbufferBedrock); 50 | glDeleteBuffers(1, &glbufferIceIn); 51 | glDeleteBuffers(1, &glbufferIceOut); 52 | glDeleteBuffers(1, &glbufferD); 53 | glDeleteBuffers(1, &glbufferBeta); 54 | glDeleteBuffers(1, &glbufferELA); 55 | } 56 | } 57 | 58 | 59 | void GlacierTerrain::initSimulationGL() 60 | { 61 | // load shader 62 | std::string definitions = ""; 63 | definitions += "#define WORK_GROUP_SIZE_X " + std::to_string(WORK_GROUP_SIZE_X) + "\n"; 64 | definitions += "#define WORK_GROUP_SIZE_Y " + std::to_string(WORK_GROUP_SIZE_Y) + "\n"; 65 | shaderSIAaxis = read_program("./Shaders/sia.glsl", (definitions + "#define SIA_DIR_AXES\n").c_str()); 66 | shaderSIAdiag = read_program("./Shaders/sia.glsl", (definitions + "#define SIA_DIR_DIAGONALS\n").c_str()); 67 | std::cout << "Compute shader loaded!" << std::endl; 68 | 69 | if (initializedBuffers) return; 70 | 71 | // create buffers 72 | bufferElems = ice.getSizeX() * ice.getSizeY(); 73 | 74 | glGenBuffers(1, &glbufferBedrock); 75 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferBedrock); 76 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&bedrock[0]), GL_STATIC_DRAW); 77 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 78 | 79 | glGenBuffers(1, &glbufferIceIn); 80 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferIceIn); 81 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ice[0]), GL_DYNAMIC_DRAW); 82 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 83 | 84 | glGenBuffers(1, &glbufferELA); 85 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferELA); 86 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ela[0]), GL_STATIC_DRAW); 87 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 88 | 89 | glGenBuffers(1, &glbufferBeta); 90 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferBeta); 91 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&beta[0]), GL_STATIC_DRAW); 92 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 93 | 94 | glGenBuffers(1, &glbufferIceOut); 95 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferIceOut); 96 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), NULL, GL_DYNAMIC_DRAW); 97 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 98 | 99 | glGenBuffers(1, &glbufferD); 100 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferD); 101 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), NULL, GL_DYNAMIC_DRAW); 102 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 103 | 104 | // run one simulation step with dt=0, to initialize diffusivity buffer 105 | initializedBuffers = true; 106 | shaderSIA = shaderSIAaxis; 107 | doPassthrough = true; 108 | } 109 | 110 | 111 | void GlacierTerrain::resetSimulation() 112 | { 113 | ice.fill(0); 114 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferIceIn); 115 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ice[0]), GL_DYNAMIC_DRAW); 116 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 117 | 118 | doPassthrough = true; 119 | } 120 | 121 | 122 | void GlacierTerrain::setIceMap(const ScalarField2& icemap) 123 | { 124 | for (int i = 0; i < icemap.getSizeX(); i++) { 125 | for (int j = 0; j < icemap.getSizeY(); j++) { 126 | ice(i, j) = icemap.at(i, j); 127 | } 128 | } 129 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferIceIn); 130 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ice[0]), GL_DYNAMIC_DRAW); 131 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 132 | 133 | doPassthrough = true; 134 | } 135 | 136 | 137 | void GlacierTerrain::updateBedrockGPU() 138 | { 139 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferBedrock); 140 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&bedrock[0]), GL_STATIC_DRAW); 141 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 142 | 143 | doPassthrough = true; 144 | } 145 | 146 | void GlacierTerrain::updateIceGPU() 147 | { 148 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferIceIn); 149 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ice[0]), GL_DYNAMIC_DRAW); 150 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 151 | 152 | doPassthrough = true; 153 | } 154 | 155 | void GlacierTerrain::updateELAGPU() 156 | { 157 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferELA); 158 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ela[0]), GL_STATIC_DRAW); 159 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 160 | } 161 | 162 | void GlacierTerrain::updateAccumRateGPU() 163 | { 164 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferBeta); 165 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&beta[0]), GL_STATIC_DRAW); 166 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 167 | } 168 | 169 | 170 | void GlacierTerrain::configSimulation(const ScalarField2& mapELA, const ScalarField2& mapAccum, 171 | double accumRate, double ablateRate, double factorUdeform, double factorUslide) 172 | { 173 | ela = mapELA; 174 | beta = mapAccum; 175 | simAccumRate = accumRate; 176 | simAblateRate = ablateRate; 177 | simFactorUdeform = factorUdeform; 178 | simFactorUslide = factorUslide; 179 | 180 | std::cerr << "Config simulation " << std::endl; 181 | std::cerr << " - accum rate: " << accumRate << std::endl; 182 | std::cerr << " - ablate rate: " << ablateRate << std::endl; 183 | std::cerr << " - Udeform * " << factorUdeform << std::endl; 184 | std::cerr << " - Uslide * " << factorUslide << std::endl; 185 | 186 | glGenBuffers(1, &glbufferELA); 187 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferELA); 188 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&ela[0]), GL_STATIC_DRAW); 189 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 190 | 191 | glGenBuffers(1, &glbufferBeta); 192 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferBeta); 193 | glBufferData(GL_SHADER_STORAGE_BUFFER, bufferElems * sizeof(double), (const void*)(&beta[0]), GL_STATIC_DRAW); 194 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 195 | } 196 | 197 | 198 | double GlacierTerrain::runSimulationSteps(unsigned int numSimulSteps, double maxSimulTime) 199 | { 200 | const double minTimeStep = 1e-8; 201 | const double maxTimeStep = 1.0; 202 | 203 | // run n steps and count the ellapsed time 204 | double deltaTime = 0; 205 | for (unsigned int i = 0; i < numSimulSteps && deltaTime < maxSimulTime; i++) { 206 | shaderSIA = i % 2 == 0 ? shaderSIAaxis : shaderSIAdiag; 207 | 208 | double dt = (doPassthrough ? minTimeStep : 1) * std::max(minTimeStep, std::min(maxTimeStep, getAdaptiveTimestep())); 209 | dt = std::max(0.0, std::min(maxSimulTime - deltaTime, dt)); 210 | stepSIA(dt); 211 | 212 | deltaTime += dt; 213 | std::swap(glbufferIceIn, glbufferIceOut); // double buffer for ice in/out 214 | doPassthrough = false; 215 | } 216 | 217 | // copy the ice buffer from GPU to cpu 218 | glUseProgram(shaderSIA); 219 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferIceIn); // note we have the most recent buffer here due to swap! 220 | void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, bufferElems * sizeof(double), GL_MAP_READ_BIT); 221 | memcpy(&ice[0], ptr, bufferElems * sizeof(double)); 222 | glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); 223 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 224 | glUseProgram(0); 225 | 226 | return deltaTime; 227 | } 228 | 229 | 230 | void GlacierTerrain::stepSIA(double dt) 231 | { 232 | glUseProgram(shaderSIA); 233 | 234 | glProgramUniform1i(shaderSIA, glGetUniformLocation(shaderSIA, "GridSizeX"), ny); // attention X,Y 235 | glProgramUniform1i(shaderSIA, glGetUniformLocation(shaderSIA, "GridSizeY"), nx); 236 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "dx"), cellHeight()); 237 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "dy"), cellWidth()); 238 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "dt"), dt); 239 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "betaAccum"), simAccumRate); 240 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "betaAblate"), simAblateRate); 241 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "factorUdeform"), simFactorUdeform); 242 | glProgramUniform1d(shaderSIA, glGetUniformLocation(shaderSIA, "factorUslide"), simFactorUslide); 243 | 244 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, glbufferBedrock); 245 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, glbufferIceIn); 246 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, glbufferELA); 247 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, glbufferBeta); 248 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, glbufferIceOut); 249 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, glbufferD); 250 | 251 | glDispatchCompute((ny / WORK_GROUP_SIZE_X) + 1, (nx / WORK_GROUP_SIZE_Y) + 1, 1); // attention X,Y 252 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 253 | 254 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, 0); 255 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, 0); 256 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, 0); 257 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, 0); 258 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, 0); 259 | glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 6, 0); 260 | 261 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, glbufferD); 262 | void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, bufferElems * sizeof(double), GL_MAP_READ_BIT); 263 | memcpy(&diffusivity[0], ptr, bufferElems * sizeof(double)); 264 | glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); 265 | glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); 266 | 267 | glUseProgram(0); 268 | } 269 | 270 | double GlacierTerrain::getAdaptiveTimestep() 271 | { 272 | const double c_stab = 0.125; 273 | double dx = cellWidth(); 274 | double dy = cellHeight(); 275 | 276 | double dmax = 0; 277 | for (unsigned int i = 0; i < bufferElems; i++) { 278 | dmax = std::max(dmax, diffusivity[i]); 279 | } 280 | 281 | return c_stab * std::min(dx*dx, dy*dy)/std::max(dmax, 1.0); 282 | } 283 | 284 | ScalarField2 GlacierTerrain::remapIceSurface(const ScalarField2& hiresBed) const 285 | { 286 | int rx = hiresBed.getSizeX(); 287 | int ry = hiresBed.getSizeY(); 288 | 289 | // upsample ice surface (set to 0 where there is no ice) 290 | ScalarField2 surface(bedrock.getDomain(), nx, ny, 0); 291 | for (int i = 0; i < nx; i++) { 292 | for (int j = 0; j < ny; j++) { 293 | if (ice.at(i, j) > 0) { 294 | surface(i, j) = ice.at(i,j) + bedrock.at(i, j); 295 | } 296 | } 297 | } 298 | ScalarField2 surfaceUp = surface.setResolution(rx, ry); 299 | 300 | // iceUp = surfaceUp - hiresBed 301 | ScalarField2 iceUp(bedrock.getDomain(), rx, ry, 0); 302 | for (int i = 0; i < rx; i++) { 303 | for (int j = 0; j < ry; j++) { 304 | iceUp(i, j) = std::max(0.0, surfaceUp.at(i, j) - hiresBed.at(i, j)); 305 | } 306 | } 307 | 308 | // cover holes 309 | for (int k = 0; k < 2*(rx/nx)*(ry/ny); k++) { 310 | ScalarField2 surfaceUp(hiresBed); 311 | surfaceUp += iceUp; 312 | 313 | for (int i = 1; i < rx-1; i++) { 314 | for (int j = 1; j < ry-1; j++) { 315 | 316 | bool hole = true; 317 | int numNeighbors = 0; 318 | double surfaceHeight = 0; 319 | 320 | for (int di = -1; hole && di <= 1; di++) { 321 | for (int dj = -1; hole && dj <= 1; dj++) { 322 | if (di == 0 && dj == 0) continue; 323 | 324 | hole = hole && (surfaceUp.at(i + di, j + dj) > surfaceUp(i, j)); 325 | if (iceUp.at(i + di, j + dj) > 0) { 326 | surfaceHeight += surfaceUp.at(i + di, j + dj); 327 | numNeighbors++; 328 | } 329 | } 330 | } 331 | 332 | if (hole) { 333 | iceUp(i, j) = std::max(0.0, surfaceHeight/numNeighbors - hiresBed.at(i,j)); 334 | } 335 | else if (iceUp.at(i,j) <= 0 && numNeighbors >= 3) { 336 | if (iceUp.at(i - 1, j) > 0 && iceUp.at(i + 1, j) <= 0 && surfaceUp.at(i + 1, j) > surfaceUp(i - 1, j)) 337 | iceUp(i, j) = std::max(0.0, surfaceUp.at(i - 1, j) - hiresBed.at(i, j)); 338 | if (iceUp.at(i + 1, j) > 0 && iceUp.at(i - 1, j) <= 0 && surfaceUp.at(i - 1, j) > surfaceUp(i + 1, j)) 339 | iceUp(i, j) = std::max(0.0, surfaceUp.at(i + 1, j) - hiresBed.at(i, j)); 340 | 341 | if (iceUp.at(i, j - 1) > 0 && iceUp.at(i, j + 1) <= 0 && surfaceUp.at(i, j + 1) > surfaceUp(i, j - 1)) 342 | iceUp(i, j) = std::max(0.0, surfaceUp.at(i, j - 1) - hiresBed.at(i, j)); 343 | if (iceUp.at(i, j + 1) > 0 && iceUp.at(i, j - 1) <= 0 && surfaceUp.at(i, j - 1) > surfaceUp(i, j + 1)) 344 | iceUp(i, j) = std::max(0.0, surfaceUp.at(i, j + 1) - hiresBed.at(i, j)); 345 | } 346 | } 347 | } 348 | }; 349 | 350 | return iceUp; 351 | } 352 | 353 | bool GlacierTerrain::Intersect(const Ray &ray, double &t, Vector3 &q) const 354 | { 355 | // Compute bounding box 356 | Box2 box = getDomain(); 357 | double ta, tb; 358 | 359 | // Check the intersection with the bounding box 360 | Vector3 r0 = ray(0); 361 | Vector3 r1 = ray(1); 362 | if (!box.Intersect(Vector2(r0[0], r0[1]), Vector2(r1[0], r1[1]), ta, tb)) 363 | return false; 364 | 365 | t = ta + 0.0001; 366 | if (ta < 0.0) 367 | { 368 | t = 0.0; 369 | } 370 | 371 | // Ray marching 372 | while (t < tb) 373 | { 374 | // Point along the ray 375 | Vector3 p = ray(t); 376 | double h = Height(Vector2(p[0], p[1])); 377 | if (h > p[2]) { 378 | q = Vector3(p[0], p[1], h); 379 | return true; 380 | } 381 | else { 382 | t += 1.0; 383 | } 384 | } 385 | return false; 386 | } 387 | -------------------------------------------------------------------------------- /code/glacierterrain.h: -------------------------------------------------------------------------------- 1 | #ifndef GLACIERTERRAIN_H 2 | #define GLACIERTERRAIN_H 3 | 4 | #include "glew.h" 5 | #include "core.h" 6 | #include "scalarfield2.h" 7 | #include 8 | 9 | class GlacierTerrain 10 | { 11 | 12 | public: 13 | GlacierTerrain(); 14 | GlacierTerrain(int nx, int ny); 15 | GlacierTerrain(const ScalarField2& bedrock); 16 | GlacierTerrain(const ScalarField2& bedrock, const ScalarField2& ice); 17 | ~GlacierTerrain(); 18 | 19 | void initSimulationGL(); 20 | void configSimulation(const ScalarField2& mapELA, const ScalarField2& mapAccum, double accumRate, double ablateRate, double factorUdeform, double factorUslide); 21 | void resetSimulation(); 22 | double runSimulationSteps(unsigned int numSimulSteps, double maxSimulTime = -1); 23 | 24 | void setIceMap(const ScalarField2& icemap); 25 | ScalarField2 remapIceSurface(const ScalarField2& hiresBed) const; 26 | 27 | 28 | // ScalarFields 29 | ScalarField2& GetBedrock() { return bedrock; } 30 | ScalarField2& GetIce() { return ice; } 31 | ScalarField2& GetELA() { return ela; } 32 | ScalarField2& GetAccumRate() { return beta; } 33 | ScalarField2 GetHeightfield() const { return bedrock + ice; } 34 | const ScalarField2& GetBedrock() const { return bedrock; } 35 | const ScalarField2& GetIce() const { return ice; } 36 | const ScalarField2& GetELA() const {return ela; } 37 | const ScalarField2& GetAccumRate() const { return beta; } 38 | const ScalarField2& GetDiffusivity() const { return diffusivity; } 39 | 40 | // Getters 41 | double Bedrock(const Vector2& p) const { return bedrock.value(p); } 42 | double Bedrock(int i, int j) const { return bedrock.at(i,j); } 43 | double Ice(const Vector2& p) const { return ice.value(p); } 44 | double Ice(int i, int j) const { return ice.at(i, j); } 45 | double Height(const Vector2& p) const { return bedrock.value(p) + ice.value(p); } 46 | double Height(int i, int j) const { return bedrock.at(i,j) + ice.at(i,j); } 47 | 48 | // Physical magnitudes 49 | double terrainArea() { return terrainSize[0]*terrainSize[1]; } 50 | double cellWidth() const { return terrainSize[0]/nx; } 51 | double cellHeight() const { return terrainSize[1]/ny; } 52 | double cellArea() const { return cellWidth()*cellHeight(); } 53 | bool isEmpty() const { return (nx <= 0) || (ny <= 0); } 54 | int numCellsX() const { return nx; } 55 | int numCellsY() const { return ny; } 56 | int vertexIndex(int i, int j) { return i + nx * j; } 57 | Box2 getDomain() const { return bedrock.getDomain(); } 58 | Box3 getBoundingBox() const { 59 | Box2 dom = getDomain(); 60 | double hmin, hmax; 61 | bedrock.getRange(hmin, hmax); 62 | return Box3(Vector3(dom.getMin()[0], dom.getMin()[1], hmin), 63 | Vector3(dom.getMax()[0], dom.getMax()[1], hmax)); 64 | } 65 | 66 | double iceVolume() const; 67 | double elaValue(int, int) const; 68 | bool aboveELA(int i, int j) const; 69 | bool accumArea(int i, int j) const; 70 | double iceThickness(int, int) const; 71 | Vector2 iceGradient(int, int) const; 72 | double iceStress(int, int) const; 73 | double iceSpeed(int, int) const; 74 | double iceSpeedDeform(int, int) const; 75 | double iceSpeedSlide(int, int) const; 76 | Vector2 iceVelocity(int, int) const; 77 | double diffusionTerm(int, int) const; 78 | double diffusionTermRaw(int, int) const; 79 | double yearlyIce(bool withIce) const; 80 | double glaciatedArea() const; 81 | 82 | 83 | // Modifiers 84 | void SmoothRock(int = 1); 85 | void SmoothIce(int = 1); 86 | 87 | // Query 88 | bool Intersect(const Ray& ray, double& t, Vector3& q) const; 89 | QString GetStats() const; 90 | 91 | // GL Buffers 92 | GLuint bufferBedrockId() const { return glbufferBedrock; }; 93 | GLuint bufferIceInId() const { return glbufferIceIn; }; 94 | GLuint bufferIceOutId() const { return glbufferIceOut; }; 95 | GLuint bufferDiffusionId() const { return glbufferD; }; 96 | 97 | void updateBedrockGPU(); 98 | void updateIceGPU(); 99 | void updateELAGPU(); 100 | void updateAccumRateGPU(); 101 | 102 | 103 | protected: 104 | 105 | int nx, ny; 106 | Vector2 terrainSize; 107 | ScalarField2 bedrock; // Bedrock 108 | ScalarField2 ice; // Ice cover 109 | ScalarField2 ela; // ELA map 110 | ScalarField2 beta; // Precipitation modifier 111 | 112 | // Simulation params and constants 113 | double simAccumRate, simAblateRate; 114 | double simFactorUdeform, simFactorUslide; 115 | static constexpr double g = 9.81; // gravity 116 | static constexpr double rho = 910.0; // ice density 117 | static constexpr double Gamma_d = 7.26e-5; // from [Headley et al. 2012] 118 | static constexpr double Gamma_s = 3.27; // from [Headley et al. 2012] 119 | 120 | // Simulation compute shader 121 | static const unsigned int WORK_GROUP_SIZE_X = 32; 122 | static const unsigned int WORK_GROUP_SIZE_Y = 32; 123 | GLuint shaderSIA; 124 | GLuint shaderSIAaxis, shaderSIAdiag; 125 | GLuint glbufferBedrock; 126 | GLuint glbufferIceIn, glbufferIceOut; 127 | GLuint glbufferELA, glbufferBeta; 128 | GLuint glbufferD; 129 | 130 | // buffers 131 | bool initializedBuffers = false; 132 | bool doPassthrough = true; 133 | ScalarField2 diffusivity; 134 | unsigned int bufferElems; 135 | 136 | // simulation functions 137 | void stepSIA(double dt); 138 | double getAdaptiveTimestep(); 139 | 140 | // internal funcs 141 | void updateELA(double avgELA); 142 | 143 | }; 144 | 145 | 146 | inline double GlacierTerrain::iceVolume() const 147 | { 148 | const ScalarField2& field = GetIce(); 149 | double v = 0; 150 | double a = cellArea(); 151 | for (int i = 0; i < nx; i++) { 152 | for (int j = 0; j < ny; j++) { 153 | v += field.at(i, j); 154 | } 155 | } 156 | return a * v; 157 | } 158 | 159 | inline double GlacierTerrain::elaValue(int i, int j) const 160 | { 161 | return ela.at(i, j); 162 | } 163 | 164 | inline bool GlacierTerrain::aboveELA(int i, int j) const 165 | { 166 | return (bedrock.at(i, j) + ice.at(i,j)) > ela.at(i,j); 167 | } 168 | 169 | inline bool GlacierTerrain::accumArea(int i, int j) const 170 | { 171 | return aboveELA(i, j); 172 | } 173 | 174 | inline double GlacierTerrain::iceThickness(int i, int j) const 175 | { 176 | return ice.at(i, j); 177 | } 178 | 179 | inline Vector2 GlacierTerrain::iceGradient(int i, int j) const 180 | { 181 | double dip = i < nx - 1 ? Height(i + 1, j) : Height(i, j); 182 | double dim = i > 0 ? Height(i - 1, j) : Height(i, j); 183 | double dx = i > 0 && i < nx - 1 ? 2 * cellWidth() : cellWidth(); 184 | double djp = j < ny - 1 ? Height(i, j + 1) : Height(i, j); 185 | double djm = j > 0 ? Height(i, j - 1) : Height(i, j); 186 | double dy = j > 0 && j < ny - 1 ? 2 * cellHeight() : cellHeight(); 187 | return Vector2((dip - dim)/dx, (djp - djm)/dy); 188 | } 189 | 190 | inline double GlacierTerrain::iceStress(int i, int j) const 191 | { 192 | return rho * g * iceThickness(i, j) * Norm(iceGradient(i, j)); 193 | } 194 | 195 | inline double GlacierTerrain::iceSpeed(int i, int j) const 196 | { 197 | return iceSpeedDeform(i, j) + iceSpeedSlide(i, j); 198 | } 199 | 200 | inline double GlacierTerrain::iceSpeedDeform(int i, int j) const 201 | { 202 | double h = iceThickness(i, j); 203 | double s = Norm(iceGradient(i, j)); 204 | return simFactorUdeform * Gamma_d * h*h*h*h * s*s; 205 | } 206 | 207 | inline double GlacierTerrain::iceSpeedSlide(int i, int j) const 208 | { 209 | double h = iceThickness(i, j); 210 | double s = Norm(iceGradient(i, j)); 211 | return simFactorUslide * Gamma_s * h*h * s*s; 212 | } 213 | 214 | inline Vector2 GlacierTerrain::iceVelocity(int i, int j) const 215 | { 216 | return iceSpeed(i, j) * iceGradient(i, j)/Norm(iceGradient(i,j)); 217 | } 218 | 219 | inline double GlacierTerrain::diffusionTerm(int i, int j) const 220 | { 221 | return diffusivity.at(i, j); 222 | } 223 | 224 | inline double GlacierTerrain::diffusionTermRaw(int i, int j) const 225 | { 226 | return iceThickness(i,j) * iceSpeed(i, j); 227 | } 228 | 229 | inline double GlacierTerrain::yearlyIce(bool withIce) const { 230 | double iceAccum = 0; 231 | for (int i = 0; i < nx*ny; i++) { 232 | iceAccum += std::max(0.0, simAccumRate * beta.at(i) * (bedrock.at(i) + withIce*ice.at(i) - ela.at(i))); 233 | } 234 | return iceAccum * cellArea(); 235 | } 236 | 237 | inline double GlacierTerrain::glaciatedArea() const { 238 | int iceCells = 0; 239 | for (int i = 0; i < nx*ny; i++) { 240 | if (ice.at(i) > 0) iceCells++; 241 | } 242 | return iceCells * cellArea(); 243 | } 244 | 245 | #endif 246 | -------------------------------------------------------------------------------- /data/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/arrow.png -------------------------------------------------------------------------------- /data/maps/aiguestortes/dem-hires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortes/dem-hires.png -------------------------------------------------------------------------------- /data/maps/aiguestortes/dem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortes/dem.png -------------------------------------------------------------------------------- /data/maps/aiguestortes/ela.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortes/ela.png -------------------------------------------------------------------------------- /data/maps/aiguestortes/ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortes/ice.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/crevassesLong.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/crevassesLong.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/crevassesMargin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/crevassesMargin.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/crevassesTrans.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/crevassesTrans.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/dem-hires.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/dem-hires.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/dem.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/dem.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/ela.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/ela.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/ice.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/ice.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/icefalls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/icefalls.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/icefallsRaw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/icefallsRaw.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/ogivesDistCtr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/ogivesDistCtr.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/ogivesDistSrc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/ogivesDistSrc.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/ogivesId.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/ogivesId.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/rimayes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/rimayes.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/segmentation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/segmentation.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/seracs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/seracs.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/valleydist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/valleydist.png -------------------------------------------------------------------------------- /data/maps/aiguestortesCrop/valleywidth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/maps/aiguestortesCrop/valleywidth.png -------------------------------------------------------------------------------- /data/stamps/crevasses_1_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_1_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_2_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_2_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_3_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_3_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_4_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_4_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_5_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_5_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_6_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_6_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_7_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_7_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_8_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_8_mask.png -------------------------------------------------------------------------------- /data/stamps/crevasses_9_mask.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/stamps/crevasses_9_mask.png -------------------------------------------------------------------------------- /data/terrains/aiguestortes_20m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/aiguestortes_20m.png -------------------------------------------------------------------------------- /data/terrains/aiguestortes_precipitation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/aiguestortes_precipitation.png -------------------------------------------------------------------------------- /data/terrains/aiguestortes_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/aiguestortes_sun.png -------------------------------------------------------------------------------- /data/terrains/canyon_10m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/canyon_10m.png -------------------------------------------------------------------------------- /data/terrains/canyon_precipitation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/canyon_precipitation.png -------------------------------------------------------------------------------- /data/terrains/canyon_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/canyon_sun.png -------------------------------------------------------------------------------- /data/terrains/chartreuse_20m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/chartreuse_20m.png -------------------------------------------------------------------------------- /data/terrains/chartreuse_edit_20m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/chartreuse_edit_20m.png -------------------------------------------------------------------------------- /data/terrains/chartreuse_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/chartreuse_sun.png -------------------------------------------------------------------------------- /data/terrains/ecrins_30m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/ecrins_30m.png -------------------------------------------------------------------------------- /data/terrains/ecrins_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/ecrins_sun.png -------------------------------------------------------------------------------- /data/terrains/kungsleden_25m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/kungsleden_25m.png -------------------------------------------------------------------------------- /data/terrains/kungsleden_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/kungsleden_sun.png -------------------------------------------------------------------------------- /data/terrains/mtperdu_20m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/mtperdu_20m.png -------------------------------------------------------------------------------- /data/terrains/mtperdu_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/mtperdu_sun.png -------------------------------------------------------------------------------- /data/terrains/ruapehu_25m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/ruapehu_25m.png -------------------------------------------------------------------------------- /data/terrains/ruapehu_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/ruapehu_sun.png -------------------------------------------------------------------------------- /data/terrains/smokies_10m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/smokies_10m.png -------------------------------------------------------------------------------- /data/terrains/smokies_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/smokies_sun.png -------------------------------------------------------------------------------- /data/terrains/sthelens_10m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/sthelens_10m.png -------------------------------------------------------------------------------- /data/terrains/sthelens_sun.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/data/terrains/sthelens_sun.png -------------------------------------------------------------------------------- /img/Crevasses-Rimayes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/Crevasses-Rimayes.jpg -------------------------------------------------------------------------------- /img/Crevasses_Longitudinal-Marginal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/Crevasses_Longitudinal-Marginal.jpg -------------------------------------------------------------------------------- /img/Crevasses_Transverse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/Crevasses_Transverse.jpg -------------------------------------------------------------------------------- /img/Icefalls-Seracs.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/Icefalls-Seracs.jpg -------------------------------------------------------------------------------- /img/Moraines-Transverse.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/Moraines-Transverse.jpg -------------------------------------------------------------------------------- /img/Ogives-Moraines.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/Ogives-Moraines.jpg -------------------------------------------------------------------------------- /img/scene-aiguestortes.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/scene-aiguestortes.jpg -------------------------------------------------------------------------------- /img/scene-ecrins.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/scene-ecrins.jpg -------------------------------------------------------------------------------- /img/teaser.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oargudo/glaciers/c6eafa9c6b0142c461f87ccaaf14ba43ddd6961f/img/teaser.jpg -------------------------------------------------------------------------------- /shaders/instanced-cube.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | 3 | #ifdef VERTEX_SHADER 4 | 5 | // model attributes 6 | in vec3 a_position; 7 | in vec3 a_normal; 8 | 9 | // instance attributes 10 | in vec3 i_translation; 11 | in float i_height; 12 | 13 | // uniforms 14 | uniform mat4 ModelViewMatrix; 15 | uniform mat4 ProjectionMatrix; 16 | uniform vec2 u_cellSize; 17 | uniform ivec2 u_gridCells; 18 | 19 | // output 20 | out vec3 worldPos; 21 | out vec2 cellCenter; 22 | out vec3 eyePos; 23 | out vec3 worldNormal; 24 | out float cubeHeight; 25 | out flat ivec2 cellCoords; 26 | 27 | void main(void) 28 | { 29 | mat4 MVP = ProjectionMatrix * ModelViewMatrix; 30 | 31 | worldPos = a_position*vec3(u_cellSize, i_height) + i_translation; 32 | eyePos = vec3(ModelViewMatrix*vec4(worldPos, 1.0)); 33 | worldNormal = a_normal; 34 | cubeHeight = i_height; 35 | 36 | cellCoords = ivec2(gl_InstanceID/u_gridCells.y, gl_InstanceID%u_gridCells.y); 37 | cellCenter = (vec2(cellCoords) + 0.5) * u_cellSize; 38 | 39 | gl_Position = MVP * vec4(worldPos, 1.0); 40 | } 41 | #endif 42 | 43 | #ifdef FRAGMENT_SHADER 44 | 45 | #define PI 3.1415926538 46 | 47 | // input from vertex shader 48 | in vec3 worldPos; 49 | in vec2 cellCenter; 50 | in vec3 eyePos; 51 | in vec3 worldNormal; 52 | in float cubeHeight; 53 | in flat ivec2 cellCoords; 54 | 55 | // uniforms 56 | uniform vec4 u_materialAmbient; 57 | uniform vec4 u_materialDiffuse; 58 | uniform vec4 u_materialSpecular; 59 | uniform float u_materialShininess; 60 | uniform vec3 u_lightPos; 61 | 62 | uniform sampler2D u_texture; 63 | uniform sampler2D u_arrowTexture; 64 | 65 | uniform int u_useTexture; // 0: ambient material color, 1,2: texture, 3: custom shader defined 66 | uniform bool u_shadeCube; 67 | uniform vec2 u_worldSize; 68 | uniform vec2 u_cellSize; 69 | 70 | // raw data for custom shader texturing 71 | uniform sampler2D u_datatexBed; 72 | uniform sampler2D u_datatexIce; 73 | uniform vec3 u_bedRange; // min, max, range 74 | uniform vec3 u_iceRange; 75 | 76 | // edit cursor 77 | uniform bool u_drawCursor; 78 | uniform vec3 u_cursorPoint; 79 | uniform float u_cursorRadius; 80 | 81 | // output 82 | out vec4 fragColor; 83 | 84 | vec4 phongModel(vec4 c_amb, vec4 c_diff, vec4 c_spec) { 85 | 86 | // Returned color set to ambient 87 | vec3 c = c_amb.rgb; 88 | 89 | // Modified diffuse lighting 90 | vec3 L = normalize(u_lightPos - worldPos); 91 | vec3 N = normalize(worldNormal); 92 | float d = 0.5*(1.0 + dot(N, L)); 93 | c += c_diff.rgb * vec3(d*d); 94 | 95 | // Specular 96 | vec3 R = reflect(L, N); 97 | float l = 0.5*(1.0 + dot(R, eyePos)); 98 | float s = pow(l*l, u_materialShininess); 99 | c += c_spec.rgb * vec3(s); 100 | 101 | return vec4(c, 1.0); 102 | } 103 | 104 | 105 | // All components are in the range [0…1], including hue. 106 | vec3 hsv2rgb(vec3 c) 107 | { 108 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 109 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 110 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 111 | } 112 | 113 | vec2 rotate(vec2 v, float a) { 114 | float s = sin(a); 115 | float c = cos(a); 116 | mat2 m = mat2(c, -s, s, c); 117 | return m * v; 118 | } 119 | 120 | 121 | void main() 122 | { 123 | vec4 colorAmbient; 124 | 125 | // uniform color 126 | if (u_useTexture == 0) { 127 | colorAmbient = u_materialAmbient; 128 | } 129 | 130 | // shading using a texture with blocky appearance 131 | else if (u_useTexture == 1) { 132 | vec2 texCoords = cellCenter.xy/u_worldSize.xy; 133 | colorAmbient = texture(u_texture, texCoords); 134 | } 135 | 136 | // shading using a texture with interpolation 137 | else if (u_useTexture == 2) { 138 | vec2 texCoords = worldPos.xy/u_worldSize.xy; 139 | colorAmbient = texture(u_texture, texCoords); 140 | } 141 | 142 | // custom shading with access to bed and ice layers, for debugging purposes 143 | else if (u_useTexture == 3) { 144 | 145 | vec2 texCoords = cellCenter.xy/u_worldSize.xy; 146 | vec2 texelSize = u_cellSize/u_worldSize.xy; 147 | 148 | float h = u_iceRange.x + u_iceRange.z*texture(u_datatexIce, texCoords).r; 149 | float b = u_bedRange.x + u_bedRange.z*texture(u_datatexBed, texCoords).r; 150 | float s = h + b; 151 | float hxm = u_iceRange.x + u_iceRange.z*texture(u_datatexIce, texCoords + vec2(-1, 0)*texelSize).r; 152 | float hxp = u_iceRange.x + u_iceRange.z*texture(u_datatexIce, texCoords + vec2( 1, 0)*texelSize).r; 153 | float hym = u_iceRange.x + u_iceRange.z*texture(u_datatexIce, texCoords + vec2( 0 -1)*texelSize).r; 154 | float hyp = u_iceRange.x + u_iceRange.z*texture(u_datatexIce, texCoords + vec2( 0, 1)*texelSize).r; 155 | float bxm = u_bedRange.x + u_bedRange.z*texture(u_datatexBed, texCoords + vec2(-1, 0)*texelSize).r; 156 | float bxp = u_bedRange.x + u_bedRange.z*texture(u_datatexBed, texCoords + vec2( 1, 0)*texelSize).r; 157 | float bym = u_bedRange.x + u_bedRange.z*texture(u_datatexBed, texCoords + vec2( 0 -1)*texelSize).r; 158 | float byp = u_bedRange.x + u_bedRange.z*texture(u_datatexBed, texCoords + vec2( 0, 1)*texelSize).r; 159 | float sxm = bxm + hxm; 160 | float sxp = bxp + hxp; 161 | float sym = bym + hym; 162 | float syp = byp + hyp; 163 | 164 | float grad_s_x = 0.5*(sxp - sxm)/u_cellSize.x; 165 | float grad_s_y = 0.5*(syp - sym)/u_cellSize.y; 166 | float grad_s = sqrt(grad_s_x*grad_s_x + grad_s_y*grad_s_y); 167 | vec3 surfNormal = vec3(-grad_s_x, -grad_s_y, 1.0); 168 | float slopeAngle = 0.5*PI - acos(length(vec3(grad_s_x, grad_s_y, 0))/length(surfNormal)); 169 | vec2 flowDir = -normalize(vec2(grad_s_x, grad_s_y)); 170 | float flowDirAngle = atan(flowDir.y, flowDir.x); 171 | 172 | vec3 c; 173 | float ct = PI/8.0; 174 | if (slopeAngle <= ct) 175 | c = mix(vec3( 97.0,130.0,234.0), vec3(221.0,220.0,219.0), slopeAngle/ct); 176 | else 177 | c = mix(vec3(221.0,220.0,219.0), vec3(220.0, 94.0, 75.0), clamp(slopeAngle/ct - 1.0, 0.0, 1.0)); 178 | colorAmbient = vec4(c/255.0, 1.0); 179 | 180 | // draw arrow 181 | vec2 arrowCoords = 1.6*(fract(worldPos.xy/u_cellSize.xy) - 0.5); 182 | arrowCoords = rotate(arrowCoords, flowDirAngle); 183 | vec4 arrowTex = texture(u_arrowTexture, 0.5 + arrowCoords); 184 | float aa = pow(arrowTex.a, 5); 185 | colorAmbient = aa*arrowTex + (1 - aa)*colorAmbient; 186 | } 187 | 188 | vec3 color; 189 | if (u_shadeCube) 190 | color = phongModel(colorAmbient, u_materialDiffuse, u_materialSpecular).rgb; 191 | else 192 | color = colorAmbient.rgb; 193 | 194 | if (u_drawCursor) { 195 | float t = length(worldPos.xy - u_cursorPoint.xy)/u_cursorRadius; 196 | t = 0.5*(1.0 - smoothstep(0.5, 1.0, t)); 197 | color = color*(1 - t) + vec3(0.4,0.8,0.0)*t; 198 | } 199 | 200 | float alpha = min(cubeHeight/1.0, 1.0); 201 | 202 | fragColor = vec4(color, alpha); 203 | } 204 | #endif 205 | -------------------------------------------------------------------------------- /shaders/sia.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | #extension GL_ARB_compute_shader : enable 3 | #extension GL_ARB_shader_storage_buffer_object : enable 4 | 5 | # ifdef COMPUTE_SHADER 6 | 7 | uniform int GridSizeX; 8 | uniform int GridSizeY; 9 | uniform double dx; 10 | uniform double dy; 11 | uniform double dt; 12 | 13 | uniform double betaAccum; 14 | uniform double betaAblate; 15 | uniform double factorUdeform; 16 | uniform double factorUslide; 17 | 18 | 19 | layout(std430, binding=1) buffer Bedrock { 20 | double bedrock[]; 21 | }; 22 | 23 | layout(std430, binding=2) buffer InIce { 24 | double inIce[]; 25 | }; 26 | 27 | layout(std430, binding=3) buffer MapELA { 28 | double mapELA[]; 29 | }; 30 | 31 | layout(std430, binding=4) buffer MapBeta { 32 | double mapBeta[]; 33 | }; 34 | 35 | layout(std430, binding=5) buffer OutIce { 36 | double outIce[]; 37 | }; 38 | 39 | layout(std430, binding=6) buffer Diffusion { 40 | double diffusion[]; 41 | }; 42 | 43 | 44 | layout(local_size_x = WORK_GROUP_SIZE_X, local_size_y = WORK_GROUP_SIZE_Y, local_size_z = 1) in; 45 | 46 | 47 | 48 | // avoid divisions by zero 49 | const double eps = 1e-6; 50 | 51 | // Glenn law exponent 52 | const double n = 3; 53 | 54 | // These parameters are defined in section 7.1 55 | const double A = 7.57e-17;//1e-16; 56 | const double g = 9.81; 57 | const double rho = 910.0; 58 | const double rg = rho * g; 59 | const double Gamma = 2.0 * A * rg*rg*rg / (n + 2); 60 | 61 | const double Gamma_d = 7.26e-5; 62 | const double Gamma_s = 3.27; 63 | 64 | 65 | 66 | int GetOffset(int i, int j) 67 | { 68 | return i * GridSizeY + j; 69 | } 70 | 71 | double H(int i, int j) 72 | { 73 | if (i < 0) return 0; 74 | if (j < 0) return 0; 75 | if (i >= GridSizeX) return 0; 76 | if (j >= GridSizeY) return 0; 77 | return inIce[GetOffset(i, j)]; 78 | } 79 | 80 | double B(int i, int j) 81 | { 82 | if (i < 0) return 0; 83 | if (j < 0) return 0; 84 | if (i >= GridSizeX) return 0; 85 | if (j >= GridSizeY) return 0; 86 | return bedrock[GetOffset(i, j)]; 87 | } 88 | 89 | double ELA(int i, int j) 90 | { 91 | if (i < 0) return 0; 92 | if (j < 0) return 0; 93 | if (i >= GridSizeX) return 0; 94 | if (j >= GridSizeY) return 0; 95 | return mapELA[GetOffset(i, j)]; 96 | } 97 | 98 | double BetaFactor(int i, int j) 99 | { 100 | if (i < 0) return 0; 101 | if (j < 0) return 0; 102 | if (i >= GridSizeX) return 0; 103 | if (j >= GridSizeY) return 0; 104 | return mapBeta[GetOffset(i, j)]; 105 | } 106 | 107 | double mdot(double z, double ela, double k) 108 | { 109 | if (z >= ela) return betaAccum * (z - ela) * k; 110 | else return betaAblate * (z - ela); 111 | } 112 | 113 | double phi(double r) 114 | { 115 | const double b = 2; 116 | return max(0, max(min(b*r, 1), min(r, b))); 117 | } 118 | 119 | double diffusivity(double grad_s, double h_p, double h_m, double s_p, double s_m) 120 | { 121 | double D_p = Gamma * h_p*h_p*h_p*h_p*h_p * grad_s; 122 | double D_m = Gamma * h_m*h_m*h_m*h_m*h_m * grad_s; 123 | double D_min = min(D_p, D_m); 124 | double D_max = max(D_p, D_m); 125 | if (s_p <= s_m && h_m <= h_p) return D_min; 126 | if (s_p <= s_m && h_m > h_p) return D_max; 127 | if (s_p > s_m && h_m <= h_p) return D_max; 128 | if (s_p > s_m && h_m > h_p) return D_min; 129 | return 0; 130 | } 131 | 132 | double diffusivityWithSliding(double grad_s, double h_p, double h_m, double s_p, double s_m) 133 | { 134 | double D_p = h_p*h_p*h_p * (factorUdeform*Gamma_d*h_p*h_p + factorUslide*Gamma_s) * grad_s; 135 | double D_m = h_m*h_m*h_m * (factorUdeform*Gamma_d*h_m*h_m + factorUslide*Gamma_s) * grad_s; 136 | double D_min = min(D_p, D_m); 137 | double D_max = max(D_p, D_m); 138 | if (s_p <= s_m && h_m <= h_p) return D_min; 139 | if (s_p <= s_m && h_m > h_p) return D_max; 140 | if (s_p > s_m && h_m <= h_p) return D_max; 141 | if (s_p > s_m && h_m > h_p) return D_min; 142 | return 0; 143 | } 144 | 145 | void main() 146 | { 147 | int i = int(gl_GlobalInvocationID.x); 148 | int j = int(gl_GlobalInvocationID.y); 149 | 150 | if (i < 0) return; 151 | if (j < 0) return; 152 | if (i >= GridSizeX) return; 153 | if (j >= GridSizeY) return; 154 | 155 | double ela = ELA(i, j); 156 | double beta = BetaFactor(i, j); 157 | 158 | # ifdef SIA_DIR_AXES 159 | 160 | double dx2 = dx*dx; 161 | double dy2 = dy*dy; 162 | 163 | // access ice height positions in buffer 164 | double h = H(i,j); 165 | double h_ip = H(i+1,j); 166 | double h_ipp = H(i+2,j); 167 | double h_im = H(i-1,j); 168 | double h_imm = H(i-2,j); 169 | double h_jp = H(i,j+1); 170 | double h_jpp = H(i,j+2); 171 | double h_jm = H(i,j-1); 172 | double h_jmm = H(i,j-2); 173 | 174 | // access bedrock positions and compute surface 175 | double z = B(i,j); 176 | double s = h + z; 177 | double s_ip = h_ip + B(i+1,j); 178 | double s_im = h_im + B(i-1,j); 179 | double s_jp = h_jp + B(i,j+1); 180 | double s_jm = h_jm + B(i,j-1); 181 | 182 | # endif 183 | # ifdef SIA_DIR_DIAGONALS 184 | 185 | double dx2 = dx*dx + dy*dy; 186 | double dy2 = dx*dx + dy*dy; 187 | 188 | // access ice height positions in buffer 189 | double h = H(i,j); 190 | double h_ip = H(i+1,j+1); 191 | double h_ipp = H(i+2,j+2); 192 | double h_im = H(i-1,j-1); 193 | double h_imm = H(i-2,j-2); 194 | double h_jp = H(i-1,j+1); 195 | double h_jpp = H(i-2,j+2); 196 | double h_jm = H(i+1,j-1); 197 | double h_jmm = H(i+2,j-2); 198 | 199 | // access bedrock positions and compute surface 200 | double z = B(i,j); 201 | double s = h + z; 202 | double s_ip = h_ip + B(i+1,j+1); 203 | double s_im = h_im + B(i-1,j-1); 204 | double s_jp = h_jp + B(i-1,j+1); 205 | double s_jm = h_jm + B(i+1,j-1); 206 | 207 | # endif 208 | 209 | // compute downstream to upstream ice thickness ratio 210 | double r_i = (h - h_im) /(h_ip - h + eps); 211 | double r_ip = (h_ip - h) /(h_ipp - h_ip + eps); 212 | double r_im = (h_im - h_imm)/(h - h_im + eps); 213 | 214 | double r_j = (h - h_jm) /(h_jp - h + eps); 215 | double r_jp = (h_jp - h) /(h_jpp - h_jp + eps); 216 | double r_jm = (h_jm - h_jmm)/(h - h_jm + eps); 217 | 218 | 219 | // ice thickness at cell boundary (staggered grid) 220 | // up = +1/2, dn = -1/2 221 | double h_iup_m = h + 0.5 * phi(r_i) * (h_ip - h); 222 | double h_iup_p = h_ip - 0.5 * phi(r_ip) * (h_ipp - h_ip); 223 | double h_idn_m = h_im + 0.5 * phi(r_im) * (h - h_im); 224 | double h_idn_p = h - 0.5 * phi(r_i) * (h_ip - h); 225 | 226 | double h_jup_m = h + 0.5 * phi(r_j) * (h_jp - h); 227 | double h_jup_p = h_jp - 0.5 * phi(r_jp) * (h_jpp - h_jp); 228 | double h_jdn_m = h_jm + 0.5 * phi(r_jm) * (h - h_jm); 229 | double h_jdn_p = h - 0.5 * phi(r_j) * (h_jp - h); 230 | 231 | 232 | // slope gradients 233 | double grad_s_iup = (s_ip - s)*(s_ip - s)/dy2; 234 | double grad_s_idn = (s - s_im)*(s - s_im)/dy2; 235 | double grad_s_jup = (s_jp - s)*(s_jp - s)/dx2; 236 | double grad_s_jdn = (s - s_jm)*(s - s_jm)/dx2; 237 | 238 | 239 | // diffusivities at the 4 cell boundaries 240 | double D_iup = diffusivityWithSliding(grad_s_iup, h_iup_p, h_iup_m, s_ip, s); 241 | double D_idn = diffusivityWithSliding(grad_s_idn, h_idn_p, h_idn_m, s, s_im); 242 | double D_jup = diffusivityWithSliding(grad_s_jup, h_jup_p, h_jup_m, s_jp, s); 243 | double D_jdn = diffusivityWithSliding(grad_s_jdn, h_jdn_p, h_jdn_m, s, s_jm); 244 | 245 | 246 | // flux q divergence 247 | double div_q_i = (D_iup*(s_ip - s) - D_idn*(s - s_im))/dy2; 248 | double div_q_j = (D_jup*(s_jp - s) - D_jdn*(s - s_jm))/dx2; 249 | double div_q = div_q_i + div_q_j; 250 | 251 | 252 | // mass balance 253 | double m = mdot(s, ela, beta); 254 | 255 | 256 | // explicit time stepping 257 | double h_res = h + dt*(m + div_q); 258 | h_res = max(h_res, 0); 259 | 260 | // max diffusion value, needed for adaptive timestep in next iteration 261 | double maxD = max(max(abs(D_iup), abs(D_idn)), max(abs(D_jup), abs(D_jdn))); 262 | // ice delta, used for checking glacier stability 263 | double dIce = h_res - h; 264 | 265 | 266 | // output 267 | int idx = GetOffset(i, j); 268 | outIce[idx] = h_res; 269 | diffusion[idx] = maxD; 270 | 271 | } 272 | 273 | #endif -------------------------------------------------------------------------------- /shaders/skybox.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | 3 | #ifdef VERTEX_SHADER 4 | void main(void) 5 | { 6 | vec4 vertices[4] = vec4[4](vec4(-1.0, -1.0, 1.0, 1.0), 7 | vec4( 1.0, -1.0, 1.0, 1.0), 8 | vec4(-1.0, 1.0, 1.0, 1.0), 9 | vec4( 1.0, 1.0, 1.0, 1.0)); 10 | vec4 pos = vertices[gl_VertexID]; 11 | 12 | gl_Position = pos; 13 | } 14 | #endif 15 | 16 | #ifdef FRAGMENT_SHADER 17 | layout (depth_greater) out float gl_FragDepth; 18 | 19 | layout(location = 0) uniform vec3 CamPos; 20 | layout(location = 1) uniform vec3 CamLookAt; 21 | layout(location = 2) uniform vec3 CamUp; 22 | layout(location = 3) uniform vec2 iResolution; 23 | 24 | 25 | out vec4 color; 26 | 27 | vec3 BuildRd() 28 | { 29 | vec3 ro = CamPos; 30 | vec3 ta = CamLookAt; 31 | vec3 camUp = CamUp; 32 | 33 | // Calculate orthonormal camera reference system 34 | vec3 camDir = normalize(ta-ro); // direction for center ray 35 | vec3 camRight = normalize(cross(camDir,camUp)); 36 | 37 | vec2 coord =-1.0+2.0*gl_FragCoord.xy/iResolution.xy; 38 | coord.x *= iResolution.x/iResolution.y; 39 | 40 | // Get direction for this pixel 41 | vec3 rd = normalize(camDir + (coord.x*camRight + coord.y*camUp)) ; 42 | 43 | return rd; 44 | } 45 | 46 | // Compute sky color 47 | // d Ray direction 48 | vec3 SkyShadeBlue(in vec3 d) 49 | { 50 | // light direction 51 | vec3 lig = normalize(vec3( 0.3,0.5, 0.6)); 52 | float sun = 0.5*(dot(lig,d)+1.0); 53 | vec3 color = vec3(0.35,0.45,0.75)*(0.75-0.25*d.z); 54 | color += vec3(0.65,0.6,0.55)*pow( sun, 18.0 ); 55 | return color; 56 | } 57 | 58 | void main(void) 59 | { 60 | vec3 rd = BuildRd(); 61 | 62 | color = vec4(SkyShadeBlue(rd),1.0); 63 | 64 | gl_FragDepth = 1.0; 65 | } 66 | #endif 67 | -------------------------------------------------------------------------------- /shaders/tex-mesh.glsl: -------------------------------------------------------------------------------- 1 | #version 430 core 2 | 3 | #ifdef VERTEX_SHADER 4 | in vec3 a_position; 5 | 6 | uniform mat4 ModelViewMatrix; 7 | uniform mat4 ProjectionMatrix; 8 | 9 | out vec3 worldPos; 10 | 11 | void main(void) 12 | { 13 | mat4 MVP = ProjectionMatrix * ModelViewMatrix; 14 | gl_Position = MVP * (vec4(a_position, 1.0)); 15 | worldPos = a_position; 16 | } 17 | #endif 18 | 19 | #ifdef FRAGMENT_SHADER 20 | uniform vec2 u_worldMin; 21 | uniform vec2 u_worldSize; 22 | uniform sampler2D u_texture; 23 | 24 | in vec3 worldPos; 25 | 26 | out vec4 fragment; 27 | 28 | void main() 29 | { 30 | vec2 uv = (worldPos.xy - u_worldMin)/u_worldSize; 31 | vec4 c = texture(u_texture, uv); 32 | fragment = vec4(c.rgb, 1.0); 33 | } 34 | #endif 35 | --------------------------------------------------------------------------------