├── reading.md ├── resources ├── cirrus.exr ├── obstacle.png └── sky_no_sun.exr ├── about.txt ├── LICENCE ├── README.md ├── scripts └── make_dist.rb ├── erosion_maths.md ├── library_licences.txt ├── CMakeLists.txt ├── erosion_kernel.cl └── terraingen.cpp /reading.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ono-Sendai/terraingen/HEAD/reading.md -------------------------------------------------------------------------------- /resources/cirrus.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ono-Sendai/terraingen/HEAD/resources/cirrus.exr -------------------------------------------------------------------------------- /resources/obstacle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ono-Sendai/terraingen/HEAD/resources/obstacle.png -------------------------------------------------------------------------------- /about.txt: -------------------------------------------------------------------------------- 1 | TerrainGen is written by Nicholas Chapman 2 | 3 | https://github.com/Ono-Sendai/terraingen 4 | -------------------------------------------------------------------------------- /resources/sky_no_sun.exr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Ono-Sendai/terraingen/HEAD/resources/sky_no_sun.exr -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Nicholas Chapman 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 | # TerrainGen 2 | 3 | TerrainGen is an open source terrain generator and erosion simulator. 4 | It's GPU powered so runs pretty fast. 5 | 6 | Currently I've only made a build for Windows but I would consider making a build for Mac if requested. 7 | 8 | You can download the Windows build here: https://github.com/Ono-Sendai/terraingen/releases 9 | 10 | ![terraingen](https://github.com/user-attachments/assets/35a1109e-c927-4be1-8249-8990135e40d8) 11 | 12 | [![Watch vid on YouTube](https://github.com/user-attachments/assets/c1c95efb-b4c3-43d7-bff1-bcb8c4c55d84)](https://www.youtube.com/watch?v=qBPaR19crXY "Watch vid on YouTube") 13 | 14 | ![Gh5rGZTaUAAKiv4](https://github.com/user-attachments/assets/c7722701-1fd5-4941-bfe9-485eb598271c) 15 | 16 | ![t2](https://github.com/Ono-Sendai/terraingen/assets/30285/6fd2c8ab-076b-4198-bc92-584d376f690d) 17 | 18 | ![Gh5h2g0aYAENYcH](https://github.com/user-attachments/assets/fec869f4-285c-417f-bee8-2e5a7c2b7671) 19 | 20 | ![t3](https://github.com/Ono-Sendai/terraingen/assets/30285/c774ee56-17a9-4425-a161-1eebf1f32c91) 21 | 22 | ![t4](https://github.com/Ono-Sendai/terraingen/assets/30285/344b9f00-c14a-46db-9d10-08f6e2116db7) 23 | 24 | ![t1](https://github.com/Ono-Sendai/terraingen/assets/30285/8f2d0c12-238d-41c4-8f07-2db01aac859c) 25 | 26 | [Compiling/Building TerrainGen instructions](building.md) 27 | 28 | [Erosion Maths](erosion_maths.md) 29 | 30 | [Reading and Related Work](reading.md) 31 | -------------------------------------------------------------------------------- /scripts/make_dist.rb: -------------------------------------------------------------------------------- 1 | #===================================================================== 2 | #make_dist.rb 3 | #------------ 4 | #Copyright Nicholas Chapman 2025 - 5 | #===================================================================== 6 | 7 | require 'FileUtils' 8 | require './make_dist_private.rb' 9 | 10 | 11 | def copyAllFilesInDir(dir_from, dir_to) 12 | if !Dir.exist?(dir_from) 13 | STDERR.puts "Error: Source directory doesn't exist: #{dir_from}" 14 | exit(1) 15 | end 16 | 17 | if !Dir.exist?(dir_to) 18 | STDERR.puts "Error: Target directory doesn't exist: #{dir_to}" 19 | exit(1) 20 | end 21 | 22 | Dir.foreach(dir_from) do |f| 23 | FileUtils.cp(dir_from + "/" + f, dir_to, verbose: true) if f != ".." && f != "." 24 | end 25 | end 26 | 27 | def getAndCheckEnvVar(name) 28 | env_var = ENV[name] 29 | 30 | if env_var.nil? 31 | STDERR.puts "#{name} env var not defined." 32 | exit(1) 33 | end 34 | 35 | puts "#{name}: #{env_var}" 36 | return env_var 37 | end 38 | 39 | def copyVCRedist(vs_version, target_dir) 40 | 41 | redist_path = "C:/Program Files/Microsoft Visual Studio/#{vs_version}/Community/VC/Redist/MSVC/14.42.34433/x64" 42 | copyAllFilesInDir("#{redist_path}/Microsoft.VC143.CRT", target_dir) 43 | 44 | end 45 | 46 | def exec_command(cmd) 47 | res = Kernel.system(cmd) 48 | if !res 49 | STDERR.puts "Command failed: " + cmd 50 | exit(1) 51 | end 52 | end 53 | 54 | 55 | 56 | dist_dir = "d:/files/temp_terraingen_dist" 57 | 58 | FileUtils.rm_r(dist_dir, :verbose=>true) if File.exist?(dist_dir) 59 | FileUtils.mkdir(dist_dir, :verbose=>true) 60 | 61 | # Copy and sign executable and SDL DLL 62 | FileUtils.cp("c:\\programming\\terraingen\\vs2022_build\\Release\\terraingen.exe", dist_dir, :verbose=>true) 63 | sign_app("TerrainGen", dist_dir + "/terraingen.exe") 64 | 65 | FileUtils.cp("C:\\programming\\SDL\\sdl_2.30.0_build\\Release\\SDL2.dll", dist_dir, :verbose=>true) 66 | sign_app("SDL", dist_dir + "/SDL2.dll") 67 | 68 | # Copy VC redist 69 | copyVCRedist(2022, dist_dir) 70 | 71 | # Copy resources 72 | FileUtils.cp_r("../resources", dist_dir, :verbose => true) 73 | 74 | # Copy erosion_kernel.cl 75 | FileUtils.cp_r("../erosion_kernel.cl", dist_dir, :verbose => true) 76 | 77 | 78 | # Copy OpenGL shaders and OpenGL data. 79 | glare_core_repos_dir = getAndCheckEnvVar('GLARE_CORE_TRUNK_DIR') 80 | FileUtils.cp_r("#{glare_core_repos_dir}/opengl/shaders", dist_dir, :verbose => true) 81 | FileUtils.cp_r("#{glare_core_repos_dir}/opengl/gl_data", dist_dir, :verbose => true) 82 | 83 | # Copy library_licences.txt 84 | FileUtils.cp_r("../library_licences.txt", dist_dir, :verbose => true) 85 | 86 | # Copy LICENCE 87 | FileUtils.cp_r("../LICENCE", dist_dir, :verbose => true) 88 | 89 | # Copy about.txt 90 | FileUtils.cp_r("../about.txt", dist_dir, :verbose => true) 91 | 92 | 93 | # Make zip of directory 94 | zip_path = "TerrainGen_v0.3.zip" 95 | 96 | # Remove existing zip if present 97 | FileUtils.rm(zip_path, :verbose=>true) if File.exist?(zip_path) 98 | 99 | # -mx9: level 9 compression (max_ 100 | # -tzip : zip archive type 101 | exec_command("sevenz a -mx9 -tzip \"#{zip_path}\" #{dist_dir}/*") 102 | -------------------------------------------------------------------------------- /erosion_maths.md: -------------------------------------------------------------------------------- 1 | 2 | For the origin of the pipe stuff, see 'Fast Hydraulic Erosion Simulation and Visualization on GPU'. 3 | 4 | See also [Reading / Literature](reading.md) 5 | 6 | ## Acceleration from water depth differences 7 | 8 | Pressure in cell at depth $h$ is 9 | 10 | $$ 11 | p(h) = h \rho g + p_0 12 | $$ 13 | 14 | Where $p_0$ is atmospheric pressure, $\rho$ is density, and $g$ is magnitude of gravitational accell. (See https://pressbooks.online.ucf.edu/osuniversityphysics/chapter/14-1-fluids-density-and-pressure/) 15 | 16 | Consider a 'pipe' of length $l$, connecting cells 1 and 2. 17 | Suppose pipe has width $w$ and height $h_p$. 18 | 19 | The pressure on the water in the pipe at cell 1 at height z above ground level is 20 | 21 | $$ 22 | p_1(z) = p(h_1 - z) = (h_1 - z) \rho g + p_0 23 | $$ 24 | 25 | The pressure on the water in the pipe at cell 2 at height z above ground level is 26 | 27 | 28 | $$ 29 | p_2(z) = p(h_2 - z) = (h_2 - z) \rho g + p_0 30 | $$ 31 | 32 | Assuming pipe connects from the bottom of cell 1 to bottom of cell 2, the pressure difference at height z is 33 | 34 | $$ 35 | p_2(z) - p_1(z) = ((h_2 - z) \rho g + p_0) - ((h_1 - z) \rho g + p_0) 36 | $$ 37 | 38 | $$ 39 | p_2(z) - p_1(z) = (h_2 - h_1) \rho g 40 | $$ 41 | 42 | Since force = pressure * area, 43 | The net force on the fluid in the pipe in the positive x direction is 44 | 45 | $$ 46 | f_1 - f_2 = ((h_1 - h_2) \rho g) A 47 | $$ 48 | 49 | Where the pipe cross-sectional area is $A = w \times h_p$ 50 | 51 | The mass of the fluid in the pipe is 52 | $$m = V \rho = A l \rho$$ 53 | 54 | The acceleration on the fluid is (from $f = ma$): 55 | 56 | $$a = (f_1 - f_2)/m = {(h_1 - h_2) \rho g A \over A l \rho } 57 | = (h_1 - h_2) g / l 58 | $$ 59 | 60 | This is independent of the height of the pipe ($h_p$), and the fluid density ($\rho$). 61 | 62 | The volume flux (volume flowing through the pipe per unit time), is: 63 | 64 | $$F_{1,2}$$ 65 | 66 | (units of $m^3 s^{-1}$) 67 | 68 | The flow velocity is 69 | 70 | $$v = F_{1,2} / A$$ 71 | 72 | e.g. 73 | 74 | $$F_{1,2} = v A$$ 75 | 76 | the rate of change of flux is 77 | 78 | $${dF_{1,2} \over dt} = {dvA \over dt} 79 | $$ 80 | 81 | Assuming A is constant (not correct but will do for now) 82 | gives 83 | 84 | $${dF_{1,2} \over dt} = A {dv \over dt} = A (h_1 - h_2) g / l = 85 | w h_p (h_1 - h_2) g / l 86 | $$ 87 | 88 | Note that this depends on h_p, the height of the tube. 89 | If we assume that h_p = current fluid height in the cell, then the rate of flux change depends on the fluid height. 90 | 91 | If we assume the width of the pipe ($w$) = pipe length ($l$) we get 92 | 93 | $${dF_{1,2} \over dt} = (h_1 - h_2) h_p g 94 | $$ 95 | 96 | For a time step of $\Delta_t$, we have 97 | 98 | $$ dF_{1,2}^{t+\Delta_t} = dF_{1,2}^t + {dF_{1,2} \over dt} \Delta_t = $$ 99 | 100 | $$ dF_{1,2}^{t+\Delta_t} = dF_{1,2}^t + (h_1 - h_2) h_p g \Delta_t 101 | $$ 102 | 103 | ## Acceleration from ground height differences 104 | 105 | Assume the ground heightfield is h(x, y) 106 | 107 | The non-unit length normal is 108 | 109 | $$n = (-\partial h(x, y)/\partial x, -\partial h(x, y)/\partial y, 1) 110 | $$ 111 | 112 | with 113 | 114 | $$||n|| = \sqrt{(\partial h/\partial x)^2 + (\partial h/\partial y)^2 + 1} 115 | $$ 116 | 117 | 118 | The x component of the gravity accel vector projected onto the ground plane is 119 | 120 | $$ a_x = g (-\partial h/\partial x/||n||) 121 | $$ 122 | 123 | Suppose we have ground heights $g_1$ and $g_2$ under cells 1 and 2, and they are spaced $l$ apart. 124 | Then 125 | 126 | $$\partial h/\partial x = (g_2 - g_1) / l 127 | $$ 128 | 129 | And so 130 | 131 | $$a_x = g (-(g_2 - g_1) / l) / ||n|| 132 | $$ 133 | 134 | For small ground slope angles $||n|| \simeq 1$, so 135 | 136 | $$ 137 | a_x = g (g_1 - g_2) / l 138 | $$ 139 | 140 | Which is basically the same result as for water depth differences. 141 | 142 | ## Friction force 143 | 144 | See 'Friction force on a water stream flowing downhill' (https://forwardscattering.org/post/63) 145 | -------------------------------------------------------------------------------- /library_licences.txt: -------------------------------------------------------------------------------- 1 | 2 | ================================================ SDL ================================================ 3 | 4 | Copyright (C) 1997-2024 Sam Lantinga 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | 22 | 23 | ================================================ Dear ImGui ================================================ 24 | 25 | The MIT License (MIT) 26 | 27 | Copyright (c) 2014-2024 Omar Cornut 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy 30 | of this software and associated documentation files (the "Software"), to deal 31 | in the Software without restriction, including without limitation the rights 32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | copies of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all 37 | copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 45 | SOFTWARE. 46 | 47 | 48 | ================================================ LibPNG ================================================ 49 | * COPYRIGHT NOTICE, DISCLAIMER, and LICENSE 50 | * ========================================= 51 | * 52 | * PNG Reference Library License version 2 53 | * --------------------------------------- 54 | * 55 | * * Copyright (c) 1995-2019 The PNG Reference Library Authors. 56 | * * Copyright (c) 2018-2019 Cosmin Truta. 57 | * * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. 58 | * * Copyright (c) 1996-1997 Andreas Dilger. 59 | * * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. 60 | * 61 | * The software is supplied "as is", without warranty of any kind, 62 | * express or implied, including, without limitation, the warranties 63 | * of merchantability, fitness for a particular purpose, title, and 64 | * non-infringement. In no event shall the Copyright owners, or 65 | * anyone distributing the software, be liable for any damages or 66 | * other liability, whether in contract, tort or otherwise, arising 67 | * from, out of, or in connection with the software, or the use or 68 | * other dealings in the software, even if advised of the possibility 69 | * of such damage. 70 | * 71 | * Permission is hereby granted to use, copy, modify, and distribute 72 | * this software, or portions hereof, for any purpose, without fee, 73 | * subject to the following restrictions: 74 | * 75 | * 1. The origin of this software must not be misrepresented; you 76 | * must not claim that you wrote the original software. If you 77 | * use this software in a product, an acknowledgment in the product 78 | * documentation would be appreciated, but is not required. 79 | * 80 | * 2. Altered source versions must be plainly marked as such, and must 81 | * not be misrepresented as being the original software. 82 | * 83 | * 3. This Copyright notice may not be removed or altered from any 84 | * source or altered source distribution. 85 | 86 | 87 | ================================================ meshoptimizer ================================================ 88 | 89 | MIT License 90 | 91 | Copyright (c) 2016-2021 Arseny Kapoulkine 92 | 93 | Permission is hereby granted, free of charge, to any person obtaining a copy 94 | of this software and associated documentation files (the "Software"), to deal 95 | in the Software without restriction, including without limitation the rights 96 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 97 | copies of the Software, and to permit persons to whom the Software is 98 | furnished to do so, subject to the following conditions: 99 | 100 | The above copyright notice and this permission notice shall be included in all 101 | copies or substantial portions of the Software. 102 | 103 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 104 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 105 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 106 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 107 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 108 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 109 | SOFTWARE. 110 | 111 | 112 | ================================================ OpenEXR ================================================ 113 | /////////////////////////////////////////////////////////////////////////// 114 | // 115 | // Copyright (c) 2002, Industrial Light & Magic, a division of Lucas 116 | // Digital Ltd. LLC 117 | // 118 | // All rights reserved. 119 | // 120 | // Redistribution and use in source and binary forms, with or without 121 | // modification, are permitted provided that the following conditions are 122 | // met: 123 | // * Redistributions of source code must retain the above copyright 124 | // notice, this list of conditions and the following disclaimer. 125 | // * Redistributions in binary form must reproduce the above 126 | // copyright notice, this list of conditions and the following disclaimer 127 | // in the documentation and/or other materials provided with the 128 | // distribution. 129 | // * Neither the name of Industrial Light & Magic nor the names of 130 | // its contributors may be used to endorse or promote products derived 131 | // from this software without specific prior written permission. 132 | // 133 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 134 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 135 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 136 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 137 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 138 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 139 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 140 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 141 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 142 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 143 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 144 | // 145 | /////////////////////////////////////////////////////////////////////////// 146 | 147 | 148 | ================================================ Pugixml ================================================ 149 | /** 150 | * Copyright (c) 2006-2018 Arseny Kapoulkine 151 | * 152 | * Permission is hereby granted, free of charge, to any person 153 | * obtaining a copy of this software and associated documentation 154 | * files (the "Software"), to deal in the Software without 155 | * restriction, including without limitation the rights to use, 156 | * copy, modify, merge, publish, distribute, sublicense, and/or sell 157 | * copies of the Software, and to permit persons to whom the 158 | * Software is furnished to do so, subject to the following 159 | * conditions: 160 | * 161 | * The above copyright notice and this permission notice shall be 162 | * included in all copies or substantial portions of the Software. 163 | * 164 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 165 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 166 | * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 167 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 168 | * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 169 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 170 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 171 | * OTHER DEALINGS IN THE SOFTWARE. 172 | */ 173 | 174 | 175 | ================================================ Zlib ================================================ 176 | (C) 1995-2022 Jean-loup Gailly and Mark Adler 177 | 178 | This software is provided 'as-is', without any express or implied 179 | warranty. In no event will the authors be held liable for any damages 180 | arising from the use of this software. 181 | 182 | Permission is granted to anyone to use this software for any purpose, 183 | including commercial applications, and to alter it and redistribute it 184 | freely, subject to the following restrictions: 185 | 186 | 1. The origin of this software must not be misrepresented; you must not 187 | claim that you wrote the original software. If you use this software 188 | in a product, an acknowledgment in the product documentation would be 189 | appreciated but is not required. 190 | 2. Altered source versions must be plainly marked as such, and must not be 191 | misrepresented as being the original software. 192 | 3. This notice may not be removed or altered from any source distribution. 193 | 194 | Jean-loup Gailly Mark Adler 195 | jloup@gzip.org madler@alumni.caltech.edu 196 | 197 | If you use the zlib library in a product, we would appreciate *not* receiving 198 | lengthy legal documents to sign. The sources are provided for free but without 199 | warranty of any kind. The library has been entirely written by Jean-loup 200 | Gailly and Mark Adler; it does not include third-party code. We make all 201 | contributions to and distributions of this project solely in our personal 202 | capacity, and are not conveying any rights to any intellectual property of 203 | any third parties. 204 | 205 | If you redistribute modified sources, we would appreciate that you include in 206 | the file ChangeLog history information documenting your changes. Please read 207 | the FAQ for more information on the distribution of modified source versions. 208 | 209 | 210 | ================================================ Zstandard ================================================ 211 | 212 | BSD License 213 | 214 | For Zstandard software 215 | 216 | Copyright (c) 2016-present, Facebook, Inc. All rights reserved. 217 | 218 | Redistribution and use in source and binary forms, with or without modification, 219 | are permitted provided that the following conditions are met: 220 | 221 | * Redistributions of source code must retain the above copyright notice, this 222 | list of conditions and the following disclaimer. 223 | 224 | * Redistributions in binary form must reproduce the above copyright notice, 225 | this list of conditions and the following disclaimer in the documentation 226 | and/or other materials provided with the distribution. 227 | 228 | * Neither the name Facebook nor the names of its contributors may be used to 229 | endorse or promote products derived from this software without specific 230 | prior written permission. 231 | 232 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 233 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 234 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 235 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 236 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 237 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 238 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 239 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 240 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 241 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 242 | 243 | 244 | ================================================ double conversion ================================================ 245 | // Copyright 2010 the V8 project authors. All rights reserved. 246 | // Redistribution and use in source and binary forms, with or without 247 | // modification, are permitted provided that the following conditions are 248 | // met: 249 | // 250 | // * Redistributions of source code must retain the above copyright 251 | // notice, this list of conditions and the following disclaimer. 252 | // * Redistributions in binary form must reproduce the above 253 | // copyright notice, this list of conditions and the following 254 | // disclaimer in the documentation and/or other materials provided 255 | // with the distribution. 256 | // * Neither the name of Google Inc. nor the names of its 257 | // contributors may be used to endorse or promote products derived 258 | // from this software without specific prior written permission. 259 | // 260 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 261 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 262 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 263 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 264 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 265 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 266 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 267 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 268 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 269 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 270 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | 3 | project(terraingen) 4 | 5 | add_definitions(/MP) # Enable multi-processor compilation. 6 | add_definitions(/W4) 7 | add_definitions(-DUNICODE -D_UNICODE) 8 | add_definitions(-D__SSE3__ -D__SSSE3__ -D__SSE4_1__) 9 | 10 | add_definitions(-DPNG_ALLOW_BENIGN_ERRORS=1) 11 | add_definitions(-DPNG_INTEL_SSE=1) 12 | add_definitions(-DPNG_NO_SETJMP=1) 13 | 14 | add_definitions(-DBUILD_TESTS=1) 15 | add_definitions(-DOPENCL_OPENGL_INTEROP=1) 16 | 17 | add_definitions(-DNO_LCMS_SUPPORT=1) 18 | 19 | add_definitions(-DNO_BASIS_SUPPORT=1) 20 | add_definitions(-DNO_KTX_SUPPORT=1) 21 | add_definitions(-DNO_GIF_SUPPORT=1) 22 | 23 | 24 | if(WIN32) 25 | if(MSVC_VERSION GREATER_EQUAL 1920 AND MSVC_VERSION LESS 1930) 26 | set(TERRAINGEN_VS_VER 2019) 27 | elseif(MSVC_VERSION GREATER_EQUAL 1930) 28 | set(TERRAINGEN_VS_VER 2022) 29 | else() 30 | message(FATAL_ERROR "Unhandled MSVC version") 31 | endif() 32 | endif() 33 | 34 | 35 | SET(GLARE_CORE_LIBS $ENV{GLARE_CORE_LIBS}) 36 | 37 | #---------------------------- GLARE_CORE_TRUNK ---------------------------- 38 | set(GLARE_CORE_TRUNK "" CACHE FILEPATH "Glare-core trunk source directory") 39 | MESSAGE("GLARE_CORE_TRUNK: '${GLARE_CORE_TRUNK}'") 40 | 41 | if("${GLARE_CORE_TRUNK}" STREQUAL "") 42 | MESSAGE(FATAL_ERROR "Please specify the Glare-core trunk source directory with -DGLARE_CORE_TRUNK=XXX") 43 | endif() 44 | 45 | set(GLARE_CORE_TRUNK_DIR_ENV "${GLARE_CORE_TRUNK}") # Needs to be set for opengl.cmake 46 | 47 | #---------------------------- SDL_BUILD_DIR ---------------------------- 48 | set(SDL_BUILD_DIR "" CACHE FILEPATH "SDL build directory") 49 | MESSAGE("SDL_BUILD_DIR: '${SDL_BUILD_DIR}'") 50 | 51 | if("${SDL_BUILD_DIR}" STREQUAL "") 52 | MESSAGE(FATAL_ERROR "Please specify the SDL2 build directory with -DSDL_BUILD_DIR=XXX") 53 | endif() 54 | if(NOT EXISTS "${SDL_BUILD_DIR}/include/SDL2/SDL.h") 55 | MESSAGE(FATAL_ERROR "Could not find SDL.h from your SDL2 build at '${SDL_BUILD_DIR}/include/SDL2/SDL.h'. Please specify the SDL2 build directory with -DSDL_BUILD_DIR=XXX") 56 | endif() 57 | #---------------------------- End SDL_BUILD_DIR ---------------------------- 58 | 59 | add_definitions(-DBASE_SOURCE_DIR="${PROJECT_SOURCE_DIR}") 60 | 61 | set(jpegdir "${GLARE_CORE_LIBS}/libjpeg-turbo/libjpeg-turbo-3.0.0-vs${TERRAINGEN_VS_VER}-install") 62 | 63 | # These libraries are checked into the glare-core repo to make setting up builds easier. 64 | set(zlibdir "${GLARE_CORE_TRUNK}/zlib") 65 | set(pugixmldir "${GLARE_CORE_TRUNK}/pugixml/src") 66 | set(pngdir "${GLARE_CORE_TRUNK}/libpng") 67 | set(zstddir "${GLARE_CORE_TRUNK}/zstd") 68 | 69 | #set(imguidir "${GLARE_CORE_LIBS}/imgui") 70 | set(imguidir "${GLARE_CORE_TRUNK}/ImGui") 71 | set(libjpegturbodir "${GLARE_CORE_LIBS}/libjpeg-turbo-builds") 72 | 73 | if(WIN32) 74 | set(libjpegturbodir "${libjpegturbodir}/vs_${TERRAINGEN_VS_VER}_64") # Append dir suffix for VS version and bitness. 75 | endif() 76 | 77 | include_directories("./") 78 | include_directories(${SDL_BUILD_DIR}/include/SDL2) 79 | if(WIN32) 80 | include_directories(${SDL_BUILD_DIR}/include-config-release/SDL2) # For SDL_config.h. NOTE: SDL_config.h seems to be the same for both Debug and Release configs, at least on Windows. 81 | else() 82 | include_directories(${SDL_BUILD_DIR}/include-config-/SDL2) 83 | endif() 84 | include_directories(${GLARE_CORE_TRUNK}) 85 | include_directories(${GLARE_CORE_TRUNK}/utils) 86 | include_directories(${GLARE_CORE_TRUNK}/opengl) 87 | include_directories(${zstddir}/lib) 88 | include_directories(${zstddir}/lib/common) # for xxhash 89 | include_directories(${GLARE_CORE_TRUNK}/opencl/khronos) 90 | #include_directories(${GLARE_CORE_TRUNK}/opencl/) 91 | 92 | include_directories(${jpegdir}/include) # libjpeg-turbo-master (jpegdir) has most of the source, 93 | include_directories(${libjpegturbodir}) # libjpegturbodir has jconfig.h 94 | include_directories(${pngdir}) 95 | include_directories(${zlibdir}) 96 | include_directories(${imguidir}) 97 | include_directories(${pugixmldir}) 98 | 99 | 100 | 101 | #============== Tracy profiler ============== 102 | include_directories("${GLARE_CORE_TRUNK}/tracy/public") 103 | set(tracy_files "${GLARE_CORE_TRUNK}/tracy/public/TracyClient.cpp") 104 | 105 | 106 | #============== OpenEXR ============== 107 | include(${GLARE_CORE_TRUNK}/OpenEXR/openexr.cmake) 108 | 109 | #============== OpenCL ============== 110 | set (opencl 111 | ${GLARE_CORE_TRUNK}/opencl/OpenCL.cpp 112 | ${GLARE_CORE_TRUNK}/opencl/OpenCL.h 113 | ${GLARE_CORE_TRUNK}/opencl/OpenCLBuffer.cpp 114 | ${GLARE_CORE_TRUNK}/opencl/OpenCLBuffer.h 115 | ${GLARE_CORE_TRUNK}/opencl/OpenCLCommandQueue.cpp 116 | ${GLARE_CORE_TRUNK}/opencl/OpenCLCommandQueue.h 117 | ${GLARE_CORE_TRUNK}/opencl/OpenCLContext.cpp 118 | ${GLARE_CORE_TRUNK}/opencl/OpenCLContext.h 119 | ${GLARE_CORE_TRUNK}/opencl/OpenCLDevice.cpp 120 | ${GLARE_CORE_TRUNK}/opencl/OpenCLDevice.h 121 | ${GLARE_CORE_TRUNK}/opencl/OpenCLImage.cpp 122 | ${GLARE_CORE_TRUNK}/opencl/OpenCLImage.h 123 | ${GLARE_CORE_TRUNK}/opencl/OpenCLKernel.cpp 124 | ${GLARE_CORE_TRUNK}/opencl/OpenCLKernel.h 125 | ${GLARE_CORE_TRUNK}/opencl/OpenCLPlatform.cpp 126 | ${GLARE_CORE_TRUNK}/opencl/OpenCLPlatform.h 127 | ${GLARE_CORE_TRUNK}/opencl/OpenCLProgram.cpp 128 | ${GLARE_CORE_TRUNK}/opencl/OpenCLProgram.h 129 | ${GLARE_CORE_TRUNK}/opencl/OpenCLProgramCache.cpp 130 | ${GLARE_CORE_TRUNK}/opencl/OpenCLProgramCache.h 131 | #${GLARE_CORE_TRUNK}/opencl/OpenCLTests.cpp 132 | #${GLARE_CORE_TRUNK}/opencl/OpenCLTests.h 133 | ) 134 | 135 | #============== ImGui ============== 136 | set (imgui 137 | ${imguidir}/imgui.cpp 138 | ${imguidir}/imgui.h 139 | ${imguidir}/imgui_demo.cpp 140 | ${imguidir}/imgui_draw.cpp 141 | ${imguidir}/imgui_internal.h 142 | ${imguidir}/imgui_widgets.cpp 143 | ${imguidir}/imgui_tables.cpp 144 | ${imguidir}/imstb_rectpack.h 145 | ${imguidir}/imstb_textedit.h 146 | ${imguidir}/imstb_truetype.h 147 | ${imguidir}/backends/imgui_impl_sdl2.cpp 148 | ${imguidir}/backends/imgui_impl_sdl2.h 149 | ${imguidir}/backends/imgui_impl_opengl3.cpp 150 | ${imguidir}/backends/imgui_impl_opengl3.h 151 | ) 152 | 153 | 154 | #============== libpng ============== 155 | set(libpng 156 | ${pngdir}/png.c ${pngdir}/pngerror.c ${pngdir}/pngget.c ${pngdir}/pngmem.c ${pngdir}/pngpread.c ${pngdir}/pngread.c ${pngdir}/pngrio.c ${pngdir}/pngrtran.c 157 | ${pngdir}/pngrutil.c ${pngdir}/pngset.c ${pngdir}/pngtrans.c ${pngdir}/pngwio.c ${pngdir}/pngwrite.c ${pngdir}/pngwtran.c ${pngdir}/pngwutil.c 158 | ${pngdir}/png.h 159 | ${pngdir}/pngconf.h 160 | ${pngdir}/pngdebug.h 161 | ${pngdir}/pnginfo.h 162 | ${pngdir}/pnglibconf.h 163 | ${pngdir}/pngpriv.h 164 | ${pngdir}/pngstruct.h 165 | 166 | ${pngdir}/intel/intel_init.c 167 | ${pngdir}/intel/filter_sse2_intrinsics.c 168 | ) 169 | 170 | 171 | #============== zlib ============== 172 | set(zlib 173 | ${zlibdir}/adler32.c 174 | ${zlibdir}/compress.c 175 | ${zlibdir}/crc32.c 176 | ${zlibdir}/deflate.c 177 | ${zlibdir}/inffast.c 178 | ${zlibdir}/inflate.c 179 | ${zlibdir}/inftrees.c 180 | ${zlibdir}/trees.c 181 | ${zlibdir}/uncompr.c 182 | ${zlibdir}/zutil.c 183 | ${zlibdir}/crc32.h 184 | ${zlibdir}/deflate.h 185 | ${zlibdir}/inffast.h 186 | ${zlibdir}/inffixed.h 187 | ${zlibdir}/inflate.h 188 | ${zlibdir}/inftrees.h 189 | ${zlibdir}/trees.h 190 | ${zlibdir}/zconf.h 191 | ${zlibdir}/zlib.h 192 | ${zlibdir}/zutil.h 193 | ) 194 | 195 | 196 | #============== pugixml ============== 197 | set(pugixml 198 | ${pugixmldir}/pugixml.cpp 199 | ${pugixmldir}/pugixml.hpp 200 | ${pugixmldir}/pugiconfig.hpp 201 | ) 202 | 203 | # MESSAGE(${pugixml}) 204 | 205 | FILE(GLOB zstandard 206 | "${zstddir}/lib/*.h" 207 | "${zstddir}/lib/common/*.c" 208 | "${zstddir}/lib/common/*.h" 209 | "${zstddir}/lib/compress/*.c" 210 | "${zstddir}/lib/compress/*.h" 211 | "${zstddir}/lib/decompress/*.c" 212 | "${zstddir}/lib/decompress/*.h" 213 | ) 214 | 215 | SOURCE_GROUP(libpng FILES ${libpng}) 216 | SOURCE_GROUP(zlib FILES ${zlib}) 217 | SOURCE_GROUP(pugixml FILES ${pugixml}) 218 | SOURCE_GROUP(zstandard FILES ${zstandard}) 219 | 220 | 221 | #============== glare-core/indigo ============== 222 | set(indigo 223 | ${GLARE_CORE_TRUNK}/indigo/TextureServer.cpp 224 | ${GLARE_CORE_TRUNK}/indigo/TextureServer.h 225 | 226 | ${GLARE_CORE_TRUNK}/dll/IndigoMesh.cpp 227 | ${GLARE_CORE_TRUNK}/dll/include/IndigoMesh.h 228 | ${GLARE_CORE_TRUNK}/dll/IndigoAllocation.cpp 229 | ${GLARE_CORE_TRUNK}/dll/include/IndigoAllocation.h 230 | ) 231 | 232 | #============== graphics ============== 233 | set(graphics 234 | ${GLARE_CORE_TRUNK}/graphics/AnimationData.cpp 235 | ${GLARE_CORE_TRUNK}/graphics/AnimationData.h 236 | ${GLARE_CORE_TRUNK}/graphics/BatchedMesh.cpp 237 | ${GLARE_CORE_TRUNK}/graphics/BatchedMesh.h 238 | ${GLARE_CORE_TRUNK}/graphics/bitmap.cpp 239 | ${GLARE_CORE_TRUNK}/graphics/bitmap.h 240 | ${GLARE_CORE_TRUNK}/graphics/Image.cpp 241 | ${GLARE_CORE_TRUNK}/graphics/Image.h 242 | ${GLARE_CORE_TRUNK}/graphics/Map2D.cpp 243 | ${GLARE_CORE_TRUNK}/graphics/Map2D.h 244 | ${GLARE_CORE_TRUNK}/graphics/ImageMap.cpp 245 | ${GLARE_CORE_TRUNK}/graphics/ImageMap.h 246 | ${GLARE_CORE_TRUNK}/graphics/imformatdecoder.cpp 247 | ${GLARE_CORE_TRUNK}/graphics/imformatdecoder.h 248 | ${GLARE_CORE_TRUNK}/graphics/jpegdecoder.cpp 249 | ${GLARE_CORE_TRUNK}/graphics/jpegdecoder.h 250 | ${GLARE_CORE_TRUNK}/graphics/PNGDecoder.cpp 251 | ${GLARE_CORE_TRUNK}/graphics/PNGDecoder.h 252 | ${GLARE_CORE_TRUNK}/graphics/EXRDecoder.cpp 253 | ${GLARE_CORE_TRUNK}/graphics/EXRDecoder.h 254 | ${GLARE_CORE_TRUNK}/graphics/DXTCompression.cpp 255 | ${GLARE_CORE_TRUNK}/graphics/DXTCompression.h 256 | ${GLARE_CORE_TRUNK}/graphics/CompressedImage.cpp 257 | ${GLARE_CORE_TRUNK}/graphics/CompressedImage.h 258 | ${GLARE_CORE_TRUNK}/graphics/PerlinNoise.cpp 259 | ${GLARE_CORE_TRUNK}/graphics/PerlinNoise.h 260 | ${GLARE_CORE_TRUNK}/graphics/Voronoi.cpp 261 | ${GLARE_CORE_TRUNK}/graphics/Voronoi.h 262 | ${GLARE_CORE_TRUNK}/graphics/GridNoise.cpp 263 | ${GLARE_CORE_TRUNK}/graphics/GridNoise.h 264 | ${GLARE_CORE_TRUNK}/graphics/TextureProcessing.cpp 265 | ${GLARE_CORE_TRUNK}/graphics/TextureProcessing.h 266 | ${GLARE_CORE_TRUNK}/graphics/SRGBUtils.cpp 267 | ${GLARE_CORE_TRUNK}/graphics/SRGBUtils.h 268 | ${GLARE_CORE_TRUNK}/graphics/Colour4f.cpp 269 | ${GLARE_CORE_TRUNK}/graphics/Colour4f.h 270 | ${GLARE_CORE_TRUNK}/graphics/Image4f.cpp 271 | ${GLARE_CORE_TRUNK}/graphics/Image4f.h 272 | ${GLARE_CORE_TRUNK}/graphics/BoxFilterFunction.cpp 273 | ${GLARE_CORE_TRUNK}/graphics/BoxFilterFunction.h 274 | ${GLARE_CORE_TRUNK}/graphics/FilterFunction.cpp 275 | ${GLARE_CORE_TRUNK}/graphics/FilterFunction.h 276 | ${GLARE_CORE_TRUNK}/graphics/TextureData.cpp 277 | ${GLARE_CORE_TRUNK}/graphics/TextureData.h 278 | ) 279 | 280 | include(${GLARE_CORE_TRUNK}/opengl/opengl.cmake) 281 | 282 | set(utils 283 | ${GLARE_CORE_TRUNK}/utils/BestFitAllocator.cpp 284 | ${GLARE_CORE_TRUNK}/utils/BestFitAllocator.h 285 | ${GLARE_CORE_TRUNK}/utils/BufferViewInStream.cpp 286 | ${GLARE_CORE_TRUNK}/utils/BufferViewInStream.h 287 | ${GLARE_CORE_TRUNK}/utils/Clock.cpp 288 | ${GLARE_CORE_TRUNK}/utils/Clock.h 289 | ${GLARE_CORE_TRUNK}/utils/CycleTimer.cpp 290 | ${GLARE_CORE_TRUNK}/utils/CycleTimer.h 291 | ${GLARE_CORE_TRUNK}/utils/ConPrint.cpp 292 | ${GLARE_CORE_TRUNK}/utils/ConPrint.h 293 | ${GLARE_CORE_TRUNK}/utils/StringUtils.cpp 294 | ${GLARE_CORE_TRUNK}/utils/StringUtils.h 295 | ${GLARE_CORE_TRUNK}/utils/Mutex.cpp 296 | ${GLARE_CORE_TRUNK}/utils/Mutex.h 297 | ${GLARE_CORE_TRUNK}/utils/Lock.cpp 298 | ${GLARE_CORE_TRUNK}/utils/Lock.h 299 | ${GLARE_CORE_TRUNK}/utils/DynamicLib.cpp 300 | ${GLARE_CORE_TRUNK}/utils/DynamicLib.h 301 | ${GLARE_CORE_TRUNK}/utils/StandardPrintOutput.cpp 302 | ${GLARE_CORE_TRUNK}/utils/StandardPrintOutput.h 303 | ${GLARE_CORE_TRUNK}/utils/FileUtils.cpp 304 | ${GLARE_CORE_TRUNK}/utils/FileUtils.h 305 | ${GLARE_CORE_TRUNK}/utils/MemMappedFile.cpp 306 | ${GLARE_CORE_TRUNK}/utils/MemMappedFile.h 307 | ${GLARE_CORE_TRUNK}/utils/PlatformUtils.cpp 308 | ${GLARE_CORE_TRUNK}/utils/PlatformUtils.h 309 | ${GLARE_CORE_TRUNK}/utils/TaskManager.cpp 310 | ${GLARE_CORE_TRUNK}/utils/TaskManager.h 311 | ${GLARE_CORE_TRUNK}/utils/Task.cpp 312 | ${GLARE_CORE_TRUNK}/utils/Task.h 313 | ${GLARE_CORE_TRUNK}/utils/Condition.cpp 314 | ${GLARE_CORE_TRUNK}/utils/Condition.h 315 | ${GLARE_CORE_TRUNK}/utils/MyThread.cpp 316 | ${GLARE_CORE_TRUNK}/utils/MyThread.h 317 | ${GLARE_CORE_TRUNK}/utils/TaskRunnerThread.cpp 318 | ${GLARE_CORE_TRUNK}/utils/TaskRunnerThread.h 319 | ${GLARE_CORE_TRUNK}/utils/ThreadManager.cpp 320 | ${GLARE_CORE_TRUNK}/utils/ThreadManager.h 321 | ${GLARE_CORE_TRUNK}/utils/Parser.cpp 322 | ${GLARE_CORE_TRUNK}/utils/Parser.h 323 | ${GLARE_CORE_TRUNK}/utils/FileHandle.cpp 324 | ${GLARE_CORE_TRUNK}/utils/FileHandle.h 325 | ${GLARE_CORE_TRUNK}/utils/Timer.cpp 326 | ${GLARE_CORE_TRUNK}/utils/Timer.h 327 | ${GLARE_CORE_TRUNK}/utils/MemAlloc.cpp 328 | ${GLARE_CORE_TRUNK}/utils/MemAlloc.h 329 | ${GLARE_CORE_TRUNK}/utils/FileInStream.cpp 330 | ${GLARE_CORE_TRUNK}/utils/FileInStream.h 331 | ${GLARE_CORE_TRUNK}/utils/FileOutStream.cpp 332 | ${GLARE_CORE_TRUNK}/utils/FileOutStream.h 333 | ${GLARE_CORE_TRUNK}/utils/OutStream.cpp 334 | ${GLARE_CORE_TRUNK}/utils/OutStream.h 335 | ${GLARE_CORE_TRUNK}/utils/InStream.cpp 336 | ${GLARE_CORE_TRUNK}/utils/InStream.h 337 | ${GLARE_CORE_TRUNK}/utils/BufferInStream.cpp 338 | ${GLARE_CORE_TRUNK}/utils/BufferInStream.h 339 | ${GLARE_CORE_TRUNK}/utils/BufferOutStream.cpp 340 | ${GLARE_CORE_TRUNK}/utils/BufferOutStream.h 341 | ${GLARE_CORE_TRUNK}/utils/TestUtils.cpp 342 | ${GLARE_CORE_TRUNK}/utils/TestUtils.h 343 | ${GLARE_CORE_TRUNK}/utils/RuntimeCheck.cpp 344 | ${GLARE_CORE_TRUNK}/utils/RuntimeCheck.h 345 | ${GLARE_CORE_TRUNK}/utils/MessageableThread.cpp 346 | ${GLARE_CORE_TRUNK}/utils/MessageableThread.h 347 | ${GLARE_CORE_TRUNK}/utils/GeneralMemAllocator.cpp 348 | ${GLARE_CORE_TRUNK}/utils/GeneralMemAllocator.h 349 | ${GLARE_CORE_TRUNK}/utils/PoolAllocator.cpp 350 | ${GLARE_CORE_TRUNK}/utils/PoolAllocator.h 351 | ${GLARE_CORE_TRUNK}/utils/KillThreadMessage.cpp 352 | ${GLARE_CORE_TRUNK}/utils/KillThreadMessage.h 353 | ${GLARE_CORE_TRUNK}/utils/ThreadMessage.cpp 354 | ${GLARE_CORE_TRUNK}/utils/ThreadMessage.h 355 | ${GLARE_CORE_TRUNK}/utils/GlareString.cpp 356 | ${GLARE_CORE_TRUNK}/utils/GlareString.h 357 | ${GLARE_CORE_TRUNK}/utils/FileDialogs.cpp 358 | ${GLARE_CORE_TRUNK}/utils/FileDialogs.h 359 | ${GLARE_CORE_TRUNK}/utils/XMLWriteUtils.cpp 360 | ${GLARE_CORE_TRUNK}/utils/XMLWriteUtils.h 361 | ${GLARE_CORE_TRUNK}/utils/XMLParseUtils.cpp 362 | ${GLARE_CORE_TRUNK}/utils/XMLParseUtils.h 363 | ${GLARE_CORE_TRUNK}/utils/IndigoXMLDoc.cpp 364 | ${GLARE_CORE_TRUNK}/utils/IndigoXMLDoc.h 365 | ${GLARE_CORE_TRUNK}/utils/IndigoXMLDoc.cpp 366 | ${GLARE_CORE_TRUNK}/utils/IndigoXMLDoc.h 367 | ${GLARE_CORE_TRUNK}/utils/ArgumentParser.cpp 368 | ${GLARE_CORE_TRUNK}/utils/ArgumentParser.h 369 | ) 370 | 371 | set(maths 372 | ${GLARE_CORE_TRUNK}/maths/SSE.cpp 373 | ${GLARE_CORE_TRUNK}/maths/SSE.h 374 | ${GLARE_CORE_TRUNK}/maths/Vec4f.cpp 375 | ${GLARE_CORE_TRUNK}/maths/Vec4f.h 376 | ${GLARE_CORE_TRUNK}/maths/Matrix4f.cpp 377 | ${GLARE_CORE_TRUNK}/maths/Matrix4f.h 378 | ) 379 | 380 | set(MESHOPTIMIZER_DIR "${GLARE_CORE_TRUNK}/meshoptimizer/src") 381 | 382 | set(meshoptimizer 383 | ${MESHOPTIMIZER_DIR}/allocator.cpp 384 | ${MESHOPTIMIZER_DIR}/meshoptimizer.h 385 | ${MESHOPTIMIZER_DIR}/allocator.cpp 386 | ${MESHOPTIMIZER_DIR}/clusterizer.cpp 387 | ${MESHOPTIMIZER_DIR}/indexcodec.cpp 388 | ${MESHOPTIMIZER_DIR}/indexgenerator.cpp 389 | ${MESHOPTIMIZER_DIR}/overdrawanalyzer.cpp 390 | ${MESHOPTIMIZER_DIR}/overdrawoptimizer.cpp 391 | ${MESHOPTIMIZER_DIR}/simplifier.cpp 392 | ${MESHOPTIMIZER_DIR}/spatialorder.cpp 393 | ${MESHOPTIMIZER_DIR}/stripifier.cpp 394 | ${MESHOPTIMIZER_DIR}/vcacheanalyzer.cpp 395 | ${MESHOPTIMIZER_DIR}/vcacheoptimizer.cpp 396 | ${MESHOPTIMIZER_DIR}/vertexcodec.cpp 397 | ${MESHOPTIMIZER_DIR}/vertexfilter.cpp 398 | ${MESHOPTIMIZER_DIR}/vfetchanalyzer.cpp 399 | ${MESHOPTIMIZER_DIR}/vfetchoptimizer.cpp 400 | ) 401 | 402 | 403 | 404 | FILE(GLOB double_conversion "${GLARE_CORE_TRUNK}/double-conversion/*.cc" "${GLARE_CORE_TRUNK}/double-conversion/*.h") 405 | 406 | 407 | SOURCE_GROUP(indigo FILES ${indigo}) 408 | SOURCE_GROUP(graphics FILES ${graphics}) 409 | SOURCE_GROUP(opengl FILES ${opengl}) 410 | SOURCE_GROUP(utils FILES ${utils}) 411 | SOURCE_GROUP(maths FILES ${maths}) 412 | SOURCE_GROUP(double-conversion FILES ${double_conversion}) 413 | SOURCE_GROUP(imgui FILES ${imgui}) 414 | SOURCE_GROUP(opencl FILES ${opencl}) 415 | SOURCE_GROUP(meshoptimizer FILES ${meshoptimizer}) 416 | 417 | 418 | add_executable(terraingen 419 | terraingen.cpp 420 | erosion_kernel.cl 421 | #notes.txt 422 | ${indigo} 423 | ${graphics} 424 | ${opengl} 425 | ${utils} 426 | ${maths} 427 | ${double_conversion} 428 | ${openexr_all_files} 429 | ${libjpg} 430 | ${libpng} 431 | ${zlib} 432 | ${pugixml} 433 | ${zstandard} 434 | ${imgui} 435 | ${opencl} 436 | ${meshoptimizer} 437 | ) 438 | 439 | 440 | #if(WIN32) 441 | # SET(TURBOJPEG_LIB ${libjpegturbodir}/$(Configuration)/turbojpeg-static.lib) 442 | #else() 443 | # SET(TURBOJPEG_LIB ${libjpegturbodir}/libjpeg.a) 444 | #endif() 445 | 446 | 447 | target_link_libraries(terraingen 448 | debug "${SDL_BUILD_DIR}/Debug/SDL2d.lib" 449 | debug "${SDL_BUILD_DIR}/Debug/SDL2maind.lib" 450 | optimized "${SDL_BUILD_DIR}/RelWithDebInfo/SDL2.lib" 451 | optimized "${SDL_BUILD_DIR}/RelWithDebInfo/SDL2main.lib" 452 | Opengl32.lib 453 | debug "${jpegdir}/lib/turbojpeg-static.lib" 454 | optimized "${jpegdir}/lib/turbojpeg-static.lib" 455 | ) 456 | -------------------------------------------------------------------------------- /erosion_kernel.cl: -------------------------------------------------------------------------------- 1 | /*===================================================================== 2 | erosion_kernel.cl 3 | ----------------- 4 | Copyright Nicholas Chapman 2025 - 5 | =====================================================================*/ 6 | 7 | // See "Fast Hydraulic Erosion Simulation and Visualization on GPU" 8 | // Also 9 | // "Fast Hydraulic and Thermal Erosion on the GPU" 10 | // http://www.cescg.org/CESCG-2011/papers/TUBudapest-Jako-Balazs.pdf 11 | 12 | 13 | #define TextureShow_Default 0 14 | #define TextureShow_WaterSpeed 1 15 | #define TextureShow_WaterDepth 2 16 | #define TextureShow_SuspendedSedimentVol 3 17 | #define TextureShow_DepositedSedimentH 4 18 | 19 | 20 | inline float square(float x) 21 | { 22 | return x*x; 23 | } 24 | 25 | // Needs to match TerrainState in terraingen.cpp! 26 | typedef struct 27 | { 28 | float height; // height of uneroded terrain (m) (deposited sediment sits on top of this) 29 | float suspended_vol; // Volume of suspended sediment. (m^3) 30 | float deposited_sed_h; // Height of deposited sediment (m) 31 | 32 | float water_mass; // water_mass = water_depth * cell_w^2 * water_density, water_depth = water_mass / (cell_w^2 * water_density) 33 | float2 water_vel; // average velocity of water in cell in x and y directions. m/s. 34 | 35 | float new_water_mass; 36 | float new_suspended_vol; 37 | float2 new_water_vel; 38 | 39 | float2 water_vel_laplacian; 40 | float2 dterrainh_dxy; 41 | //float2 duv_dx; 42 | //float2 duv_dy; 43 | 44 | float2 thermal_vel; // Velocity of thermally eroded material, in pixel coordinates. 45 | float thermal_move_vol; // Volume of thermally eroded material 46 | 47 | float height_laplacian; 48 | } TerrainState; 49 | 50 | 51 | // Not used currently 52 | typedef struct 53 | { 54 | float f_L, f_R, f_T, f_B; // outflow flux. (m^3 s^-1) 55 | float sed_f_L, sed_f_R, sed_f_T, sed_f_B; // outflow sediment flux. (m^3 s^-1) 56 | 57 | } FlowState; 58 | 59 | // Not used currently 60 | typedef struct 61 | { 62 | float flux[8]; // outflow flux per unit area of cell. m^3 s^-1 / m^2 = m s^-1 63 | 64 | } ThermalErosionState; 65 | 66 | 67 | // Needs to match Constants in terraingen.cpp! 68 | typedef struct 69 | { 70 | int W; // grid width 71 | int H; // grid height 72 | float cell_w; // Width of cell = spacing between grid cells (metres) 73 | float recip_cell_w; // 1 / cell_w 74 | 75 | float delta_t; // time step 76 | float r; // rainfall rate 77 | float A; // cross-sectional 'pipe' area 78 | float g; // gravity accel magnitude. positive. 79 | float l; // virtual pipe length 80 | float f; // friction constant 81 | //float k; // viscous drag coefficient (see https://en.wikipedia.org/wiki/Shallow_water_equations) 82 | float nu; // kinematic viscosity 83 | 84 | float K_c;// = 0.01; // 1; // sediment capacity constant 85 | float K_s;// = 0.01; // 0.5; // dissolving constant. 86 | float K_d;// = 0.01; // 1; // deposition constant 87 | float K_dmax;// = 0.1f; // Maximum erosion depth: water depth at which erosion stops. 88 | float K_coll; // K_coll = 0: collision cos(angle) not used, K_coll = 1: erosion rate proportional to collision cos(angle) 89 | float K_cos_angle_threshold; // K_coll = 0: collision cos(angle) not used, K_coll = 1: erosion rate proportional to collision cos(angle) 90 | float q_0; // Minimum unit water discharge for sediment carrying. 91 | float K_e; // Evaporation constant 92 | 93 | float K_smooth; // smoothing constant 94 | float laplacian_threshold; 95 | float K_t; // thermal erosion constant 96 | float K_tdep; // thermal erosion constant for deposited sediment 97 | float max_talus_angle; 98 | float tan_max_talus_angle; 99 | float max_deposited_talus_angle; 100 | float tan_max_deposited_talus_angle; 101 | float sea_level; 102 | float current_time; 103 | 104 | // Draw options: 105 | int include_water_height; 106 | int draw_water; 107 | 108 | float rock_col[3]; 109 | float sediment_col[3]; 110 | float sediment_col_step; 111 | float sediment_col_weight; 112 | float vegetation_col[3]; 113 | float water_depth_col[3]; 114 | float water_depth_col_step; 115 | float water_depth_col_weight; 116 | float water_speed_col[3]; 117 | float water_speed_col_step; 118 | float water_speed_col_weight; 119 | 120 | int debug_draw_channel; 121 | float debug_display_max_val; 122 | 123 | float water_z_bias; 124 | } Constants; 125 | 126 | 127 | float rainfallFactorForCoords(int x, int y) 128 | { 129 | const float px = (float)x; 130 | const float py = (float)y; 131 | 132 | //return length((float2)(px, py) - (float2)(W - 70, H/2.f)) < 50.f ? 1.f : 0.f; 133 | return 1.f; 134 | } 135 | 136 | // NEW: sets water_vel_laplacian, water_vel_partial_derivs (duv_dx, duv_dy) 137 | __kernel void computeWaterVelDerivs( 138 | __global TerrainState* restrict const terrain_state, 139 | __constant Constants* restrict const constants 140 | ) 141 | { 142 | const int x = get_global_id(0); 143 | const int y = get_global_id(1); 144 | 145 | const int x_minus_1 = max(x-1, 0); 146 | const int x_plus_1 = min(x+1, constants->W-1); 147 | const int y_minus_1 = max(y-1, 0); 148 | const int y_plus_1 = min(y+1, constants->W-1); 149 | 150 | __global const TerrainState* const state_left = &terrain_state[x_minus_1 + y * constants->W]; 151 | __global const TerrainState* const state_right = &terrain_state[x_plus_1 + y * constants->W]; 152 | __global const TerrainState* const state_top = &terrain_state[x + y_plus_1 * constants->W]; 153 | __global const TerrainState* const state_bot = &terrain_state[x + y_minus_1 * constants->W]; 154 | __global TerrainState* const state_middle = &terrain_state[x + y *constants->W]; 155 | 156 | const float2 uv_m = state_middle ->water_vel; 157 | const float2 uv_L = state_left ->water_vel; 158 | const float2 uv_R = state_right ->water_vel; 159 | const float2 uv_T = state_top ->water_vel; 160 | const float2 uv_B = state_bot ->water_vel; 161 | 162 | //const float2 duv_dx = (uv_R - uv_L) / (2 * constants->cell_w); 163 | //const float2 duv_dy = (uv_T - uv_B) / (2 * constants->cell_w); 164 | 165 | /*const float d2_u_dx2 = (uv_R.x - 2*uv_m + uv_L) / square(constants->cell_w); 166 | const float d2_u_dy2 = (uv_T.x - 2*uv_m + uv_B) / square(constants->cell_w); 167 | const float d2_v_dx2 = (uv_R.y - 2*uv_m + uv_L) / square(constants->cell_w); 168 | const float d2_v_dy2 = (uv_T.y - 2*uv_m + uv_B) / square(constants->cell_w);*/ 169 | const float2 d2_uv_dx2 = (uv_R - 2*uv_m + uv_L) / square(constants->cell_w); // (d^2u/dx^2, d^2v/dx^2) 170 | const float2 d2_uv_dy2 = (uv_T - 2*uv_m + uv_B) / square(constants->cell_w); // (d^2u/dy^2, d^2v/dy^2) 171 | 172 | const float2 uv_curv = d2_uv_dx2 + d2_uv_dy2; // (d^2u/dx^2 + d^2u/dy^2, d^2v/dx^2 + d^2v/dy^2) 173 | 174 | state_middle->water_vel_laplacian = uv_curv; 175 | 176 | //state_middle->duv_dx = duv_dx; 177 | //state_middle->duv_dy = duv_dy; 178 | } 179 | 180 | 181 | float waterHeightForMass(float water_mass, __constant Constants* restrict const constants) 182 | { 183 | float water_density = 1000.f; 184 | return water_mass * square(constants->recip_cell_w) * (1.f / water_density); // h = m / (cell_w^2 * rho) 185 | } 186 | 187 | float waterMassForHeight(float water_height, __constant Constants* restrict const constants) 188 | { 189 | float water_density = 1000.f; 190 | return water_height * square(constants->cell_w) * water_density; // m = h * cell_w^2 * rho 191 | } 192 | 193 | 194 | 195 | // Updates dterrainh_dxy, water_vel 196 | __kernel void waterVelFieldUpdateKernel( 197 | __global TerrainState* restrict const terrain_state, 198 | __constant Constants* restrict const constants 199 | ) 200 | { 201 | const int x = get_global_id(0); 202 | const int y = get_global_id(1); 203 | 204 | const int x_minus_1 = max(x-1, 0); 205 | const int x_plus_1 = min(x+1, constants->W-1); 206 | const int y_minus_1 = max(y-1, 0); 207 | const int y_plus_1 = min(y+1, constants->H-1); 208 | 209 | __global const TerrainState* const state_left = &terrain_state[x_minus_1 + y * constants->W]; 210 | __global const TerrainState* const state_right = &terrain_state[x_plus_1 + y * constants->W]; 211 | __global const TerrainState* const state_top = &terrain_state[x + y_plus_1 * constants->W]; 212 | __global const TerrainState* const state_bot = &terrain_state[x + y_minus_1 * constants->W]; 213 | __global TerrainState* const state_middle = &terrain_state[x + y * constants->W]; 214 | 215 | const float d_m = waterHeightForMass(state_middle->water_mass, constants); 216 | const float d_L = waterHeightForMass(state_left ->water_mass, constants); 217 | const float d_T = waterHeightForMass(state_top ->water_mass, constants); 218 | const float d_R = waterHeightForMass(state_right ->water_mass, constants); 219 | const float d_B = waterHeightForMass(state_bot ->water_mass, constants); 220 | 221 | const float t_m = state_middle->height + state_middle ->deposited_sed_h; 222 | const float t_L = state_left ->height + state_left ->deposited_sed_h; 223 | const float t_R = state_right ->height + state_right ->deposited_sed_h; 224 | const float t_T = state_top ->height + state_top ->deposited_sed_h; 225 | const float t_B = state_bot ->height + state_bot ->deposited_sed_h; 226 | 227 | // Compute partial derivs of terrain height, will be used in erosionAndDepositionKernel. 228 | state_middle->dterrainh_dxy = (float2)( 229 | (t_R - t_L) * 0.5f * constants->recip_cell_w, // (t_R - t_L) / (2.0 * constants->cell_w) 230 | (t_T - t_B) * 0.5f * constants->recip_cell_w 231 | ); 232 | 233 | // Compute total water surface height: (terrain height + water depth) 234 | const float h_m = t_m + d_m; 235 | const float h_L = t_L + d_L; 236 | const float h_R = t_R + d_R; 237 | const float h_T = t_T + d_T; 238 | const float h_B = t_B + d_B; 239 | 240 | // Compute gradient of resulting water surface height 241 | float2 grad = (float2)( 242 | (h_R - h_L) * 0.5f * constants->recip_cell_w, 243 | (h_T - h_B) * 0.5f * constants->recip_cell_w 244 | ); 245 | 246 | // Compute acceleration of the water in this grid cell, based on some terms in the shallow water equations: https://en.wikipedia.org/wiki/Shallow_water_equations 247 | float2 accel = -constants->g * grad 248 | + constants->nu * state_middle->water_vel_laplacian 249 | - constants->f * state_middle->water_vel * length(state_middle->water_vel) / max(0.01f, d_m); // See 'Friction force on a water stream flowing downhill', https://forwardscattering.org/post/63 250 | 251 | // -/*u=*/state_middle->water_vel.x * state_middle->duv_dx // NOTE: not using these terms currently, not sure if they are needed. 252 | // -/*v=*/state_middle->water_vel.y * state_middle->duv_dy; 253 | 254 | // Integrate acceleration, adding to velocity 255 | state_middle->water_vel += constants->delta_t * accel; 256 | 257 | // Limit water speed so that water can't move more than 1 grid cell per time step, otherwise the reintegration procedure will 'lose' the water. 258 | float v = length(state_middle->water_vel); 259 | float max_v = constants->cell_w; 260 | if(v > max_v) 261 | state_middle->water_vel *= max_v / v; 262 | 263 | // Boundary conditions: force zero velocity out of boundaries: 264 | if(x == 0) 265 | state_middle->water_vel.x = max(state_middle->water_vel.x, 0.f); 266 | else if(x == constants->W - 1) 267 | state_middle->water_vel.x = min(state_middle->water_vel.x, 0.f); 268 | 269 | if(y == 0) 270 | state_middle->water_vel.y = max(state_middle->water_vel.y, 0.f); 271 | else if(y == constants->H - 1) 272 | state_middle->water_vel.y = min(state_middle->water_vel.y, 0.f); 273 | 274 | // TODO: reenable: Sea boundary conditions: 275 | // If this is an edge cell, and if terrain level is below sea level, set water height so that the total terrain + water height = sea level. 276 | /*if((x == 0) || (x == W-1) || (y == 0) || (y == H-1)) 277 | { 278 | float sea_level = constants->sea_level; 279 | //if(x == 0) 280 | // sea_level = constants->sea_level + sin(constants->current_time) * 6.0; // incoming water waves! 281 | const float total_terrain_h = terrain_state_middle->height + terrain_state_middle->deposited_sed_h; 282 | if(total_terrain_h < sea_level) 283 | d_2 = sea_level - total_terrain_h; 284 | }*/ 285 | } 286 | 287 | 288 | // Transports both water and sediment 289 | // Updates new_water_vel, new_water_mass, new_suspended_vol 290 | __kernel void waterAndSedimentTransportationKernel( 291 | __global TerrainState* restrict const terrain_state, 292 | __constant Constants* restrict const constants 293 | ) 294 | { 295 | const int x = get_global_id(0); 296 | const int y = get_global_id(1); 297 | 298 | __global TerrainState* const state_middle = &terrain_state[x + y *constants->W]; 299 | 300 | // Loop over neighbouring cells 301 | float2 total_water_momentum_in = (float2)(0.f, 0.f); // Aka total water momentum 302 | float total_mass_in = 0.f; 303 | float total_suspended_vol_in = 0.f; 304 | for(int ny = y-1; ny <= y+1; ny++) 305 | for(int nx = x-1; nx <= x+1; nx++) 306 | { 307 | if(nx >= 0 && nx < constants->W && ny >= 0 && ny < constants->H) 308 | { 309 | __global const TerrainState* const n_state = &terrain_state[nx + ny * constants->W]; 310 | float2 vel_px_coords = n_state->water_vel * constants->recip_cell_w; 311 | float2 new_pos = (float2)(nx, ny) + vel_px_coords * constants->delta_t; 312 | 313 | /*if(x == 100 && y == 100) 314 | { 315 | printf("nx, ny: %f, %f \n", (float)nx, (float)ny); 316 | printf("n_state->water_position: %f, %f \n", n_state->water_position.x, n_state->water_position.y); 317 | printf("n_state->water_vel: %f, %f \n", n_state->water_vel.x, n_state->water_vel.y); 318 | printf("n_state->water_mass: %f \n", n_state->water_mass); 319 | printf("new_pos: %f, %f \n", new_pos.x, new_pos.y); 320 | }*/ 321 | 322 | const float x_diff = fabs((float)x - new_pos.x); 323 | const float y_diff = fabs((float)y - new_pos.y); 324 | const float weight = max(0.f, (1 - x_diff)) * max(0.f, (1 - y_diff)); 325 | const float weighted_mass = n_state->water_mass * weight; 326 | total_mass_in += weighted_mass; 327 | total_water_momentum_in += n_state->water_vel * weighted_mass; 328 | total_suspended_vol_in += n_state->suspended_vol * weight; 329 | } 330 | } 331 | 332 | if(total_mass_in > 0) 333 | total_water_momentum_in /= total_mass_in; // Convert from momentum to velocity 334 | 335 | state_middle->new_water_vel = total_water_momentum_in; 336 | state_middle->new_water_mass = total_mass_in; 337 | state_middle->new_suspended_vol = total_suspended_vol_in; 338 | } 339 | 340 | 341 | // updates height, suspended_vol, deposited_sed_h, also assigns new_water_mass -> water_mass etc. 342 | __kernel void erosionAndDepositionKernel( 343 | __global TerrainState* restrict const terrain_state, 344 | __constant Constants* restrict const constants 345 | ) 346 | { 347 | const int x = get_global_id(0); 348 | const int y = get_global_id(1); 349 | 350 | const int x_minus_1 = max(x-1, 0); 351 | const int x_plus_1 = min(x+1, constants->W-1); 352 | const int y_minus_1 = max(y-1, 0); 353 | const int y_plus_1 = min(y+1, constants->H-1); 354 | 355 | __global TerrainState* const state_middle = &terrain_state[x + y * constants->W]; 356 | 357 | 358 | state_middle->water_mass = state_middle->new_water_mass; 359 | state_middle->water_vel = state_middle->new_water_vel; 360 | state_middle->suspended_vol = state_middle->new_suspended_vol; 361 | 362 | const float2 dterrainh_dxy = state_middle->dterrainh_dxy; 363 | const float3 normal = normalize((float3)(-dterrainh_dxy, 1.f)); 364 | 365 | //const float cos_alpha = normal.z; 366 | //const float sin_alpha = sqrt(1 - min(1.0f, cos_alpha*cos_alpha)); 367 | //const float use_sin_alpha = sin_alpha;//max(0.1f, sin_alpha); // NOTE: min sin alpha 368 | 369 | // float water_flux = sqrt(square(state_middle->u) + square(state_middle->v)); // Volume of water passing through cell per unit time (m^3 s^-1) 370 | 371 | // Compute l_max as a function of water height (d) (eqn. 10 from 'Fast Hydraulic and Thermal Erosion on the GPU') 372 | 373 | // const float3 water_flux_vec = (float3)(state_middle->u, state_middle->v, (state_middle->u * dh_dx + state_middle->v * dh_dy) * constants->cell_w); 374 | 375 | const float3 water_vel3 = (float3)(state_middle->water_vel.x, state_middle->water_vel.y, state_middle->water_vel.x * dterrainh_dxy.x + state_middle->water_vel.y * dterrainh_dxy.y); 376 | const float3 unit_water_vel = normalize(water_vel3); 377 | const float hit_dot = max(constants->K_cos_angle_threshold, -dot(unit_water_vel, normal)); 378 | 379 | const float water_d = waterHeightForMass(state_middle->water_mass, constants); 380 | 381 | const float q = length(state_middle->water_vel) * min(water_d, constants->K_dmax);//min(10.0f, water_flux / constants->cell_w); 382 | //float q_to_gamma = q;//square(q); 383 | //q_to_gamma = max(0.f, q_to_gamma - constants->q_0); 384 | 385 | // Compute Sediment transport capacity 386 | const float current_vol = state_middle->suspended_vol; // current vol 387 | const float max_vol = /*sediment capacity constant=*/constants->K_c * q * square(constants->cell_w); // max vol 388 | 389 | float height = state_middle->height; 390 | float suspended_vol = state_middle->suspended_vol; 391 | float deposited_sed_h = state_middle->deposited_sed_h; 392 | 393 | if(max_vol > current_vol) 394 | { 395 | float sed_change_vol = mix(1.f, hit_dot, constants->K_coll) * constants->delta_t * constants->K_s * length(state_middle->water_vel)/* * (max_vol - current_vol)*/; 396 | float sed_change_rock_vol = sed_change_vol * 0.3f; 397 | float sed_change_dep_vol = sed_change_vol * 0.7f; 398 | 399 | // Dissolve any deposited sediment into the water 400 | const float sed_change_dep_h = sed_change_dep_vol / square(constants->cell_w); // m = m^3 / m^2 401 | const float deposited_sed_delta_h = min(sed_change_dep_h, deposited_sed_h); // Dissolve <= the amount of deposited sediment here. 402 | deposited_sed_h -= deposited_sed_delta_h; 403 | suspended_vol += deposited_sed_delta_h * square(constants->cell_w); 404 | 405 | //sed_change_dep -= deposited_sed_delta; 406 | 407 | //if(sed_change > 0) // If we have dissolved all deposited sediment, and there is still dissolving to be done: 408 | { 409 | // Dissolve underlying rock 410 | 411 | height -= sed_change_rock_vol / square(constants->cell_w);// Reduce terrain height 412 | suspended_vol += sed_change_rock_vol; // Add to suspended height 413 | } 414 | } 415 | else // else suspended amount exceeds transport capacity, so deposit sediment: 416 | { 417 | float sed_change_vol = constants->delta_t * constants->K_d * current_vol/* * (current_vol - max_vol)*//*(cur_suspended_rate - C)*/; 418 | sed_change_vol = min(sed_change_vol, suspended_vol); // Don't exceed current suspended volume 419 | 420 | suspended_vol -= sed_change_vol; 421 | deposited_sed_h += sed_change_vol / square(constants->cell_w); 422 | } 423 | 424 | // Write 425 | state_middle->height = height; 426 | state_middle->suspended_vol = suspended_vol; 427 | state_middle->deposited_sed_h = deposited_sed_h; 428 | } 429 | 430 | 431 | inline float biLerp(float a, float b, float c, float d, float t_x, float t_y) 432 | { 433 | const float one_t_x = 1 - t_x; 434 | const float one_t_y = 1 - t_y; 435 | return 436 | one_t_x * one_t_y * a + 437 | t_x * one_t_y * b + 438 | one_t_x * t_y * c + 439 | t_x * t_y * d; 440 | } 441 | 442 | 443 | #if 0 444 | inline float mitchellNetravaliEval(float x) 445 | { 446 | float B = 0.5f; 447 | float C = 0.25f; 448 | 449 | float region_0_a = ((12) - B*9 - C*6) * (1.f/6); 450 | float region_0_b = ((-18) + B*12 + C*6) * (1.f/6); 451 | float region_0_d = ((6) - B*2 ) * (1.f/6); 452 | 453 | float region_1_a = (-B - C*6) * (1.f/6); 454 | float region_1_b = (B*6 + C*30) * (1.f/6); 455 | float region_1_c = (B*-12 - C*48) * (1.f/6); 456 | float region_1_d = (B*8 + C*24) * (1.f/6); 457 | 458 | float region_0_f = region_0_a * (x*x*x) + region_0_b * (x*x) + region_0_d; 459 | float region_1_f = region_1_a * (x*x*x) + region_1_b * (x*x) + region_1_c * x + region_1_d; 460 | if(x < 1.0f) 461 | return region_0_f; 462 | else if(x < 2.f) 463 | return region_1_f; 464 | else 465 | return 0; 466 | } 467 | 468 | 469 | inline float mitchellNetravaliCubic(float px, float py, __global TerrainState* restrict const terrain_state) 470 | { 471 | int ut_minus_1 = clamp((int)px - 1, 0, W); 472 | int ut = clamp((int)px , 0, W); 473 | int ut_1 = clamp((int)px + 1, 0, W); 474 | int ut_2 = clamp((int)px + 2, 0, W); 475 | 476 | int vt_minus_1 = clamp((int)py - 1, 0, H); 477 | int vt = clamp((int)py , 0, H); 478 | int vt_1 = clamp((int)py + 1, 0, H); 479 | int vt_2 = clamp((int)py + 2, 0, H); 480 | 481 | float sq_dx_minus_1 = square(px - (float)ut_minus_1); 482 | float sq_dx = square(px - (float)ut); 483 | float sq_dx_1 = square(px - (float)ut_1); 484 | float sq_dx_2 = square(px - (float)ut_2); 485 | 486 | float sq_dy_minus_1 = square(py - (float)vt_minus_1); 487 | float sq_dy = square(py - (float)vt); 488 | float sq_dy_1 = square(py - (float)vt_1); 489 | float sq_dy_2 = square(py - (float)vt_2); 490 | 491 | const float v0 = terrain_state[(ut_minus_1 + W * vt_minus_1 )].suspended; 492 | const float v1 = terrain_state[(ut + W * vt_minus_1 )].suspended; 493 | const float v2 = terrain_state[(ut_1 + W * vt_minus_1 )].suspended; 494 | const float v3 = terrain_state[(ut_2 + W * vt_minus_1 )].suspended; 495 | 496 | const float v4 = terrain_state[(ut_minus_1 + W * vt )].suspended; 497 | const float v5 = terrain_state[(ut + W * vt )].suspended; 498 | const float v6 = terrain_state[(ut_1 + W * vt )].suspended; 499 | const float v7 = terrain_state[(ut_2 + W * vt )].suspended; 500 | 501 | const float v8 = terrain_state[(ut_minus_1 + W * vt_1 )].suspended; 502 | const float v9 = terrain_state[(ut + W * vt_1 )].suspended; 503 | const float v10 = terrain_state[(ut_1 + W * vt_1 )].suspended; 504 | const float v11 = terrain_state[(ut_2 + W * vt_1 )].suspended; 505 | 506 | const float v12 = terrain_state[(ut_minus_1 + W * vt_2 )].suspended; 507 | const float v13 = terrain_state[(ut + W * vt_2 )].suspended; 508 | const float v14 = terrain_state[(ut_1 + W * vt_2 )].suspended; 509 | const float v15 = terrain_state[(ut_2 + W * vt_2 )].suspended; 510 | 511 | float w0 = mitchellNetravaliEval(sqrt(sq_dx_minus_1 + sq_dy_minus_1)); 512 | float w1 = mitchellNetravaliEval(sqrt(sq_dx + sq_dy_minus_1)); 513 | float w2 = mitchellNetravaliEval(sqrt(sq_dx_1 + sq_dy_minus_1)); 514 | float w3 = mitchellNetravaliEval(sqrt(sq_dx_2 + sq_dy_minus_1)); 515 | 516 | float w4 = mitchellNetravaliEval(sqrt(sq_dx_minus_1 + sq_dy)); 517 | float w5 = mitchellNetravaliEval(sqrt(sq_dx + sq_dy)); 518 | float w6 = mitchellNetravaliEval(sqrt(sq_dx_1 + sq_dy)); 519 | float w7 = mitchellNetravaliEval(sqrt(sq_dx_2 + sq_dy)); 520 | 521 | float w8 = mitchellNetravaliEval(sqrt(sq_dx_minus_1 + sq_dy_1)); 522 | float w9 = mitchellNetravaliEval(sqrt(sq_dx + sq_dy_1)); 523 | float w10 = mitchellNetravaliEval(sqrt(sq_dx_1 + sq_dy_1)); 524 | float w11 = mitchellNetravaliEval(sqrt(sq_dx_2 + sq_dy_1)); 525 | 526 | float w12 = mitchellNetravaliEval(sqrt(sq_dx_minus_1 + sq_dy_2)); 527 | float w13 = mitchellNetravaliEval(sqrt(sq_dx + sq_dy_2)); 528 | float w14 = mitchellNetravaliEval(sqrt(sq_dx_1 + sq_dy_2)); 529 | float w15 = mitchellNetravaliEval(sqrt(sq_dx_2 + sq_dy_2)); 530 | 531 | const float filter_sum = 532 | ((w0 + w1 + w2 + w3) + 533 | (w4 + w5 + w6 + w7)) + 534 | ((w8 + w9 + w10 + w11) + 535 | (w12 + w13 + w14 + w15)); 536 | 537 | const float sum = 538 | (((v0 * w0 + 539 | v1 * w1) + 540 | (v2 * w2 + 541 | v3 * w3)) + 542 | 543 | ((v4 * w4 + 544 | v5 * w5) + 545 | (v6 * w6 + 546 | v7 * w7))) + 547 | 548 | (((v8 * w8 + 549 | v9 * w9) + 550 | (v10 * w10 + 551 | v11 * w11)) + 552 | 553 | ((v12 * w12 + 554 | v13 * w13) + 555 | (v14 * w14 + 556 | v15 * w15))); 557 | 558 | return sum / filter_sum; 559 | } 560 | #endif 561 | 562 | 563 | // Sets height_laplacian, thermal_vel, thermal_move_vol 564 | // OLD: Sets flux in thermal_erosion_state 565 | __kernel void thermalErosionFluxKernel( 566 | __global TerrainState* restrict const terrain_state, 567 | __global ThermalErosionState* restrict const thermal_erosion_state, 568 | __constant Constants* restrict const constants, 569 | int process_deposited_sed 570 | ) 571 | { 572 | const int x = get_global_id(0); 573 | const int y = get_global_id(1); 574 | 575 | const int x_minus_1 = max(x-1, 0); 576 | const int x_plus_1 = min(x+1, constants->W-1); 577 | const int y_minus_1 = max(y-1, 0); 578 | const int y_plus_1 = min(y+1, constants->H-1); 579 | 580 | 581 | #if 1 582 | __global const TerrainState* const state_left = &terrain_state[x_minus_1 + y * constants->W]; 583 | __global const TerrainState* const state_right = &terrain_state[x_plus_1 + y * constants->W]; 584 | __global const TerrainState* const state_top = &terrain_state[x + y_plus_1 * constants->W]; 585 | __global const TerrainState* const state_bot = &terrain_state[x + y_minus_1 * constants->W]; 586 | __global TerrainState* const state_middle = &terrain_state[x + y * constants->W]; 587 | 588 | const float L_h = state_left ->height + ((process_deposited_sed != 0) ? state_left ->deposited_sed_h : 0.0f); 589 | const float R_h = state_right ->height + ((process_deposited_sed != 0) ? state_right ->deposited_sed_h : 0.0f); 590 | const float B_h = state_bot ->height + ((process_deposited_sed != 0) ? state_bot ->deposited_sed_h : 0.0f); 591 | const float T_h = state_top ->height + ((process_deposited_sed != 0) ? state_top ->deposited_sed_h : 0.0f); 592 | const float h = state_middle->height + ((process_deposited_sed != 0) ? state_middle->deposited_sed_h : 0.0f); 593 | 594 | const float dh_dx = (R_h - L_h) * (0.5f * constants->recip_cell_w); // dh/dx = (R_h - L_h) / (2*cell_w) = (R_h - L_h) * 0.5 * (1/cell_w) 595 | const float dh_dy = (T_h - B_h) * (0.5f * constants->recip_cell_w); 596 | 597 | // Compute curvature (second deriv) 598 | const float d2_h_dx2 = (R_h - 2*h + L_h) / square(constants->cell_w); // (d^2u/dx^2, d^2v/dx^2) 599 | const float d2_h_dy2 = (T_h - 2*h + B_h) / square(constants->cell_w); // (d^2u/dy^2, d^2v/dy^2) 600 | 601 | state_middle->height_laplacian = d2_h_dx2 + d2_h_dy2; 602 | 603 | float2 thermal_vel = (float2)(0.0, 0.0); 604 | float thermal_move_vol = 0.0; 605 | 606 | float2 grad_h = (float2)(dh_dx, dh_dy); 607 | float grad_h_len = length(grad_h); 608 | if(grad_h_len > 1.0e-4f) 609 | { 610 | /* We are moving material 'downhill' according to the gradient vector. 611 | However the current cell might lie in a local minimum, with the adjacent 'downhill' cell actually being uphill, e.g. this case: 612 | ^ 613 | ^ / \ 614 | / \ / \ 615 | / \__---/ \ 616 | x-1 x x+1 617 | 618 | Here dh/dx at x is negative, even though h(x-1) > h(x) 619 | 620 | We don't want to move material to the adjacent cell in this case, where it is higher. 621 | So check if it's actually higher. We could do this in a couple of ways: with a Taylor expansion around (x, y) using the second derivatives, or by taking a cell width step 622 | in the 'downhill' direciton and evaluating the height there. We will take the second approach for now. 623 | */ 624 | float2 unit_step_vec = -grad_h / grad_h_len; // normalised step vector 'downhill' 625 | 626 | float2 dv = unit_step_vec; 627 | float2 downhill_p = (float2)((float)x, (float)y) + dv; 628 | // float step_delta_h = dv.x * dh_dx + dv.y * dh_dy + 0.5f * (square(dv.x) * d2_h_dx2 + square(dv.y) * d2_h_dy2); 629 | 630 | // Read height values at (downhill_p.x, downhill_p.y) 631 | const int old_xi = clamp((int)downhill_p.x, 0, constants->W-1); 632 | const int old_yi = clamp((int)downhill_p.y, 0, constants->H-1); 633 | const int old_xi1 = clamp((int)downhill_p.x + 1, 0, constants->W-1); 634 | const int old_yi1 = clamp((int)downhill_p.y + 1, 0, constants->H-1); 635 | 636 | const float t_x = downhill_p.x - (int)downhill_p.x; 637 | const float t_y = downhill_p.y - (int)downhill_p.y; 638 | 639 | float downhill_h = biLerp( 640 | terrain_state[old_xi + old_yi * constants->W].height + ((process_deposited_sed != 0) ? terrain_state[old_xi + old_yi * constants->W].deposited_sed_h : 0.0f), 641 | terrain_state[old_xi1 + old_yi * constants->W].height + ((process_deposited_sed != 0) ? terrain_state[old_xi1 + old_yi * constants->W].deposited_sed_h : 0.0f), 642 | terrain_state[old_xi + old_yi1 * constants->W].height + ((process_deposited_sed != 0) ? terrain_state[old_xi + old_yi1 * constants->W].deposited_sed_h : 0.0f), 643 | terrain_state[old_xi1 + old_yi1 * constants->W].height + ((process_deposited_sed != 0) ? terrain_state[old_xi1 + old_yi1 * constants->W].deposited_sed_h : 0.0f), 644 | t_x, t_y); 645 | 646 | const float tan_slope_angle = (h - downhill_h) / constants->cell_w; 647 | 648 | /*if(x == W/2 + 10 && y == W/2 + 10) 649 | { 650 | printf("----------------------\n"); 651 | printf("(x, y): %f %f \n", (float)x, (float)y); 652 | printf("downhill_p: %f %f \n", downhill_p.x, downhill_p.y); 653 | printf("grad_h: %f %f \n", grad_h.x, grad_h.y); 654 | printf("unit_step_vec: %f %f \n", unit_step_vec.x, unit_step_vec.y); 655 | printf("h: %f \n", h); 656 | printf("downhill_h: %f \n", downhill_h); 657 | printf("tan_slope_angle: %f \n", tan_slope_angle); 658 | }*/ 659 | 660 | //const float max_second_deriv = 0.1f; 661 | 662 | const float tan_max_talus_angle = (process_deposited_sed != 0) ? constants->tan_max_deposited_talus_angle : constants->tan_max_talus_angle; 663 | 664 | if(tan_slope_angle > tan_max_talus_angle/* && (d2_h_dx2 < max_second_deriv) && (d2_h_dy2 < max_second_deriv)*/) 665 | { 666 | // Move some material downhill 667 | thermal_vel = unit_step_vec; // In pixel coords 668 | 669 | // NOTE: could make the amount of material moved also a factor of (tan_slope_angle - max_talus_angle) or grad_h_len. 670 | // However (tan_slope_angle - tan_max_talus_angle) factor introduces ridgeline artifacts on x and y axes. 671 | float thermal_move_h = constants->delta_t * ((process_deposited_sed != 0) ? constants->K_tdep : constants->K_t); // Height of material to move 672 | 673 | if(process_deposited_sed != 0) 674 | thermal_move_h = min(thermal_move_h, state_middle->deposited_sed_h); // Make sure we don't move out more than present in this cell 675 | 676 | thermal_move_vol = thermal_move_h * square(constants->cell_w); // Compute volume of material to move 677 | } 678 | 679 | /*if(x == W/2 + 10 && y == W/2 + 10) 680 | { 681 | printf("----------------------\n"); 682 | printf("thermal_vel: %f %f \n", thermal_vel.x, thermal_vel.y); 683 | }*/ 684 | } 685 | 686 | state_middle->thermal_vel = thermal_vel; 687 | state_middle->thermal_move_vol = thermal_move_vol; 688 | 689 | #else 690 | __global const TerrainState* const state_0 = &terrain_state[x_minus_1 + y_plus_1 * W]; 691 | __global const TerrainState* const state_1 = &terrain_state[x + y_plus_1 * W]; 692 | __global const TerrainState* const state_2 = &terrain_state[x_plus_1 + y_plus_1 * W]; 693 | __global const TerrainState* const state_3 = &terrain_state[x_minus_1 + y * W]; 694 | __global const TerrainState* const state_middle = &terrain_state[x + y * W]; 695 | __global const TerrainState* const state_4 = &terrain_state[x_plus_1 + y * W]; 696 | __global const TerrainState* const state_5 = &terrain_state[x_minus_1 + y_minus_1 * W]; 697 | __global const TerrainState* const state_6 = &terrain_state[x + y_minus_1 * W]; 698 | __global const TerrainState* const state_7 = &terrain_state[x_plus_1 + y_minus_1 * W]; 699 | 700 | __global ThermalErosionState* const thermal_erosion_state_middle = &thermal_erosion_state[x + y *W]; 701 | 702 | const float middle_h = state_middle->height;// + state_middle->deposited_sed; 703 | float h_0 = middle_h - state_0->height/* + state_0->deposited_sed*/; // height diff between adjacent cell and middle cell 704 | float h_1 = middle_h - state_1->height/* + state_1->deposited_sed*/; 705 | float h_2 = middle_h - state_2->height/* + state_2->deposited_sed*/; 706 | float h_3 = middle_h - state_3->height/* + state_3->deposited_sed*/; 707 | float h_4 = middle_h - state_4->height/* + state_4->deposited_sed*/; 708 | float h_5 = middle_h - state_5->height/* + state_5->deposited_sed*/; 709 | float h_6 = middle_h - state_6->height/* + state_6->deposited_sed*/; 710 | float h_7 = middle_h - state_7->height/* + state_7->deposited_sed*/; 711 | 712 | const float max_height_diff = // H 713 | max( 714 | max( 715 | max(h_0, h_1), 716 | max(h_2, h_3) 717 | ), 718 | max( 719 | max(h_4, h_5), 720 | max(h_6, h_7) 721 | ) 722 | ); 723 | 724 | // tan(theta) = h / cell_w [for immediately adjacent cells] 725 | const float tan_angle_0 = h_0 * constants->recip_cell_w * (1 / sqrt(2.f)); 726 | const float tan_angle_1 = h_1 * constants->recip_cell_w; 727 | const float tan_angle_2 = h_2 * constants->recip_cell_w * (1 / sqrt(2.f)); 728 | const float tan_angle_3 = h_3 * constants->recip_cell_w; 729 | const float tan_angle_4 = h_4 * constants->recip_cell_w; 730 | const float tan_angle_5 = h_5 * constants->recip_cell_w * (1 / sqrt(2.f)); 731 | const float tan_angle_6 = h_6 * constants->recip_cell_w; 732 | const float tan_angle_7 = h_7 * constants->recip_cell_w * (1 / sqrt(2.f)); 733 | 734 | const float tan_max_talus_angle = constants->tan_max_talus_angle; 735 | 736 | if(tan_angle_0 < tan_max_talus_angle) h_0 = 0; 737 | if(tan_angle_1 < tan_max_talus_angle) h_1 = 0; 738 | if(tan_angle_2 < tan_max_talus_angle) h_2 = 0; 739 | if(tan_angle_3 < tan_max_talus_angle) h_3 = 0; 740 | if(tan_angle_4 < tan_max_talus_angle) h_4 = 0; 741 | if(tan_angle_5 < tan_max_talus_angle) h_5 = 0; 742 | if(tan_angle_6 < tan_max_talus_angle) h_6 = 0; 743 | if(tan_angle_7 < tan_max_talus_angle) h_7 = 0; 744 | 745 | // Total height difference, for cells for which the height difference exceeds the max talus angle 746 | /*const float total_height_diff = 747 | ((tan_angle_0 > tan_max_talus_angle) ? h_0 : 0.0) + 748 | ((tan_angle_1 > tan_max_talus_angle) ? h_1 : 0.0) + 749 | ((tan_angle_2 > tan_max_talus_angle) ? h_2 : 0.0) + 750 | ((tan_angle_3 > tan_max_talus_angle) ? h_3 : 0.0) + 751 | ((tan_angle_4 > tan_max_talus_angle) ? h_4 : 0.0) + 752 | ((tan_angle_5 > tan_max_talus_angle) ? h_5 : 0.0) + 753 | ((tan_angle_6 > tan_max_talus_angle) ? h_6 : 0.0) + 754 | ((tan_angle_7 > tan_max_talus_angle) ? h_7 : 0.0);*/ 755 | const float total_height_diff = 756 | h_0 + 757 | h_1 + 758 | h_2 + 759 | h_3 + 760 | h_4 + 761 | h_5 + 762 | h_6 + 763 | h_7; 764 | 765 | 766 | 767 | const float a = square(constants->cell_w); // cell area 768 | const float R = 1.0f; // hardness 769 | float common_factors; 770 | if(max_height_diff > 0 && total_height_diff > 0) 771 | { 772 | const float norm_factor = 1.f / total_height_diff; 773 | common_factors = norm_factor * a * constants->delta_t * constants->K_t * R * max_height_diff * 0.5f; 774 | } 775 | else 776 | common_factors = 0; 777 | 778 | thermal_erosion_state_middle->flux[0] = h_0 * common_factors; 779 | thermal_erosion_state_middle->flux[1] = h_1 * common_factors; 780 | thermal_erosion_state_middle->flux[2] = h_2 * common_factors; 781 | thermal_erosion_state_middle->flux[3] = h_3 * common_factors; 782 | thermal_erosion_state_middle->flux[4] = h_4 * common_factors; 783 | thermal_erosion_state_middle->flux[5] = h_5 * common_factors; 784 | thermal_erosion_state_middle->flux[6] = h_6 * common_factors; 785 | thermal_erosion_state_middle->flux[7] = h_7 * common_factors; 786 | #endif 787 | } 788 | 789 | 790 | // Sets flux in thermal_erosion_state 791 | #if 0 792 | __kernel void thermalErosionDepositedFluxKernel( 793 | __global TerrainState* restrict const terrain_state, 794 | __global ThermalErosionState* restrict const thermal_erosion_state, 795 | __constant Constants* restrict const constants 796 | ) 797 | { 798 | const int x = get_global_id(0); 799 | const int y = get_global_id(1); 800 | 801 | const int x_minus_1 = max(x-1, 0); 802 | const int x_plus_1 = min(x+1, constants->W-1); 803 | const int y_minus_1 = max(y-1, 0); 804 | const int y_plus_1 = min(y+1, constants->H-1); 805 | 806 | __global const TerrainState* const state_0 = &terrain_state[x_minus_1 + y_plus_1 * W]; 807 | __global const TerrainState* const state_1 = &terrain_state[x + y_plus_1 * W]; 808 | __global const TerrainState* const state_2 = &terrain_state[x_plus_1 + y_plus_1 * W]; 809 | __global const TerrainState* const state_3 = &terrain_state[x_minus_1 + y * W]; 810 | __global const TerrainState* const state_middle = &terrain_state[x + y * W]; 811 | __global const TerrainState* const state_4 = &terrain_state[x_plus_1 + y * W]; 812 | __global const TerrainState* const state_5 = &terrain_state[x_minus_1 + y_minus_1 * W]; 813 | __global const TerrainState* const state_6 = &terrain_state[x + y_minus_1 * W]; 814 | __global const TerrainState* const state_7 = &terrain_state[x_plus_1 + y_minus_1 * W]; 815 | 816 | __global ThermalErosionState* const thermal_erosion_state_middle = &thermal_erosion_state[x + y *W]; 817 | 818 | 819 | const float middle_h = state_middle->height + state_middle->deposited_sed; // state_middle->sediment[0] + state_middle->sediment[1] + state_middle->sediment[2]; 820 | float h_0 = middle_h - (state_0->height + state_0->deposited_sed);// state_0->sediment[0] + state_0->sediment[1] + state_0->sediment[2]); // height diff between adjacent cell and middle cell 821 | float h_1 = middle_h - (state_1->height + state_1->deposited_sed);// state_1->sediment[0] + state_1->sediment[1] + state_1->sediment[2]); 822 | float h_2 = middle_h - (state_2->height + state_2->deposited_sed);// state_2->sediment[0] + state_2->sediment[1] + state_2->sediment[2]); 823 | float h_3 = middle_h - (state_3->height + state_3->deposited_sed);// state_3->sediment[0] + state_3->sediment[1] + state_3->sediment[2]); 824 | float h_4 = middle_h - (state_4->height + state_4->deposited_sed);// state_4->sediment[0] + state_4->sediment[1] + state_4->sediment[2]); 825 | float h_5 = middle_h - (state_5->height + state_5->deposited_sed);// state_5->sediment[0] + state_5->sediment[1] + state_5->sediment[2]); 826 | float h_6 = middle_h - (state_6->height + state_6->deposited_sed);// state_6->sediment[0] + state_6->sediment[1] + state_6->sediment[2]); 827 | float h_7 = middle_h - (state_7->height + state_7->deposited_sed);// state_7->sediment[0] + state_7->sediment[1] + state_7->sediment[2]); 828 | 829 | const float max_height_diff = // H 830 | max( 831 | max( 832 | max(h_0, h_1), 833 | max(h_2, h_3) 834 | ), 835 | max( 836 | max(h_4, h_5), 837 | max(h_6, h_7) 838 | ) 839 | ); 840 | 841 | const float tan_angle_0 = h_0 * constants->recip_cell_w * (1 / sqrt(2.f)); 842 | const float tan_angle_1 = h_1 * constants->recip_cell_w; 843 | const float tan_angle_2 = h_2 * constants->recip_cell_w * (1 / sqrt(2.f)); 844 | const float tan_angle_3 = h_3 * constants->recip_cell_w; 845 | const float tan_angle_4 = h_4 * constants->recip_cell_w; 846 | const float tan_angle_5 = h_5 * constants->recip_cell_w * (1 / sqrt(2.f)); 847 | const float tan_angle_6 = h_6 * constants->recip_cell_w; 848 | const float tan_angle_7 = h_7 * constants->recip_cell_w * (1 / sqrt(2.f)); 849 | 850 | const float tan_max_talus_angle = constants->tan_max_deposited_talus_angle; 851 | 852 | if(tan_angle_0 < tan_max_talus_angle) h_0 = 0; 853 | if(tan_angle_1 < tan_max_talus_angle) h_1 = 0; 854 | if(tan_angle_2 < tan_max_talus_angle) h_2 = 0; 855 | if(tan_angle_3 < tan_max_talus_angle) h_3 = 0; 856 | if(tan_angle_4 < tan_max_talus_angle) h_4 = 0; 857 | if(tan_angle_5 < tan_max_talus_angle) h_5 = 0; 858 | if(tan_angle_6 < tan_max_talus_angle) h_6 = 0; 859 | if(tan_angle_7 < tan_max_talus_angle) h_7 = 0; 860 | 861 | // Total height difference, for cells for which the height difference exceeds the max talus angle 862 | /*const float total_height_diff = 863 | ((tan_angle_0 > tan_max_talus_angle) ? h_0 : 0.0) + 864 | ((tan_angle_1 > tan_max_talus_angle) ? h_1 : 0.0) + 865 | ((tan_angle_2 > tan_max_talus_angle) ? h_2 : 0.0) + 866 | ((tan_angle_3 > tan_max_talus_angle) ? h_3 : 0.0) + 867 | ((tan_angle_4 > tan_max_talus_angle) ? h_4 : 0.0) + 868 | ((tan_angle_5 > tan_max_talus_angle) ? h_5 : 0.0) + 869 | ((tan_angle_6 > tan_max_talus_angle) ? h_6 : 0.0) + 870 | ((tan_angle_7 > tan_max_talus_angle) ? h_7 : 0.0);*/ 871 | const float total_height_diff = 872 | h_0 + 873 | h_1 + 874 | h_2 + 875 | h_3 + 876 | h_4 + 877 | h_5 + 878 | h_6 + 879 | h_7; 880 | 881 | const float a = square(constants->cell_w); // cell area 882 | const float R = 1.f; // hardness TEMP 883 | float common_factors; 884 | if(max_height_diff > 0 && total_height_diff > 0) 885 | { 886 | const float norm_factor = 1.f / total_height_diff; 887 | common_factors = norm_factor * a * constants->delta_t * constants->K_tdep * R * max_height_diff * 0.5f; 888 | } 889 | else 890 | common_factors = 0; 891 | 892 | /*float sum_flux = 893 | h_0 * common_factors + 894 | h_1 * common_factors + 895 | h_2 * common_factors + 896 | h_3 * common_factors + 897 | h_4 * common_factors + 898 | h_5 * common_factors + 899 | h_6 * common_factors + 900 | h_7 * common_factors;*/ 901 | float sum_flux = total_height_diff * common_factors; 902 | 903 | const float cur_deposited_sed_h = state_middle->deposited_sed; 904 | 905 | float K = 1; 906 | if(cur_deposited_sed_h > 0) 907 | { 908 | if(sum_flux > cur_deposited_sed_h) 909 | { 910 | K = cur_deposited_sed_h / sum_flux; 911 | } 912 | } 913 | else 914 | K = 0; 915 | 916 | thermal_erosion_state_middle->flux[0] = h_0 * common_factors * K; 917 | thermal_erosion_state_middle->flux[1] = h_1 * common_factors * K; 918 | thermal_erosion_state_middle->flux[2] = h_2 * common_factors * K; 919 | thermal_erosion_state_middle->flux[3] = h_3 * common_factors * K; 920 | thermal_erosion_state_middle->flux[4] = h_4 * common_factors * K; 921 | thermal_erosion_state_middle->flux[5] = h_5 * common_factors * K; 922 | thermal_erosion_state_middle->flux[6] = h_6 * common_factors * K; 923 | thermal_erosion_state_middle->flux[7] = h_7 * common_factors * K; 924 | } 925 | #endif 926 | 927 | 928 | /* 929 | 930 | 0 1 2 931 | 932 | 933 | 3 x 4 934 | 935 | 936 | 5 6 7 937 | 938 | */ 939 | // Sets deposited_sed_h, height 940 | __kernel void thermalErosionMovementKernel( 941 | __global const ThermalErosionState* restrict const thermal_erosion_state, 942 | __global TerrainState* restrict const terrain_state, 943 | __constant Constants* restrict const constants, 944 | int process_deposited_sed 945 | ) 946 | { 947 | const int x = get_global_id(0); 948 | const int y = get_global_id(1); 949 | 950 | #if 1 951 | __global TerrainState* const state_middle = &terrain_state[x + y *constants->W]; 952 | 953 | // Loop over neighbouring cells 954 | float2 water_pos = (float2)(0.f, 0.f); 955 | float2 water_vel = (float2)(0.f, 0.f); 956 | float total_in_thermal_move_vol = 0.f; // Total volume of solid moved into this cell in this timestep 957 | for(int ny = y-1; ny <= y+1; ny++) 958 | for(int nx = x-1; nx <= x+1; nx++) 959 | { 960 | if(nx >= 0 && nx < constants->W && ny >= 0 && ny < constants->H) 961 | { 962 | __global const TerrainState* const n_state = &terrain_state[nx + ny * constants->W]; 963 | float2 cell_n_thermal_pos = (float2)((float)nx, (float)ny); // source position for cell n 964 | float2 new_pos = cell_n_thermal_pos + n_state->thermal_vel * constants->delta_t; 965 | 966 | /*if(x == W/2-20 && y == W/2-20) 967 | { 968 | printf("nx, ny: %f, %f \n", (float)nx, (float)ny); 969 | printf("n_state->thermal_vel: %f, %f \n", n_state->thermal_vel.x, n_state->thermal_vel.y); 970 | printf("n_state->thermal_move_vol: %f \n", n_state->thermal_move_vol); 971 | }*/ 972 | 973 | const float x_diff = fabs((float)x - new_pos.x); 974 | const float y_diff = fabs((float)y - new_pos.y); 975 | const float weight = max(0.f, (1 - x_diff)) * max(0.f, (1 - y_diff)); 976 | 977 | float in_thermal_move_vol = n_state->thermal_move_vol * weight; 978 | 979 | total_in_thermal_move_vol += in_thermal_move_vol; 980 | } 981 | } 982 | 983 | // We moved some material out of the cell in this timestep, and moved some material in. Compute net change. 984 | const float delta_vol = total_in_thermal_move_vol - state_middle->thermal_move_vol; 985 | 986 | if(process_deposited_sed != 0) 987 | state_middle->deposited_sed_h += delta_vol / square(constants->cell_w); 988 | else 989 | state_middle->height += delta_vol / square(constants->cell_w); 990 | 991 | 992 | // Apply height_laplacian smoothing 993 | if(process_deposited_sed == 0) 994 | { 995 | if(fabs(state_middle->height_laplacian) > constants->laplacian_threshold) 996 | state_middle->height += state_middle->height_laplacian * constants->K_smooth * constants->delta_t; 997 | } 998 | 999 | #else 1000 | const int x_minus_1 = max(x-1, 0); 1001 | const int x_plus_1 = min(x+1, constants->W-1); 1002 | const int y_minus_1 = max(y-1, 0); 1003 | const int y_plus_1 = min(y+1, constants->H-1); 1004 | 1005 | __global const ThermalErosionState* const state_0 = &thermal_erosion_state[x_minus_1 + y_plus_1 * W]; 1006 | __global const ThermalErosionState* const state_1 = &thermal_erosion_state[x + y_plus_1 * W]; 1007 | __global const ThermalErosionState* const state_2 = &thermal_erosion_state[x_plus_1 + y_plus_1 * W]; 1008 | __global const ThermalErosionState* const state_3 = &thermal_erosion_state[x_minus_1 + y * W]; 1009 | __global const ThermalErosionState* const state_middle = &thermal_erosion_state[x + y * W]; 1010 | __global const ThermalErosionState* const state_4 = &thermal_erosion_state[x_plus_1 + y * W]; 1011 | __global const ThermalErosionState* const state_5 = &thermal_erosion_state[x_minus_1 + y_minus_1 * W]; 1012 | __global const ThermalErosionState* const state_6 = &thermal_erosion_state[x + y_minus_1 * W]; 1013 | __global const ThermalErosionState* const state_7 = &thermal_erosion_state[x_plus_1 + y_minus_1 * W]; 1014 | 1015 | __global TerrainState* const middle_terrain_state = &terrain_state[x + y *W]; 1016 | 1017 | float flux_0 = state_0->flux[7]; // Flux_0 = flux from cell located up and to the left of this one, in the down and right direction. 1018 | float flux_1 = state_1->flux[6]; 1019 | float flux_2 = state_2->flux[5]; 1020 | float flux_3 = state_3->flux[4]; 1021 | float flux_4 = state_4->flux[3]; 1022 | float flux_5 = state_5->flux[2]; 1023 | float flux_6 = state_6->flux[1]; 1024 | float flux_7 = state_7->flux[0]; 1025 | 1026 | if(x == 0) // If this cell is on the left edge: 1027 | { 1028 | flux_0 = flux_3 = flux_5 = 0; // Zero flux coming from cells located to the left. 1029 | } 1030 | else if(x == W - 1) 1031 | { 1032 | flux_2 = flux_4 = flux_7 = 0; 1033 | } 1034 | 1035 | if(y == 0) // If this cell is on the bottom edge: 1036 | { 1037 | flux_5 = flux_6 = flux_7 = 0; // Zero flux coming from cells located to the bottom. 1038 | } 1039 | else if(y == W - 1) // If this cell is on the top edge: 1040 | { 1041 | flux_0 = flux_1 = flux_2 = 0; 1042 | } 1043 | 1044 | const float sum_material_in = 1045 | flux_0 + 1046 | flux_1 + 1047 | flux_2 + 1048 | flux_3 + 1049 | flux_4 + 1050 | flux_5 + 1051 | flux_6 + 1052 | flux_7; 1053 | 1054 | const float sum_material_out = 1055 | state_middle->flux[0] + 1056 | state_middle->flux[1] + 1057 | state_middle->flux[2] + 1058 | state_middle->flux[3] + 1059 | state_middle->flux[4] + 1060 | state_middle->flux[5] + 1061 | state_middle->flux[6] + 1062 | state_middle->flux[7]; 1063 | 1064 | const float net_material_change = sum_material_in - sum_material_out; 1065 | 1066 | middle_terrain_state->height += net_material_change; 1067 | #endif 1068 | } 1069 | 1070 | #if 0 1071 | __kernel void thermalErosionDepositedMovementKernel( 1072 | __global const ThermalErosionState* restrict const thermal_erosion_state, 1073 | __global TerrainState* restrict const terrain_state, 1074 | __constant Constants* restrict const constants 1075 | ) 1076 | { 1077 | const int x = get_global_id(0); 1078 | const int y = get_global_id(1); 1079 | 1080 | const int x_minus_1 = max(x-1, 0); 1081 | const int x_plus_1 = min(x+1, constants->W-1); 1082 | const int y_minus_1 = max(y-1, 0); 1083 | const int y_plus_1 = min(y+1, constants->H-1); 1084 | 1085 | 1086 | __global const ThermalErosionState* const state_0 = &thermal_erosion_state[x_minus_1 + y_plus_1 * W]; 1087 | __global const ThermalErosionState* const state_1 = &thermal_erosion_state[x + y_plus_1 * W]; 1088 | __global const ThermalErosionState* const state_2 = &thermal_erosion_state[x_plus_1 + y_plus_1 * W]; 1089 | __global const ThermalErosionState* const state_3 = &thermal_erosion_state[x_minus_1 + y * W]; 1090 | __global const ThermalErosionState* const state_middle = &thermal_erosion_state[x + y * W]; 1091 | __global const ThermalErosionState* const state_4 = &thermal_erosion_state[x_plus_1 + y * W]; 1092 | __global const ThermalErosionState* const state_5 = &thermal_erosion_state[x_minus_1 + y_minus_1 * W]; 1093 | __global const ThermalErosionState* const state_6 = &thermal_erosion_state[x + y_minus_1 * W]; 1094 | __global const ThermalErosionState* const state_7 = &thermal_erosion_state[x_plus_1 + y_minus_1 * W]; 1095 | 1096 | __global TerrainState* const middle_terrain_state = &terrain_state[x + y *W]; 1097 | 1098 | float flux_0 = state_0->flux[7]; // Flux_0 = flux from cell located up and to the left of this one, in the down and right direction. 1099 | float flux_1 = state_1->flux[6]; 1100 | float flux_2 = state_2->flux[5]; 1101 | float flux_3 = state_3->flux[4]; 1102 | float flux_4 = state_4->flux[3]; 1103 | float flux_5 = state_5->flux[2]; 1104 | float flux_6 = state_6->flux[1]; 1105 | float flux_7 = state_7->flux[0]; 1106 | 1107 | if(x == 0) // If this cell is on the left edge: 1108 | { 1109 | flux_0 = flux_3 = flux_5 = 0; // Zero flux coming from cells located to the left. 1110 | } 1111 | else if(x == W - 1) 1112 | { 1113 | flux_2 = flux_4 = flux_7 = 0; 1114 | } 1115 | 1116 | if(y == 0) // If this cell is on the bottom edge: 1117 | { 1118 | flux_5 = flux_6 = flux_7 = 0; // Zero flux coming from cells located to the bottom. 1119 | } 1120 | else if(y == W - 1) // If this cell is on the top edge: 1121 | { 1122 | flux_0 = flux_1 = flux_2 = 0; 1123 | } 1124 | 1125 | const float sum_material_in = 1126 | flux_0 + 1127 | flux_1 + 1128 | flux_2 + 1129 | flux_3 + 1130 | flux_4 + 1131 | flux_5 + 1132 | flux_6 + 1133 | flux_7; 1134 | 1135 | const float sum_material_out = 1136 | state_middle->flux[0] + 1137 | state_middle->flux[1] + 1138 | state_middle->flux[2] + 1139 | state_middle->flux[3] + 1140 | state_middle->flux[4] + 1141 | state_middle->flux[5] + 1142 | state_middle->flux[6] + 1143 | state_middle->flux[7]; 1144 | 1145 | const float net_material_change = sum_material_in - sum_material_out; 1146 | 1147 | middle_terrain_state->deposited_sed += net_material_change; 1148 | } 1149 | #endif 1150 | 1151 | 1152 | // Updates water_mass, water_vel 1153 | __kernel void evaporationKernel( 1154 | __global TerrainState* restrict const terrain_state, 1155 | __constant Constants* restrict const constants 1156 | ) 1157 | { 1158 | const int x = get_global_id(0); 1159 | const int y = get_global_id(1); 1160 | 1161 | __global TerrainState* const state_middle = &terrain_state[x + y *constants->W]; 1162 | 1163 | const float old_water_depth = waterHeightForMass(state_middle->water_mass, constants); 1164 | 1165 | // Apply evaporation 1166 | float d_new = old_water_depth * (1 - constants->K_e * constants->delta_t);// evaporation rate depends on water depth. Makes no physical sense but useful. 1167 | //const float d_new = max(0.f, old_water_depth - constants->K_e * constants->delta_t); // Make evaporation rate not depend on water depth 1168 | 1169 | float pre_rainfall_water_mass = waterMassForHeight(d_new, constants); 1170 | 1171 | // Apply rainfall 1172 | const float rainfall_delta_h = constants->delta_t * constants->r * rainfallFactorForCoords(x, y); 1173 | d_new += rainfall_delta_h; 1174 | 1175 | const float new_water_mass = waterMassForHeight(d_new, constants); 1176 | const float rainfall_mass = new_water_mass - pre_rainfall_water_mass; 1177 | 1178 | state_middle->water_mass = new_water_mass; 1179 | 1180 | // Update water_vel based on weighted mass of old water and new rainfall water 1181 | if(new_water_mass > 0.0) 1182 | { 1183 | const float orig_mass_frac = pre_rainfall_water_mass / new_water_mass; 1184 | state_middle->water_vel *= orig_mass_frac; // Rainfall has zero lateral velocity, adjust cell water vel accordingly. 1185 | } 1186 | } 1187 | 1188 | 1189 | typedef struct 1190 | { 1191 | float pos[3]; 1192 | float normal[3]; 1193 | float uv[2]; 1194 | } Vertex; 1195 | 1196 | inline float totalTerrainHeight(__global TerrainState* restrict const state, bool include_water, __constant Constants* restrict const constants) 1197 | { 1198 | return state->height + state->deposited_sed_h + (include_water ? waterHeightForMass(state->water_mass, constants) : 0.f); 1199 | } 1200 | 1201 | __kernel void setHeightFieldMeshKernel( 1202 | __global TerrainState* restrict const terrain_state, 1203 | __constant Constants* restrict const constants, 1204 | __global Vertex* restrict const vertex_buffer, 1205 | unsigned int vertex_buffer_offset_B, 1206 | write_only image2d_t terrain_texture, 1207 | __global Vertex* restrict const water_vertex_buffer, 1208 | unsigned int water_vertex_buffer_offset_B 1209 | ) 1210 | { 1211 | const int x = get_global_id(0); 1212 | const int y = get_global_id(1); 1213 | 1214 | __global Vertex* mesh_vert_0 = & vertex_buffer[ vertex_buffer_offset_B / sizeof(Vertex)]; 1215 | __global Vertex* water_mesh_vert_0 = &water_vertex_buffer[water_vertex_buffer_offset_B / sizeof(Vertex)]; 1216 | 1217 | int vert_xres = max(2, constants->W); 1218 | int vert_yres = max(2, constants->H); 1219 | int quad_xres = vert_xres - 1; // Number of quads in x and y directions 1220 | int quad_yres = vert_yres - 1; // Number of quads in x and y directions 1221 | 1222 | __global Vertex* mesh_vert = & mesh_vert_0[x + y * vert_xres]; 1223 | __global Vertex* water_mesh_vert = &water_mesh_vert_0[x + y * vert_xres]; 1224 | 1225 | float quad_w_x = constants->cell_w; // Width in metres of each quad 1226 | float quad_w_y = quad_w_x; 1227 | if(constants->H <= 4) 1228 | { 1229 | //quad_w_y *= 20.f; // For height = 1 (1-d debugging case), display strip a bit wider 1230 | } 1231 | 1232 | const int max_src_x = constants->W - 1; 1233 | const int max_src_y = constants->H - 1; // Store these so we can handle width 1 sims 1234 | 1235 | const float p_x = x * quad_w_x; 1236 | const float p_y = y * quad_w_y; 1237 | const float dx = quad_w_x; 1238 | const float dy = quad_w_y; 1239 | 1240 | const int src_x = min(x, max_src_x); 1241 | const int src_y = min(y, max_src_y); 1242 | const int src_x_1 = min(x + 1, max_src_x); 1243 | const int src_y_1 = min(y + 1, max_src_y); 1244 | 1245 | { 1246 | const float z = totalTerrainHeight(&terrain_state[src_x + src_y *constants->W], constants->include_water_height, constants); 1247 | const float z_dx = totalTerrainHeight(&terrain_state[src_x_1 + src_y *constants->W], constants->include_water_height, constants); 1248 | const float z_dy = totalTerrainHeight(&terrain_state[src_x + src_y_1*constants->W], constants->include_water_height, constants); 1249 | 1250 | const float3 p_dx_minus_p = (float3)(dx, 0, z_dx - z); // p(p_x + dx, dy) - p(p_x, p_y) = (p_x + dx, d_y, z_dx) - (p_x, p_y, z) = (d_x, 0, z_dx - z) 1251 | const float3 p_dy_minus_p = (float3)(0, dy, z_dy - z); 1252 | 1253 | const float3 normal = normalize(cross(p_dx_minus_p, p_dy_minus_p)); 1254 | 1255 | mesh_vert->pos[0] = p_x; 1256 | mesh_vert->pos[1] = p_y; 1257 | mesh_vert->pos[2] = z; 1258 | 1259 | mesh_vert->normal[0] = normal.x; 1260 | mesh_vert->normal[1] = normal.y; 1261 | mesh_vert->normal[2] = normal.z; 1262 | } 1263 | 1264 | 1265 | // Set water mesh 1266 | { 1267 | const float z = totalTerrainHeight(&terrain_state[src_x + src_y *constants->W], /*include_water=*/true, constants); 1268 | const float z_dx = totalTerrainHeight(&terrain_state[src_x_1 + src_y *constants->W], /*include_water=*/true, constants); 1269 | const float z_dy = totalTerrainHeight(&terrain_state[src_x + src_y_1*constants->W], /*include_water=*/true, constants); 1270 | 1271 | const float3 p_dx_minus_p = (float3)(dx, 0, z_dx - z); // p(p_x + dx, dy) - p(p_x, p_y) = (p_x + dx, d_y, z_dx) - (p_x, p_y, z) = (d_x, 0, z_dx - z) 1272 | const float3 p_dy_minus_p = (float3)(0, dy, z_dy - z); 1273 | 1274 | const float3 normal = normalize(cross(p_dx_minus_p, p_dy_minus_p)); 1275 | 1276 | water_mesh_vert->pos[0] = p_x; 1277 | water_mesh_vert->pos[1] = p_y; 1278 | water_mesh_vert->pos[2] = z + constants->water_z_bias; 1279 | 1280 | water_mesh_vert->normal[0] = normal.x; 1281 | water_mesh_vert->normal[1] = normal.y; 1282 | water_mesh_vert->normal[2] = normal.z; 1283 | } 1284 | 1285 | // Write to terrain texture 1286 | const float3 rock_col = (float3)(constants->rock_col[0], constants->rock_col[1], constants->rock_col[2]); 1287 | const float3 deposited_col = (float3)(constants->sediment_col[0], constants->sediment_col[1], constants->sediment_col[2]); 1288 | const float3 water_depth_col = (float3)(constants->water_depth_col[0], constants->water_depth_col[1], constants->water_depth_col[2]); 1289 | const float3 water_speed_col = (float3)(constants->water_speed_col[0], constants->water_speed_col[1], constants->water_speed_col[2]); 1290 | 1291 | const float deposited_sed_h = terrain_state[src_x + src_y *constants->W].deposited_sed_h; 1292 | const float water_depth = waterHeightForMass(terrain_state[src_x + src_y * constants->W].water_mass, constants); 1293 | const float water_speed = length(terrain_state[src_x + src_y * constants->W].water_vel); 1294 | 1295 | const float sed_factor = smoothstep(0.f, constants->sediment_col_step, deposited_sed_h) * constants->sediment_col_weight; 1296 | const float water_depth_factor = smoothstep(0.f, constants->water_depth_col_step, water_depth) * constants->water_depth_col_weight; 1297 | const float water_speed_factor = smoothstep(0.f, constants->water_speed_col_step, water_speed) * constants->water_speed_col_weight; 1298 | 1299 | float3 col = mix(rock_col, deposited_col, sed_factor); 1300 | col = mix(col, water_depth_col, water_depth_factor); 1301 | col = mix(col, water_speed_col, water_speed_factor); 1302 | 1303 | 1304 | if(constants->debug_draw_channel == TextureShow_WaterSpeed) 1305 | { 1306 | col = (float3)(water_speed / constants->debug_display_max_val); 1307 | } 1308 | else if(constants->debug_draw_channel == TextureShow_WaterDepth) 1309 | { 1310 | col = (float3)(water_depth / constants->debug_display_max_val); 1311 | } 1312 | else if(constants->debug_draw_channel == TextureShow_SuspendedSedimentVol) 1313 | { 1314 | const float suspended_vol = terrain_state[src_x + src_y * constants->W].suspended_vol; 1315 | col = (float3)(suspended_vol / constants->debug_display_max_val); 1316 | } 1317 | else if(constants->debug_draw_channel == TextureShow_DepositedSedimentH) 1318 | { 1319 | const float h = terrain_state[src_x + src_y * constants->W].deposited_sed_h; 1320 | col = (float3)(h / constants->debug_display_max_val); 1321 | } 1322 | 1323 | write_imagef(terrain_texture, (int2)(x, y), (float4)(col, 1.f)); 1324 | } 1325 | -------------------------------------------------------------------------------- /terraingen.cpp: -------------------------------------------------------------------------------- 1 | /*===================================================================== 2 | terraingen.cpp 3 | -------------- 4 | Copyright Nicholas Chapman 2025 - 5 | =====================================================================*/ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #ifdef _WIN32 47 | #include 48 | #endif 49 | #include 50 | 51 | 52 | // Needs to match TerrainState in erosion_kernel.cl! 53 | typedef struct 54 | { 55 | float height; // terrain height ('b') (m) 56 | //float water; // water height (depth) above terrain ('d') (m) 57 | float suspended_vol; // Volume of suspended sediment. ('s') (m^3) 58 | float deposited_sed_h; // Height of deposited sediment (m) 59 | 60 | float water_mass; // water_mass = water_depth * cell_w^2 * water_density 61 | Vec2f water_vel; // average velocity of water in cell 62 | 63 | float new_water_mass; 64 | float new_suspended_vol; 65 | Vec2f new_water_vel; 66 | 67 | Vec2f water_vel_laplacian; 68 | Vec2f dterrainh_dxy; 69 | //Vec2f duv_dx; 70 | //Vec2f duv_dy; 71 | 72 | Vec2f thermal_vel; // horizontal velocity of thermally eroded solid 73 | float thermal_move_vol; // volume of thermally eroded solid 74 | float height_laplacian; 75 | 76 | } TerrainState; 77 | 78 | 79 | typedef struct 80 | { 81 | float f_L, f_R, f_T, f_B; // outflow flux 82 | float sed_f_L, sed_f_R, sed_f_T, sed_f_B; 83 | } FlowState; 84 | 85 | typedef struct 86 | { 87 | float flux[8]; 88 | 89 | } ThermalErosionState; 90 | 91 | 92 | // Needs to match Constants in erosion_kernel.cl! 93 | typedef struct 94 | { 95 | int W; // grid width 96 | int H; // grid height 97 | float cell_w; // Width of cell = spacing between grid cells (metres) 98 | float recip_cell_w; // 1 / cell_w 99 | 100 | float delta_t; // time step 101 | float r; // rainfall rate 102 | float A; // cross-sectional 'pipe' area 103 | float g; // gravity accel magnitude. positive. 104 | float l; // virtual pipe length 105 | float f; // friction constant 106 | //float k; // viscous drag coefficient (see https://en.wikipedia.org/wiki/Shallow_water_equations) 107 | float nu; // kinematic viscosity 108 | 109 | float K_c;// = 0.01; // 1; // sediment capacity constant 110 | float K_s;// = 0.01; // 0.5; // dissolving constant. 111 | float K_d;// = 0.01; // 1; // deposition constant 112 | float K_dmax;// = 0.1f; // Maximum erosion depth: water depth at which erosion stops. 113 | float K_coll; // K_coll = 0: collision cos(angle) not used, K_coll = 1: erosion rate proportional to collision cos(angle) 114 | float K_cos_angle_threshold; // K_coll = 0: collision cos(angle) not used, K_coll = 1: erosion rate proportional to collision cos(angle) 115 | float q_0; // Minimum unit water discharge for sediment carrying. 116 | float K_e; // Evaporation constant 117 | 118 | float K_smooth; // smoothing constant 119 | float laplacian_threshold; 120 | float K_t; // thermal erosion constant 121 | float K_tdep; // thermal erosion constant for deposited sediment 122 | float max_talus_angle; 123 | float tan_max_talus_angle; 124 | float max_deposited_talus_angle; 125 | float tan_max_deposited_talus_angle; 126 | float sea_level; 127 | float current_time; 128 | 129 | int include_water_height; 130 | int draw_water; 131 | Colour3f rock_col; 132 | Colour3f sediment_col; 133 | float sediment_col_step; 134 | float sediment_col_weight; 135 | Colour3f vegetation_col; 136 | Colour3f water_depth_col; 137 | float water_depth_col_step; 138 | float water_depth_col_weight; 139 | Colour3f water_speed_col; 140 | float water_speed_col_step; 141 | float water_speed_col_weight; 142 | 143 | int debug_draw_channel; // From TextureShow enum 144 | float debug_display_max_val; 145 | 146 | float water_z_bias; 147 | } Constants; 148 | 149 | 150 | 151 | enum InitialTerrainShape 152 | { 153 | InitialTerrainShape_ConstantSlope, 154 | InitialTerrainShape_Hat, 155 | InitialTerrainShape_Cone, 156 | InitialTerrainShape_Cylinder, 157 | InitialTerrainShape_FBM, 158 | InitialTerrainShape_Perlin 159 | }; 160 | 161 | const char* InitialTerrainShape_display_strings[] = 162 | { 163 | "constant slope", 164 | "hat", 165 | "cone", 166 | "cylinder", 167 | "FBM", 168 | "Perlin noise" 169 | }; 170 | 171 | // For writing to xml 172 | const char* InitialTerrainShape_storage_strings[] = 173 | { 174 | "constant_slope", 175 | "hat", 176 | "cone", 177 | "cylinder", 178 | "FBM", 179 | "Perlin" 180 | }; 181 | 182 | 183 | 184 | struct TerrainParams 185 | { 186 | InitialTerrainShape terrain_shape; 187 | float height_scale; 188 | float fine_roughness_vert_scale; 189 | float x_scale; 190 | float y_scale; 191 | float initial_water_depth; 192 | }; 193 | 194 | 195 | 196 | class Simulation 197 | { 198 | public: 199 | int sim_iteration; 200 | int W, H; 201 | 202 | Array2D terrain_state; 203 | Array2D flow_state; 204 | Array2D thermal_erosion_state; 205 | 206 | 207 | OpenCLKernelRef computeWaterVelDerivs; 208 | OpenCLKernelRef thermalErosionFluxKernel; 209 | OpenCLKernelRef thermalErosionDepositedFluxKernel; 210 | OpenCLKernelRef waterVelFieldUpdateKernel; 211 | OpenCLKernelRef erosionAndDepositionKernel; 212 | OpenCLKernelRef waterAndSedimentTransportationKernel; 213 | OpenCLKernelRef thermalErosionMovementKernel; 214 | OpenCLKernelRef thermalErosionDepositedMovementKernel; 215 | OpenCLKernelRef evaporationKernel; 216 | OpenCLKernelRef setHeightFieldMeshKernel; 217 | 218 | OpenCLBuffer terrain_state_buffer; 219 | //OpenCLBuffer flow_state_buffer_a; 220 | //OpenCLBuffer flow_state_buffer_b; 221 | OpenCLBuffer thermal_erosion_state_buffer; 222 | OpenCLBuffer constants_buffer; 223 | 224 | cl_mem heightfield_mesh_buffer; 225 | uint32 heightfield_mesh_offset_B; 226 | cl_mem water_heightfield_mesh_buffer; 227 | uint32 water_heightfield_mesh_offset_B; 228 | cl_mem terrain_tex_cl_mem; 229 | 230 | bool use_water_mesh; 231 | 232 | Timer timer; 233 | 234 | Simulation(int W_, int H_, OpenCLContextRef opencl_context, OpenCLProgramRef program, OpenCLDeviceRef opencl_device, bool profile, const Constants& constants) 235 | { 236 | W = W_; 237 | H = H_; 238 | 239 | sim_iteration = 0; 240 | 241 | terrain_state.resize(W, H); 242 | flow_state.resize(W, H); 243 | 244 | use_water_mesh = true; 245 | 246 | terrain_state_buffer.alloc(opencl_context, /*size=*/constants.W * constants.H * sizeof(TerrainState), CL_MEM_READ_WRITE); 247 | //flow_state_buffer_a.alloc(opencl_context, constants.W * constants.H * sizeof(FlowState), CL_MEM_READ_WRITE); 248 | //flow_state_buffer_b.alloc(opencl_context, constants.W * constants.H * sizeof(FlowState), CL_MEM_READ_WRITE); 249 | thermal_erosion_state_buffer.alloc(opencl_context, constants.W * constants.H * sizeof(ThermalErosionState), CL_MEM_READ_WRITE); 250 | constants_buffer.allocFrom(opencl_context, &constants, sizeof(Constants), CL_MEM_READ_ONLY); 251 | 252 | computeWaterVelDerivs = new OpenCLKernel(program, "computeWaterVelDerivs", opencl_device->opencl_device_id, profile); 253 | thermalErosionFluxKernel = new OpenCLKernel(program, "thermalErosionFluxKernel", opencl_device->opencl_device_id, profile); 254 | //thermalErosionDepositedFluxKernel = new OpenCLKernel(program, "thermalErosionDepositedFluxKernel", opencl_device->opencl_device_id, profile); 255 | waterVelFieldUpdateKernel = new OpenCLKernel(program, "waterVelFieldUpdateKernel", opencl_device->opencl_device_id, profile); 256 | erosionAndDepositionKernel = new OpenCLKernel(program, "erosionAndDepositionKernel", opencl_device->opencl_device_id, profile); 257 | waterAndSedimentTransportationKernel = new OpenCLKernel(program, "waterAndSedimentTransportationKernel", opencl_device->opencl_device_id, profile); 258 | thermalErosionMovementKernel = new OpenCLKernel(program, "thermalErosionMovementKernel", opencl_device->opencl_device_id, profile); 259 | //thermalErosionDepositedMovementKernel = new OpenCLKernel(program, "thermalErosionDepositedMovementKernel", opencl_device->opencl_device_id, profile); 260 | evaporationKernel = new OpenCLKernel(program, "evaporationKernel", opencl_device->opencl_device_id, profile); 261 | setHeightFieldMeshKernel = new OpenCLKernel(program, "setHeightFieldMeshKernel", opencl_device->opencl_device_id, profile); 262 | } 263 | 264 | 265 | void readBackToCPUMem(OpenCLCommandQueueRef command_queue) 266 | { 267 | // Read back terrain state buffer to CPU mem 268 | terrain_state_buffer.readTo(command_queue, /*dest ptr=*/&terrain_state.elem(0, 0), /*size=*/W * H * sizeof(TerrainState), /*blocking read=*/true); 269 | 270 | // TEMP: just for debugging (showing/reading flux) 271 | //flow_state_buffer_a.readTo(command_queue, /*dest ptr=*/&flow_state.elem(0, 0), /*size=*/W * H * sizeof(FlowState), /*blocking read=*/true); 272 | } 273 | 274 | 275 | void doSimIteration(OpenCLCommandQueueRef command_queue) 276 | { 277 | // Sets water_vel_laplacian 278 | computeWaterVelDerivs->setKernelArgBuffer(0, terrain_state_buffer); 279 | computeWaterVelDerivs->setKernelArgBuffer(1, constants_buffer); 280 | computeWaterVelDerivs->launchKernel2D(command_queue->getCommandQueue(), W, H); 281 | 282 | // Update dterrainh_dxy, water_vel 283 | waterVelFieldUpdateKernel->setKernelArgBuffer(0, terrain_state_buffer); 284 | waterVelFieldUpdateKernel->setKernelArgBuffer(1, constants_buffer); 285 | waterVelFieldUpdateKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 286 | 287 | // Updates new_water_vel, new_water_mass, new_suspended_vol 288 | waterAndSedimentTransportationKernel->setKernelArgBuffer(0, terrain_state_buffer); 289 | waterAndSedimentTransportationKernel->setKernelArgBuffer(1, constants_buffer); 290 | waterAndSedimentTransportationKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 291 | 292 | // Updates height, suspended_vol, deposited_sed_h, also assigns new_water_mass -> water_mass etc. 293 | erosionAndDepositionKernel->setKernelArgBuffer(0, terrain_state_buffer); 294 | erosionAndDepositionKernel->setKernelArgBuffer(1, constants_buffer); 295 | erosionAndDepositionKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 296 | 297 | 298 | for(int deposited_sed=0; deposited_sed<2; ++deposited_sed) 299 | { 300 | // Sets height_laplacian, thermal_vel, thermal_move_vol 301 | thermalErosionFluxKernel->setKernelArgBuffer(0, terrain_state_buffer); 302 | thermalErosionFluxKernel->setKernelArgBuffer(1, thermal_erosion_state_buffer); // source 303 | thermalErosionFluxKernel->setKernelArgBuffer(2, constants_buffer); 304 | thermalErosionFluxKernel->setKernelArgInt(3, deposited_sed); // erosion of deposited sediment? 305 | thermalErosionFluxKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 306 | 307 | // Sets deposited_sed_h, height 308 | thermalErosionMovementKernel->setKernelArgBuffer(0, thermal_erosion_state_buffer); 309 | thermalErosionMovementKernel->setKernelArgBuffer(1, terrain_state_buffer); 310 | thermalErosionMovementKernel->setKernelArgBuffer(2, constants_buffer); 311 | thermalErosionMovementKernel->setKernelArgInt(3, deposited_sed); // erosion of deposited sediment? 312 | thermalErosionMovementKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 313 | } 314 | 315 | //thermalErosionFluxKernel->setKernelArgBuffer(0, terrain_state_buffer); 316 | //thermalErosionFluxKernel->setKernelArgBuffer(1, thermal_erosion_state_buffer); // source 317 | //thermalErosionFluxKernel->setKernelArgBuffer(2, constants_buffer); 318 | //thermalErosionFluxKernel->setKernelArgInt(3, 1); // erosion of deposited sediment? 319 | //thermalErosionFluxKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 320 | 321 | //thermalErosionMovementKernel->setKernelArgBuffer(0, thermal_erosion_state_buffer); 322 | //thermalErosionMovementKernel->setKernelArgBuffer(1, terrain_state_buffer); 323 | //thermalErosionMovementKernel->setKernelArgBuffer(2, constants_buffer); 324 | //thermalErosionMovementKernel->setKernelArgInt(3, 1); // erosion of deposited sediment? 325 | //thermalErosionMovementKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 326 | 327 | //thermalErosionDepositedFluxKernel->setKernelArgBuffer(0, terrain_state_buffer); 328 | //thermalErosionDepositedFluxKernel->setKernelArgBuffer(1, thermal_erosion_state_buffer); // source 329 | //thermalErosionDepositedFluxKernel->setKernelArgBuffer(2, constants_buffer); 330 | //thermalErosionDepositedFluxKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 331 | 332 | //thermalErosionDepositedMovementKernel->setKernelArgBuffer(0, thermal_erosion_state_buffer); 333 | //thermalErosionDepositedMovementKernel->setKernelArgBuffer(1, terrain_state_buffer); 334 | //thermalErosionDepositedMovementKernel->setKernelArgBuffer(2, constants_buffer); 335 | //thermalErosionDepositedMovementKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 336 | 337 | // Updates water_mass, water_vel 338 | evaporationKernel->setKernelArgBuffer(0, terrain_state_buffer); 339 | evaporationKernel->setKernelArgBuffer(1, constants_buffer); 340 | evaporationKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 341 | 342 | sim_iteration++; 343 | } 344 | 345 | void updateHeightFieldMeshAndTexture(OpenCLCommandQueueRef command_queue) 346 | { 347 | SmallVector mem_objects; 348 | mem_objects.push_back(heightfield_mesh_buffer); 349 | if(use_water_mesh) 350 | mem_objects.push_back(water_heightfield_mesh_buffer); 351 | mem_objects.push_back(terrain_tex_cl_mem); 352 | ::getGlobalOpenCL()->clEnqueueAcquireGLObjects(command_queue->getCommandQueue(), 353 | /*num objects=*/(cl_uint)mem_objects.size(), /*mem objects=*/mem_objects.data(), /*num objects in wait list=*/0, /*event wait list=*/NULL, /*event=*/NULL); 354 | 355 | setHeightFieldMeshKernel->setKernelArgBuffer(0, terrain_state_buffer); 356 | setHeightFieldMeshKernel->setKernelArgBuffer(1, constants_buffer); 357 | setHeightFieldMeshKernel->setKernelArgBuffer(2, heightfield_mesh_buffer); 358 | setHeightFieldMeshKernel->setKernelArgUInt(3, heightfield_mesh_offset_B); 359 | setHeightFieldMeshKernel->setKernelArgBuffer(4, terrain_tex_cl_mem); 360 | if(use_water_mesh) 361 | { 362 | setHeightFieldMeshKernel->setKernelArgBuffer(5, water_heightfield_mesh_buffer); 363 | setHeightFieldMeshKernel->setKernelArgUInt(6, water_heightfield_mesh_offset_B); 364 | } 365 | 366 | 367 | setHeightFieldMeshKernel->launchKernel2D(command_queue->getCommandQueue(), W, H); 368 | 369 | ::getGlobalOpenCL()->clEnqueueReleaseGLObjects(command_queue->getCommandQueue(), 370 | /*num objects=*/(cl_uint)mem_objects.size(), /*mem objects=*/mem_objects.data(), /*num objects in wait list=*/0, /*event wait list=*/NULL, /*event=*/NULL); 371 | 372 | command_queue->finish(); // Make sure we have finished writing to the vertex buffer and texture before we start issuing new OpenGL commands. 373 | // TODO: work out a better way of syncing (e.g. with barrier). 374 | } 375 | }; 376 | 377 | 378 | enum HeightFieldShow 379 | { 380 | HeightFieldShow_TerrainOnly, 381 | HeightFieldShow_TerrainAndWater 382 | }; 383 | 384 | const char* HeightFieldShow_strings[] = 385 | { 386 | "terrain only", 387 | "terrain and water" 388 | }; 389 | 390 | 391 | // Should match TextureShow_Default etc. defines in erosion_kernel.cl 392 | const char* TextureShow_strings[] = 393 | { 394 | "none", 395 | "water speed", 396 | "water depth", 397 | "suspended sediment vol", 398 | "deposited sediment height" 399 | }; 400 | 401 | 402 | inline float totalTerrainHeight(const TerrainState& state) 403 | { 404 | return state.height + state.deposited_sed_h;//state.sediment[0] + state.sediment[1] + state.sediment[2]; 405 | } 406 | 407 | 408 | OpenGLMeshRenderDataRef makeTerrainMesh(const Simulation& sim, OpenGLEngine* opengl_engine, float cell_w) 409 | { 410 | /* 411 | y 412 | ^ 413 | | 414 | -------------------- 415 | | /| /| /| /| 416 | | / | / | / | / | 417 | | / | / | / | / | 418 | |----|----|----|----| 419 | | /| /| /| /| 420 | | / | / | / | / | 421 | | / | / | / | / | 422 | |----|----|----|----| 423 | | /| /| /| /| 424 | | / | / | / | / | 425 | | / | / | / | / | 426 | |----|----|----|----| 427 | | /| /| /| /| 428 | | / | / | / | / | 429 | | / | / | / | / | 430 | |----|----|----|----|---> x 431 | */ 432 | 433 | int vert_xres = myMax(2, (int)sim.terrain_state.getWidth()); 434 | int vert_yres = myMax(2, (int)sim.terrain_state.getHeight()); 435 | int quad_xres = vert_xres - 1; // Number of quads in x and y directions 436 | int quad_yres = vert_yres - 1; // Number of quads in x and y directions 437 | 438 | float quad_w_x = cell_w; 439 | float quad_w_y = quad_w_x; 440 | if(sim.terrain_state.getHeight() <= 10) 441 | { 442 | quad_w_y *= 20.f; // For height = 1 (1-d debugging case), display strip a bit wider 443 | } 444 | 445 | //const size_t normal_size_B = 4; 446 | const size_t normal_size_B = sizeof(float) * 3; 447 | const size_t vert_size_B = sizeof(float) * (3 + 2) + normal_size_B; // position, normal, uv 448 | OpenGLMeshRenderDataRef mesh_data = new OpenGLMeshRenderData(); 449 | mesh_data->vert_data.resize(vert_size_B * vert_xres * vert_yres); 450 | 451 | mesh_data->vert_index_buffer.resize(quad_xres * quad_yres * 6); 452 | 453 | OpenGLMeshRenderData& meshdata = *mesh_data; 454 | 455 | meshdata.has_uvs = true; 456 | meshdata.has_shading_normals = true; 457 | meshdata.batches.resize(1); 458 | meshdata.batches[0].material_index = 0; 459 | meshdata.batches[0].num_indices = (uint32)meshdata.vert_index_buffer.size(); 460 | meshdata.batches[0].prim_start_offset_B = 0; 461 | 462 | meshdata.num_materials_referenced = 1; 463 | 464 | meshdata.setIndexType(GL_UNSIGNED_INT); 465 | 466 | // NOTE: The order of these attributes should be the same as in OpenGLProgram constructor with the glBindAttribLocations. 467 | size_t in_vert_offset_B = 0; 468 | VertexAttrib pos_attrib; 469 | pos_attrib.enabled = true; 470 | pos_attrib.num_comps = 3; 471 | pos_attrib.type = GL_FLOAT; 472 | pos_attrib.normalised = false; 473 | pos_attrib.stride = vert_size_B; 474 | pos_attrib.offset = (uint32)in_vert_offset_B; 475 | meshdata.vertex_spec.attributes.push_back(pos_attrib); 476 | in_vert_offset_B += sizeof(float) * 3; 477 | 478 | VertexAttrib normal_attrib; 479 | normal_attrib.enabled = true; 480 | normal_attrib.num_comps = 3; 481 | normal_attrib.type = GL_FLOAT; 482 | normal_attrib.normalised = false; 483 | normal_attrib.stride = vert_size_B; 484 | normal_attrib.offset = (uint32)in_vert_offset_B; 485 | meshdata.vertex_spec.attributes.push_back(normal_attrib); 486 | in_vert_offset_B += normal_size_B; 487 | 488 | const size_t uv_offset_B = in_vert_offset_B; 489 | VertexAttrib uv_attrib; 490 | uv_attrib.enabled = true; 491 | uv_attrib.num_comps = 2; 492 | uv_attrib.type = GL_FLOAT; 493 | uv_attrib.normalised = false; 494 | uv_attrib.stride = vert_size_B; 495 | uv_attrib.offset = (uint32)uv_offset_B; 496 | meshdata.vertex_spec.attributes.push_back(uv_attrib); 497 | in_vert_offset_B += sizeof(float) * 2; 498 | 499 | assert(in_vert_offset_B == vert_size_B); 500 | 501 | uint8* const vert_data = mesh_data->vert_data.data(); 502 | 503 | //Timer timer; 504 | 505 | for(int y=0; yvert_index_buffer.data(); 531 | for(int y=0; y x 540 | 541 | // bot left tri 542 | int offset = (y*quad_xres + x) * 6; 543 | indices[offset + 0] = y * vert_xres + x; // bot left 544 | indices[offset + 1] = y * vert_xres + x + 1; // bot right 545 | indices[offset + 2] = (y + 1) * vert_xres + x; // top left 546 | 547 | // top right tri 548 | indices[offset + 3] = y * vert_xres + x + 1; // bot right 549 | indices[offset + 4] = (y + 1) * vert_xres + x + 1; // top right 550 | indices[offset + 5] = (y + 1) * vert_xres + x; // top left 551 | } 552 | 553 | //conPrint("Creating mesh took " + timer.elapsedStringMSWIthNSigFigs(4)); 554 | 555 | mesh_data->indices_vbo_handle = opengl_engine->vert_buf_allocator->allocateIndexDataSpace(mesh_data->vert_index_buffer.data(), mesh_data->vert_index_buffer.dataSizeBytes()); 556 | 557 | mesh_data->vbo_handle = opengl_engine->vert_buf_allocator->allocateVertexDataSpace(mesh_data->vertex_spec.vertStride(), mesh_data->vert_data.data(), mesh_data->vert_data.dataSizeBytes()); 558 | 559 | opengl_engine->vert_buf_allocator->getOrCreateAndAssignVAOForMesh(*mesh_data, mesh_data->vertex_spec); 560 | 561 | return mesh_data; 562 | } 563 | 564 | 565 | struct TerrainStats 566 | { 567 | float total_uneroded_volume; 568 | float total_water_mass; 569 | float total_suspended_sediment_vol; 570 | float total_deposited_sediment_vol; 571 | 572 | float total_solid_volume; // Uneroded volume + deposited volume + suspended volume 573 | }; 574 | 575 | TerrainStats computeTerrainStats(Simulation& sim, const Constants& constants) 576 | { 577 | double sum_uneroded_terrain_h = 0; 578 | double sum_water_mass = 0; 579 | double sum_suspended_vol = 0; 580 | double sum_deposited_sediment_h = 0; 581 | for(int y=0; y 0.f ? PerlinNoise::FBM(nx * fine_rough_xy_scale, ny * fine_rough_xy_scale, 12) * terrain_params.fine_roughness_vert_scale : 0.f); 672 | } 673 | } 674 | else if(terrain_params.terrain_shape == InitialTerrainShape::InitialTerrainShape_Hat) 675 | { 676 | for(int x=0; x 0.f ? PerlinNoise::FBM(nx * fine_rough_xy_scale, ny * fine_rough_xy_scale, 12) * terrain_params.fine_roughness_vert_scale : 0.f); 686 | } 687 | } 688 | else if(terrain_params.terrain_shape == InitialTerrainShape::InitialTerrainShape_Cone) 689 | { 690 | for(int x=0; x 0.f ? PerlinNoise::FBM(nx * fine_rough_xy_scale, ny * fine_rough_xy_scale, 12) * terrain_params.fine_roughness_vert_scale : 0.f); 705 | } 706 | } 707 | else if(terrain_params.terrain_shape == InitialTerrainShape::InitialTerrainShape_Cylinder) 708 | { 709 | for(int x=0; x 0.f ? PerlinNoise::FBM(nx * fine_rough_xy_scale, ny * fine_rough_xy_scale, 12) * terrain_params.fine_roughness_vert_scale : 0.f); 724 | } 725 | } 726 | else if(terrain_params.terrain_shape == InitialTerrainShape::InitialTerrainShape_FBM) 727 | { 728 | for(int x=0; x(Vec4f(nx * 0.5f, ny * 0.5f, 0, 1), 1, 2, 5); 741 | sim.terrain_state.elem(x, y).height = myMax(-100.f, perlin_factor)/3.f * W / 5.0f * total_vert_scale;// * myMax(1 - (1.1f * r / ((float)W/2)), 0.f) * 200.f; 742 | //sim.terrain_state.elem(x, y).height = nx < 0.25 ? 0 : (nx < 0.5 ? (nx - 0.25) : (1.0f - (nx-0.25)) * 200.f; 743 | //const float tent = (nx < 0.5) ? nx : (1.0f - nx); 744 | //sim.terrain_state.elem(x, y).height = myMax(0.0f, tent*2 - 0.5f) * 200.f; 745 | } 746 | } 747 | else if(terrain_params.terrain_shape == InitialTerrainShape::InitialTerrainShape_Perlin) 748 | { 749 | for(int x=0; x 0.f ? PerlinNoise::FBM(nx * fine_rough_xy_scale, ny * fine_rough_xy_scale, 12) * terrain_params.fine_roughness_vert_scale : 0.f); 758 | } 759 | } 760 | 761 | // Set water height based on sea level 762 | for(int y=0; y data(sim.W * sim.H); 780 | for(int y=0; y data(sim.W * sim.H); 813 | for(int y=0; yfinish(); 861 | glFinish(); 862 | 863 | try 864 | { 865 | FileDialogs::Options options; 866 | options.dialog_title = "Save Heightfield"; 867 | options.file_types.push_back(FileDialogs::FileTypeInfo("PNG", "*.png", "png")); 868 | const std::string path = FileDialogs::showSaveFileDialog(options); 869 | if(!path.empty()) 870 | { 871 | ImageMapUInt8 map(sim.W, sim.H, 4); 872 | map.setAsNotIndependentlyHeapAllocated(); 873 | 874 | terrain_col_tex->readBackTexture(/*mipmap level=*/0, ArrayRef(map.getData(), map.getDataSize())); 875 | 876 | // Convert to 3-component map and flip upside down (so that y=0 is at the top as PNG expects) 877 | ImageMapUInt8Ref rgb_map = new ImageMapUInt8(sim.W, sim.H, 3); // map.extract3ChannelImage(); 878 | for(int y=0; ygetPixel(x, y)[c] = map.getPixel(x, sim.H - y - 1)[c]; 883 | } 884 | PNGDecoder::write(*rgb_map, path); 885 | 886 | showNotification(info, "Saved image to '" + path + "'."); 887 | } 888 | } 889 | catch(glare::Exception& e) 890 | { 891 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Failed to save", ("Failed to save heightfield to disk: " + e.what()).c_str(), /*parent=*/NULL); 892 | } 893 | } 894 | 895 | 896 | void loadStructMemberFromEXR(Simulation& sim, const std::string& exr_path, size_t member_offset_B) 897 | { 898 | Reference map = EXRDecoder::decode(exr_path); 899 | 900 | if(map.isType()) 901 | { 902 | ImageMapFloatRef image_map = map.downcast(); 903 | 904 | if(image_map->getWidth() != sim.W) 905 | throw glare::Exception("Width does not match"); 906 | if(image_map->getHeight() != sim.H) 907 | throw glare::Exception("Height does not match"); 908 | 909 | for(int y=0; ygetPixel(x, y)[0]; 912 | } 913 | else 914 | throw glare::Exception("Unhandled image type"); 915 | } 916 | 917 | 918 | void loadHeightfieldFromDisk(Simulation& sim, OpenCLCommandQueueRef command_queue) 919 | { 920 | conPrint("Loading heightfield from disk..."); 921 | 922 | loadStructMemberFromEXR(sim, "heightfield.exr", offsetof(TerrainState, height)); 923 | 924 | loadStructMemberFromEXR(sim, "sediment_map.exr", offsetof(TerrainState, deposited_sed_h)); 925 | 926 | //TEMP loadStructMemberFromEXR(sim, "water.exr", offsetof(TerrainState, water)); 927 | 928 | // Upload to GPU 929 | sim.terrain_state_buffer.copyFrom(command_queue, /*src ptr=*/&sim.terrain_state.elem(0, 0), /*size=*/sim.W * sim.H * sizeof(TerrainState), CL_MEM_READ_WRITE); 930 | 931 | conPrint("done."); 932 | } 933 | 934 | 935 | #define WRITE_FLOAT_PARAM(param) XMLWriteUtils::writeFloatToXML(xml, #param, constants.param, tab_depth); 936 | 937 | 938 | void saveParametersToFile(const Constants& constants, const TerrainParams& terrain_params, const std::string& path) 939 | { 940 | std::string xml = "\n"; 941 | 942 | xml += "\n"; 943 | 944 | const int tab_depth = 1; 945 | 946 | XMLWriteUtils::writeInt32ToXML(xml, "version", 1, tab_depth); 947 | 948 | XMLWriteUtils::writeInt32ToXML(xml, "W", constants.W, tab_depth); 949 | XMLWriteUtils::writeInt32ToXML(xml, "H", constants.H, tab_depth); 950 | WRITE_FLOAT_PARAM(cell_w); 951 | 952 | WRITE_FLOAT_PARAM(delta_t); 953 | WRITE_FLOAT_PARAM(r); 954 | WRITE_FLOAT_PARAM(A); 955 | WRITE_FLOAT_PARAM(g); 956 | WRITE_FLOAT_PARAM(l); 957 | WRITE_FLOAT_PARAM(f); 958 | //WRITE_FLOAT_PARAM(k); 959 | WRITE_FLOAT_PARAM(nu); 960 | 961 | WRITE_FLOAT_PARAM(K_c); 962 | WRITE_FLOAT_PARAM(K_s); 963 | WRITE_FLOAT_PARAM(K_d); 964 | WRITE_FLOAT_PARAM(K_dmax); 965 | WRITE_FLOAT_PARAM(q_0); 966 | WRITE_FLOAT_PARAM(K_e); 967 | WRITE_FLOAT_PARAM(K_coll); 968 | WRITE_FLOAT_PARAM(K_cos_angle_threshold); 969 | 970 | WRITE_FLOAT_PARAM(K_smooth); 971 | WRITE_FLOAT_PARAM(laplacian_threshold); 972 | WRITE_FLOAT_PARAM(K_t); 973 | WRITE_FLOAT_PARAM(K_tdep); 974 | WRITE_FLOAT_PARAM(max_talus_angle); 975 | WRITE_FLOAT_PARAM(max_deposited_talus_angle); 976 | WRITE_FLOAT_PARAM(sea_level); 977 | 978 | XMLWriteUtils::writeInt32ToXML(xml, "include_water_height", constants.include_water_height, tab_depth); 979 | XMLWriteUtils::writeInt32ToXML(xml, "draw_water", constants.draw_water, tab_depth); 980 | XMLWriteUtils::writeColour3fToXML(xml, "rock_col", constants.rock_col, tab_depth); 981 | XMLWriteUtils::writeColour3fToXML(xml, "sediment_col", constants.sediment_col, tab_depth); 982 | WRITE_FLOAT_PARAM(sediment_col_step); 983 | WRITE_FLOAT_PARAM(sediment_col_weight); 984 | XMLWriteUtils::writeColour3fToXML(xml, "vegetation_col", constants.vegetation_col, tab_depth); 985 | XMLWriteUtils::writeColour3fToXML(xml, "water_depth_col", constants.water_depth_col, tab_depth); 986 | WRITE_FLOAT_PARAM(water_depth_col_step); 987 | WRITE_FLOAT_PARAM(water_depth_col_weight); 988 | XMLWriteUtils::writeColour3fToXML(xml, "water_speed_col", constants.water_speed_col, tab_depth); 989 | WRITE_FLOAT_PARAM(water_speed_col_step); 990 | WRITE_FLOAT_PARAM(water_speed_col_weight); 991 | 992 | // Write terrain params 993 | xml += "\t\n"; 994 | XMLWriteUtils::writeStringElemToXML(xml, "terrain_shape", InitialTerrainShape_storage_strings[terrain_params.terrain_shape], tab_depth); 995 | XMLWriteUtils::writeFloatToXML(xml, "height_scale", terrain_params.height_scale, tab_depth); 996 | XMLWriteUtils::writeFloatToXML(xml, "fine_roughness_vert_scale", terrain_params.fine_roughness_vert_scale, tab_depth); 997 | XMLWriteUtils::writeFloatToXML(xml, "x_scale", terrain_params.x_scale, tab_depth); 998 | XMLWriteUtils::writeFloatToXML(xml, "y_scale", terrain_params.y_scale, tab_depth); 999 | XMLWriteUtils::writeFloatToXML(xml, "initial_water_depth", terrain_params.initial_water_depth, tab_depth); 1000 | 1001 | xml += "\n"; 1002 | 1003 | FileUtils::writeEntireFileTextMode(path, xml); 1004 | } 1005 | 1006 | 1007 | #define PARSE_FLOAT_PARAM(param) constants.param = XMLParseUtils::parseFloatWithDefault(sim_params_node, #param, /*default val=*/constants.param); 1008 | 1009 | void loadParametersFromFile(const std::string& path, Constants& constants, TerrainParams& terrain_params) 1010 | { 1011 | std::string xml = "\n"; 1012 | 1013 | xml += "\n"; 1014 | 1015 | IndigoXMLDoc doc(path); 1016 | 1017 | pugi::xml_node sim_params_node = doc.getRootElement(); 1018 | 1019 | constants.W = XMLParseUtils::parseIntWithDefault(sim_params_node, "W", /*default=*/1024); 1020 | constants.H = XMLParseUtils::parseIntWithDefault(sim_params_node, "H", /*default=*/1024); 1021 | PARSE_FLOAT_PARAM(cell_w); 1022 | 1023 | PARSE_FLOAT_PARAM(delta_t); 1024 | PARSE_FLOAT_PARAM(r); 1025 | PARSE_FLOAT_PARAM(A); 1026 | PARSE_FLOAT_PARAM(g); 1027 | PARSE_FLOAT_PARAM(l); 1028 | PARSE_FLOAT_PARAM(f); 1029 | //PARSE_FLOAT_PARAM(k); 1030 | PARSE_FLOAT_PARAM(nu); 1031 | 1032 | PARSE_FLOAT_PARAM(K_c); 1033 | PARSE_FLOAT_PARAM(K_s); 1034 | PARSE_FLOAT_PARAM(K_d); 1035 | PARSE_FLOAT_PARAM(K_dmax); 1036 | PARSE_FLOAT_PARAM(q_0); 1037 | PARSE_FLOAT_PARAM(K_e); 1038 | PARSE_FLOAT_PARAM(K_coll); 1039 | PARSE_FLOAT_PARAM(K_cos_angle_threshold); 1040 | 1041 | PARSE_FLOAT_PARAM(K_smooth); 1042 | PARSE_FLOAT_PARAM(laplacian_threshold); 1043 | PARSE_FLOAT_PARAM(K_t); 1044 | PARSE_FLOAT_PARAM(K_tdep); 1045 | PARSE_FLOAT_PARAM(max_talus_angle); 1046 | PARSE_FLOAT_PARAM(max_deposited_talus_angle); 1047 | PARSE_FLOAT_PARAM(sea_level); 1048 | 1049 | constants.include_water_height = XMLParseUtils::parseIntWithDefault(sim_params_node, "include_water_height", constants.include_water_height); 1050 | constants.draw_water = XMLParseUtils::parseIntWithDefault(sim_params_node, "draw_water", constants.draw_water); 1051 | constants.rock_col = XMLParseUtils::parseColour3fWithDefault(sim_params_node, "rock_col", constants.rock_col); 1052 | constants.rock_col = XMLParseUtils::parseColour3fWithDefault(sim_params_node, "rock_col", constants.rock_col); 1053 | constants.sediment_col = XMLParseUtils::parseColour3fWithDefault(sim_params_node, "sediment_col", constants.sediment_col); 1054 | PARSE_FLOAT_PARAM(sediment_col_step); 1055 | PARSE_FLOAT_PARAM(sediment_col_weight); 1056 | constants.vegetation_col = XMLParseUtils::parseColour3fWithDefault(sim_params_node, "vegetation_col", constants.vegetation_col); 1057 | constants.water_depth_col = XMLParseUtils::parseColour3fWithDefault(sim_params_node, "water_depth_col", constants.water_depth_col); 1058 | PARSE_FLOAT_PARAM(water_depth_col_step); 1059 | PARSE_FLOAT_PARAM(water_depth_col_weight); 1060 | constants.water_speed_col = XMLParseUtils::parseColour3fWithDefault(sim_params_node, "water_speed_col", constants.water_speed_col); 1061 | PARSE_FLOAT_PARAM(water_speed_col_step); 1062 | PARSE_FLOAT_PARAM(water_speed_col_weight); 1063 | 1064 | 1065 | // Read terrain params 1066 | const std::string terrain_shape = XMLParseUtils::parseStringWithDefault(sim_params_node, "terrain_shape", InitialTerrainShape_storage_strings[0]); 1067 | for(size_t i=0; iclCreateFromGLTexture(opencl_context->getContext(), CL_MEM_WRITE_ONLY, /*texture target=*/GL_TEXTURE_2D, /*miplevel=*/0, /*texture=*/terrain_col_tex->texture_handle, &retcode); 1086 | if(retcode != CL_SUCCESS) 1087 | throw glare::Exception("Failed to create OpenCL buffer for GL terrain texture: " + OpenCL::errorString(retcode)); 1088 | 1089 | sim->terrain_tex_cl_mem = terrain_tex_cl_mem; 1090 | } 1091 | 1092 | // Get OpenCL buffer for OpenGL terrain mesh vertex buffer 1093 | { 1094 | const GLuint buffer_name = terrain_gl_ob_mesh_data->vbo_handle.vbo->bufferName(); 1095 | const cl_mem mesh_vert_buffer_cl_mem = getGlobalOpenCL()->clCreateFromGLBuffer(opencl_context->getContext(), CL_MEM_WRITE_ONLY, buffer_name, &retcode); 1096 | if(retcode != CL_SUCCESS) 1097 | throw glare::Exception("Failed to create OpenCL buffer for GL buffer: " + OpenCL::errorString(retcode)); 1098 | 1099 | sim->heightfield_mesh_buffer = mesh_vert_buffer_cl_mem; 1100 | sim->heightfield_mesh_offset_B = (uint32)terrain_gl_ob_mesh_data->vbo_handle.offset; 1101 | } 1102 | 1103 | 1104 | // Get OpenCL buffer for OpenGL water mesh vertex buffer 1105 | if(water_gl_ob_mesh_data) 1106 | { 1107 | const GLuint buffer_name = water_gl_ob_mesh_data->vbo_handle.vbo->bufferName(); 1108 | const cl_mem mesh_vert_buffer_cl_mem = getGlobalOpenCL()->clCreateFromGLBuffer(opencl_context->getContext(), CL_MEM_WRITE_ONLY, buffer_name, &retcode); 1109 | if(retcode != CL_SUCCESS) 1110 | throw glare::Exception("Failed to create OpenCL buffer for GL buffer: " + OpenCL::errorString(retcode)); 1111 | 1112 | sim->water_heightfield_mesh_buffer = mesh_vert_buffer_cl_mem; 1113 | sim->water_heightfield_mesh_offset_B = (uint32)water_gl_ob_mesh_data->vbo_handle.offset; 1114 | } 1115 | } 1116 | 1117 | 1118 | void unshareOpenGLBuffersFromOpenCLSim(Simulation* sim) 1119 | { 1120 | cl_int result = getGlobalOpenCL()->clReleaseMemObject(sim->terrain_tex_cl_mem); 1121 | if(result != CL_SUCCESS) 1122 | throw glare::Exception("clReleaseMemObject failed: " + OpenCL::errorString(result)); 1123 | 1124 | result = getGlobalOpenCL()->clReleaseMemObject(sim->heightfield_mesh_buffer); 1125 | if(result != CL_SUCCESS) 1126 | throw glare::Exception("clReleaseMemObject failed: " + OpenCL::errorString(result)); 1127 | 1128 | result = getGlobalOpenCL()->clReleaseMemObject(sim->water_heightfield_mesh_buffer); 1129 | if(result != CL_SUCCESS) 1130 | throw glare::Exception("clReleaseMemObject failed: " + OpenCL::errorString(result)); 1131 | } 1132 | 1133 | 1134 | void createAndAddTerrainTextureAndMeshes(Constants& constants, Simulation* sim, Reference opengl_engine, OpenCLContextRef opencl_context, bool use_water_mesh, OpenGLTextureRef& terrain_col_tex, GLObjectRef& terrain_gl_ob, GLObjectRef& water_gl_ob) 1135 | { 1136 | // Create terrain colour texture. 1137 | terrain_col_tex = new OpenGLTexture(constants.W, constants.H, opengl_engine.ptr(), ArrayRef(NULL, 0), /*OpenGLTexture::Format_SRGBA_Uint8*/OpenGLTextureFormat::Format_RGBA_Linear_Uint8, OpenGLTexture::Filtering_Bilinear); 1138 | 1139 | // Add terrain mesh object 1140 | terrain_gl_ob = new GLObject(); 1141 | terrain_gl_ob->ob_to_world_matrix = Matrix4f::uniformScaleMatrix(1.f); 1142 | terrain_gl_ob->mesh_data = makeTerrainMesh(*sim, opengl_engine.ptr(), constants.cell_w); 1143 | terrain_gl_ob->materials.resize(1); 1144 | terrain_gl_ob->materials[0].albedo_texture = terrain_col_tex; 1145 | terrain_gl_ob->materials[0].fresnel_scale = 0.3f; 1146 | terrain_gl_ob->materials[0].roughness = 0.8f; 1147 | terrain_gl_ob->materials[0].convert_albedo_from_srgb = true; // Need this as the texture uses a linear colour space, but we want to treat it as non-linear sRGB. 1148 | 1149 | // Add water mesh object 1150 | if(use_water_mesh) 1151 | { 1152 | water_gl_ob = new GLObject(); 1153 | water_gl_ob->ob_to_world_matrix = Matrix4f::uniformScaleMatrix(1.f); 1154 | water_gl_ob->mesh_data = makeTerrainMesh(*sim, opengl_engine.ptr(), constants.cell_w); 1155 | water_gl_ob->materials.resize(1); 1156 | water_gl_ob->materials[0].water = true; 1157 | } 1158 | 1159 | glFinish(); 1160 | 1161 | shareOpenGLBuffersWithOpenCLSim(sim, opencl_context, terrain_col_tex, terrain_gl_ob->mesh_data, use_water_mesh ? water_gl_ob->mesh_data : OpenGLMeshRenderDataRef()); 1162 | 1163 | opengl_engine->addObject(terrain_gl_ob); 1164 | opengl_engine->addObject(water_gl_ob); 1165 | } 1166 | 1167 | 1168 | int main(int argc, char** argv) 1169 | { 1170 | Clock::init(); 1171 | 1172 | #ifdef _WIN32 1173 | // Init COM (used by file open dialog etc.) 1174 | HRESULT res = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 1175 | if(FAILED(res)) 1176 | { 1177 | conPrint("Failed to init COM"); 1178 | return 1; 1179 | } 1180 | #endif 1181 | 1182 | try 1183 | { 1184 | std::vector args(argc); 1185 | for(int i=0; i > syntax; 1189 | syntax["--params"] = std::vector(1, ArgumentParser::ArgumentType_string); // One string arg 1190 | 1191 | ArgumentParser arg_parser(args, syntax, /*allow unnamed arg=*/false); 1192 | 1193 | 1194 | 1195 | //=========================== Init SDL and OpenGL ================================ 1196 | // NOTE: OpenGL init needs to go before OpenCL init 1197 | 1198 | if(SDL_Init(SDL_INIT_VIDEO) != 0) 1199 | throw glare::Exception("SDL_Init Error: " + std::string(SDL_GetError())); 1200 | 1201 | 1202 | // Set GL attributes, needs to be done before window creation. 1203 | setGLAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4); // We need to request a specific version for a core profile. 1204 | setGLAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 6); 1205 | setGLAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 1206 | 1207 | setGLAttribute(SDL_GL_MULTISAMPLESAMPLES, 4); 1208 | 1209 | 1210 | const int primary_window_W = 1920; 1211 | const int primary_window_H = 1080; 1212 | 1213 | SDL_Window* win = SDL_CreateWindow("TerrainGen v0.3", 100, 100, primary_window_W, primary_window_H, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); 1214 | if(win == nullptr) 1215 | throw glare::Exception("SDL_CreateWindow Error: " + std::string(SDL_GetError())); 1216 | 1217 | 1218 | SDL_GLContext gl_context = SDL_GL_CreateContext(win); 1219 | if(!gl_context) 1220 | throw glare::Exception("OpenGL context could not be created! SDL Error: " + std::string(SDL_GetError())); 1221 | 1222 | if(SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1) != 0) 1223 | throw glare::Exception("SDL_GL_SetAttribute Error: " + std::string(SDL_GetError())); 1224 | 1225 | 1226 | gl3wInit(); 1227 | 1228 | 1229 | //=========================== Init OpenCL ================================ 1230 | OpenCL* opencl = getGlobalOpenCL(); 1231 | if(!opencl) 1232 | throw glare::Exception("Failed to open OpenCL: " + getGlobalOpenCLLastErrorMsg()); 1233 | 1234 | 1235 | const std::vector devices = opencl->getOpenCLDevices(); 1236 | 1237 | if(devices.empty()) 1238 | throw glare::Exception("No OpenCL devices found"); 1239 | 1240 | // Use first GPU device for now 1241 | OpenCLDeviceRef opencl_device; 1242 | for(size_t i=0; iopencl_device_type == CL_DEVICE_TYPE_GPU) 1245 | { 1246 | opencl_device = devices[i]; 1247 | break; 1248 | } 1249 | } 1250 | 1251 | if(opencl_device.isNull()) 1252 | throw glare::Exception("No OpenCL GPU devices found"); 1253 | 1254 | OpenCLContextRef opencl_context = new OpenCLContext(opencl_device, /*enable opengl interop=*/true); 1255 | 1256 | std::vector devices_to_build_for(1, opencl_device); 1257 | 1258 | const bool profile = false; 1259 | 1260 | OpenCLCommandQueueRef command_queue = new OpenCLCommandQueue(opencl_context, opencl_device->opencl_device_id, profile); 1261 | 1262 | 1263 | const std::string base_dir_path = PlatformUtils::getResourceDirectoryPath(); 1264 | 1265 | std::string kernel_dir = base_dir_path; 1266 | #if BUILD_TESTS 1267 | try 1268 | { 1269 | // For development, allow loading erosion_kernel.cl straight from the repo dir. 1270 | kernel_dir = PlatformUtils::getEnvironmentVariable("TERRAINGEN_USE_KERNEL_DIR"); // TERRAINGEN_USE_KERNEL_DIR can be set to e.g. c:/code/terraingen 1271 | conPrint("Using kernel dir from Env var: '" + kernel_dir + "'"); 1272 | } 1273 | catch(glare::Exception&) 1274 | {} 1275 | #endif 1276 | 1277 | std::string build_log; 1278 | OpenCLProgramRef program; 1279 | try 1280 | { 1281 | const std::string src = FileUtils::readEntireFile(kernel_dir + "/erosion_kernel.cl"); 1282 | 1283 | program = opencl->buildProgram( 1284 | src, 1285 | opencl_context, 1286 | devices_to_build_for, 1287 | "-cl-mad-enable", // compile options 1288 | build_log 1289 | ); 1290 | 1291 | conPrint("Build log: " + build_log); 1292 | } 1293 | catch(glare::Exception& e) 1294 | { 1295 | conPrint("Build log: " + build_log); 1296 | throw e; 1297 | } 1298 | 1299 | 1300 | //=========================== Init Simulation ================================ 1301 | 1302 | Constants constants; 1303 | constants.W = 1024; 1304 | constants.H = 1024; 1305 | constants.cell_w = 8.f; 1306 | constants.recip_cell_w = 1.f / constants.cell_w; 1307 | constants.delta_t = 0.25f; // time step 1308 | constants.r = 0.001f; // 0.012f; // rainfall rate 1309 | constants.A = 1; // cross-sectional 'pipe' area 1310 | constants.g = 9.81f; // gravity accel. NOTE: should be negative? 1311 | constants.l = 1.0; // l = pipe length 1312 | constants.f = 0.05f; // 0.05f; // friction constant 1313 | //constants.k = 0.001f; // viscous drag coefficient 1314 | constants.nu = 0.0f; // kinematic viscosity 1315 | 1316 | constants.K_c = 0.5f; // sediment capacity constant 1317 | constants.K_s = 10; // dissolving constant. 1318 | constants.K_d = 0.5f; // deposition constant 1319 | constants.K_dmax = 1.f; 1320 | constants.q_0 = 0.2f; 1321 | constants.K_e = 0.005f; // Evaporation constant 1322 | constants.K_coll = 1.f; // Collision weight 1323 | constants.K_cos_angle_threshold = 0.05f; // Collision weight 1324 | constants.K_smooth = 0.f; // Smoothing constant 1325 | constants.laplacian_threshold = 0.f; 1326 | constants.K_t = 10.f; // Thermal erosion constant 1327 | constants.K_tdep = 10.f; // thermal erosion constant for deposited sediment 1328 | constants.max_talus_angle = Maths::pi()/4; 1329 | constants.tan_max_talus_angle = std::tan(constants.max_talus_angle); 1330 | constants.max_deposited_talus_angle = 0.55f; 1331 | constants.tan_max_deposited_talus_angle = std::tan(constants.max_deposited_talus_angle); 1332 | constants.sea_level = -4.f; 1333 | constants.current_time = 0; 1334 | 1335 | constants.include_water_height = 1; 1336 | constants.draw_water = 1; 1337 | constants.rock_col = Colour3f(100 / 255.f, 100 / 255.f, 100 / 255.f); // These colours are non-linear sRGB colours. 1338 | constants.sediment_col = Colour3f(159 / 255.f, 128 / 255.f, 79 / 255.f); 1339 | constants.sediment_col_step = 0.3f; 1340 | constants.sediment_col_weight = 0.8f; 1341 | constants.vegetation_col = Colour3f(27 / 255.f, 58 / 255.f, 37 / 255.f); 1342 | constants.water_depth_col = Colour3f(90 / 255.f, 121 / 255.f, 128 / 255.f); 1343 | constants.water_depth_col_step = 0.5f; 1344 | constants.water_depth_col_weight = 0.4f; 1345 | constants.water_speed_col = Colour3f(200 / 255.f, 200 / 255.f, 200 / 255.f); 1346 | constants.water_speed_col_step = 0.5f; 1347 | constants.water_speed_col_weight = 0.f; 1348 | constants.water_z_bias = -0.1f; 1349 | 1350 | constants.debug_draw_channel = 0; 1351 | constants.debug_display_max_val = 1.f; 1352 | 1353 | TerrainParams terrain_params; 1354 | terrain_params.terrain_shape = InitialTerrainShape::InitialTerrainShape_FBM; 1355 | terrain_params.height_scale = 0.8f; 1356 | terrain_params.fine_roughness_vert_scale = 0.01f; 1357 | terrain_params.x_scale = 3; 1358 | terrain_params.y_scale = 3; 1359 | terrain_params.initial_water_depth = 0; 1360 | 1361 | bool exr_use_DWAB = true; // Use DWAB compression mode when saving EXRs? 1362 | bool display_water = true; // Show flowing water? 1363 | bool display_sea = false; // Display a flat sea surface around the terrain? 1364 | const bool use_water_mesh = true; // Show water as a mesh instead of just as a colour tint on the terrain? 1365 | 1366 | if(arg_parser.isArgPresent("--params")) 1367 | { 1368 | const std::string param_path = arg_parser.getArgStringValue("--params"); 1369 | loadParametersFromFile(param_path, constants, terrain_params); 1370 | } 1371 | 1372 | 1373 | Simulation* sim = new Simulation(constants.W, constants.H, opencl_context, program, opencl_device, profile, constants); 1374 | sim->use_water_mesh = use_water_mesh; 1375 | 1376 | resetTerrain(*sim, command_queue, terrain_params, constants.cell_w, constants.sea_level); 1377 | 1378 | 1379 | //=========================== Initialise ImGUI ================================ 1380 | ImGui::CreateContext(); 1381 | 1382 | ImGui_ImplSDL2_InitForOpenGL(win, gl_context); 1383 | ImGui_ImplOpenGL3_Init(); 1384 | 1385 | 1386 | //=========================== Create OpenGL engine ================================ 1387 | OpenGLEngineSettings settings; 1388 | settings.compress_textures = true; 1389 | settings.shadow_mapping = true; 1390 | settings.depth_fog = true; 1391 | settings.render_sun_and_clouds = false; 1392 | settings.render_water_caustics = false; 1393 | Reference opengl_engine = new OpenGLEngine(settings); 1394 | 1395 | TextureServer* texture_server = new TextureServer(/*use_canonical_path_keys=*/false); 1396 | 1397 | // opengl_data_dir should have 'shaders' and 'gl_data' dirs in it. 1398 | const std::string opengl_data_dir = base_dir_path; 1399 | 1400 | StandardPrintOutput print_output; 1401 | glare::TaskManager main_task_manager(1); 1402 | glare::TaskManager high_priority_task_manager(1); 1403 | Reference malloc_mem_allocator = new glare::MallocAllocator(); 1404 | opengl_engine->initialise(opengl_data_dir, texture_server, &print_output, &main_task_manager, &high_priority_task_manager, malloc_mem_allocator); 1405 | if(!opengl_engine->initSucceeded()) 1406 | throw glare::Exception("OpenGL init failed: " + opengl_engine->getInitialisationErrorMsg()); 1407 | opengl_engine->setViewportDims(primary_window_W, primary_window_H); 1408 | opengl_engine->setMainViewportDims(primary_window_W, primary_window_H); 1409 | 1410 | const float sun_phi = 1.f; 1411 | const float sun_theta = Maths::pi() / 4; 1412 | opengl_engine->setSunDir(normalise(Vec4f(std::cos(sun_phi) * sin(sun_theta), std::sin(sun_phi) * sin(sun_theta), cos(sun_theta), 0))); 1413 | opengl_engine->setEnvMapTransform(Matrix3f::rotationMatrix(Vec3f(0,0,1), sun_phi)); 1414 | 1415 | // Set env material 1416 | { 1417 | OpenGLMaterial env_mat; 1418 | opengl_engine->setEnvMat(env_mat); 1419 | } 1420 | 1421 | opengl_engine->setCirrusTexture(opengl_engine->getTexture(base_dir_path + "/resources/cirrus.exr")); 1422 | 1423 | 1424 | OpenGLTextureRef terrain_col_tex; 1425 | GLObjectRef terrain_gl_ob; 1426 | GLObjectRef water_gl_ob; 1427 | 1428 | createAndAddTerrainTextureAndMeshes(constants, sim, opengl_engine, opencl_context, use_water_mesh, terrain_col_tex, terrain_gl_ob, water_gl_ob); 1429 | 1430 | const float large_water_quad_w = 1000000; 1431 | 1432 | // Create water plane, add to OpenGL engine if needed 1433 | GLObjectRef sea_water_gl_ob; 1434 | { 1435 | OpenGLMaterial water_mat; 1436 | water_mat.water = true; 1437 | 1438 | sea_water_gl_ob = new GLObject(); 1439 | sea_water_gl_ob->ob_to_world_matrix = Matrix4f::translationMatrix(0, 0, constants.sea_level) * Matrix4f::uniformScaleMatrix(large_water_quad_w) * Matrix4f::translationMatrix(-0.5f, -0.5f, 0); 1440 | sea_water_gl_ob->mesh_data = MeshPrimitiveBuilding::makeQuadMesh(*opengl_engine->vert_buf_allocator, Vec4f(1,0,0,0), Vec4f(0,1,0,0), /*res=*/64); // Tessellate ground mesh, to avoid texture shimmer due to large quads. 1441 | 1442 | sea_water_gl_ob->materials.resize(1); 1443 | sea_water_gl_ob->materials[0] = water_mat; 1444 | if(display_sea) 1445 | opengl_engine->addObject(sea_water_gl_ob); 1446 | } 1447 | 1448 | Timer timer; 1449 | Timer time_since_mesh_update; 1450 | 1451 | TerrainStats stats = computeTerrainStats(*sim, constants); 1452 | 1453 | float cam_phi = 0.0; // azimuthal angle 1454 | float cam_theta = 2.1f; // zenith angle 1455 | 1456 | const float terrain_w = constants.W * constants.cell_w; 1457 | const float terrain_h = constants.H * constants.cell_w; 1458 | Vec4f cam_pos; 1459 | { 1460 | const Vec4f terrain_centre = Vec4f(terrain_w / 2.f, terrain_h / 2.f, sim->terrain_state.elem(constants.W/2, constants.H/2).height, 1); 1461 | const Vec4f cam_forwards = GeometrySampling::dirForSphericalCoords(cam_phi + Maths::pi_2(), cam_theta); // cam_phi = 0 => cam forwards is (0,1,0). 1462 | cam_pos = terrain_centre - cam_forwards * (terrain_h * 1.f);// - Vec4f(cos(cam_phi), sin(cam_phi), 1463 | } 1464 | bool orbit_camera = false; 1465 | float orbit_angular_vel = 0.1f; 1466 | float orbit_dist = terrain_w; 1467 | 1468 | bool sim_running = true; 1469 | 1470 | Timer time_since_last_frame; 1471 | Timer stats_timer; 1472 | int stats_last_num_iters = 0; 1473 | double stats_last_iters_per_sec = 0; 1474 | bool reset = false; 1475 | 1476 | 1477 | NotificationInfo notification_info; 1478 | 1479 | // Keep these around, are applied by 'apply' button. 1480 | int new_W = constants.W; 1481 | int new_H = constants.H; 1482 | float new_cell_w = constants.cell_w; 1483 | 1484 | bool quit = false; 1485 | while(!quit) 1486 | { 1487 | if(orbit_camera) 1488 | { 1489 | cam_phi = (float)(timer.elapsed() * orbit_angular_vel); 1490 | const Vec4f terrain_centre = Vec4f(terrain_w / 2.f, terrain_h / 2.f, sim->terrain_state.elem(constants.W/2, constants.H/2).height, 1); 1491 | const Vec4f cam_forwards = GeometrySampling::dirForSphericalCoords(cam_phi + Maths::pi_2(), cam_theta); // cam_phi = 0 => cam forwards is (0,1,0). 1492 | cam_pos = terrain_centre - cam_forwards * orbit_dist; 1493 | } 1494 | 1495 | 1496 | if(SDL_GL_MakeCurrent(win, gl_context) != 0) 1497 | conPrint("SDL_GL_MakeCurrent failed."); 1498 | 1499 | const Matrix4f z_rot = Matrix4f::rotationAroundZAxis(-cam_phi); 1500 | const Matrix4f x_rot = Matrix4f::rotationAroundXAxis(cam_theta - Maths::pi_2()); 1501 | const Matrix4f rot = x_rot * z_rot; 1502 | 1503 | const Matrix4f world_to_camera_space_matrix = rot * Matrix4f::translationMatrix(-cam_pos); 1504 | 1505 | const float sensor_width = 0.035f; 1506 | const float lens_sensor_dist = 0.025f; 1507 | const float render_aspect_ratio = opengl_engine->getViewPortAspectRatio(); 1508 | 1509 | int gl_w, gl_h; 1510 | SDL_GL_GetDrawableSize(win, &gl_w, &gl_h); 1511 | 1512 | opengl_engine->setViewportDims(gl_w, gl_h); 1513 | opengl_engine->setMainViewportDims(gl_w, gl_h); 1514 | opengl_engine->setMaxDrawDistance(1000000.f); 1515 | opengl_engine->setPerspectiveCameraTransform(world_to_camera_space_matrix, sensor_width, lens_sensor_dist, render_aspect_ratio, /*lens shift up=*/0.f, /*lens shift right=*/0.f); 1516 | opengl_engine->setCurrentTime((float)timer.elapsed()); 1517 | opengl_engine->draw(); 1518 | 1519 | 1520 | // Draw ImGUI GUI controls 1521 | ImGui_ImplOpenGL3_NewFrame(); 1522 | ImGui_ImplSDL2_NewFrame(); 1523 | ImGui::NewFrame(); 1524 | 1525 | //ImGui::ShowDemoWindow(); 1526 | 1527 | const float spacing_vert_pixels = 15; 1528 | const float subsection_spacing_vert_pixels = 5; 1529 | 1530 | ImGui::SetNextWindowSize(ImVec2(600, 1200)); 1531 | ImGui::Begin("TerrainGen"); 1532 | 1533 | //-------------------------------------- Render Simulation parameters section -------------------------------------- 1534 | ImGui::TextColored(ImVec4(1,1,0,1), "Simulation parameters"); 1535 | ImGui::BeginGroup(); // Begin Simulation parameters group 1536 | 1537 | ImGui::Text("Grid"); 1538 | ImGui::InputInt(/*label=*/"grid x res", /*val=*/&new_W, /*step=*/1, /*step fast=*/32); 1539 | ImGui::InputInt(/*label=*/"grid Y res", /*val=*/&new_H, /*step=*/1, /*step fast=*/32); 1540 | ImGui::SliderFloat(/*label=*/"cell width (m)", /*val=*/&new_cell_w, /*min=*/0.0001f, /*max=*/100.f, "%.3f"); 1541 | bool grid_changed = ImGui::Button("Apply"); 1542 | 1543 | ImGui::SliderFloat(/*label=*/"delta_t (s)", /*val=*/&constants.delta_t, /*min=*/0.0f, /*max=*/0.3f, "%.3f"); 1544 | 1545 | ImGui::Dummy(ImVec2(30, subsection_spacing_vert_pixels)); 1546 | ImGui::Text("Water"); 1547 | ImGui::SliderFloat(/*label=*/"rainfall rate (m/s)", /*val=*/&constants.r, /*min=*/0.0f, /*max=*/0.01f, "%.4f"); 1548 | ImGui::SliderFloat(/*label=*/"evaporation constant", /*val=*/&constants.K_e, /*min=*/0.0f, /*max=*/0.1f, "%.3f"); 1549 | ImGui::SliderFloat(/*label=*/"friction constant", /*val=*/&constants.f, /*min=*/0.0f, /*max=*/0.2f, "%.3f"); 1550 | //ImGui::SliderFloat(/*label=*/"viscous drag coeff (k)", /*val=*/&constants.k, /*min=*/0.0f, /*max=*/1.f, "%.5f"); 1551 | ImGui::SliderFloat(/*label=*/"kinematic viscosity", /*val=*/&constants.nu, /*min=*/0.0f, /*max=*/10.f, "%.3f"); 1552 | //param_changed = param_changed || ImGui::SliderFloat(/*label=*/"cross-sectional 'pipe' area (m)", /*val=*/&constants.A, /*min=*/0.0f, /*max=*/100.f, "%.5f"); 1553 | //param_changed = param_changed || ImGui::SliderFloat(/*label=*/"gravity mag (m/s^2)", /*val=*/&constants.g, /*min=*/0.0f, /*max=*/100.f, "%.5f"); 1554 | //param_changed = param_changed || ImGui::SliderFloat(/*label=*/"virtual pipe length (m)", /*val=*/&constants.l, /*min=*/0.0f, /*max=*/100.f, "%.5f"); 1555 | 1556 | ImGui::Dummy(ImVec2(30, subsection_spacing_vert_pixels)); 1557 | ImGui::Text("Sediment"); 1558 | ImGui::SliderFloat(/*label=*/"sediment capacity constant", /*val=*/&constants.K_c, /*min=*/0.0f, /*max=*/4.f, "%.3f"); 1559 | ImGui::SliderFloat(/*label=*/"dissolving constant", /*val=*/&constants.K_s, /*min=*/0.0f, /*max=*/20.f, "%.3f"); 1560 | ImGui::SliderFloat(/*label=*/"deposition constant", /*val=*/&constants.K_d, /*min=*/0.0f, /*max=*/4.f, "%.3f"); 1561 | ImGui::SliderFloat(/*label=*/"max erosion water depth ", /*val=*/&constants.K_dmax, /*min=*/0.0f, /*max=*/1.f, "%.3f"); 1562 | ImGui::SliderFloat(/*label=*/"collision weight", /*val=*/&constants.K_coll, /*min=*/0.0f, /*max=*/1.f, "%.3f"); 1563 | ImGui::SliderFloat(/*label=*/"collision angle threshold", /*val=*/&constants.K_cos_angle_threshold, /*min=*/0.0f, /*max=*/1.f, "%.3f"); 1564 | ImGui::SliderFloat(/*label=*/"min unit water discharge", /*val=*/&constants.q_0, /*min=*/0.0f, /*max=*/1.f, "%.3f"); 1565 | 1566 | ImGui::Dummy(ImVec2(30, subsection_spacing_vert_pixels)); 1567 | ImGui::Text("Smoothing"); 1568 | ImGui::SliderFloat(/*label=*/"Smoothing constant", /*val=*/&constants.K_smooth, /*min=*/0.0f, /*max=*/10.f, "%.3f"); 1569 | ImGui::SliderFloat(/*label=*/"Smoothing laplacian threshold", /*val=*/&constants.laplacian_threshold, /*min=*/0.0f, /*max=*/1.f, "%.3f"); 1570 | 1571 | ImGui::Dummy(ImVec2(30, subsection_spacing_vert_pixels)); 1572 | ImGui::Text("Thermal erosion"); 1573 | ImGui::SliderFloat(/*label=*/"Thermal erosion constant", /*val=*/&constants.K_t, /*min=*/0.0f, /*max=*/100.f, "%.3f"); 1574 | ImGui::SliderFloat(/*label=*/"Thermal erosion const, deposited", /*val=*/&constants.K_tdep, /*min=*/0.0f, /*max=*/100.f, "%.3f"); 1575 | ImGui::SliderFloat(/*label=*/"Max talus angle (rad)", /*val=*/&constants.max_talus_angle, /*min=*/0.0f, /*max=*/1.5f, "%.3f"); 1576 | ImGui::SliderFloat(/*label=*/"Max talus angle, deposited (rad)", /*val=*/&constants.max_deposited_talus_angle, /*min=*/0.0f, /*max=*/1.5f, "%.3f"); 1577 | 1578 | ImGui::Dummy(ImVec2(30, subsection_spacing_vert_pixels)); 1579 | ImGui::Text("Terrain initialisation"); 1580 | if(ImGui::BeginCombo("heightfield shape", InitialTerrainShape_display_strings[terrain_params.terrain_shape])) 1581 | { 1582 | for(int i=0; iaddObject(water_gl_ob); 1678 | else 1679 | opengl_engine->removeObject(water_gl_ob); 1680 | } 1681 | } 1682 | if(display_water) 1683 | ImGui::SliderFloat(/*label=*/"water height bias", /*val=*/&constants.water_z_bias, /*min=*/-1.f, /*max=*/0.f, "%.5f"); 1684 | 1685 | if(ImGui::Checkbox("Display sea surface", &display_sea)) 1686 | { 1687 | if(display_sea) 1688 | opengl_engine->addObject(sea_water_gl_ob); 1689 | else 1690 | opengl_engine->removeObject(sea_water_gl_ob); 1691 | } 1692 | if(display_sea) 1693 | { 1694 | if(ImGui::InputFloat(/*label=*/"Sea level (m)", /*val=*/&constants.sea_level, /*step=*/1.f, /*step fast=*/10.f, "%.3f")) 1695 | { 1696 | // Sea height changed, move water plane 1697 | sea_water_gl_ob->ob_to_world_matrix = Matrix4f::translationMatrix(0, 0, constants.sea_level) * Matrix4f::uniformScaleMatrix(large_water_quad_w) * Matrix4f::translationMatrix(-0.5f, -0.5f, 0); 1698 | if(display_sea) 1699 | opengl_engine->updateObjectTransformData(*sea_water_gl_ob); 1700 | } 1701 | } 1702 | 1703 | ImGui::Checkbox("orbit camera", &orbit_camera); 1704 | if(orbit_camera) 1705 | { 1706 | ImGui::SliderFloat(/*label=*/"orbit speed", /*val=*/&orbit_angular_vel, /*min=*/0.f, /*max=*/1.f, "%.3f"); 1707 | ImGui::SliderFloat(/*label=*/"orbit dist", /*val=*/&orbit_dist, /*min=*/0.f, /*max=*/myMax(terrain_w, terrain_h) * 3.f, "%.3f"); 1708 | } 1709 | 1710 | ImGui::Text("Debug visualisation"); 1711 | if(ImGui::BeginCombo("texture showing", TextureShow_strings[constants.debug_draw_channel])) 1712 | { 1713 | for(int i=0; isim_iteration * constants.delta_t; 1741 | 1742 | sim->constants_buffer.copyFrom(command_queue, /*src ptr=*/&constants, sizeof(Constants), /*blocking write=*/false); 1743 | } 1744 | 1745 | bool do_advance = false; 1746 | if(sim_running) 1747 | { 1748 | do_advance = true; 1749 | if(ImGui::Button("pause")) 1750 | { 1751 | sim_running = false; 1752 | showNotification(notification_info, "Paused"); 1753 | } 1754 | } 1755 | else // Else if paused: 1756 | { 1757 | if(ImGui::Button("resume")) 1758 | { 1759 | sim_running = true; 1760 | showNotification(notification_info, "Resumed"); 1761 | } 1762 | 1763 | ImGui::SameLine(); // Put buttons on same line 1764 | const bool single_step = ImGui::Button("single step"); 1765 | if(single_step) 1766 | do_advance = true; 1767 | } 1768 | 1769 | // Advance simulation (if not paused) 1770 | if(do_advance) 1771 | sim->doSimIteration(command_queue); 1772 | 1773 | ImGui::SameLine(); // Put buttons on same line 1774 | reset = ImGui::Button("Reset") || reset; 1775 | 1776 | 1777 | ImGui::Dummy(ImVec2(60, spacing_vert_pixels)); 1778 | if(ImGui::Button("Save heightfield to disk")) 1779 | { 1780 | saveHeightfieldToDisk(*sim, exr_use_DWAB, command_queue, notification_info); 1781 | } 1782 | 1783 | if(ImGui::Button("Save colour texture to disk")) 1784 | { 1785 | saveColourTextureToDisk(*sim, command_queue, terrain_col_tex, notification_info); 1786 | } 1787 | 1788 | ImGui::Checkbox("EXR: use DWAB", &exr_use_DWAB); 1789 | 1790 | //-------------------------------------- Render Info section -------------------------------------- 1791 | ImGui::Dummy(ImVec2(60, spacing_vert_pixels)); 1792 | ImGui::TextColored(ImVec4(1,1,0,1), "Info"); 1793 | ImGui::Text((std::string(sim_running ? "Sim running" : "Sim paused") + ", iteration: " + toString(sim->sim_iteration)).c_str()); 1794 | ImGui::Text(("Speed: " + toString((int)stats_last_iters_per_sec) + " iters/s").c_str()); 1795 | 1796 | ImGui::Text(("Total water mass: " + doubleToStringMaxNDecimalPlaces(stats.total_water_mass, 4) + " kg").c_str()); 1797 | ImGui::Text(("Total uneroded terrain volume: " + doubleToStringScientific(stats.total_uneroded_volume, 4) + " m^3").c_str()); 1798 | ImGui::Text(("Total suspended sediment volume: " + doubleToStringMaxNDecimalPlaces(stats.total_suspended_sediment_vol, 4) + " m^3").c_str()); 1799 | ImGui::Text(("Total deposited sediment volume: " + doubleToStringMaxNDecimalPlaces(stats.total_deposited_sediment_vol, 4) + " m^3").c_str()); 1800 | ImGui::Text(("Total solid volume: " + doubleToStringScientific(stats.total_solid_volume, 4) + " m^3").c_str()); 1801 | ImGui::Text(("cam position: " + cam_pos.toStringMaxNDecimalPlaces(1)).c_str()); 1802 | //ImGui::Text(("cam theta, phi: " + doubleToStringMaxNDecimalPlaces(cam_theta, 2) + ", " + doubleToStringMaxNDecimalPlaces(cam_phi, 2)).c_str()); 1803 | //ImGui::Text(("max texture value: " + toString(results.max_value)).c_str()); 1804 | ImGui::End(); 1805 | 1806 | 1807 | 1808 | // Show notification widget 1809 | if(!notification_info.notification.empty() && notification_info.notification_start_display_timer.elapsed() < 3.0) 1810 | { 1811 | ImVec2 tex_dims = ImGui::CalcTextSize(notification_info.notification.c_str()); 1812 | 1813 | const float notification_window_w = tex_dims.x + 20.f; 1814 | ImGui::SetNextWindowSize(ImVec2(notification_window_w, tex_dims.y + 10)); 1815 | ImGui::SetNextWindowPos(ImVec2(gl_w/2 - notification_window_w/2, 25)); 1816 | ImGui::Begin("Notification", NULL, ImGuiWindowFlags_NoDecoration); 1817 | ImGui::Text(notification_info.notification.c_str()); 1818 | ImGui::End(); 1819 | } 1820 | 1821 | 1822 | ImGui::Render(); 1823 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 1824 | 1825 | // Update terrain OpenGL mesh and texture from the sim 1826 | //if(time_since_mesh_update.elapsed() > 0.1) 1827 | { 1828 | glFinish(); 1829 | sim->updateHeightFieldMeshAndTexture(command_queue); // Do with OpenGL - OpenCL interop 1830 | 1831 | //time_since_mesh_update.reset(); 1832 | } 1833 | 1834 | 1835 | if(stats_timer.elapsed() > 1.0) 1836 | { 1837 | // Update statistics 1838 | sim->readBackToCPUMem(command_queue); 1839 | 1840 | stats = computeTerrainStats(*sim, constants); 1841 | 1842 | stats_last_iters_per_sec = (sim->sim_iteration - stats_last_num_iters) / stats_timer.elapsed(); 1843 | 1844 | stats_timer.reset(); 1845 | stats_last_num_iters = sim->sim_iteration; 1846 | } 1847 | 1848 | 1849 | // Display 1850 | SDL_GL_SwapWindow(win); 1851 | 1852 | if(grid_changed) 1853 | { 1854 | glFinish(); 1855 | 1856 | unshareOpenGLBuffersFromOpenCLSim(sim); 1857 | 1858 | delete sim; 1859 | 1860 | // Remove old meshes from OpenGL engine 1861 | terrain_col_tex = NULL; 1862 | 1863 | opengl_engine->removeObject(terrain_gl_ob); 1864 | terrain_gl_ob = NULL; 1865 | 1866 | if(water_gl_ob) 1867 | { 1868 | opengl_engine->removeObject(water_gl_ob); 1869 | water_gl_ob = NULL; 1870 | } 1871 | 1872 | constants.W = new_W; 1873 | constants.H = new_H; 1874 | constants.cell_w = new_cell_w; 1875 | 1876 | sim = new Simulation(constants.W, constants.H, opencl_context, program, opencl_device, profile, constants); 1877 | 1878 | resetTerrain(*sim, command_queue, terrain_params, constants.cell_w, constants.sea_level); 1879 | 1880 | createAndAddTerrainTextureAndMeshes(constants, sim, opengl_engine, opencl_context, use_water_mesh, terrain_col_tex, terrain_gl_ob, water_gl_ob); 1881 | } 1882 | else if(reset) 1883 | { 1884 | resetTerrain(*sim, command_queue, terrain_params, constants.cell_w, constants.sea_level); 1885 | stats_last_num_iters = 0; 1886 | reset = false; 1887 | } 1888 | 1889 | const float dt = (float)time_since_last_frame.elapsed(); 1890 | time_since_last_frame.reset(); 1891 | 1892 | const Vec4f forwards = GeometrySampling::dirForSphericalCoords(cam_phi + Maths::pi_2(), cam_theta); // cam_phi = 0 => cam forwards is (0,1,0). 1893 | const Vec4f right = normalise(crossProduct(forwards, Vec4f(0,0,1,0))); 1894 | const Vec4f up = crossProduct(right, forwards); 1895 | 1896 | // Handle any events 1897 | SDL_Event e; 1898 | while(SDL_PollEvent(&e)) 1899 | { 1900 | if(ImGui::GetIO().WantCaptureMouse) 1901 | { 1902 | ImGui_ImplSDL2_ProcessEvent(&e); // Pass event onto ImGUI 1903 | continue; 1904 | } 1905 | 1906 | if(e.type == SDL_QUIT) // "An SDL_QUIT event is generated when the user clicks on the close button of the last existing window" - https://wiki.libsdl.org/SDL_EventType#Remarks 1907 | quit = true; 1908 | else if(e.type == SDL_WINDOWEVENT) // If user closes the window: 1909 | { 1910 | if(e.window.event == SDL_WINDOWEVENT_CLOSE) 1911 | quit = true; 1912 | else if(e.window.event == SDL_WINDOWEVENT_RESIZED || e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) 1913 | { 1914 | int w, h; 1915 | SDL_GL_GetDrawableSize(win, &w, &h); 1916 | 1917 | opengl_engine->setViewportDims(w, h); 1918 | opengl_engine->setMainViewportDims(w, h); 1919 | } 1920 | } 1921 | else if(e.type == SDL_KEYDOWN) 1922 | { 1923 | if(e.key.keysym.sym == SDLK_r) 1924 | reset = true; 1925 | else if(e.key.keysym.sym == SDLK_PAUSE) 1926 | { 1927 | sim_running = !sim_running; 1928 | showNotification(notification_info, sim_running ? "Resumed" : "Paused"); 1929 | } 1930 | } 1931 | else if(e.type == SDL_MOUSEMOTION) 1932 | { 1933 | if(e.motion.state & SDL_BUTTON_LMASK) 1934 | { 1935 | const float move_scale = 0.005f; 1936 | cam_phi -= e.motion.xrel * move_scale; 1937 | cam_theta = myClamp(cam_theta + (float)e.motion.yrel * move_scale, 0.01f, Maths::pi() - 0.01f); 1938 | } 1939 | 1940 | if((e.motion.state & SDL_BUTTON_MMASK) || (e.motion.state & SDL_BUTTON_RMASK)) 1941 | { 1942 | //const float move_scale = 1.f; 1943 | //cam_target_pos += right * -(float)e.motion.xrel * move_scale + up * (float)e.motion.yrel * move_scale; 1944 | } 1945 | } 1946 | else if(e.type == SDL_MOUSEWHEEL) 1947 | { 1948 | //cam_dist = myClamp(cam_dist - cam_dist * e.wheel.y * 0.2f, 0.01f, 10000.f); 1949 | const float move_speed = 30.f * constants.cell_w; 1950 | cam_pos += forwards * (float)e.wheel.y * move_speed; 1951 | } 1952 | } 1953 | 1954 | SDL_PumpEvents(); 1955 | const uint8* keystate = SDL_GetKeyboardState(NULL); 1956 | const float shift_factor = (keystate[SDL_SCANCODE_LSHIFT] != 0) ? 3.f : 1.f; 1957 | if(keystate[SDL_SCANCODE_LEFT]) 1958 | cam_phi += dt * 0.25f * shift_factor; 1959 | if(keystate[SDL_SCANCODE_RIGHT]) 1960 | cam_phi -= dt * 0.25f * shift_factor; 1961 | 1962 | const float move_speed = 140.f * constants.cell_w * shift_factor; 1963 | if(keystate[SDL_SCANCODE_W]) 1964 | cam_pos += forwards * dt * move_speed; 1965 | if(keystate[SDL_SCANCODE_S]) 1966 | cam_pos -= forwards * dt * move_speed; 1967 | if(keystate[SDL_SCANCODE_A]) 1968 | cam_pos -= right * dt * move_speed; 1969 | if(keystate[SDL_SCANCODE_D]) 1970 | cam_pos += right * dt * move_speed; 1971 | if(keystate[SDL_SCANCODE_SPACE]) 1972 | cam_pos += up * dt * move_speed; 1973 | if(keystate[SDL_SCANCODE_C]) 1974 | cam_pos -= up * dt * move_speed; 1975 | } 1976 | SDL_Quit(); 1977 | return 0; 1978 | } 1979 | catch(glare::Exception& e) 1980 | { 1981 | stdErrPrint(e.what()); 1982 | SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Error", e.what().c_str(), /*parent=*/NULL); 1983 | return 1; 1984 | } 1985 | } 1986 | --------------------------------------------------------------------------------