From f1fc192ddc4c739fa8b4b376c759b7d3218a34eb Mon Sep 17 00:00:00 2001 From: Mel Date: Fri, 7 Jul 2023 21:39:42 +0200 Subject: Chunk-bound tree decoration --- CMakeLists.txt | 10 +- assets/images/atlas.png | Bin 3705 -> 4653 bytes assets/images/atlas.ppm | 6238 ++++++++++++++++----------------- src/Math/Matrix.hpp | 21 +- src/Math/Random.cpp | 31 + src/Math/Random.hpp | 31 + src/Math/Vector.hpp | 79 +- src/World/BlockType.hpp | 2 + src/World/Chunk.cpp | 155 +- src/World/Chunk.hpp | 15 +- src/World/ChunkMeshing.cpp | 170 - src/World/ChunkMeshing.hpp | 11 - src/World/Generation/ChunkMeshing.cpp | 183 + src/World/Generation/ChunkMeshing.hpp | 11 + src/World/Generation/Decoration.cpp | 97 + src/World/Generation/Decoration.hpp | 34 + src/World/Generation/Generator.cpp | 331 ++ src/World/Generation/Generator.hpp | 76 + src/World/Generator.cpp | 321 -- src/World/Generator.hpp | 63 - src/World/World.cpp | 4 +- src/World/World.hpp | 4 +- 22 files changed, 4011 insertions(+), 3876 deletions(-) create mode 100644 src/Math/Random.cpp create mode 100644 src/Math/Random.hpp delete mode 100644 src/World/ChunkMeshing.cpp delete mode 100644 src/World/ChunkMeshing.hpp create mode 100644 src/World/Generation/ChunkMeshing.cpp create mode 100644 src/World/Generation/ChunkMeshing.hpp create mode 100644 src/World/Generation/Decoration.cpp create mode 100644 src/World/Generation/Decoration.hpp create mode 100644 src/World/Generation/Generator.cpp create mode 100644 src/World/Generation/Generator.hpp delete mode 100644 src/World/Generator.cpp delete mode 100644 src/World/Generator.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 61b6c34..7f8bfff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ add_executable(meowcraft src/GFX/Image/PPMParser.cpp src/GFX/Image/PPMParser.hpp src/World/Chunk.cpp src/World/Chunk.hpp src/World/BlockType.hpp - src/World/Generator.cpp src/World/Generator.hpp + src/World/Generation/Generator.cpp src/World/Generation/Generator.hpp src/World/BlockSide.hpp src/World/World.cpp src/World/World.hpp src/World/ChunkIndex.hpp @@ -45,10 +45,14 @@ add_executable(meowcraft src/Compute/Queue.hpp src/Math/Constants.hpp src/Math/Sigmoid.hpp - src/World/ChunkMeshing.cpp - src/World/ChunkMeshing.hpp + src/World/Generation/ChunkMeshing.cpp + src/World/Generation/ChunkMeshing.hpp src/Math/Tensor.hpp src/Math/MatrixZoom.hpp + src/World/Generation/Decoration.cpp + src/World/Generation/Decoration.hpp + src/Math/Random.cpp + src/Math/Random.hpp ) target_link_libraries(meowcraft glfw GLEW::GLEW) diff --git a/assets/images/atlas.png b/assets/images/atlas.png index b8b9dd7..72ff329 100644 Binary files a/assets/images/atlas.png and b/assets/images/atlas.png differ diff --git a/assets/images/atlas.ppm b/assets/images/atlas.ppm index df8dce7..bf31022 100644 --- a/assets/images/atlas.ppm +++ b/assets/images/atlas.ppm @@ -3074,54 +3074,6 @@ P3 145 140 133 -251 -199 -142 -255 -208 -144 -255 -208 -144 -255 -208 -144 -248 -199 -136 -255 -216 -148 -248 -199 -136 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -251 -199 -142 93 191 225 @@ -3170,6 +3122,54 @@ P3 93 191 225 +251 +199 +142 +255 +208 +144 +255 +208 +144 +255 +208 +144 +248 +199 +136 +255 +216 +148 +248 +199 +136 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +251 +199 +142 249 250 252 @@ -3266,54 +3266,6 @@ P3 249 250 252 -255 -208 -144 -255 -208 -144 -255 -208 -144 -255 -216 -148 -255 -208 -144 -248 -199 -136 -248 -199 -136 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -251 -199 -142 120 232 236 @@ -3362,6 +3314,54 @@ P3 120 232 236 +255 +208 +144 +255 +208 +144 +255 +208 +144 +255 +216 +148 +255 +208 +144 +248 +199 +136 +248 +199 +136 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +251 +199 +142 236 241 247 @@ -3458,6 +3458,54 @@ P3 249 250 252 +120 +232 +236 +112 +217 +232 +94 +192 +225 +94 +192 +225 +90 +184 +225 +90 +184 +225 +112 +213 +232 +112 +213 +232 +112 +217 +232 +105 +200 +229 +88 +177 +223 +88 +177 +223 +93 +191 +225 +120 +232 +236 +120 +232 +236 +120 +232 +236 255 208 144 @@ -3506,63 +3554,15 @@ P3 255 208 144 -120 -232 +249 +250 +252 +249 +250 +252 236 -112 -217 -232 -94 -192 -225 -94 -192 -225 -90 -184 -225 -90 -184 -225 -112 -213 -232 -112 -213 -232 -112 -217 -232 -105 -200 -229 -88 -177 -223 -88 -177 -223 -93 -191 -225 -120 -232 -236 -120 -232 -236 -120 -232 -236 -249 -250 -252 -249 -250 -252 -236 -241 -247 +241 +247 225 230 236 @@ -3650,54 +3650,6 @@ P3 225 230 236 -251 -199 -142 -255 -208 -144 -251 -199 -142 -255 -208 -144 -251 -199 -142 -251 -199 -142 -255 -224 -152 -255 -224 -152 -251 -199 -142 -255 -216 -148 -255 -216 -148 -255 -216 -148 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -230 -157 120 232 236 @@ -3746,6 +3698,54 @@ P3 95 199 228 +251 +199 +142 +255 +208 +144 +251 +199 +142 +255 +208 +144 +251 +199 +142 +251 +199 +142 +255 +224 +152 +255 +224 +152 +251 +199 +142 +255 +216 +148 +255 +216 +148 +255 +216 +148 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +230 +157 249 250 252 @@ -3842,54 +3842,6 @@ P3 236 241 247 -251 -199 -142 -255 -208 -144 -255 -208 -144 -255 -208 -144 -242 -185 -131 -242 -185 -131 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 95 199 228 @@ -3938,6 +3890,54 @@ P3 93 191 225 +251 +199 +142 +255 +208 +144 +255 +208 +144 +255 +208 +144 +242 +185 +131 +242 +185 +131 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 249 250 252 @@ -4034,58 +4034,10 @@ P3 236 241 247 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -216 -148 -248 -199 -136 -242 -185 -131 -255 -224 -152 -255 -224 -152 -255 -216 -148 -255 -216 -148 -255 -216 -148 -255 -208 -144 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -230 -157 -93 -191 -225 -95 +93 +191 +225 +95 199 228 120 @@ -4130,6 +4082,54 @@ P3 93 191 225 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +216 +148 +248 +199 +136 +242 +185 +131 +255 +224 +152 +255 +224 +152 +255 +216 +148 +255 +216 +148 +255 +216 +148 +255 +208 +144 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +230 +157 249 250 252 @@ -4226,54 +4226,6 @@ P3 249 250 252 -251 -199 -142 -251 -199 -142 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -216 -148 -255 -216 -148 -255 -208 -144 -255 -208 -144 -251 -199 -142 -255 -221 -148 -251 -199 -142 -251 -199 -142 90 184 223 @@ -4322,6 +4274,54 @@ P3 84 166 219 +251 +199 +142 +251 +199 +142 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +216 +148 +255 +216 +148 +255 +208 +144 +255 +208 +144 +251 +199 +142 +255 +221 +148 +251 +199 +142 +251 +199 +142 249 250 252 @@ -4418,6 +4418,54 @@ P3 249 250 252 +85 +170 +221 +95 +199 +228 +120 +232 +236 +120 +232 +236 +95 +199 +228 +93 +191 +225 +93 +191 +225 +93 +191 +225 +105 +205 +228 +120 +232 +236 +120 +232 +236 +93 +191 +225 +85 +170 +221 +85 +170 +221 +80 +154 +217 +80 +154 +217 251 199 142 @@ -4466,66 +4514,18 @@ P3 251 199 142 -85 -170 -221 -95 -199 -228 -120 -232 -236 -120 -232 -236 -95 -199 -228 -93 -191 -225 -93 -191 -225 -93 -191 -225 -105 -205 -228 -120 -232 -236 -120 -232 -236 -93 -191 -225 -85 -170 -221 -85 -170 -221 -80 -154 -217 -80 -154 -217 -249 -250 -252 -249 -250 -252 -249 -250 -252 -249 -250 -252 +249 +250 +252 +249 +250 +252 +249 +250 +252 +249 +250 +252 236 241 247 @@ -4610,54 +4610,6 @@ P3 236 241 247 -251 -199 -142 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -216 -148 -255 -216 -148 -255 -208 -144 -251 -199 -142 -242 -185 -131 -251 -199 -142 -242 -185 -131 -251 -199 -142 -251 -199 -142 90 184 225 @@ -4706,6 +4658,54 @@ P3 88 177 223 +251 +199 +142 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +216 +148 +255 +216 +148 +255 +208 +144 +251 +199 +142 +242 +185 +131 +251 +199 +142 +242 +185 +131 +251 +199 +142 +251 +199 +142 236 241 247 @@ -4802,54 +4802,6 @@ P3 55 25 13 -242 -185 -131 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -216 -148 -255 -221 -148 -255 -208 -144 -251 -199 -142 -242 -185 -131 -242 -185 -131 -242 -185 -131 -255 -216 -148 -242 -185 -131 90 184 225 @@ -4898,6 +4850,54 @@ P3 90 184 225 +242 +185 +131 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +216 +148 +255 +221 +148 +255 +208 +144 +251 +199 +142 +242 +185 +131 +242 +185 +131 +242 +185 +131 +255 +216 +148 +242 +185 +131 225 230 236 @@ -4955,12 +4955,12 @@ P3 117 71 50 -105 -60 -40 -93 -54 +66 +35 +19 +66 35 +19 93 54 35 @@ -4994,54 +4994,6 @@ P3 66 35 19 -242 -185 -131 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -208 -144 -255 -208 -144 -255 -208 -144 -255 -208 -144 -255 -208 -144 -251 -199 -142 -255 -216 -148 -255 -216 -148 -242 -185 -131 112 213 232 @@ -5090,6 +5042,54 @@ P3 112 213 232 +242 +185 +131 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +208 +144 +255 +208 +144 +255 +208 +144 +255 +208 +144 +255 +208 +144 +251 +199 +142 +255 +216 +148 +255 +216 +148 +242 +185 +131 249 250 252 @@ -5141,18 +5141,18 @@ P3 71 38 21 -100 -56 -37 -105 -60 -40 -95 -56 -37 -107 -62 -42 +66 +35 +19 +66 +35 +19 +66 +35 +19 +66 +35 +19 105 60 40 @@ -5186,54 +5186,6 @@ P3 66 35 19 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -216 -148 -255 -216 -148 -251 -199 -142 -255 -208 -144 -251 -199 -142 -255 -208 -144 -255 -208 -144 -251 -199 -142 -251 -199 -142 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 105 200 229 @@ -5282,6 +5234,54 @@ P3 120 232 236 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +216 +148 +255 +216 +148 +251 +199 +142 +255 +208 +144 +251 +199 +142 +255 +208 +144 +255 +208 +144 +251 +199 +142 +251 +199 +142 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 236 241 247 @@ -5330,15 +5330,15 @@ P3 249 250 252 -71 -38 -21 66 35 19 -80 -46 -27 +66 +35 +19 +66 +35 +19 114 69 48 @@ -5378,54 +5378,6 @@ P3 93 54 35 -255 -224 -152 -255 -224 -152 -255 -216 -148 -255 -216 -148 -255 -216 -148 -255 -221 -148 -251 -199 -142 -255 -208 -144 -255 -216 -148 -255 -216 -148 -251 -199 -142 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 -255 -224 -152 105 200 229 @@ -5474,6 +5426,54 @@ P3 93 191 225 +255 +224 +152 +255 +224 +152 +255 +216 +148 +255 +216 +148 +255 +216 +148 +255 +221 +148 +251 +199 +142 +255 +208 +144 +255 +216 +148 +255 +216 +148 +251 +199 +142 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 +255 +224 +152 249 250 252 @@ -5570,54 +5570,6 @@ P3 93 54 35 -251 -199 -142 -251 -199 -142 -255 -216 -148 -255 -216 -148 -255 -221 -148 -255 -208 -144 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -216 -148 -255 -216 -148 -255 -224 -152 -255 -224 -152 -242 -185 -131 -242 -185 -131 -251 -199 -142 105 200 229 @@ -5666,6 +5618,54 @@ P3 112 217 232 +251 +199 +142 +251 +199 +142 +255 +216 +148 +255 +216 +148 +255 +221 +148 +255 +208 +144 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +216 +148 +255 +216 +148 +255 +224 +152 +255 +224 +152 +242 +185 +131 +242 +185 +131 +251 +199 +142 249 250 252 @@ -5762,54 +5762,6 @@ P3 105 60 40 -255 -216 -148 -251 -199 -142 -251 -199 -142 -255 -208 -144 -255 -221 -148 -255 -208 -144 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -216 -148 -255 -224 -152 -255 -224 -152 -242 -185 -131 -242 -185 -131 -255 -216 -148 88 177 223 @@ -5858,6 +5810,54 @@ P3 112 217 232 +255 +216 +148 +251 +199 +142 +251 +199 +142 +255 +208 +144 +255 +221 +148 +255 +208 +144 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +216 +148 +255 +224 +152 +255 +224 +152 +242 +185 +131 +242 +185 +131 +255 +216 +148 249 250 252 @@ -5954,54 +5954,6 @@ P3 105 60 40 -255 -216 -148 -251 -199 -142 -255 -208 -144 -255 -221 -148 -255 -221 -148 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -251 -199 -142 -255 -224 -152 -255 -224 -152 -251 -199 -142 -251 -199 -142 -255 -216 -148 88 177 223 @@ -6050,6 +6002,54 @@ P3 90 184 223 +255 +216 +148 +251 +199 +142 +255 +208 +144 +255 +221 +148 +255 +221 +148 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +251 +199 +142 +255 +224 +152 +255 +224 +152 +251 +199 +142 +251 +199 +142 +255 +216 +148 249 250 252 @@ -6146,6 +6146,150 @@ P3 105 60 40 +71 +39 +32 +115 +73 +55 +112 +66 +55 +89 +51 +43 +125 +79 +59 +125 +79 +59 +99 +60 +50 +136 +87 +65 +125 +79 +59 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +99 +60 +50 +115 +73 +55 +71 +39 +32 +71 +39 +32 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +71 +39 +32 +44 +113 +68 +39 +92 +67 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +44 +113 +68 +44 +113 +68 192 0 255 @@ -6194,6 +6338,150 @@ P3 192 0 255 +71 +39 +32 +89 +51 +43 +89 +51 +43 +89 +51 +43 +125 +79 +59 +125 +79 +59 +99 +60 +50 +125 +79 +59 +136 +87 +65 +99 +60 +50 +125 +79 +59 +125 +79 +59 +89 +51 +43 +125 +79 +59 +115 +73 +55 +71 +39 +32 +89 +51 +43 +89 +51 +43 +99 +60 +50 +99 +60 +50 +99 +60 +50 +99 +60 +50 +115 +73 +55 +115 +73 +55 +136 +87 +65 +136 +87 +65 +136 +87 +65 +99 +60 +50 +115 +73 +55 +115 +73 +55 +89 +51 +43 +89 +51 +43 +39 +92 +67 +39 +92 +67 +39 +128 +70 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +92 +67 +44 +113 +68 +39 +92 +67 +39 +128 +70 +39 +92 +67 +39 +92 +67 +44 +113 +68 +44 +113 +68 +39 +92 +67 +44 +113 +68 192 0 255 @@ -6242,6 +6530,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +125 +79 +59 +89 +51 +43 +89 +51 +43 +99 +60 +50 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +89 +51 +43 +99 +60 +50 +99 +60 +50 +125 +79 +59 +105 +64 +51 +71 +39 +32 +89 +51 +43 +115 +73 +55 +136 +87 +65 +99 +60 +50 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +136 +87 +65 +99 +60 +50 +105 +64 +51 +89 +51 +43 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 192 0 255 @@ -6290,6 +6722,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +112 +66 +55 +89 +51 +43 +136 +87 +65 +136 +87 +65 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +136 +87 +65 +125 +79 +59 +99 +60 +50 +112 +66 +55 +115 +73 +55 +71 +39 +32 +89 +51 +43 +115 +73 +55 +147 +101 +72 +89 +51 +43 +135 +81 +53 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +135 +81 +53 +89 +51 +43 +136 +87 +65 +115 +73 +55 +89 +51 +43 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 192 0 255 @@ -6338,6 +6914,150 @@ P3 192 0 255 +71 +39 +32 +105 +64 +51 +125 +79 +59 +89 +51 +43 +125 +79 +59 +125 +79 +59 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +136 +87 +65 +136 +87 +65 +99 +60 +50 +125 +79 +59 +105 +64 +51 +71 +39 +32 +89 +51 +43 +105 +64 +51 +89 +51 +43 +135 +81 +53 +163 +119 +85 +163 +119 +85 +147 +101 +72 +147 +101 +72 +147 +101 +72 +147 +101 +72 +147 +101 +72 +163 +119 +85 +135 +81 +53 +89 +51 +43 +105 +64 +51 +89 +51 +43 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 192 0 255 @@ -6386,6 +7106,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +112 +66 +55 +89 +51 +43 +99 +60 +50 +99 +60 +50 +99 +60 +50 +147 +101 +72 +136 +87 +65 +89 +51 +43 +136 +87 +65 +125 +79 +59 +89 +51 +43 +112 +66 +55 +105 +64 +51 +71 +39 +32 +89 +51 +43 +136 +87 +65 +89 +51 +43 +163 +119 +85 +163 +119 +85 +147 +101 +72 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +89 +51 +43 +105 +64 +51 +89 +51 +43 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +92 +67 +44 +113 +68 +44 +113 +68 +39 +92 +67 +44 +113 +68 +44 +113 +68 +39 +128 +70 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +44 +113 +68 192 0 255 @@ -6434,6 +7298,150 @@ P3 192 0 255 +71 +39 +32 +105 +64 +51 +112 +66 +55 +89 +51 +43 +147 +101 +72 +125 +79 +59 +99 +60 +50 +136 +87 +65 +125 +79 +59 +99 +60 +50 +125 +79 +59 +136 +87 +65 +99 +60 +50 +112 +66 +55 +105 +64 +51 +71 +39 +32 +89 +51 +43 +136 +87 +65 +89 +51 +43 +163 +119 +85 +147 +101 +72 +163 +119 +85 +163 +119 +85 +147 +101 +72 +147 +101 +72 +147 +101 +72 +147 +101 +72 +163 +119 +85 +163 +119 +85 +89 +51 +43 +105 +64 +51 +89 +51 +43 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +44 +113 +68 +39 +92 +67 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +44 +113 +68 192 0 255 @@ -6482,6 +7490,150 @@ P3 192 0 255 +71 +39 +32 +99 +60 +50 +99 +60 +50 +89 +51 +43 +136 +87 +65 +125 +79 +59 +99 +60 +50 +125 +79 +59 +136 +87 +65 +99 +60 +50 +99 +60 +50 +99 +60 +50 +99 +60 +50 +89 +51 +43 +99 +60 +50 +71 +39 +32 +89 +51 +43 +136 +87 +65 +89 +51 +43 +163 +119 +85 +147 +101 +72 +163 +119 +85 +147 +101 +72 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +135 +81 +53 +163 +119 +85 +89 +51 +43 +99 +60 +50 +89 +51 +43 +44 +113 +68 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 192 0 255 @@ -6530,6 +7682,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +125 +79 +59 +89 +51 +43 +125 +79 +59 +136 +87 +65 +89 +51 +43 +125 +79 +59 +125 +79 +59 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +125 +79 +59 +115 +73 +55 +71 +39 +32 +89 +51 +43 +136 +87 +65 +89 +51 +43 +163 +119 +85 +147 +101 +72 +163 +119 +85 +135 +81 +53 +163 +119 +85 +163 +119 +85 +135 +81 +53 +163 +119 +85 +147 +101 +72 +163 +119 +85 +89 +51 +43 +115 +73 +55 +89 +51 +43 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 192 0 255 @@ -6578,6 +7874,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +112 +66 +55 +89 +51 +43 +136 +87 +65 +125 +79 +59 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +136 +87 +65 +136 +87 +65 +99 +60 +50 +125 +79 +59 +105 +64 +51 +71 +39 +32 +89 +51 +43 +136 +87 +65 +89 +51 +43 +163 +119 +85 +135 +81 +53 +163 +119 +85 +163 +119 +85 +135 +81 +53 +147 +101 +72 +163 +119 +85 +163 +119 +85 +147 +101 +72 +163 +119 +85 +89 +51 +43 +136 +87 +65 +89 +51 +43 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +44 +113 +68 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +44 +113 +68 192 0 255 @@ -6626,6 +8066,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +112 +66 +55 +89 +51 +43 +125 +79 +59 +125 +79 +59 +99 +60 +50 +125 +79 +59 +125 +79 +59 +89 +51 +43 +136 +87 +65 +125 +79 +59 +89 +51 +43 +112 +66 +55 +105 +64 +51 +71 +39 +32 +89 +51 +43 +136 +87 +65 +89 +51 +43 +163 +119 +85 +163 +119 +85 +147 +101 +72 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +147 +101 +72 +163 +119 +85 +163 +119 +85 +89 +51 +43 +136 +87 +65 +89 +51 +43 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +44 +113 +68 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +44 +113 +68 192 0 255 @@ -6674,6 +8258,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +112 +66 +55 +89 +51 +43 +125 +79 +59 +125 +79 +59 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +125 +79 +59 +125 +79 +59 +89 +51 +43 +89 +51 +43 +89 +51 +43 +71 +39 +32 +89 +51 +43 +115 +73 +55 +89 +51 +43 +135 +81 +53 +163 +119 +85 +163 +119 +85 +135 +81 +53 +147 +101 +72 +147 +101 +72 +147 +101 +72 +163 +119 +85 +163 +119 +85 +135 +81 +53 +89 +51 +43 +136 +87 +65 +89 +51 +43 +44 +113 +68 +39 +92 +67 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 192 0 255 @@ -6722,6 +8450,150 @@ P3 192 0 255 +71 +39 +32 +105 +64 +51 +125 +79 +59 +89 +51 +43 +99 +60 +50 +99 +60 +50 +99 +60 +50 +125 +79 +59 +125 +79 +59 +99 +60 +50 +99 +60 +50 +99 +60 +50 +89 +51 +43 +125 +79 +59 +105 +64 +51 +71 +39 +32 +89 +51 +43 +105 +64 +51 +136 +87 +65 +89 +51 +43 +135 +81 +53 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +163 +119 +85 +135 +81 +53 +89 +51 +43 +147 +101 +72 +105 +64 +51 +89 +51 +43 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 192 0 255 @@ -6770,6 +8642,150 @@ P3 192 0 255 +71 +39 +32 +89 +51 +43 +89 +51 +43 +89 +51 +43 +147 +101 +72 +136 +87 +65 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +147 +101 +72 +136 +87 +65 +89 +51 +43 +125 +79 +59 +105 +64 +51 +71 +39 +32 +89 +51 +43 +89 +51 +43 +99 +60 +50 +147 +101 +72 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +136 +87 +65 +136 +87 +65 +105 +64 +51 +89 +51 +43 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +92 +67 192 0 255 @@ -6818,6 +8834,150 @@ P3 192 0 255 +71 +39 +32 +115 +73 +55 +125 +79 +59 +89 +51 +43 +147 +101 +72 +136 +87 +65 +89 +51 +43 +125 +79 +59 +136 +87 +65 +89 +51 +43 +147 +101 +72 +125 +79 +59 +99 +60 +50 +112 +66 +55 +105 +64 +51 +71 +39 +32 +89 +51 +43 +89 +51 +43 +125 +79 +59 +99 +60 +50 +147 +101 +72 +136 +87 +65 +99 +60 +50 +125 +79 +59 +136 +87 +65 +147 +101 +72 +147 +101 +72 +99 +60 +50 +136 +87 +65 +136 +87 +65 +89 +51 +43 +89 +51 +43 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +128 +70 +39 +92 +67 +39 +92 +67 +39 +92 +67 192 0 255 @@ -6866,2310 +9026,150 @@ P3 192 0 255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 -192 -0 -255 +71 +39 +32 +105 +64 +51 +125 +79 +59 +89 +51 +43 +136 +87 +65 +125 +79 +59 +89 +51 +43 +147 +101 +72 +125 +79 +59 +89 +51 +43 +136 +87 +65 +125 +79 +59 +99 +60 +50 +112 +66 +55 +105 +64 +51 +71 +39 +32 +71 +39 +32 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +89 +51 +43 +71 +39 +32 +39 +92 +67 +39 +92 +67 +44 +113 +68 +39 +128 +70 +39 +92 +67 +39 +92 +67 +44 +113 +68 +44 +113 +68 +44 +113 +68 +39 +92 +67 +39 +92 +67 +39 +128 +70 +39 +128 +70 +39 +92 +67 +44 +113 +68 +44 +113 +68 192 0 255 diff --git a/src/Math/Matrix.hpp b/src/Math/Matrix.hpp index 69241d3..d467f72 100644 --- a/src/Math/Matrix.hpp +++ b/src/Math/Matrix.hpp @@ -9,14 +9,14 @@ template struct Matrix { Matrix() : elements{} {}; - explicit Matrix(T scalar) { + explicit Matrix(const T scalar) { std::fill(elements, elements + R * C, scalar); }; template = 0> Matrix(Args... args): elements{ args... } {}; - explicit Matrix(T values[R * C]) { + explicit Matrix(const T values[R * C]) { std::copy(values, values + R * C, elements); }; @@ -67,11 +67,11 @@ struct Matrix { return rotation_x * rotation_y * rotation_z; } - Vector row(size_t index) { + Vector row(size_t index) const { return { &elements[index * C] }; } - Vector col(size_t index) { + Vector col(size_t index) const { Vector result{}; for (int i = 0; i < R; i++) { result[i] = this->operator()(index, i); @@ -79,7 +79,7 @@ struct Matrix { return result; } - Matrix transpose() { + Matrix transpose() const { Matrix result{}; for (int y = 0; y < R; y++) { for (int x = 0; x < C; x++) { @@ -89,7 +89,7 @@ struct Matrix { return result; } - Matrix operator+(Matrix other) { + Matrix operator+(const Matrix other) const { Matrix result{}; for (int i = 0; i < R * C; i++) { result.elements[i] = elements[i] + other.elements[i]; @@ -106,7 +106,7 @@ struct Matrix { } template - Matrix operator*(Matrix other) { + Matrix operator*(const Matrix other) const { Matrix result{}; for (int y = 0; y < R; y++) { for (int x = 0; x < N; x++) { @@ -121,13 +121,16 @@ struct Matrix { return result; } - Vector operator*(Vector vector) { + Vector operator*(const Vector vector) const { Matrix matrix(vector.elements); matrix = this->operator*(matrix); return { matrix.elements }; } - auto& operator()(size_t x, size_t y) { + const T& operator()(const size_t x, const size_t y) const { + return elements[y * C + x]; + } + T& operator()(const size_t x, const size_t y) { return elements[y * C + x]; } diff --git a/src/Math/Random.cpp b/src/Math/Random.cpp new file mode 100644 index 0000000..e35cda7 --- /dev/null +++ b/src/Math/Random.cpp @@ -0,0 +1,31 @@ +#include "Random.hpp" + +namespace Math::Random { + +std::array break_float(const float f) { + static_assert(sizeof(float) == 4); + + union { float f; uint8_t u[4]; } t{}; + t.f = f; + + return { + t.u[0], t.u[1], t.u[2], t.u[3] + }; +} + +float to_float(uint8_t u) { + return (float)u / (float)255; +} + +uint8_t hash(uint8_t x) { + auto o = ((x ^ 0xAA) * 5); + auto rot = o % 8; + return o << rot | o >> (8 - rot); +} + +float random() { + uint8_t r = std::rand() % 255; + return to_float(hash(r)); +} + +} \ No newline at end of file diff --git a/src/Math/Random.hpp b/src/Math/Random.hpp new file mode 100644 index 0000000..ab94871 --- /dev/null +++ b/src/Math/Random.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "array" +#include "Common.hpp" + +namespace Math::Random { + +std::array break_float(float f); +float to_float(uint8_t u); + +uint8_t hash(uint8_t x); + +float random(); + +template +struct Noise { + float at(Vector pos) const { + uint8_t to_hash[D * 4]; + for (int i = 0; i < D; i++) { + auto b = break_float(pos[i]); + to_hash[i*4] = b[0]; to_hash[i*4+1] = b[1]; to_hash[i*4+2] = b[2]; to_hash[i*4+3] = b[3]; + } + uint8_t h = 0; + for (int i = 0; i < D * 4; i++) { + h = hash(h) + to_hash[i]; + } + return to_float(h); + } +}; + +} \ No newline at end of file diff --git a/src/Math/Vector.hpp b/src/Math/Vector.hpp index 260891d..54207f0 100644 --- a/src/Math/Vector.hpp +++ b/src/Math/Vector.hpp @@ -10,31 +10,36 @@ struct Vector { Vector(): elements{} {}; template = 0> - Vector(Args... args) : elements{ args... } {}; + Vector(Args... args) : elements{ args... } {}; - Vector(T values[S]) { + Vector(const T values[S]) { std::copy(values, values + S, elements); }; - Vector(T scalar) { + explicit Vector(const T scalar) { std::fill(elements, elements + S, scalar); }; - Vector(Vector vector, T scalar) { + Vector(const Vector vector, const T scalar) { std::copy(vector.elements, vector.elements + S - 1, elements); elements[S - 1] = scalar; } - Vector map(std::function f) const { - Vector result{}; + template S), int> = 0> + explicit Vector(const Vector vector) { + std::copy(vector.elements, vector.elements + S, elements); + } + + Vector map(std::function f) const { + Vector result{}; for (int i = 0; i < S; i++) { result[i] = f(elements[i]); } return result; } - Vector map(std::function f) const { - Vector result{}; + Vector map(std::function f) const { + Vector result{}; for (int i = 0; i < S; i++) { result[i] = f(i, elements[i]); } @@ -57,20 +62,27 @@ struct Vector { return sqrt(map([](auto x) { return x * x;}).sum()); } - Vector normalize() const { + Vector normalize() const { auto m = magnitude(); return map([=](auto x) { return x / m; }); } - T distance(Vector other) const { + T distance(const Vector other) const { return (*this - other).magnitude(); } - Vector abs() const { + Vector<3, T> any_orthogonal() { + if (Vector a{y(), -x(), 0.0f}; a != zero()) return a; + if (Vector b{z(), 0.0f, -x()}; b != zero()) return b; + if (Vector c{0.0f, z(), -y()}; c != zero()) return c; + return zero(); + } + + Vector abs() const { return map([=](auto x) { return std::abs(x); }); } - Vector<3, T> cross(Vector<3, T> other) const { + Vector<3, T> cross(const Vector<3, T> other) const { return { y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(), @@ -86,34 +98,52 @@ struct Vector { return elements[index]; } - Vector operator+(Vector other) const { + Vector operator+(const Vector other) const { return map([&](auto i, auto x) { return x + other[i]; }); } - Vector operator+(T scalar) const { + Vector operator+(T scalar) const { return map([=](auto x) { return x + scalar; }); } - Vector operator*(T scalar) const { + Vector operator*(T scalar) const { return map([=](auto x) { return x * scalar; }); } - T operator*(Vector other) const { + T operator*(const Vector other) const { return map([&](auto i, auto x) { return x * other[i]; }).sum(); } - Vector operator-(Vector other) const { + Vector operator-(const Vector other) const { return map([&](auto i, auto x) { return x - other[i]; }); } - Vector operator-() const { + Vector operator-() const { return map([](T x) -> T { return -x; }); } - Vector operator/(T scalar) const { + Vector operator/(T scalar) const { return map([=](auto x) { return x / scalar; }); } + bool operator==(const Vector& other) { + for (int i = 0; i < S; i++) { + if (elements[i] != other[i]) { + return false; + } + } + return true; + } + + bool operator!=(const Vector& other) { + return !this->operator==(other); + } + + Vector& operator+=(const Vector& other) { + *this = *this + other; + return *this; + } + T& x() { static_assert(S > 0); return elements[0]; } const T& x() const { static_assert(S > 0); return elements[0]; } @@ -138,5 +168,16 @@ struct Vector { return str.str(); } + static Vector<3, T> up() { return {(T)0, (T)1, (T)0}; } + static Vector<3, T> down() { return {(T)0, (T)-1, (T)0}; } + static Vector<3, T> forward() { return {(T)0, (T)0, (T)1}; } + static Vector<3, T> back() { return {(T)0, (T)0, (T)-1}; } + static Vector<3, T> right() { return {(T)1, (T)0, (T)0}; } + static Vector<3, T> left() { return {(T)-1, (T)0, (T)0}; } + + static Vector<3, T> one() { return Vector{(T)1}; } + static Vector<3, T> zero() { return Vector{(T)0}; } + static Vector<3, T> max() { return Vector{(T)std::numeric_limits::max()}; } + T elements[S]; }; diff --git a/src/World/BlockType.hpp b/src/World/BlockType.hpp index dcb8e44..472523f 100644 --- a/src/World/BlockType.hpp +++ b/src/World/BlockType.hpp @@ -14,6 +14,8 @@ public: Stone, Sand, Snow, + Wood, + Leaves, Water, }; diff --git a/src/World/Chunk.cpp b/src/World/Chunk.cpp index 9642fa9..b75516f 100644 --- a/src/World/Chunk.cpp +++ b/src/World/Chunk.cpp @@ -11,163 +11,16 @@ Chunk::BlockData Chunk::get(uint32_t x, uint32_t y, uint32_t z) const { return m_blocks.at(pos(x, y, z)); } -GFX::Mesh Chunk::mesh() { - std::vector> positions{}; - std::vector> normals{}; - std::vector> tex_coords{}; - std::vector indices{}; - - for (int x = 0; x < Width; x++) { - for (int y = 0; y < Height; y++) { - for (int z = 0; z < Width; z++) { - auto type = get(x, y, z).type; - if (type == BlockType::Air) { - continue; - } - - for (auto side: BlockSide::all()) { - if (!is_face_visible(x, y, z, side)) { - continue; - } - - auto side_positions = side.face(); - auto side_normals = face_normals(side); - auto side_tex_coords = face_tex_coords(type, side); - - for (auto& position : side_positions) { - position = position + Vector<3>{static_cast(x), static_cast(y), static_cast(z)}; - } - - uint32_t s = positions.size(); - - positions.insert(positions.end(), side_positions.begin(), side_positions.end()); - normals.insert(normals.end(), side_normals.begin(), side_normals.end()); - tex_coords.insert(tex_coords.end(), side_tex_coords.begin(), side_tex_coords.end()); - indices.insert(indices.end(), {s, s + 1, s + 3, s + 1, s + 2, s + 3}); - } - } - } - } - - return { - {positions, normals, tex_coords}, - indices, - }; +bool Chunk::is_empty(uint32_t x, uint32_t y, uint32_t z) const { + return get(x, y, z).empty(); } Vector<3> Chunk::position() { return m_position; } -bool Chunk::is_face_visible(uint32_t x, uint32_t y, uint32_t z, BlockSide side) { - Vector<3, int32_t> offset{}; - switch (side) { - case BlockSide::Front: - offset[2] = 1; - break; - case BlockSide::Back: - offset[2] = -1; - break; - case BlockSide::Top: - offset[1] = 1; - break; - case BlockSide::Bottom: - offset[1] = -1; - break; - case BlockSide::Left: - offset[0] = -1; - break; - case BlockSide::Right: - offset[0] = 1; - break; - } - - Vector<3, uint32_t> neighbor_pos{ - x + offset.x(), y + offset.y(), z + offset.z(), - }; - - if ( - neighbor_pos.x() >= Width || neighbor_pos.x() < 0 || - neighbor_pos.y() >= Height || neighbor_pos.y() < 0 || - neighbor_pos.z() >= Width || neighbor_pos.z() < 0 - ) { - return true; - } - - auto neighbor = get(neighbor_pos.x(), neighbor_pos.y(), neighbor_pos.z()); - if (neighbor.type == BlockType::Air) { - return true; - } - - return false; -} - -std::array, 4> Chunk::face_tex_coords(BlockType type, BlockSide side) { - uint8_t atlas_width = 4; - uint8_t atlas_height = 4; - - float width_step = 1.0f / atlas_width; - float height_step = 1.0f / atlas_height; - - auto block_coords = [=](uint8_t x, uint8_t y) { - auto t = y * height_step; - auto l = x * width_step; - auto b = t + height_step; - auto r = l + width_step; - - return std::array, 4>{{ - {l, b}, {r, b}, {r, t}, {l, t}, - }}; - }; - - switch (type) { - case BlockType::Dirt: - return block_coords(1, 0); - case BlockType::Grass: - switch (side) { - case BlockSide::Front: - case BlockSide::Back: - case BlockSide::Left: - case BlockSide::Right: - return block_coords(2, 0); - case BlockSide::Bottom: - return block_coords(1, 0); - case BlockSide::Top: - return block_coords(0, 0); - } - case BlockType::Stone: - return block_coords(3, 0); - case BlockType::Sand: - return block_coords(0, 1); - case BlockType::Water: - return block_coords(1, 1); - case BlockType::Snow: - switch (side) { - case BlockSide::Front: - case BlockSide::Back: - case BlockSide::Left: - case BlockSide::Right: - return block_coords(3, 1); - case BlockSide::Bottom: - return block_coords(1, 0); - case BlockSide::Top: - return block_coords(2, 1); - } - case BlockType::Air: - return {}; - } -} - -std::array, 4> Chunk::face_normals(BlockSide side) { - auto is_side = [=](BlockSide s) -> float { return s == side; }; - - Vector<3> normal = { - is_side(BlockSide::Right) - is_side(BlockSide::Left), - is_side(BlockSide::Top) - is_side(BlockSide::Bottom), - is_side(BlockSide::Front) - is_side(BlockSide::Back), - }; - - return {normal, normal, normal, normal}; +bool Chunk::is_valid_position(uint32_t x, uint32_t y, uint32_t z) { + return x < Width && y < Height && z < Width; } uint64_t Chunk::pos(uint32_t x, uint32_t y, uint32_t z) { diff --git a/src/World/Chunk.hpp b/src/World/Chunk.hpp index d200039..8780700 100644 --- a/src/World/Chunk.hpp +++ b/src/World/Chunk.hpp @@ -19,27 +19,30 @@ public: struct BlockData { BlockType type; + + bool empty() const { return type == BlockType::Air; } }; void set(uint32_t x, uint32_t y, uint32_t z, BlockData data); BlockData get(uint32_t x, uint32_t y, uint32_t z) const; + bool is_empty(uint32_t x, uint32_t y, uint32_t z) const; struct Details { Matrix landmass_values{}; Matrix hill_values{}; + + Matrix temperature_values{}; + Matrix humidity_values{}; + Matrix biome_values{}; }; void set_details(const Details& details) { m_details = details; } Details& details(){ return m_details; } Vector<3> position(); - GFX::Mesh mesh(); -private: - bool is_face_visible(uint32_t x, uint32_t y, uint32_t z, BlockSide side); - - static std::array, 4> face_tex_coords(BlockType type, BlockSide side); - static std::array, 4> face_normals(BlockSide side); + static bool is_valid_position(uint32_t x, uint32_t y, uint32_t z); +private: static uint64_t pos(uint32_t x, uint32_t y, uint32_t z); Vector<3> m_position; diff --git a/src/World/ChunkMeshing.cpp b/src/World/ChunkMeshing.cpp deleted file mode 100644 index 3e50427..0000000 --- a/src/World/ChunkMeshing.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "ChunkMeshing.hpp" - -namespace MC::World { - -std::array, 4> face_tex_coords(BlockType type, BlockSide side) { - uint8_t atlas_width = 4; - uint8_t atlas_height = 4; - - float width_step = 1.0f / atlas_width; - float height_step = 1.0f / atlas_height; - - auto block_coords = [=](uint8_t x, uint8_t y) { - auto t = y * height_step; - auto l = x * width_step; - auto b = t + height_step; - auto r = l + width_step; - - return std::array, 4>{{ - {l, b}, {r, b}, {r, t}, {l, t}, - }}; - }; - - switch (type) { - case BlockType::Dirt: - return block_coords(1, 0); - case BlockType::Grass: - switch (side) { - case BlockSide::Front: - case BlockSide::Back: - case BlockSide::Left: - case BlockSide::Right: - return block_coords(2, 0); - case BlockSide::Bottom: - return block_coords(1, 0); - case BlockSide::Top: - return block_coords(0, 0); - } - case BlockType::Stone: - return block_coords(3, 0); - case BlockType::Sand: - return block_coords(0, 1); - case BlockType::Water: - return block_coords(1, 1); - case BlockType::Snow: - switch (side) { - case BlockSide::Front: - case BlockSide::Back: - case BlockSide::Left: - case BlockSide::Right: - return block_coords(3, 1); - case BlockSide::Bottom: - return block_coords(1, 0); - case BlockSide::Top: - return block_coords(2, 1); - } - case BlockType::Air: - return {}; - } -} - -std::array, 4> face_normals(BlockSide side) { - auto is_side = [=](BlockSide s) -> float { return s == side; }; - - Vector<3> normal = { - is_side(BlockSide::Right) - is_side(BlockSide::Left), - is_side(BlockSide::Top) - is_side(BlockSide::Bottom), - is_side(BlockSide::Front) - is_side(BlockSide::Back), - }; - - return {normal, normal, normal, normal}; -} - -bool is_face_visible(Chunk& chunk, ChunkMeshing::ChunkNeighbors neighbors, uint32_t x, uint32_t y, uint32_t z, BlockSide side) { - Vector<3, int32_t> offset{}; - switch (side) { - case BlockSide::Front: - offset[2] = 1; - break; - case BlockSide::Back: - offset[2] = -1; - break; - case BlockSide::Top: - offset[1] = 1; - break; - case BlockSide::Bottom: - offset[1] = -1; - break; - case BlockSide::Left: - offset[0] = -1; - break; - case BlockSide::Right: - offset[0] = 1; - break; - } - - Vector<3, int32_t> neighbor_pos{ - (int32_t)x + offset.x(), (int32_t)y + offset.y(), (int32_t)z + offset.z(), - }; - - Chunk* chunk_to_ask; - - if (neighbor_pos.z() < 0) { - chunk_to_ask = &neighbors.north; - neighbor_pos.z() += Chunk::Width; - } else if (neighbor_pos.x() >= (int32_t)Chunk::Width) { - chunk_to_ask = &neighbors.east; - neighbor_pos.x() -= Chunk::Width; - } else if (neighbor_pos.z() >= (int32_t)Chunk::Width) { - chunk_to_ask = &neighbors.south; - neighbor_pos.z() -= Chunk::Width; - } else if (neighbor_pos.x() < 0) { - chunk_to_ask = &neighbors.west; - neighbor_pos.x() += Chunk::Width; - } else { - chunk_to_ask = &chunk; - } - - auto neighbor = chunk_to_ask->get(neighbor_pos.x(), neighbor_pos.y(), neighbor_pos.z()); - if (neighbor.type == BlockType::Air) { - return true; - } - - return false; -} - -GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, ChunkMeshing::ChunkNeighbors neighbors) { - std::vector> positions{}; - std::vector> normals{}; - std::vector> tex_coords{}; - std::vector indices{}; - - for (int x = 0; x < Chunk::Width; x++) { - for (int y = 0; y < Chunk::Height; y++) { - for (int z = 0; z < Chunk::Width; z++) { - auto type = chunk.get(x, y, z).type; - if (type == BlockType::Air) { - continue; - } - - for (auto side: BlockSide::all()) { - if (!is_face_visible(chunk, neighbors, x, y, z, side)) { - continue; - } - - auto side_positions = side.face(); - auto side_normals = face_normals(side); - auto side_tex_coords = face_tex_coords(type, side); - - for (auto& position : side_positions) { - position = position + Vector<3>{static_cast(x), static_cast(y), static_cast(z)}; - } - - uint32_t s = positions.size(); - - positions.insert(positions.end(), side_positions.begin(), side_positions.end()); - normals.insert(normals.end(), side_normals.begin(), side_normals.end()); - tex_coords.insert(tex_coords.end(), side_tex_coords.begin(), side_tex_coords.end()); - indices.insert(indices.end(), {s, s + 1, s + 3, s + 1, s + 2, s + 3}); - } - } - } - } - - return { - {positions, normals, tex_coords}, - indices, - }; -} - -} \ No newline at end of file diff --git a/src/World/ChunkMeshing.hpp b/src/World/ChunkMeshing.hpp deleted file mode 100644 index 1348817..0000000 --- a/src/World/ChunkMeshing.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "../GFX/Mesh.hpp" -#include "Chunk.hpp" - -namespace MC::World::ChunkMeshing { - -struct ChunkNeighbors { Chunk &north, &east, &south, &west; }; -GFX::Mesh create_mesh_for_chunk(Chunk& chunk, ChunkNeighbors neighbors); - -} diff --git a/src/World/Generation/ChunkMeshing.cpp b/src/World/Generation/ChunkMeshing.cpp new file mode 100644 index 0000000..54abc85 --- /dev/null +++ b/src/World/Generation/ChunkMeshing.cpp @@ -0,0 +1,183 @@ +#include "ChunkMeshing.hpp" + +namespace MC::World::Generation { + +std::array, 4> face_tex_coords(BlockType type, BlockSide side) { + uint8_t atlas_width = 4; + uint8_t atlas_height = 4; + + float width_step = 1.0f / atlas_width; + float height_step = 1.0f / atlas_height; + + auto block_coords = [=](uint8_t x, uint8_t y) { + auto t = y * height_step; + auto l = x * width_step; + auto b = t + height_step; + auto r = l + width_step; + + return std::array, 4>{{ + {l, b}, {r, b}, {r, t}, {l, t}, + }}; + }; + + switch (type) { + case BlockType::Dirt: + return block_coords(1, 0); + case BlockType::Grass: + switch (side) { + case BlockSide::Front: + case BlockSide::Back: + case BlockSide::Left: + case BlockSide::Right: + return block_coords(2, 0); + case BlockSide::Bottom: + return block_coords(1, 0); + case BlockSide::Top: + return block_coords(0, 0); + } + case BlockType::Stone: + return block_coords(3, 0); + case BlockType::Sand: + return block_coords(1, 1); + case BlockType::Water: + return block_coords(0, 1); + case BlockType::Snow: + switch (side) { + case BlockSide::Front: + case BlockSide::Back: + case BlockSide::Left: + case BlockSide::Right: + return block_coords(3, 1); + case BlockSide::Bottom: + return block_coords(1, 0); + case BlockSide::Top: + return block_coords(2, 1); + } + case BlockType::Wood: + switch (side) { + case BlockSide::Front: + case BlockSide::Back: + case BlockSide::Right: + case BlockSide::Left: + return block_coords(0, 2); + case BlockSide::Bottom: + case BlockSide::Top: + return block_coords(1, 2); + } + case BlockType::Leaves: + return block_coords(2, 2); + case BlockType::Air: + return {}; + } +} + +std::array, 4> face_normals(BlockSide side) { + auto is_side = [=](BlockSide s) -> float { return s == side; }; + + Vector<3> normal = { + is_side(BlockSide::Right) - is_side(BlockSide::Left), + is_side(BlockSide::Top) - is_side(BlockSide::Bottom), + is_side(BlockSide::Front) - is_side(BlockSide::Back), + }; + + return {normal, normal, normal, normal}; +} + +bool is_face_visible(Chunk& chunk, ChunkMeshing::ChunkNeighbors neighbors, uint32_t x, uint32_t y, uint32_t z, BlockSide side) { + Vector<3, int32_t> offset{}; + switch (side) { + case BlockSide::Front: + offset[2] = 1; + break; + case BlockSide::Back: + offset[2] = -1; + break; + case BlockSide::Top: + offset[1] = 1; + break; + case BlockSide::Bottom: + offset[1] = -1; + break; + case BlockSide::Left: + offset[0] = -1; + break; + case BlockSide::Right: + offset[0] = 1; + break; + } + + Vector<3, int32_t> neighbor_pos{ + (int32_t)x + offset.x(), (int32_t)y + offset.y(), (int32_t)z + offset.z(), + }; + + Chunk* chunk_to_ask; + + if (neighbor_pos.z() < 0) { + chunk_to_ask = &neighbors.north; + neighbor_pos.z() += Chunk::Width; + } else if (neighbor_pos.x() >= (int32_t)Chunk::Width) { + chunk_to_ask = &neighbors.east; + neighbor_pos.x() -= Chunk::Width; + } else if (neighbor_pos.z() >= (int32_t)Chunk::Width) { + chunk_to_ask = &neighbors.south; + neighbor_pos.z() -= Chunk::Width; + } else if (neighbor_pos.x() < 0) { + chunk_to_ask = &neighbors.west; + neighbor_pos.x() += Chunk::Width; + } else { + chunk_to_ask = &chunk; + } + + auto neighbor = chunk_to_ask->get(neighbor_pos.x(), neighbor_pos.y(), neighbor_pos.z()); + if (neighbor.type == BlockType::Air) { + return true; + } + + return false; +} + +GFX::Mesh ChunkMeshing::create_mesh_for_chunk(Chunk& chunk, ChunkMeshing::ChunkNeighbors neighbors) { + std::vector> positions{}; + std::vector> normals{}; + std::vector> tex_coords{}; + std::vector indices{}; + + for (int x = 0; x < Chunk::Width; x++) { + for (int y = 0; y < Chunk::Height; y++) { + for (int z = 0; z < Chunk::Width; z++) { + auto type = chunk.get(x, y, z).type; + if (type == BlockType::Air) { + continue; + } + + for (auto side: BlockSide::all()) { + if (!is_face_visible(chunk, neighbors, x, y, z, side)) { + continue; + } + + auto side_positions = side.face(); + auto side_normals = face_normals(side); + auto side_tex_coords = face_tex_coords(type, side); + + for (auto& position : side_positions) { + position = position + Vector<3>{static_cast(x), static_cast(y), static_cast(z)}; + } + + uint32_t s = positions.size(); + + positions.insert(positions.end(), side_positions.begin(), side_positions.end()); + normals.insert(normals.end(), side_normals.begin(), side_normals.end()); + tex_coords.insert(tex_coords.end(), side_tex_coords.begin(), side_tex_coords.end()); + indices.insert(indices.end(), {s, s + 1, s + 3, s + 1, s + 2, s + 3}); + } + } + } + } + + return { + {positions, normals, tex_coords}, + indices, + }; +} + +} \ No newline at end of file diff --git a/src/World/Generation/ChunkMeshing.hpp b/src/World/Generation/ChunkMeshing.hpp new file mode 100644 index 0000000..a4fed25 --- /dev/null +++ b/src/World/Generation/ChunkMeshing.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "../../GFX/Mesh.hpp" +#include "../Chunk.hpp" + +namespace MC::World::Generation::ChunkMeshing { + +struct ChunkNeighbors { Chunk &north, &east, &south, &west; }; +GFX::Mesh create_mesh_for_chunk(Chunk& chunk, ChunkNeighbors neighbors); + +} diff --git a/src/World/Generation/Decoration.cpp b/src/World/Generation/Decoration.cpp new file mode 100644 index 0000000..26eb397 --- /dev/null +++ b/src/World/Generation/Decoration.cpp @@ -0,0 +1,97 @@ +#include "Decoration.hpp" + +#include + +namespace MC::World::Generation { +void Decorator::put_block(Chunk& chunk, Pos pos, BlockType block) { + if (!Chunk::is_valid_position(pos.x(), pos.y(), pos.z())) { + return; + } + if (chunk.is_empty(pos.x(), pos.y(), pos.z())) { + chunk.set(pos.x(), pos.y(), pos.z(), {block}); + } +} + +void Decorator::draw_column(Chunk& chunk, Pos pos, uint height, BlockType block) { + Pos current_pos = pos; + for (uint i = 0; i < height; i++) { + put_block(chunk, current_pos, block); + current_pos += Pos::up(); + } +} + +void Decorator::draw_circle(Chunk& chunk, Pos pos, Vector<3> axis, float radius, BlockType block) { + auto normalized_axis = axis.normalize(); + + auto ortho1 = normalized_axis.any_orthogonal(); + auto ortho2 = normalized_axis.cross(ortho1); + + auto r = [](const float x) { return static_cast(std::round(x)); }; + + int radius_round = std::round(radius); + for (int32_t d1 = -radius_round; d1 <= radius_round; d1++) { + float height = std::sqrt(radius * radius - d1 * d1); + for (int32_t d2 = -height; d2 <= (int)height; d2++) { + auto p = ortho1 * d1 + ortho2 * d2; + Pos block_pos = pos + Pos{r(p.x()), r(p.y()), r(p.z())}; + put_block(chunk, block_pos, block); + } + } +} + +void TreeDecorator::decorate_chunk(Chunk& chunk) { + Pos last_tree = Pos::max(); + for (uint x = 0; x < Chunk::Width; x++) { + for (uint z = 0; z < Chunk::Width; z++) { + for (uint y = Chunk::Height; y > 1; y--) { + Pos pos{x, y, z}; + if (!is_valid_position(pos)) + continue; + + auto block_below = chunk.get(x, y-1, z); + if (block_below.empty()) + continue; + + auto type = block_below.type; + if (type != BlockType::Snow && type != BlockType::Grass && type != BlockType::Dirt) + break; + + auto noise = m_tree_noise.at({(float)x, (float)z}); + if (noise < 0.8f) + continue; + + if (last_tree.distance(pos) < s_tree_radius * 3) + continue; + + draw_tree(chunk, pos); + chunk.set(x, y-1, z, {BlockType::Dirt}); + last_tree = pos; + break; + } + } + } +} + +void TreeDecorator::draw_tree(Chunk& chunk, Pos pos) const { + auto noise = m_tree_noise.at({(float)pos.x(), (float)pos.z()}); + uint height = std::round(10 * noise - 4.75f); + + draw_column(chunk, pos, height, BlockType::Wood); + + uint max_leaf_height = 4; + for (int x = 0; x < max_leaf_height; x++) { + Pos p{pos.x(), pos.y() + height + x - 2, pos.z()}; + float radius = s_tree_radius - 0.5f + x * ((0.3f * x - 1.45f) * x + 1.25f); + draw_circle(chunk, p, Vector<3>::up(), radius, BlockType::Leaves); + } +} + +bool TreeDecorator::is_valid_position(Vector<3, uint> pos) { + int tree_radius = s_tree_radius; + return (int)pos.x() - tree_radius >= 0 + && pos.x() + tree_radius <= Chunk::Width + && (int)pos.z() - tree_radius >= 0 + && pos.z() + tree_radius <= Chunk::Width; +} + +} diff --git a/src/World/Generation/Decoration.hpp b/src/World/Generation/Decoration.hpp new file mode 100644 index 0000000..a592e72 --- /dev/null +++ b/src/World/Generation/Decoration.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include "../Chunk.hpp" +#include "../../Math/Random.hpp" + +namespace MC::World::Generation { + +class Decorator { +public: + using Pos = Vector<3, uint>; + + virtual ~Decorator() = default; + + virtual void decorate_chunk(Chunk& chunk) = 0; + + static void put_block(Chunk& chunk, Pos pos, BlockType block); + static void draw_column(Chunk& chunk, Pos pos, uint height, BlockType block); + static void draw_circle(Chunk& chunk, Pos pos, Vector<3> axis, float radius, BlockType block); +}; + +class TreeDecorator final : public Decorator { +public: + void decorate_chunk(Chunk& chunk) override; +private: + void draw_tree(Chunk& chunk, Pos pos) const; + + static bool is_valid_position(Pos pos); + + static constexpr uint s_tree_radius = 3; + + Math::Random::Noise<2> m_tree_noise; +}; + +} diff --git a/src/World/Generation/Generator.cpp b/src/World/Generation/Generator.cpp new file mode 100644 index 0000000..af3c54d --- /dev/null +++ b/src/World/Generation/Generator.cpp @@ -0,0 +1,331 @@ +#include "Generator.hpp" +#include "../../Math/Interpolation.hpp" +#include "../../Math/Sigmoid.hpp" + +namespace MC::World::Generation { + +Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) { + Chunk chunk(chunk_x, chunk_y); + + auto landmass_map = generate_landmass_map(chunk_x, chunk_y); + auto hill_map = generate_hill_map(chunk_x, chunk_y); + auto height_map = generate_height_map(landmass_map, hill_map, chunk_x, chunk_y); + + auto temperature_map = generate_temperature_map(chunk_x, chunk_y); + auto humidity_map = generate_humidity_map(chunk_x, chunk_y); + auto biome_map = generate_biome_map(landmass_map, hill_map, temperature_map, humidity_map, chunk_x, chunk_y); + auto terrain_map = generate_terrain(height_map, chunk_x, chunk_y); + + decorate_soil(chunk, biome_map, terrain_map); + + chunk.set_details({landmass_map, hill_map, temperature_map, humidity_map, biome_map}); + + return chunk; +} + +#define SIMPLE_MAP_GENERATOR(name, getter) \ +Generator::Map2D Generator::name(int64_t chunk_x, int64_t chunk_y) { \ + Matrix map{}; \ + for (uint x = 0; x < Chunk::Width; x++) { \ + for (uint y = 0; y < Chunk::Width; y++) { \ + auto pos = chunk_position_to_world_vector(chunk_x, chunk_y, x, y); \ + map(x, y) = getter(pos); \ + } \ + } \ + return map; \ +} + +SIMPLE_MAP_GENERATOR(generate_landmass_map, get_landmass) +SIMPLE_MAP_GENERATOR(generate_hill_map, get_hill) + +Generator::Map2D Generator::generate_height_map(Map2D& landmass_map, Map2D& hill_map, int64_t chunk_x, int64_t chunk_y) { + Map2D height_map{}; + + for (uint x = 0; x < Chunk::Width; x++) { + for (uint y = 0; y < Chunk::Width; y++) { + auto landmass_effect = landmass_map(x, y); + auto hill_effect = hill_map(x, y); + + auto hill = hill_effect * landmass_effect * 1.4 - 0.4; + auto landmass = landmass_effect * 0.6 + 0.4; + + auto height = (hill + landmass) / 2.0f; + + height_map(x, y) = height; + } + } + + return height_map; +} + +SIMPLE_MAP_GENERATOR(generate_temperature_map, get_temperature) +SIMPLE_MAP_GENERATOR(generate_humidity_map, get_humidity) + +Generator::Map2D Generator::generate_biome_map( + Map2D& landmass_map, Map2D& hill_map, + Map2D& temperature_map, Map2D& humidity_map, + int64_t chunk_x, int64_t chunk_y +) { + Map2D biome_map{}; + + for (uint x = 0; x < Chunk::Width; x++) { + for (uint y = 0; y < Chunk::Width; y++) { + float landmass = landmass_map(x, y); + float hill = hill_map(x, y); + + float temperature = temperature_map(x, y); + float humidity = humidity_map(x, y); + + HillSlice hill_slice; + if (hill > 0.55) { hill_slice = HillSlice::Mountain; } + else if (hill > 0.03) { hill_slice = HillSlice::Middle; } + else { hill_slice = HillSlice::Valley; } + + LandmassSlice landmass_slice; + if (landmass > 0.8) { landmass_slice = LandmassSlice::Land; } + else if (landmass > 0.45) { landmass_slice = LandmassSlice::Beach; } + else { landmass_slice = LandmassSlice::Ocean; } + + TemperatureZone temparature_zone; + if (temperature > 0.6) { temparature_zone = TemperatureZone::Hot; } + else if (temperature > 0.4) { temparature_zone = TemperatureZone::Fair; } + else { temparature_zone = TemperatureZone::Cold; } + + HumidityZone humidity_zone; + if (humidity > 0.66) { humidity_zone = HumidityZone::Wet; } + else if (humidity > 0.33) { humidity_zone = HumidityZone::Temperate; } + else { humidity_zone = HumidityZone::Dry; } + + auto biome = lookup_biome(hill_slice, landmass_slice, temparature_zone, humidity_zone); + biome_map(x, y) = biome; + } + } + + return biome_map; +} + +Generator::Map3D Generator::generate_terrain(Map2D& height_map, int64_t chunk_x, int64_t chunk_y) { + float jaggedness = 0.10f; + + Map3D terrain_map{}; + for (uint x = 0; x < Chunk::Width; x++) { + for (uint z = 0; z < Chunk::Width; z++) { + auto height = height_map(x, z); + + for (uint y = 0; y < Chunk::Height; y++) { + float density = get_density({Chunk::Width * chunk_x + (float)x, (float)y, Chunk::Width * chunk_y + (float)z}); + float threshold = Math::sigmoid(((float)y / (Chunk::Height * height * 2) - 0.5f) / jaggedness); + + if (density > threshold) { + terrain_map(x, y, z) = true; + } + } + } + } + + return terrain_map; +} + +void Generator::decorate_soil(Chunk& chunk, Map2D& biome_map, Map3D& terrain_map) { + constexpr uint dirt_depth = 4; + constexpr uint water_height = 40; + + for (uint x = 0; x < Chunk::Width; x++) { + for (uint z = 0; z < Chunk::Width; z++) { + auto biome = biome_map(x, z); + + BlockType top_block{}; + BlockType soil_block{}; + switch (biome) { + case BiomeType::Beach: + case BiomeType::Desert: + top_block = BlockType::Sand; + soil_block = BlockType::Sand; + break; + case BiomeType::Jungle: + case BiomeType::Forest: + case BiomeType::Plains: + case BiomeType::Ocean: + case BiomeType::River: + top_block = BlockType::Grass; + soil_block = BlockType::Dirt; + break; + case BiomeType::Alpine: + top_block = BlockType::Snow; + soil_block = BlockType::Dirt; + break; + case BiomeType::Shore: + case BiomeType::RockyPeaks: + top_block = BlockType::Stone; + soil_block = BlockType::Stone; + } + + auto column_depth = 0; + for (uint y = Chunk::Height - 1; y > 0; y--) { + auto block = terrain_map(x, y, z); + if (block) { + if (column_depth == 0 && y >= water_height - 1) { + chunk.set(x, y, z, {top_block}); + } else if (column_depth < dirt_depth) { + chunk.set(x, y, z, {soil_block}); + } else { + chunk.set(x, y, z, {BlockType::Stone}); + } + column_depth++; + } else { + if (y < water_height) { + chunk.set(x, y, z, {BlockType::Water}); + } + column_depth = 0; + } + } + } + } + + for (auto& decorator : s_decorators) { + decorator->decorate_chunk(chunk); + } +} + +#define CURVE_START(y) constexpr auto lerp = Math::linear_interpolation; float _py = y; float _px = 0.0f; +#define CURVE_POINT(x, y) if (v < x) return lerp({_py, y}, _px, x, v); _py = y; _px = x +#define CURVE_END(y) return lerp({_py, y}, _px, 1.0f, v); + +float Generator::get_landmass(Vector<2> pos) const { + auto v = m_landmass_noise.at(pos); + + CURVE_START(1.0f); + CURVE_POINT(0.1f, 0.8f); + CURVE_POINT(0.3f, 0.8f); + CURVE_POINT(0.37f, 0.125f); + CURVE_POINT(0.4f, 0.4f); + CURVE_POINT(0.46f, 0.45f); + CURVE_POINT(0.47f, 0.875f); + CURVE_POINT(0.48f, 0.9f); + CURVE_POINT(0.55f, 0.95f); + CURVE_END(1.0f); +} + +float Generator::get_hill(Vector<2> pos) const { + auto v = m_hill_noise.at(pos); + + CURVE_START(0.33f); + CURVE_POINT(0.25f, 1.0f); + + CURVE_POINT(0.49f, 0.1f); + CURVE_POINT(0.5f, 0.0f); + CURVE_POINT(0.51f, 0.1f); + + CURVE_POINT(0.75f, 1.0f); + CURVE_END(0.33f); +} + +float Generator::get_humidity(Vector<2> pos) const { + auto v = m_humidity_noise.at(pos); + return v; +} + +float Generator::get_temperature(Vector<2> pos) const { + auto v = m_temperature_noise.at(pos); + return v; +} + +float Generator::get_density(Vector<3> pos) const { + auto v = m_density_noise.at(pos); + return v; +} + +Vector<2> Generator::chunk_position_to_world_vector(int64_t chunk_x, int64_t chunk_y, uint x, uint y) { + return {(float)x + chunk_x * Chunk::Width, (float)y + chunk_y * Chunk::Width}; +} + +std::array Generator::create_biome_lookup_table() { + std::array table{}; + + auto inc = [](auto& x) { x = (std::remove_reference_t)((uint)x + 1); }; + auto cmp = [](auto x, auto s) { return (uint)x < (uint)s; }; + + for (HillSlice hill_slice{}; cmp(hill_slice, HillSliceSize); inc(hill_slice)) { + for (LandmassSlice landmass_slice{}; cmp(landmass_slice, LandmassSliceSize); inc(landmass_slice)) { + for (TemperatureZone temperature_zone{}; cmp(temperature_zone, TemperatureZoneSize); inc(temperature_zone)) { + for (HumidityZone humidity_zone{}; cmp(humidity_zone, HumidityZoneSize); inc(humidity_zone)) { + BiomeType biome{}; + if (landmass_slice == LandmassSlice::Ocean) { + biome = BiomeType::Ocean; + goto set; + } + if (landmass_slice == LandmassSlice::Beach) { + if (temperature_zone == TemperatureZone::Cold) { + biome = BiomeType::Shore; + } else { + biome = BiomeType::Beach; + } + goto set; + } + + if (hill_slice == HillSlice::Valley) { + biome = BiomeType::River; + goto set; + } + if (hill_slice == HillSlice::Mountain) { + if (temperature_zone == TemperatureZone::Cold) { + biome = BiomeType::Alpine; + } else { + biome = BiomeType::RockyPeaks; + } + goto set; + } + + switch (temperature_zone) { + case TemperatureZone::Hot: + biome = BiomeType::Desert; + break; + case TemperatureZone::Fair: + switch (humidity_zone) { + case HumidityZone::Wet: + biome = BiomeType::Jungle; + break; + case HumidityZone::Lush: + biome = BiomeType::Forest; + break; + case HumidityZone::Temperate: + case HumidityZone::Dry: + biome = BiomeType::Plains; + break; + } + break; + case TemperatureZone::Cold: + switch (humidity_zone) { + case HumidityZone::Wet: + case HumidityZone::Lush: + biome = BiomeType::Alpine; + break; + case HumidityZone::Temperate: + case HumidityZone::Dry: + biome = BiomeType::Plains; + break; + } + break; + } + + set: + table[biome_lookup_table_index(hill_slice, landmass_slice, temperature_zone, humidity_zone)] = biome; + } + } + } + } + + return table; +} + +size_t Generator::biome_lookup_table_index(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone) { + auto hs = (uint8_t)hill_slice; auto ls = (uint8_t)landmass_slice; auto tz = (uint8_t)temperature_zone; auto hz = (uint8_t)humidity_zone; + auto LS_S = (uint8_t)LandmassSliceSize; auto TZ_S = (uint8_t)TemperatureZoneSize; auto HZ_S = (uint8_t)HumidityZoneSize; + return (hs * LS_S * TZ_S * HZ_S) + (ls * TZ_S * HZ_S) + (tz * HZ_S) + hz; +} + +BiomeType Generator::lookup_biome(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone) { + return biome_lookup_table.at(biome_lookup_table_index(hill_slice, landmass_slice, temperature_zone, humidity_zone)); +} + +} diff --git a/src/World/Generation/Generator.hpp b/src/World/Generation/Generator.hpp new file mode 100644 index 0000000..464e36f --- /dev/null +++ b/src/World/Generation/Generator.hpp @@ -0,0 +1,76 @@ +#pragma once + +#include + +#include "Decoration.hpp" +#include "../Chunk.hpp" +#include "../BiomeType.hpp" +#include "../../Math/Perlin.hpp" +#include "../../Math/Tensor.hpp" + +namespace MC::World::Generation { + +class Generator { +public: + Generator() = default; + Chunk generate(int64_t chunk_x, int64_t chunk_y); +private: + template using Map2D = Matrix; + template using Map3D = Tensor<3, V, Chunk::Width, Chunk::Height, Chunk::Width>; + + Map2D generate_landmass_map(int64_t chunk_x, int64_t chunk_y); + Map2D generate_hill_map(int64_t chunk_x, int64_t chunk_y); + Map2D generate_height_map(Map2D& landmass_map, Map2D& hill_map, int64_t chunk_x, int64_t chunk_y); + + Map2D generate_temperature_map(int64_t chunk_x, int64_t chunk_y); + Map2D generate_humidity_map(int64_t chunk_x, int64_t chunk_y); + + Map2D generate_biome_map( + Map2D& landmass_map, Map2D& hill_map, + Map2D& temperature_map, Map2D& humidity_map, + int64_t chunk_x, int64_t chunk_y + ); + + Map3D generate_terrain(Map2D& height_map, int64_t chunk_x, int64_t chunk_y); + + void decorate_soil(Chunk& chunk, Map2D& biome_map, Map3D& terrain_map); + + [[nodiscard]] float get_landmass(Vector<2> pos) const; + [[nodiscard]] float get_hill(Vector<2> pos) const; + + [[nodiscard]] float get_humidity(Vector<2> pos) const; + [[nodiscard]] float get_temperature(Vector<2> pos) const; + + [[nodiscard]] float get_density(Vector<3> pos) const; + + static Vector<2> chunk_position_to_world_vector(int64_t chunk_x, int64_t chunk_y, uint x, uint y); + + static inline std::vector s_decorators = { + new TreeDecorator(), + }; + + Math::Perlin::Noise<2> m_landmass_noise{.scale=800.0f, .octaves=4, .persistence=0.3f, .lacunarity=3.5f}; + Math::Perlin::Noise<2> m_hill_noise{.scale=400.0f, .octaves=3, .persistence=0.5f, .lacunarity=2.0f}; + + Math::Perlin::Noise<2> m_temperature_noise{.scale=700.0f, .octaves=4, .persistence=0.4f, .lacunarity=3.0f}; + Math::Perlin::Noise<2> m_humidity_noise{.scale=400.0f, .octaves=2, .persistence=0.4f, .lacunarity=3.0f}; + + Math::Perlin::Noise<3> m_density_noise{.scale=30.0f, .octaves=2, .persistence=0.7f, .lacunarity=2.0f}; + + enum class HillSlice { Mountain, Middle, Valley }; + enum class LandmassSlice { Land, Beach, Ocean }; + enum class TemperatureZone { Hot, Fair, Cold }; + enum class HumidityZone { Wet, Lush, Temperate, Dry }; + static constexpr uint HillSliceSize = (uint)HillSlice::Valley + 1; + static constexpr uint LandmassSliceSize = (uint)LandmassSlice::Ocean + 1; + static constexpr uint TemperatureZoneSize = (uint)TemperatureZone::Cold + 1; + static constexpr uint HumidityZoneSize = (uint)HumidityZone::Dry + 1; + + static constexpr size_t biome_lookup_table_size = (size_t)HillSliceSize * (size_t)LandmassSliceSize * (size_t)TemperatureZoneSize * (size_t)HumidityZoneSize; + static std::array create_biome_lookup_table(); + static size_t biome_lookup_table_index(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone); + static BiomeType lookup_biome(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone); + static inline std::array biome_lookup_table = create_biome_lookup_table(); +}; + +} diff --git a/src/World/Generator.cpp b/src/World/Generator.cpp deleted file mode 100644 index ada2b7b..0000000 --- a/src/World/Generator.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "Generator.hpp" -#include "../Math/Interpolation.hpp" -#include "../Math/Sigmoid.hpp" - -namespace MC::World { - -Chunk Generator::generate(int64_t chunk_x, int64_t chunk_y) { - Chunk chunk(chunk_x, chunk_y); - - auto landmass_map = generate_landmass_map(chunk_x, chunk_y); - auto hill_map = generate_hill_map(chunk_x, chunk_y); - auto height_map = generate_height_map(landmass_map, hill_map, chunk_x, chunk_y); - - auto biome_map = generate_biome_map(landmass_map, hill_map, height_map, chunk_x, chunk_y); - auto terrain_map = generate_terrain(height_map, chunk_x, chunk_y); - - decorate_soil(chunk, biome_map, terrain_map); - - chunk.set_details({{landmass_map}, {hill_map}, {biome_map}}); - - return chunk; -} - -Generator::Map2D Generator::generate_landmass_map(int64_t chunk_x, int64_t chunk_y) { - Matrix landmass_map{}; - - for (uint x = 0; x < Chunk::Width; x++) { - for (uint y = 0; y < Chunk::Width; y++) { - auto world_pos = chunk_position_to_world_vector(chunk_x, chunk_y, x, y); - landmass_map(x, y) = get_landmass(world_pos); - } - } - - return landmass_map; -} - -Generator::Map2D Generator::generate_hill_map(int64_t chunk_x, int64_t chunk_y) { - Map2D hill_map{}; - - for (uint x = 0; x < Chunk::Width; x++) { - for (uint y = 0; y < Chunk::Width; y++) { - hill_map(x, y) = get_hill(chunk_position_to_world_vector(chunk_x, chunk_y, x, y)); - } - } - - return hill_map; -} - -Generator::Map2D Generator::generate_height_map(Map2D& landmass_map, Map2D& hill_map, int64_t chunk_x, int64_t chunk_y) { - Map2D height_map{}; - - for (uint x = 0; x < Chunk::Width; x++) { - for (uint y = 0; y < Chunk::Width; y++) { - auto landmass_effect = landmass_map(x, y); - auto hill_effect = hill_map(x, y); - - auto hill = hill_effect * landmass_effect * 1.4 - 0.4; - auto landmass = landmass_effect * 0.6 + 0.4; - - auto height = (hill + landmass) / 2.0f; - - height_map(x, y) = height; - } - } - - return height_map; -} - -Generator::Map2D Generator::generate_biome_map(Map2D& landmass_map, Map2D& hill_map, Map2D& height_map, int64_t chunk_x, int64_t chunk_y) { - Map2D biome_map{}; - - for (uint x = 0; x < Chunk::Width; x++) { - for (uint y = 0; y < Chunk::Width; y++) { - float landmass = landmass_map(x, y); - float hill = hill_map(x, y); - - auto world_pos = chunk_position_to_world_vector(chunk_x, chunk_y, x, y); - float temperature = get_temperature(world_pos); - float humidity = get_humidity(world_pos); - - HillSlice hill_slice; - if (hill > 0.9) { hill_slice = HillSlice::Mountain; } - else if (hill > 0.33) { hill_slice = HillSlice::Middle; } - else { hill_slice = HillSlice::Valley; } - - LandmassSlice landmass_slice; - if (landmass > 0.8) { landmass_slice = LandmassSlice::Land; } - else if (landmass > 0.45) { landmass_slice = LandmassSlice::Beach; } - else { landmass_slice = LandmassSlice::Ocean; } - - TemperatureZone temparature_zone; - if (temperature > 0.66) { temparature_zone = TemperatureZone::Hot; } - else if (temperature > 0.33) { temparature_zone = TemperatureZone::Fair; } - else { temparature_zone = TemperatureZone::Cold; } - - HumidityZone humidity_zone; - if (humidity > 0.66) { humidity_zone = HumidityZone::Wet; } - else if (humidity > 0.33) { humidity_zone = HumidityZone::Temperate; } - else { humidity_zone = HumidityZone::Dry; } - - auto biome = lookup_biome(hill_slice, landmass_slice, temparature_zone, humidity_zone); - biome_map(x, y) = biome; - } - } - - return biome_map; -} - -Generator::Map3D Generator::generate_terrain(Map2D& height_map, int64_t chunk_x, int64_t chunk_y) { - float jaggedness = 0.10f; - - Map3D terrain_map{}; - for (uint x = 0; x < Chunk::Width; x++) { - for (uint z = 0; z < Chunk::Width; z++) { - auto height = height_map(x, z); - - for (uint y = 0; y < Chunk::Height; y++) { - float density = get_density({Chunk::Width * chunk_x + (float)x, (float)y, Chunk::Width * chunk_y + (float)z}); - float threshold = Math::sigmoid(((float)y / (Chunk::Height * height * 2) - 0.5f) / jaggedness); - - if (density > threshold) { - terrain_map(x, y, z) = true; - } - } - } - } - - return terrain_map; -} - -void Generator::decorate_soil(Chunk& chunk, Map2D& biome_map, Map3D& terrain_map) { - constexpr uint dirt_depth = 4; - constexpr uint water_height = 39; - - for (uint x = 0; x < Chunk::Width; x++) { - for (uint z = 0; z < Chunk::Width; z++) { - auto biome = biome_map(x, z); - - BlockType top_block{}; - BlockType soil_block{}; - switch (biome) { - case BiomeType::Beach: - case BiomeType::Desert: - top_block = BlockType::Sand; - soil_block = BlockType::Sand; - break; - case BiomeType::Jungle: - case BiomeType::Forest: - case BiomeType::Plains: - case BiomeType::River: - case BiomeType::Ocean: - top_block = BlockType::Grass; - soil_block = BlockType::Dirt; - break; - case BiomeType::Alpine: - top_block = BlockType::Snow; - soil_block = BlockType::Dirt; - break; - } - - auto column_depth = 0; - for (uint y = Chunk::Height - 1; y > 0; y--) { - auto block = terrain_map(x, y, z); - if (block) { - if (column_depth == 0 && y >= water_height - 1) { - chunk.set(x, y, z, {top_block}); - } else if (column_depth < dirt_depth) { - chunk.set(x, y, z, {soil_block}); - } else { - chunk.set(x, y, z, {BlockType::Stone}); - } - column_depth++; - } else { - if (y < water_height) { - chunk.set(x, y, z, {BlockType::Water}); - } - column_depth = 0; - } - } - } - } -} - -#define CURVE_START(y) constexpr auto lerp = Math::linear_interpolation; float _py = y; float _px = 0.0f; -#define CURVE_POINT(x, y) if (v < x) return lerp({_py, y}, _px, x, v); _py = y; _px = x -#define CURVE_END(y) return lerp({_py, y}, _px, 1.0f, v); - -float Generator::get_landmass(Vector<2> pos) const { - auto v = m_landmass_noise.at(pos); - - CURVE_START(1.0f); - CURVE_POINT(0.1f, 0.8f); - CURVE_POINT(0.3f, 0.8f); - CURVE_POINT(0.37f, 0.125f); - CURVE_POINT(0.4f, 0.4f); - CURVE_POINT(0.46f, 0.45f); - CURVE_POINT(0.47f, 0.875f); - CURVE_POINT(0.48f, 0.9f); - CURVE_POINT(0.55f, 0.95f); - CURVE_END(1.0f); -} - -float Generator::get_hill(Vector<2> pos) const { - auto v = m_hill_noise.at(pos); - - CURVE_START(0.33f); - CURVE_POINT(0.25f, 1.0f); - - CURVE_POINT(0.49f, 0.1f); - CURVE_POINT(0.5f, 0.0f); - CURVE_POINT(0.51f, 0.1f); - - CURVE_POINT(0.75f, 1.0f); - CURVE_END(0.33f); -} - -float Generator::get_humidity(Vector<2> pos) const { - auto v = m_humidity_noise.at(pos); - return v; -} - -float Generator::get_temperature(Vector<2> pos) const { - auto v = m_temperature_noise.at(pos); - return v; -} - -float Generator::get_density(Vector<3> pos) const { - auto v = m_density_noise.at(pos); - return v; -} - -Vector<2> Generator::chunk_position_to_world_vector(int64_t chunk_x, int64_t chunk_y, uint x, uint y) { - return {(float)x + chunk_x * Chunk::Width, (float)y + chunk_y * Chunk::Width}; -} - -std::array Generator::create_biome_lookup_table() { - std::array table{}; - - auto inc = [](auto& x) { x = (std::remove_reference_t)((uint)x + 1); }; - auto cmp = [](auto x, auto s) { return (uint)x < (uint)s; }; - - for (HillSlice hill_slice{}; cmp(hill_slice, HillSliceSize); inc(hill_slice)) { - for (LandmassSlice landmass_slice{}; cmp(landmass_slice, LandmassSliceSize); inc(landmass_slice)) { - for (TemperatureZone temperature_zone{}; cmp(temperature_zone, TemperatureZoneSize); inc(temperature_zone)) { - for (HumidityZone humidity_zone{}; cmp(humidity_zone, HumidityZoneSize); inc(humidity_zone)) { - BiomeType biome{}; - if (landmass_slice == LandmassSlice::Ocean) { - biome = BiomeType::Ocean; - goto set; - } - if (landmass_slice == LandmassSlice::Beach) { - biome = BiomeType::Beach; - goto set; - } - - if (hill_slice == HillSlice::Valley) { - biome = BiomeType::River; - goto set; - } - if (hill_slice == HillSlice::Mountain) { - if (temperature_zone == TemperatureZone::Hot) { - biome = BiomeType::Desert; - } else { - biome = BiomeType::Alpine; - } - goto set; - } - - switch (temperature_zone) { - case TemperatureZone::Hot: - biome = BiomeType::Desert; - break; - case TemperatureZone::Fair: - switch (humidity_zone) { - case HumidityZone::Wet: - biome = BiomeType::Jungle; - break; - case HumidityZone::Lush: - biome = BiomeType::Forest; - break; - case HumidityZone::Temperate: - case HumidityZone::Dry: - biome = BiomeType::Plains; - break; - } - break; - case TemperatureZone::Cold: - switch (humidity_zone) { - case HumidityZone::Wet: - case HumidityZone::Lush: - biome = BiomeType::Alpine; - break; - case HumidityZone::Temperate: - case HumidityZone::Dry: - biome = BiomeType::Plains; - break; - } - break; - } - - set: - table[biome_lookup_table_index(hill_slice, landmass_slice, temperature_zone, humidity_zone)] = biome; - } - } - } - } - - return table; -} - -size_t Generator::biome_lookup_table_index(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone) { - auto hs = (uint8_t)hill_slice; auto ls = (uint8_t)landmass_slice; auto tz = (uint8_t)temperature_zone; auto hz = (uint8_t)humidity_zone; - auto LS_S = (uint8_t)LandmassSliceSize; auto TZ_S = (uint8_t)TemperatureZoneSize; auto HZ_S = (uint8_t)HumidityZoneSize; - return (hs * LS_S * TZ_S * HZ_S) + (ls * TZ_S * HZ_S) + (tz * HZ_S) + hz; -} - -BiomeType Generator::lookup_biome(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone) { - return biome_lookup_table.at(biome_lookup_table_index(hill_slice, landmass_slice, temperature_zone, humidity_zone)); -} - -} diff --git a/src/World/Generator.hpp b/src/World/Generator.hpp deleted file mode 100644 index a90d1bc..0000000 --- a/src/World/Generator.hpp +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include "Chunk.hpp" -#include "BiomeType.hpp" -#include "../Math/Perlin.hpp" -#include "../Math/Tensor.hpp" - -namespace MC::World { - -class Generator { -public: - Generator() = default; - Chunk generate(int64_t chunk_x, int64_t chunk_y); -private: - template using Map2D = Matrix; - template using Map3D = Tensor<3, V, Chunk::Width, Chunk::Height, Chunk::Width>; - - Map2D generate_landmass_map(int64_t chunk_x, int64_t chunk_y); - Map2D generate_hill_map(int64_t chunk_x, int64_t chunk_y); - Map2D generate_height_map(Map2D& landmass_map, Map2D& hill_map, int64_t chunk_x, int64_t chunk_y); - - Map2D generate_biome_map(Map2D& landmass_map, Map2D& hill_map, Map2D& height_map, int64_t chunk_x, int64_t chunk_y); - - Map3D generate_terrain(Map2D& height_map, int64_t chunk_x, int64_t chunk_y); - - void decorate_soil(Chunk& chunk, Map2D& biome_map, Map3D& terrain_map); - - [[nodiscard]] float get_landmass(Vector<2> pos) const; - [[nodiscard]] float get_hill(Vector<2> pos) const; - - [[nodiscard]] float get_humidity(Vector<2> pos) const; - [[nodiscard]] float get_temperature(Vector<2> pos) const; - - [[nodiscard]] float get_density(Vector<3> pos) const; - - static Vector<2> chunk_position_to_world_vector(int64_t chunk_x, int64_t chunk_y, uint x, uint y); - - Math::Perlin::Noise<2> m_landmass_noise{.scale=800.0f, .octaves=4, .persistence=0.3f, .lacunarity=3.5f}; - Math::Perlin::Noise<2> m_hill_noise{.scale=400.0f, .octaves=3, .persistence=0.5f, .lacunarity=2.0f}; - - Math::Perlin::Noise<2> m_temperature_noise{.scale=700.0f, .octaves=3, .persistence=0.5f, .lacunarity=2.0f}; - Math::Perlin::Noise<2> m_humidity_noise{.scale=400.0f, .octaves=2, .persistence=0.5f, .lacunarity=2.0f}; - - Math::Perlin::Noise<3> m_density_noise{.scale=30.0f, .octaves=2, .persistence=0.7f, .lacunarity=2.0f}; - - enum class HillSlice { Mountain, Middle, Valley }; - enum class LandmassSlice { Land, Beach, Ocean }; - enum class TemperatureZone { Hot, Fair, Cold }; - enum class HumidityZone { Wet, Lush, Temperate, Dry }; - static constexpr uint HillSliceSize = (uint)HillSlice::Valley + 1; - static constexpr uint LandmassSliceSize = (uint)LandmassSlice::Ocean + 1; - static constexpr uint TemperatureZoneSize = (uint)TemperatureZone::Cold + 1; - static constexpr uint HumidityZoneSize = (uint)HumidityZone::Dry + 1; - - static constexpr size_t biome_lookup_table_size = (size_t)HillSliceSize * (size_t)LandmassSliceSize * (size_t)TemperatureZoneSize * (size_t)HumidityZoneSize; - static std::array create_biome_lookup_table(); - static size_t biome_lookup_table_index(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone); - static BiomeType lookup_biome(HillSlice hill_slice, LandmassSlice landmass_slice, TemperatureZone temperature_zone, HumidityZone humidity_zone); - static inline std::array biome_lookup_table = create_biome_lookup_table(); -}; - -} diff --git a/src/World/World.cpp b/src/World/World.cpp index 289d86b..6e8489a 100644 --- a/src/World/World.cpp +++ b/src/World/World.cpp @@ -1,5 +1,5 @@ #include "World.hpp" -#include "ChunkMeshing.hpp" +#include "Generation/ChunkMeshing.hpp" namespace MC::World { @@ -127,7 +127,7 @@ void World::try_to_create_mesh_for_chunk(ChunkData& data) { return; } - data.mesh_data = ChunkMeshing::create_mesh_for_chunk( + data.mesh_data = Generation::ChunkMeshing::create_mesh_for_chunk( data.chunk.value(), {north.chunk.value(), east.chunk.value(), south.chunk.value(), west.chunk.value()} ); diff --git a/src/World/World.hpp b/src/World/World.hpp index e6b3cd2..ae8b37f 100644 --- a/src/World/World.hpp +++ b/src/World/World.hpp @@ -3,7 +3,7 @@ #include #include #include -#include "Generator.hpp" +#include "Generation/Generator.hpp" #include "ChunkIndex.hpp" #include "../GFX/Binder.hpp" #include "../Compute/Queue.hpp" @@ -53,7 +53,7 @@ private: uint64_t generation_duration; }; Compute::Queue m_queue; - Generator m_generator; + Generation::Generator m_generator; std::unordered_map m_chunks; std::unordered_set m_visible_chunks; -- cgit 1.4.1