├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── README.md ├── Res ├── Blocks │ ├── Air.block │ ├── Cactus.block │ ├── CobbleStone │ ├── DeadShrub.block │ ├── Dirt.block │ ├── Glass.block │ ├── GlassBorderless.block │ ├── Grass.block │ ├── OakBark.block │ ├── OakLeaf.block │ ├── OakPlank.block │ ├── OakSapling.block │ ├── Rose.block │ ├── Sand.block │ ├── Stone.block │ ├── TallGrass.block │ └── Water.block ├── Fonts │ └── rs.ttf ├── Textures │ ├── DefaultPack.png │ ├── DefaultPack2.png │ ├── back.png │ ├── bottom.png │ ├── bottomsb.png │ ├── ch.png │ ├── db.png │ ├── dm.png │ ├── dt.png │ ├── front.png │ ├── left.png │ ├── middlesb.png │ ├── right.png │ ├── test.png │ ├── top.png │ └── topsb.png └── info.txt ├── Shaders ├── Basic.frag ├── Basic.vert ├── Chunk.frag ├── Chunk.vert ├── Flora.vert ├── Skybox.frag ├── Skybox.vert └── Water.vert ├── Source ├── Application.cpp ├── Application.h ├── Camera.cpp ├── Camera.h ├── Config.h ├── Context.cpp ├── Context.h ├── Controller.cpp ├── Controller.h ├── Entity.h ├── GL │ ├── GLFunctions.cpp │ └── GLFunctions.h ├── Input │ ├── Keyboard.cpp │ ├── Keyboard.h │ ├── ToggleKey.cpp │ └── ToggleKey.h ├── Item │ ├── ItemStack.cpp │ ├── ItemStack.h │ ├── ItemType.h │ ├── Material.cpp │ └── Material.h ├── Main.cpp ├── Maths │ ├── Frustum.cpp │ ├── Frustum.h │ ├── GeneralMaths.cpp │ ├── GeneralMaths.h │ ├── Matrix.cpp │ ├── Matrix.h │ ├── NoiseGenerator.cpp │ ├── NoiseGenerator.h │ ├── Ray.cpp │ ├── Ray.h │ ├── Vector2XZ.cpp │ ├── Vector2XZ.h │ └── glm.h ├── Mesh.h ├── Model.cpp ├── Model.h ├── Physics │ └── AABB.h ├── Player │ ├── Player.cpp │ └── Player.h ├── Renderer │ ├── ChunkRenderer.cpp │ ├── ChunkRenderer.h │ ├── FloraRenderer.cpp │ ├── FloraRenderer.h │ ├── RenderInfo.h │ ├── RenderMaster.cpp │ ├── RenderMaster.h │ ├── SkyboxRenderer.cpp │ ├── SkyboxRenderer.h │ ├── WaterRenderer.cpp │ └── WaterRenderer.h ├── Shaders │ ├── BasicShader.cpp │ ├── BasicShader.h │ ├── ChunkShader.cpp │ ├── ChunkShader.h │ ├── FloraShader.cpp │ ├── FloraShader.h │ ├── Shader.cpp │ ├── Shader.h │ ├── ShaderLoader.cpp │ ├── ShaderLoader.h │ ├── SkyboxShader.cpp │ ├── SkyboxShader.h │ ├── WaterShader.cpp │ └── WaterShader.h ├── States │ ├── PlayState.cpp │ ├── PlayState.h │ └── StateBase.h ├── Texture │ ├── BasicTexture.cpp │ ├── BasicTexture.h │ ├── CubeTexture.cpp │ ├── CubeTexture.h │ ├── TextureAtlas.cpp │ └── TextureAtlas.h ├── Util │ ├── Array2D.h │ ├── FPSCounter.cpp │ ├── FPSCounter.h │ ├── FileUtil.cpp │ ├── FileUtil.h │ ├── NonCopyable.h │ ├── NonMovable.h │ ├── Random.cpp │ ├── Random.h │ └── Singleton.h └── World │ ├── Block │ ├── BlockData.cpp │ ├── BlockData.h │ ├── BlockDatabase.cpp │ ├── BlockDatabase.h │ ├── BlockId.h │ ├── BlockTypes │ │ ├── BlockType.cpp │ │ └── BlockType.h │ ├── ChunkBlock.cpp │ └── ChunkBlock.h │ ├── Chunk │ ├── Chunk.cpp │ ├── Chunk.h │ ├── ChunkManager.cpp │ ├── ChunkManager.h │ ├── ChunkMesh.cpp │ ├── ChunkMesh.h │ ├── ChunkMeshBuilder.cpp │ ├── ChunkMeshBuilder.h │ ├── ChunkSection.cpp │ ├── ChunkSection.h │ └── IChunk.h │ ├── Event │ ├── IWorldEvent.h │ ├── PlayerDigEvent.cpp │ └── PlayerDigEvent.h │ ├── Generation │ ├── Biome │ │ ├── Biome.cpp │ │ ├── Biome.h │ │ ├── DesertBiome.cpp │ │ ├── DesertBiome.h │ │ ├── GrasslandBiome.cpp │ │ ├── GrasslandBiome.h │ │ ├── LightForest.cpp │ │ ├── LightForest.h │ │ ├── OceanBiome.cpp │ │ ├── OceanBiome.h │ │ ├── TemperateForestBiome.cpp │ │ └── TemperateForestBiome.h │ ├── Structures │ │ ├── StructureBuilder.cpp │ │ ├── StructureBuilder.h │ │ ├── TreeGenerator.cpp │ │ └── TreeGenerator.h │ └── Terrain │ │ ├── ClassicOverWorldGenerator.cpp │ │ ├── ClassicOverWorldGenerator.h │ │ ├── SuperFlatGenerator.cpp │ │ ├── SuperFlatGenerator.h │ │ └── TerrainGenerator.h │ ├── World.cpp │ ├── World.h │ └── WorldConstants.h ├── config.txt ├── deps ├── CMakeLists.txt └── glad │ ├── CMakeLists.txt │ ├── glad.c │ ├── glad.h │ └── khrplatform.h ├── mc-one-week-challenge.sln ├── mc-one-week-challenge.vcxproj ├── scripts ├── build.sh ├── debug.sh └── run.sh ├── vcpkg-configuration.json └── vcpkg.json /.clang-format: -------------------------------------------------------------------------------- 1 | Language: Cpp 2 | BasedOnStyle: LLVM 3 | IndentWidth: 4 4 | BreakBeforeBraces: Allman 5 | FixNamespaceComments: true 6 | IndentCaseLabels: true 7 | NamespaceIndentation: All 8 | BreakConstructorInitializersBeforeComma: true 9 | ColumnLimit: 100 10 | AlwaysBreakTemplateDeclarations: true 11 | PointerAlignment: Left 12 | AllowShortFunctionsOnASingleLine: None 13 | 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Linux 2 | build/ 3 | 4 | # Windows 5 | *.dll 6 | *.exe 7 | *.filters 8 | *.user 9 | *.vs 10 | .vscode 11 | Debug/ 12 | Release/ 13 | 14 | # Misc 15 | *.zip 16 | *.temp 17 | *.srctrlprj 18 | *.srctrldb 19 | *.srctrlbm 20 | 21 | # Vcpkg 22 | vcpkg_installed/ 23 | 24 | mc-one-week-challenge/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project( 4 | mc-one-week-challenge 5 | VERSION 1.0 6 | ) 7 | 8 | include("$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") 9 | 10 | add_executable(${PROJECT_NAME} 11 | Source/Item/Material.cpp 12 | Source/Item/ItemStack.cpp 13 | Source/Application.cpp 14 | Source/World/Event/PlayerDigEvent.cpp 15 | Source/World/Block/BlockDatabase.cpp 16 | Source/World/Block/BlockData.cpp 17 | Source/World/Block/ChunkBlock.cpp 18 | Source/World/Block/BlockTypes/BlockType.cpp 19 | Source/World/World.cpp 20 | Source/World/Generation/Biome/LightForest.cpp 21 | Source/World/Generation/Biome/Biome.cpp 22 | Source/World/Generation/Biome/DesertBiome.cpp 23 | Source/World/Generation/Biome/GrasslandBiome.cpp 24 | Source/World/Generation/Biome/TemperateForestBiome.cpp 25 | Source/World/Generation/Biome/OceanBiome.cpp 26 | Source/World/Generation/Structures/TreeGenerator.cpp 27 | Source/World/Generation/Structures/StructureBuilder.cpp 28 | Source/World/Generation/Terrain/SuperFlatGenerator.cpp 29 | Source/World/Generation/Terrain/ClassicOverWorldGenerator.cpp 30 | Source/World/Chunk/ChunkMesh.cpp 31 | Source/World/Chunk/ChunkManager.cpp 32 | Source/World/Chunk/Chunk.cpp 33 | Source/World/Chunk/ChunkSection.cpp 34 | Source/World/Chunk/ChunkMeshBuilder.cpp 35 | Source/States/PlayState.cpp 36 | Source/Player/Player.cpp 37 | Source/Maths/Ray.cpp 38 | Source/Maths/Frustum.cpp 39 | Source/Maths/NoiseGenerator.cpp 40 | Source/Maths/Vector2XZ.cpp 41 | Source/Maths/Matrix.cpp 42 | Source/Maths/GeneralMaths.cpp 43 | Source/Camera.cpp 44 | Source/GL/GLFunctions.cpp 45 | Source/Context.cpp 46 | Source/Texture/CubeTexture.cpp 47 | Source/Texture/BasicTexture.cpp 48 | Source/Texture/TextureAtlas.cpp 49 | Source/Input/ToggleKey.cpp 50 | Source/Input/Keyboard.cpp 51 | Source/Main.cpp 52 | Source/Controller.cpp 53 | Source/Util/Random.cpp 54 | Source/Util/FPSCounter.cpp 55 | Source/Util/FileUtil.cpp 56 | Source/Shaders/FloraShader.cpp 57 | Source/Shaders/WaterShader.cpp 58 | Source/Shaders/SkyboxShader.cpp 59 | Source/Shaders/Shader.cpp 60 | Source/Shaders/ChunkShader.cpp 61 | Source/Shaders/BasicShader.cpp 62 | Source/Shaders/ShaderLoader.cpp 63 | Source/Renderer/RenderMaster.cpp 64 | Source/Renderer/WaterRenderer.cpp 65 | Source/Renderer/ChunkRenderer.cpp 66 | Source/Renderer/SkyboxRenderer.cpp 67 | Source/Renderer/FloraRenderer.cpp 68 | Source/Model.cpp 69 | ) 70 | 71 | target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) 72 | set_target_properties(${PROJECT_NAME} PROPERTIES CXX_EXTENSIONS OFF) 73 | 74 | target_compile_definitions(${PROJECT_NAME} PRIVATE GLM_ENABLE_EXPERIMENTAL) 75 | 76 | if(MSVC) 77 | set(CMAKE_CXX_FLAGS_RELEASE "/O2") 78 | set(CMAKE_CXX_FLAGS_RELEASE "/Ox") 79 | target_compile_options(${PROJECT_NAME} PRIVATE 80 | /W4) 81 | else() 82 | target_compile_options(${PROJECT_NAME} PRIVATE 83 | -Wall -Wextra -pedantic) 84 | endif() 85 | 86 | find_package(glm CONFIG REQUIRED) 87 | find_package(SFML COMPONENTS system audio network window graphics CONFIG REQUIRED) 88 | 89 | add_subdirectory(deps) 90 | target_include_directories( 91 | ${PROJECT_NAME} 92 | PRIVATE 93 | deps 94 | ) 95 | 96 | target_link_libraries(${PROJECT_NAME} PRIVATE 97 | sfml-system sfml-audio sfml-network sfml-graphics sfml-window 98 | glm::glm 99 | glad 100 | ) 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MineCraft-One-Week-Challenge 2 | 3 | I challenged myself to see if I could create Minecraft in just one week... So lets go! 4 | 5 | Video: https://www.youtube.com/watch?v=Xq3isov6mZ8 6 | 7 | Note: I continued to edit after the 7 days, however the version seen in the video is found here https://github.com/Hopson97/MineCraft-One-Week-Challenge/tree/eb01640580cc5ad403f6a8b9fb58af37e2f03f0c 8 | 9 | And the "optimized" version can be found here: https://github.com/Hopson97/MineCraft-One-Week-Challenge/tree/792df07e9780b444be5290fd05a3c8598aacafc8 (~1 week later version) 10 | 11 | There also is a version of this game with very good graphics, and things like a day/night cycle. However, it was causing rendering issues for many people. This version can be found here: 12 | https://github.com/Hopson97/MineCraft-One-Week-Challenge/tree/aa50ad8077ef0e617a9cfc336bdb7db81c313017 13 | 14 | ## Other People's Projects 15 | 16 | This was made in a week, as a challenge for a video. There do exist other, more mature and developed Minecraft clones written in C++. 17 | 18 | MineTest here: https://github.com/minetest/minetest 19 | 20 | ## Building and Running 21 | 22 | ### Windows (Visual Studio) 23 | 24 | The easiest way to build is to use [vcpkg](https://vcpkg.io/en/index.html) and install dependencies through this: 25 | 26 | ```bash 27 | vcpkg install sfml 28 | vcpkg install glm 29 | vcpkg integrate install 30 | ``` 31 | 32 | Then open the Visual Studio project file to build and run. 33 | 34 | ### Linux 35 | 36 | #### Pre-requisites 37 | 38 | Install Vcpkg and other required packages using your distribution's package manager: 39 | 40 | ```sh 41 | git clone https://github.com/microsoft/vcpkg.git 42 | cd vcpkg 43 | ./bootstrap-vcpkg.sh 44 | 45 | # These are required to build some packages 46 | sudo apt install cmake make autoconf libtool pkg-config 47 | 48 | # The following are required for SFML 49 | sudo apt install libx11-dev xorg-dev freeglut3-dev libudev-dev 50 | ``` 51 | 52 | Ensure paths are set correctly: 53 | 54 | ```sh 55 | export VCPKG_ROOT=/path/to/vcpkg 56 | export PATH=$VCPKG_ROOT:$PATH 57 | ``` 58 | 59 | RECOMMENDED: Add the above lines to your `.bashrc` or `.zshrc` file: 60 | 61 | ```sh 62 | echo 'export VCPKG_ROOT=/path/to/vcpkg' >> ~/.bashrc 63 | echo 'export PATH=$VCPKG_ROOT:$PATH' >> ~/.bashrc 64 | ``` 65 | 66 | #### Build and Run 67 | 68 | To build, at the root of the project: 69 | 70 | ```sh 71 | vcpkg install # First time only 72 | sh scripts/build.sh 73 | ``` 74 | 75 | To run, at the root of the project: 76 | 77 | ```sh 78 | sh scripts/run.sh 79 | ``` 80 | 81 | To build and run in release mode, simply add the `release` suffix: 82 | 83 | ```sh 84 | sh scripts/build.sh release 85 | sh scripts/run.sh release 86 | ``` 87 | 88 | ## The Challenge 89 | 90 | ### Day One 91 | 92 | End of day one commit: https://github.com/Hopson97/MineCraft-One-Week-Challenge/tree/44ace72573833796da05a97972be5765b05ce94f 93 | 94 | The first day was spent setting up boilerplate code such as the game state/ game screen system, and the basic rendering engines, starting off with a mere quad. 95 | 96 | The day was finished off by creating a first person camera. 97 | 98 | ![Quad](http://i.imgur.com/fJDgA2a.png) 99 | 100 | End of day stats: 101 | 102 | | Title | Data | 103 | | ---------------------- | ------- | 104 | | Time programming Today | 3:21:51 | 105 | | Lines of Code Today | 829 | 106 | | Total Time programming | 3:21:51 | 107 | | Total Lines of Code | 829 | 108 | 109 | ### Day Two 110 | 111 | End of day two commit: https://github.com/Hopson97/MineCraft-One-Week-Challenge/tree/98055215f735335de80193221a30c0bb8586fba5 112 | 113 | The second day was spent setting up the basic ChunkSection and various block classes. 114 | 115 | I also worked out the coordinates for a cube, and thus created a cube renderer. 116 | 117 | I finished up the day attempting to create a mesh builder for the chunk; however, this did not go well at all, and two had ended before I got it to work correctly. 118 | 119 | ![Messed up chunk](http://i.imgur.com/UsKHJrR.png) 120 | 121 | End of day stats: 122 | 123 | | Title | Data | 124 | | ---------------------- | ------- | 125 | | Time programming Today | 4:16:07 | 126 | | Lines of Code Today | 732 | 127 | | Total Time programming | 7:37:58 | 128 | | Total Lines of Code | 1561 | 129 | 130 | ### Day Three 131 | 132 | End of day three commit: https://github.com/Hopson97/MineCraft-One-Week-Challenge/commit/78bd637581542576372d75cf7638f76381e933b4 133 | 134 | To start the day off, I fixed the chunk drawing. Turns out I was telling OpenGL the indices were `GL_UNSIGNED_BYTE`, but they were actually `GL_UNSIGNED_INT`. This took 3 hours to work out... 135 | 136 | ![gl bytesss](http://i.imgur.com/PD44aRg.png) 137 | 138 | Anyways, after this I got the game working with more chunks. I now have an area of 16x16 chunks, made out of chunk sections of 16x16x16 blocks. 139 | 140 | To finish the day off, I got some naive block editing to work. 141 | 142 | ![Block editing](http://i.imgur.com/ilTJr8i.png) 143 | 144 | End of day stats: 145 | 146 | | Title | Data | 147 | | ---------------------- | -------- | 148 | | Time programming Today | 3:15:38 | 149 | | Lines of Code Today | 410 | 150 | | Total Time programming | 10:53:36 | 151 | | Total Lines of Code | 1974 | 152 | 153 | ### Day 4 154 | 155 | The first thing I did on day 4 was create a sky box using OpenGL cube maps. 156 | 157 | After this, I started work on the world generation, eg adding height map and trees. 158 | 159 | ![Skybox and world gen](http://i.imgur.com/mzUwqPo.png) 160 | 161 | End of day stats: 162 | 163 | | Title | Data | 164 | | ---------------------- | -------- | 165 | | Time programming Today | 3:14:15 | 166 | | Lines of Code Today | 523 | 167 | | Total Time programming | 14:07:51 | 168 | | Total Lines of Code | 2489 | 169 | 170 | ### Day 5 171 | 172 | I started off the day by cleaning up some of the chunk code, and then proceeded to make the world infinite, but 173 | I felt it was not needed, so I simply went back to a fixed-sized world. 174 | 175 | I then added an item system. My implementation probably was not great for this, but it was my first time 176 | at creating that sort of the thing. 177 | 178 | Basically, when a player breaks a block, it gets added to their inventory. When they place a block, a block 179 | is placed. 180 | 181 | | Title | Data | 182 | | ---------------------- | -------- | 183 | | Time programming Today | 2:54:14 | 184 | | Lines of Code Today | 560 | 185 | | Total Time programming | 17:02:05 | 186 | | Total Lines of Code | 3049 | 187 | 188 | ### Day 6 189 | 190 | Mostly optimizations, such as view-frustum culling and making the mesh building faster. 191 | 192 | ### Day 7 193 | 194 | Focus on improving how it looks, eg adding directional lighting 195 | 196 | Also implemented concurrency :) 197 | -------------------------------------------------------------------------------- /Res/Blocks/Air.block: -------------------------------------------------------------------------------- 1 | Name 2 | Air 3 | 4 | Id 5 | 0 6 | 7 | TexTop 8 | 0 0 9 | 10 | TexSide 11 | 0 0 12 | 13 | TexBottom 14 | 0 0 15 | 16 | Opaque 17 | 0 18 | 19 | MeshType 20 | 0 21 | 22 | ShaderType 23 | 0 24 | 25 | Collidable 26 | 0 27 | -------------------------------------------------------------------------------- /Res/Blocks/Cactus.block: -------------------------------------------------------------------------------- 1 | Name 2 | Cactus 3 | 4 | Id 5 | 8 6 | 7 | TexTop 8 | 9 0 9 | 10 | TexSide 11 | 9 1 12 | 13 | TexBottom 14 | 9 0 15 | 16 | Opaque 17 | 1 18 | 19 | MeshType 20 | 0 21 | 22 | ShaderType 23 | 0 24 | 25 | Collidable 26 | 1 27 | -------------------------------------------------------------------------------- /Res/Blocks/CobbleStone: -------------------------------------------------------------------------------- 1 | Name 2 | OakBark 3 | 4 | Id 5 | 13 6 | 7 | TexAll 8 | 3 1 9 | 10 | Opaque 11 | 1 12 | 13 | MeshType 14 | 0 15 | -------------------------------------------------------------------------------- /Res/Blocks/DeadShrub.block: -------------------------------------------------------------------------------- 1 | Name 2 | Dead Shrub 3 | 4 | Id 5 | 11 6 | 7 | TexAll 8 | 12 0 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 1 15 | 16 | ShaderType 17 | 2 18 | 19 | Collidable 20 | 0 21 | -------------------------------------------------------------------------------- /Res/Blocks/Dirt.block: -------------------------------------------------------------------------------- 1 | Name 2 | Dirt 3 | 4 | Id 5 | 2 6 | 7 | TexAll 8 | 2 0 9 | 10 | Opaque 11 | 1 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 0 18 | 19 | Collidable 20 | 1 21 | -------------------------------------------------------------------------------- /Res/Blocks/Glass.block: -------------------------------------------------------------------------------- 1 | Name 2 | Glass 3 | 4 | Id 5 | 14 6 | 7 | TexAll 8 | 7 1 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 0 18 | 19 | Collidable 20 | 1 21 | 22 | -------------------------------------------------------------------------------- /Res/Blocks/GlassBorderless.block: -------------------------------------------------------------------------------- 1 | Name 2 | GlassBorderless 3 | 4 | Id 5 | 15 6 | 7 | TexAll 8 | 8 1 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 0 18 | 19 | Collidable 20 | 1 21 | -------------------------------------------------------------------------------- /Res/Blocks/Grass.block: -------------------------------------------------------------------------------- 1 | Name 2 | Grass 3 | 4 | Id 5 | 1 6 | 7 | TexTop 8 | 0 0 9 | 10 | TexSide 11 | 1 0 12 | 13 | TexBottom 14 | 2 0 15 | 16 | Opaque 17 | 1 18 | 19 | MeshType 20 | 0 21 | 22 | ShaderType 23 | 0 24 | 25 | Collidable 26 | 1 27 | -------------------------------------------------------------------------------- /Res/Blocks/OakBark.block: -------------------------------------------------------------------------------- 1 | Name 2 | OakBark 3 | 4 | Id 5 | 4 6 | 7 | TexTop 8 | 5 0 9 | 10 | TexSide 11 | 4 0 12 | 13 | TexBottom 14 | 5 0 15 | 16 | Opaque 17 | 1 18 | 19 | MeshType 20 | 0 21 | 22 | ShaderType 23 | 0 24 | 25 | Collidable 26 | 1 27 | -------------------------------------------------------------------------------- /Res/Blocks/OakLeaf.block: -------------------------------------------------------------------------------- 1 | Name 2 | Oak Leaf 3 | 4 | Id 5 | 5 6 | 7 | TexAll 8 | 6 0 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 2 18 | 19 | Collidable 20 | 1 21 | -------------------------------------------------------------------------------- /Res/Blocks/OakPlank.block: -------------------------------------------------------------------------------- 1 | Name 2 | OakPlank 3 | 4 | Id 5 | 11 6 | 7 | TexTop 8 | 5 0 9 | 10 | TexAll 11 | 4 0 12 | 13 | Opaque 14 | 1 15 | 16 | MeshType 17 | 0 18 | 19 | ShaderType 20 | 0 21 | 22 | Collidable 23 | 1 24 | -------------------------------------------------------------------------------- /Res/Blocks/OakSapling.block: -------------------------------------------------------------------------------- 1 | Name 2 | OakSapling 3 | 4 | Id 5 | 12 6 | 7 | TexAll 8 | 12 0 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 1 15 | 16 | ShaderType 17 | 2 18 | 19 | Collidable 20 | 0 21 | -------------------------------------------------------------------------------- /Res/Blocks/Rose.block: -------------------------------------------------------------------------------- 1 | Name 2 | Rose 3 | 4 | Id 5 | 9 6 | 7 | TexAll 8 | 10 0 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 1 15 | 16 | ShaderType 17 | 2 18 | 19 | Collidable 20 | 0 21 | -------------------------------------------------------------------------------- /Res/Blocks/Sand.block: -------------------------------------------------------------------------------- 1 | Name 2 | Sand 3 | 4 | Id 5 | 6 6 | 7 | TexAll 8 | 7 0 9 | 10 | Opaque 11 | 1 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 0 18 | 19 | Collidable 20 | 1 21 | -------------------------------------------------------------------------------- /Res/Blocks/Stone.block: -------------------------------------------------------------------------------- 1 | Name 2 | Stone 3 | 4 | Id 5 | 3 6 | 7 | TexAll 8 | 3 0 9 | 10 | Opaque 11 | 1 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 0 18 | 19 | Collidable 20 | 1 21 | -------------------------------------------------------------------------------- /Res/Blocks/TallGrass.block: -------------------------------------------------------------------------------- 1 | Name 2 | Tall Grass 3 | 4 | Id 5 | 10 6 | 7 | TexAll 8 | 11 0 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 1 15 | 16 | ShaderType 17 | 2 18 | 19 | Collidable 20 | 0 21 | -------------------------------------------------------------------------------- /Res/Blocks/Water.block: -------------------------------------------------------------------------------- 1 | Name 2 | Water 3 | 4 | Id 5 | 7 6 | 7 | TexAll 8 | 8 0 9 | 10 | Opaque 11 | 0 12 | 13 | MeshType 14 | 0 15 | 16 | ShaderType 17 | 1 18 | 19 | Collidable 20 | 0 21 | -------------------------------------------------------------------------------- /Res/Fonts/rs.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Fonts/rs.ttf -------------------------------------------------------------------------------- /Res/Textures/DefaultPack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/DefaultPack.png -------------------------------------------------------------------------------- /Res/Textures/DefaultPack2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/DefaultPack2.png -------------------------------------------------------------------------------- /Res/Textures/back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/back.png -------------------------------------------------------------------------------- /Res/Textures/bottom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/bottom.png -------------------------------------------------------------------------------- /Res/Textures/bottomsb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/bottomsb.png -------------------------------------------------------------------------------- /Res/Textures/ch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/ch.png -------------------------------------------------------------------------------- /Res/Textures/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/db.png -------------------------------------------------------------------------------- /Res/Textures/dm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/dm.png -------------------------------------------------------------------------------- /Res/Textures/dt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/dt.png -------------------------------------------------------------------------------- /Res/Textures/front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/front.png -------------------------------------------------------------------------------- /Res/Textures/left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/left.png -------------------------------------------------------------------------------- /Res/Textures/middlesb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/middlesb.png -------------------------------------------------------------------------------- /Res/Textures/right.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/right.png -------------------------------------------------------------------------------- /Res/Textures/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/test.png -------------------------------------------------------------------------------- /Res/Textures/top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/top.png -------------------------------------------------------------------------------- /Res/Textures/topsb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Hopson97/MineCraft-One-Week-Challenge/fead1af708dca0518a6161fbac4c2673393d5ae0/Res/Textures/topsb.png -------------------------------------------------------------------------------- /Res/info.txt: -------------------------------------------------------------------------------- 1 | 2 | ============WELCOME TO MINECRAFT IN A WEEK============ 3 | This is a very simple Minecraft clone (mostly) created in one week 4 | 5 | 6 | Because it was made in a week, some major features were missing: 7 | -Level loading (Chunks do not save! So, I wouldn't commit to a build here for now) 8 | -Caves 9 | -Mobs 10 | -Crafting 11 | 12 | However, I did manage to implement some other important things: 13 | -World generation 14 | -Simple resource collection 15 | -Biomes 16 | -Collision detection 17 | -Block breaking/ placing 18 | 19 | 20 | For now, the game it pretty much entirely a community-driven project. 21 | Source code can be found at https://github.com/Hopson97/MineCraft-One-Week-Challenge 22 | Feel free to contribute! 23 | 24 | Please view 'controls.txt for controls! 25 | 26 | ============Press enter to begin ============ 27 | -------------------------------------------------------------------------------- /Shaders/Basic.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | out vec4 outColour; 4 | in vec2 passTextureCoord; 5 | 6 | uniform sampler2D texSampler; 7 | 8 | void main() 9 | { 10 | vec4 color = texture(texSampler, passTextureCoord); 11 | 12 | outColour = color; 13 | } 14 | -------------------------------------------------------------------------------- /Shaders/Basic.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout(location = 0) in vec3 inVertexPosition; 4 | layout(location = 1) in vec2 inTextureCoord; 5 | 6 | out vec2 passTextureCoord; 7 | 8 | uniform mat4 projViewMatrix; 9 | uniform mat4 modelMatrix; 10 | 11 | void main() 12 | { 13 | gl_Position = projViewMatrix * modelMatrix * vec4(inVertexPosition, 1.0); 14 | 15 | passTextureCoord = inTextureCoord; 16 | } 17 | -------------------------------------------------------------------------------- /Shaders/Chunk.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | out vec4 outColour; 4 | in vec2 passTextureCoord; 5 | in float passCardinalLight; 6 | 7 | uniform sampler2D texSampler; 8 | 9 | vec4 color; 10 | 11 | void main() 12 | { 13 | color = texture(texSampler, passTextureCoord); 14 | 15 | outColour = color * passCardinalLight; 16 | if (outColour.a == 0) discard; 17 | } 18 | -------------------------------------------------------------------------------- /Shaders/Chunk.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout(location = 0) in vec3 inVertexPosition; 4 | layout(location = 1) in vec2 inTextureCoord; 5 | layout(location = 2) in float inCardinalLight; 6 | 7 | out vec2 passTextureCoord; 8 | out float passCardinalLight; 9 | 10 | uniform mat4 projViewMatrix; 11 | 12 | void main() 13 | { 14 | gl_Position = projViewMatrix * vec4(inVertexPosition, 1.0); 15 | 16 | passTextureCoord = inTextureCoord; 17 | passCardinalLight = inCardinalLight; 18 | } 19 | -------------------------------------------------------------------------------- /Shaders/Flora.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout(location = 0) in vec3 inVertexPosition; 4 | layout(location = 1) in vec2 inTextureCoord; 5 | layout(location = 2) in float inCardinalLight; 6 | 7 | out vec2 passTextureCoord; 8 | out float passCardinalLight; 9 | 10 | uniform mat4 projViewMatrix; 11 | uniform float globalTime; 12 | 13 | 14 | vec4 getWorldPos() 15 | { 16 | vec3 inVert = inVertexPosition.xyz; 17 | inVert.x += sin((globalTime + inVert.z + inVert.y) * 1.8f) / 15.0f; 18 | inVert.z -= cos((globalTime + inVert.x + inVert.y) * 1.8f) / 15.0f; 19 | return vec4(inVert, 1); 20 | } 21 | 22 | void main() 23 | { 24 | gl_Position = projViewMatrix * getWorldPos(); 25 | 26 | passTextureCoord = inTextureCoord; 27 | passCardinalLight = inCardinalLight; 28 | } 29 | -------------------------------------------------------------------------------- /Shaders/Skybox.frag: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | out vec4 outColour; 4 | in vec3 passTextureCoord; 5 | 6 | uniform samplerCube texSampler; 7 | 8 | vec3 brightnessContrast(vec3 value, float brightness, float contrast) 9 | { 10 | return (value - 0.5) * contrast + 0.5 + (brightness-1); 11 | } 12 | 13 | vec4 color; 14 | 15 | vec3 gamma(vec3 value, float param) 16 | { 17 | return vec3(pow(abs(value.r), param),pow(abs(value.g), param),pow(abs(value.b), param)); 18 | } 19 | 20 | 21 | void main() 22 | { 23 | color = texture(texSampler, passTextureCoord); 24 | color = vec4(brightnessContrast(color.xyz, 1.15f, 1.15f), color.w); 25 | color = vec4(gamma(color.xyz, 4.8f),color.w); 26 | 27 | outColour = color; 28 | if (outColour.a == 0) discard; 29 | } 30 | -------------------------------------------------------------------------------- /Shaders/Skybox.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout(location = 0) in vec3 inVertexPosition; 4 | 5 | out vec3 passTextureCoord; 6 | 7 | uniform mat4 projectionMatrix; 8 | uniform mat4 viewMatrix; 9 | 10 | void main() 11 | { 12 | gl_Position = projectionMatrix * viewMatrix * vec4(inVertexPosition, 1.0); 13 | 14 | passTextureCoord = inVertexPosition; 15 | } 16 | -------------------------------------------------------------------------------- /Shaders/Water.vert: -------------------------------------------------------------------------------- 1 | #version 330 2 | 3 | layout(location = 0) in vec3 inVertexPosition; 4 | layout(location = 1) in vec2 inTextureCoord; 5 | layout(location = 2) in float inCardinalLight; 6 | 7 | out vec2 passTextureCoord; 8 | out float passCardinalLight; 9 | 10 | uniform mat4 projViewMatrix; 11 | uniform float globalTime; 12 | 13 | 14 | vec4 getWorldPos() 15 | { 16 | vec3 inVert = inVertexPosition.xyz; 17 | inVert.y += sin((globalTime + inVert.x) * 1.5) / 8.8f; 18 | inVert.y += cos((globalTime + inVert.z) * 1.5) / 8.1f; 19 | inVert.y -= 0.2; 20 | return vec4(inVert, 1); 21 | } 22 | 23 | void main() 24 | { 25 | gl_Position = projViewMatrix * getWorldPos(); 26 | 27 | passTextureCoord = inTextureCoord; 28 | passCardinalLight = inCardinalLight; 29 | } 30 | -------------------------------------------------------------------------------- /Source/Application.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | #include "States/PlayState.h" 3 | #include "World/Block/BlockDatabase.h" 4 | #include 5 | 6 | Application::Application(const Config& config) 7 | : m_context(config) 8 | , m_camera(config) 9 | , m_config(config) 10 | { 11 | BlockDatabase::get(); 12 | pushState(*this, config); 13 | } 14 | 15 | float g_timeElapsed = 0; 16 | 17 | /// @brief Game loop utilizing a mixture of SFML events and GL rendering. 18 | void Application::runLoop() 19 | { 20 | sf::Clock dtTimer; 21 | sf::Clock dt; 22 | sf::Vector2i win_center; 23 | 24 | sf::Time m; 25 | 26 | // Grab the context window and force it to a certain position. 27 | // This prevents the window from sticking to the bottom of the visible screen like it does 28 | // in some Linux distros. Especially Arch. 29 | 30 | // If the window is small, use these parameters 31 | //if (m_context.window.getSize().x <= 640) 32 | //{ 33 | // win_center = {(float)sf::VideoMode::getDesktopMode().width / 3.5f, 34 | // (float)sf::VideoMode::getDesktopMode().height / 4.0f}; 35 | //} 36 | //else // Else force it to the upper-leftgit p 37 | //{ 38 | // win_center = {0, 0}; 39 | //} 40 | 41 | m_context.window.setPosition(win_center); 42 | m_context.window.setVerticalSyncEnabled(true); 43 | 44 | while (m_context.window.isOpen() && !m_states.empty()) 45 | { 46 | auto deltaTime = dtTimer.restart(); 47 | auto& state = *m_states.back(); 48 | 49 | state.handleInput(); 50 | state.update(deltaTime.asSeconds()); 51 | m_camera.update(); 52 | 53 | state.render(m_masterRenderer); 54 | m_masterRenderer.finishRender(m_context.window, m_camera); 55 | 56 | handleEvents(); 57 | if (m_isPopState) 58 | { 59 | m_isPopState = false; 60 | m_states.pop_back(); 61 | } 62 | 63 | m = dt.restart(); 64 | 65 | g_timeElapsed += m.asSeconds(); 66 | } 67 | } 68 | 69 | /// @brief Handles window events, especially window polling and keyboard inputs. 70 | void Application::handleEvents() 71 | { 72 | sf::Event e; 73 | while (m_context.window.pollEvent(e)) 74 | { 75 | m_states.back()->handleEvent(e); 76 | switch (e.type) 77 | { 78 | case sf::Event::Closed: 79 | m_context.window.close(); 80 | break; 81 | 82 | case sf::Event::KeyPressed: 83 | switch (e.key.code) 84 | { 85 | case sf::Keyboard::Escape: 86 | m_context.window.close(); 87 | break; 88 | 89 | default: 90 | break; 91 | } 92 | break; 93 | 94 | default: 95 | break; 96 | } 97 | } 98 | } 99 | 100 | /// @brief Tell the program stack to pop off the state. 101 | void Application::popState() 102 | { 103 | m_isPopState = true; 104 | } 105 | 106 | /// @brief Makes the mouse invisible, doesn't actually turn off the mouse. 107 | void Application::turnOffMouse() 108 | { 109 | m_context.window.setMouseCursorVisible(false); 110 | } 111 | 112 | /// @brief Makes the mouse visible again. 113 | void Application::turnOnMouse() 114 | { 115 | m_context.window.setMouseCursorVisible(true); 116 | } 117 | -------------------------------------------------------------------------------- /Source/Application.h: -------------------------------------------------------------------------------- 1 | #ifndef APPLICATION_H_INCLUDED 2 | #define APPLICATION_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "Renderer/RenderMaster.h" 8 | #include "States/StateBase.h" 9 | 10 | #include "Camera.h" 11 | #include "Context.h" 12 | 13 | float extern g_timeElapsed; 14 | 15 | /// @brief The main game application itself. 16 | class Application { 17 | 18 | public: 19 | Application(const Config &config); 20 | 21 | void runLoop(); 22 | 23 | template void pushState(Args &&... args) 24 | { 25 | m_states.push_back(std::make_unique(std::forward(args)...)); 26 | auto &s = m_states.back(); 27 | s->onOpen(); 28 | } 29 | 30 | void popState(); 31 | 32 | Camera &getCamera() 33 | { 34 | return m_camera; 35 | } 36 | 37 | const sf::Window &getWindow() const 38 | { 39 | return m_context.window; 40 | } 41 | 42 | void turnOffMouse(); 43 | void turnOnMouse(); 44 | 45 | private: 46 | void handleEvents(); 47 | 48 | std::vector> m_states; 49 | 50 | Context m_context; 51 | RenderMaster m_masterRenderer; 52 | Camera m_camera; 53 | 54 | const Config &m_config; 55 | 56 | bool m_isPopState = false; 57 | }; 58 | 59 | #endif // APPLICATION_H_INCLUDED 60 | -------------------------------------------------------------------------------- /Source/Camera.cpp: -------------------------------------------------------------------------------- 1 | #include "Camera.h" 2 | 3 | #include "Maths/Matrix.h" 4 | 5 | Camera::Camera(const Config &config) noexcept 6 | : m_config(config) 7 | { 8 | m_projectionMatrix = makeProjectionMatrix(config); 9 | 10 | position = {0, 0, -3.5}; 11 | } 12 | 13 | void Camera::update() noexcept 14 | { 15 | position = {m_pEntity->position.x, m_pEntity->position.y + 0.6f, 16 | m_pEntity->position.z}; 17 | rotation = m_pEntity->rotation; 18 | 19 | m_viewMatrix = makeViewMatrix(*this); 20 | m_projViewMatrx = m_projectionMatrix * m_viewMatrix; 21 | m_frustum.update(m_projViewMatrx); 22 | } 23 | 24 | void Camera::hookEntity(const Entity &entity) noexcept 25 | { 26 | m_pEntity = &entity; 27 | } 28 | 29 | const glm::mat4 &Camera::getViewMatrix() const noexcept 30 | { 31 | return m_viewMatrix; 32 | } 33 | 34 | const glm::mat4 &Camera::getProjMatrix() const noexcept 35 | { 36 | return m_projectionMatrix; 37 | } 38 | 39 | const glm::mat4 &Camera::getProjectionViewMatrix() const noexcept 40 | { 41 | return m_projViewMatrx; 42 | } 43 | 44 | const ViewFrustum &Camera::getFrustum() const noexcept 45 | { 46 | return m_frustum; 47 | } 48 | -------------------------------------------------------------------------------- /Source/Camera.h: -------------------------------------------------------------------------------- 1 | #ifndef CAMERA_H_INCLUDED 2 | #define CAMERA_H_INCLUDED 3 | 4 | #include "Config.h" 5 | #include "Entity.h" 6 | #include "Maths/Frustum.h" 7 | #include "Maths/glm.h" 8 | 9 | class Camera : public Entity { 10 | public: 11 | Camera(const Config &config) noexcept; 12 | 13 | void update() noexcept; 14 | void hookEntity(const Entity &entity) noexcept; 15 | 16 | const glm::mat4 &getViewMatrix() const noexcept; 17 | const glm::mat4 &getProjMatrix() const noexcept; 18 | const glm::mat4 &getProjectionViewMatrix() const noexcept; 19 | 20 | const ViewFrustum &getFrustum() const noexcept; 21 | 22 | private: 23 | const Entity *m_pEntity; 24 | 25 | ViewFrustum m_frustum; 26 | 27 | glm::mat4 m_projectionMatrix; 28 | glm::mat4 m_viewMatrix; 29 | glm::mat4 m_projViewMatrx; 30 | 31 | Config m_config; 32 | }; 33 | 34 | #endif // CAMERA_H_INCLUDED 35 | -------------------------------------------------------------------------------- /Source/Config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_H_INCLUDED 2 | #define CONFIG_H_INCLUDED 3 | 4 | /// @brief Default configuration for program. 5 | struct Config { 6 | int windowX = 1280; 7 | int windowY = 720; 8 | bool isFullscreen = false; 9 | int renderDistance = 8; // Set initial RD low to prevent long load times 10 | int fov = 90; 11 | }; 12 | 13 | #endif // CONFIG_H_INCLUDED 14 | -------------------------------------------------------------------------------- /Source/Context.cpp: -------------------------------------------------------------------------------- 1 | #include "Context.h" 2 | 3 | #include 4 | 5 | 6 | unsigned int g_X; 7 | unsigned int g_Y; 8 | 9 | Context::Context(const Config &config) 10 | { 11 | sf::ContextSettings settings; 12 | settings.antialiasingLevel = 0; 13 | settings.majorVersion = 3; 14 | settings.minorVersion = 3; 15 | settings.depthBits = 24; 16 | settings.stencilBits = 8; 17 | // settings.attributeFlags = sf::ContextSettings::Core; 18 | // This is no longer necessary due to the Mac Support update. 19 | 20 | if (config.isFullscreen) { 21 | window.create(sf::VideoMode::getDesktopMode(), "MineCraft Week", 22 | sf::Style::Fullscreen, settings); 23 | } 24 | else { 25 | sf::VideoMode winMode(config.windowX, config.windowY); 26 | window.create(winMode, "MineCraft Week", sf::Style::Close, settings); 27 | } 28 | 29 | if (!gladLoadGL()) { 30 | 31 | exit(-1); 32 | } 33 | 34 | glViewport(0, 0, window.getSize().x, window.getSize().y); 35 | 36 | glCullFace(GL_BACK); 37 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 38 | } 39 | -------------------------------------------------------------------------------- /Source/Context.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "Config.h" 6 | 7 | /// @brief Struct related to window and application context. 8 | struct Context { 9 | Context(const Config &config); 10 | 11 | sf::Window window; 12 | }; 13 | -------------------------------------------------------------------------------- /Source/Controller.cpp: -------------------------------------------------------------------------------- 1 | #include "Controller.h" 2 | 3 | // WIP 4 | /* 5 | glm::vec3 Controller::translateInput() 6 | { 7 | glm::vec3 change; 8 | float speed = 0.25f; 9 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) 10 | { 11 | speed *= 8; 12 | } 13 | 14 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::W)) 15 | { 16 | change.x += -glm::cos(glm::radians(rotation.y + 90)) * speed; 17 | change.z += -glm::sin(glm::radians(rotation.y + 90)) * speed; 18 | } 19 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::S)) 20 | { 21 | change.x += glm::cos(glm::radians(rotation.y + 90)) * speed; 22 | change.z += glm::sin(glm::radians(rotation.y + 90)) * speed; 23 | } 24 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::A)) 25 | { 26 | change.x += -glm::cos(glm::radians(rotation.y)) * speed; 27 | change.z += -glm::sin(glm::radians(rotation.y)) * speed; 28 | } 29 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::D)) 30 | { 31 | change.x += glm::cos(glm::radians(rotation.y)) * speed; 32 | change.z += glm::sin(glm::radians(rotation.y)) * speed; 33 | } 34 | 35 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::Space)&& (m_isOnGround)) 36 | { 37 | change.y += speed; 38 | } 39 | else if (sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) 40 | { 41 | change.y -= speed; 42 | } 43 | 44 | return change; 45 | } 46 | 47 | sf::Vector2i Controller::mouseInput() 48 | { 49 | 50 | } 51 | */ 52 | -------------------------------------------------------------------------------- /Source/Controller.h: -------------------------------------------------------------------------------- 1 | #ifndef CONTROLLER_H_INCLUDED 2 | #define CONTROLLER_H_INCLUDED 3 | 4 | #include "Maths/glm.h" 5 | #include 6 | 7 | /* Vestigial class that was going to be used for player movement. */ 8 | 9 | // WIP 10 | class Controller { 11 | glm::vec3 translateInput(); 12 | sf::Vector2i mouseInput(); 13 | }; 14 | 15 | #endif // CONTROLLER_H_INCLUDED 16 | -------------------------------------------------------------------------------- /Source/Entity.h: -------------------------------------------------------------------------------- 1 | #ifndef ENTITY_H_INCLUDED 2 | #define ENTITY_H_INCLUDED 3 | 4 | #include "Maths/Matrix.h" 5 | #include "Physics/AABB.h" 6 | 7 | struct Entity { 8 | Entity() 9 | : box({0.f, 0.f, 0.f}) 10 | , position(glm::vec3(0.f)) 11 | , rotation(glm::vec3(0.f)) 12 | , velocity(glm::vec3(0.f)) 13 | { 14 | } 15 | 16 | Entity(const glm::vec3 &pos, const glm::vec3 &rot) 17 | : position(pos) 18 | , rotation(rot) 19 | , box({0, 0, 0}) 20 | , velocity(glm::vec3(0.f)) 21 | { 22 | } 23 | Entity(const glm::vec3 &pos, const glm::vec3 &rot, const glm::vec3 &box) 24 | : position(pos) 25 | , rotation(rot) 26 | , box(box) 27 | , velocity(glm::vec3(0.f)) 28 | { 29 | } 30 | 31 | glm::vec3 position; 32 | glm::vec3 rotation; 33 | glm::vec3 velocity; 34 | 35 | AABB box; 36 | }; 37 | 38 | #endif // ENTITY_H_INCLUDED 39 | -------------------------------------------------------------------------------- /Source/GL/GLFunctions.cpp: -------------------------------------------------------------------------------- 1 | #include "GLFunctions.h" 2 | 3 | void GL::drawElements(GLuint indicesCount) noexcept 4 | { 5 | glDrawElements(GL_TRIANGLES, indicesCount, GL_UNSIGNED_INT, nullptr); 6 | } 7 | 8 | void GL::bindVAO(GLuint vao) noexcept 9 | { 10 | glBindVertexArray(vao); 11 | } 12 | -------------------------------------------------------------------------------- /Source/GL/GLFunctions.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFUNCTIONS_H_INCLUDED 2 | #define GLFUNCTIONS_H_INCLUDED 3 | 4 | #include 5 | 6 | namespace GL { 7 | void drawElements(GLuint indicesCount) noexcept; 8 | void bindVAO(GLuint vao) noexcept; 9 | 10 | namespace Enum { 11 | enum Texture { Tex2D = GL_TEXTURE_2D, TexCubeMap = GL_TEXTURE_CUBE_MAP }; 12 | } 13 | } // namespace GL 14 | 15 | #endif // GLFUNCTIONS_H_INCLUDED 16 | -------------------------------------------------------------------------------- /Source/Input/Keyboard.cpp: -------------------------------------------------------------------------------- 1 | #include "Keyboard.h" 2 | 3 | Keyboard::Keyboard() 4 | { 5 | std::fill(m_keys.begin(), m_keys.end(), false); 6 | } 7 | 8 | void Keyboard::update(sf::Event e) 9 | { 10 | m_recentlyReleased = sf::Keyboard::KeyCount; 11 | switch (e.type) { 12 | case sf::Event::KeyReleased: 13 | m_keys[e.key.code] = false; 14 | break; 15 | 16 | case sf::Event::KeyPressed: 17 | m_recentlyReleased = e.key.code; 18 | m_keys[e.key.code] = true; 19 | break; 20 | 21 | default: 22 | break; 23 | } 24 | } 25 | 26 | bool Keyboard::isKeyDown(sf::Keyboard::Key key) const 27 | { 28 | return m_keys[key]; 29 | } 30 | 31 | bool Keyboard::keyReleased(sf::Keyboard::Key key) const 32 | { 33 | return m_recentlyReleased == key; 34 | } -------------------------------------------------------------------------------- /Source/Input/Keyboard.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | /// @brief Handles keyboard inputs and events. 9 | class Keyboard { 10 | public: 11 | Keyboard(); 12 | 13 | void update(sf::Event e); 14 | 15 | bool isKeyDown(sf::Keyboard::Key key) const; 16 | bool keyReleased(sf::Keyboard::Key key) const; 17 | 18 | private: 19 | std::array m_keys; 20 | sf::Keyboard::Key m_recentlyReleased; 21 | }; -------------------------------------------------------------------------------- /Source/Input/ToggleKey.cpp: -------------------------------------------------------------------------------- 1 | #include "ToggleKey.h" 2 | 3 | ToggleKey::ToggleKey(sf::Keyboard::Key key) 4 | : m_key(key) 5 | { 6 | } 7 | 8 | bool ToggleKey::isKeyPressed() 9 | { 10 | if (m_delayTimer.getElapsedTime().asSeconds() > 0.2) { 11 | if (sf::Keyboard::isKeyPressed(m_key)) { 12 | m_delayTimer.restart(); 13 | return true; 14 | } 15 | } 16 | return false; 17 | } 18 | -------------------------------------------------------------------------------- /Source/Input/ToggleKey.h: -------------------------------------------------------------------------------- 1 | #ifndef TOGGLEKEY_H_INCLUDED 2 | #define TOGGLEKEY_H_INCLUDED 3 | 4 | #include 5 | 6 | /// @brief A keyboard related subclass that determines if a key remains pressed. 7 | class ToggleKey { 8 | public: 9 | ToggleKey(sf::Keyboard::Key); 10 | 11 | bool isKeyPressed(); 12 | 13 | private: 14 | sf::Keyboard::Key m_key; 15 | sf::Clock m_delayTimer; 16 | }; 17 | 18 | #endif // TOGGLEKEY_H_INCLUDED 19 | -------------------------------------------------------------------------------- /Source/Item/ItemStack.cpp: -------------------------------------------------------------------------------- 1 | #include "ItemStack.h" 2 | 3 | #include 4 | 5 | ItemStack::ItemStack(const Material &material, int amount) 6 | : m_pMaterial(&material) 7 | , m_numInStack(amount) 8 | { 9 | } 10 | 11 | int ItemStack::add(int amount) 12 | { 13 | m_numInStack += amount; 14 | 15 | if (m_numInStack > m_pMaterial->maxStackSize) { 16 | int leftOver = m_numInStack - m_pMaterial->maxStackSize; 17 | m_numInStack = m_pMaterial->maxStackSize; 18 | return leftOver; 19 | } 20 | else { 21 | return 0; 22 | } 23 | } 24 | 25 | void ItemStack::remove() 26 | { 27 | m_numInStack--; 28 | if (m_numInStack == 0) { 29 | m_pMaterial = &Material::NOTHING; 30 | } 31 | } 32 | 33 | int ItemStack::getNumInStack() const 34 | { 35 | return m_numInStack; 36 | } 37 | 38 | const Material &ItemStack::getMaterial() const 39 | { 40 | return *m_pMaterial; 41 | } 42 | -------------------------------------------------------------------------------- /Source/Item/ItemStack.h: -------------------------------------------------------------------------------- 1 | #ifndef ITEMSTACK_H_INCLUDED 2 | #define ITEMSTACK_H_INCLUDED 3 | 4 | #include "Material.h" 5 | 6 | /// @brief Determines if a player character is holding blocks or items, also determines placement behavior. 7 | class ItemStack { 8 | public: 9 | ItemStack(const Material &material, int amount); 10 | 11 | int add(int amount); 12 | void remove(); 13 | 14 | int getNumInStack() const; 15 | 16 | const Material &getMaterial() const; 17 | 18 | private: 19 | const Material *m_pMaterial = &Material::NOTHING; 20 | int m_numInStack = 0; 21 | }; 22 | 23 | #endif // ITEMSTACK_H_INCLUDED 24 | -------------------------------------------------------------------------------- /Source/Item/ItemType.h: -------------------------------------------------------------------------------- 1 | #ifndef ITEMTYPE_H_INCLUDED 2 | #define ITEMTYPE_H_INCLUDED 3 | 4 | // This class isn't currently being used. 5 | 6 | #endif // ITEMTYPE_H_INCLUDED 7 | -------------------------------------------------------------------------------- /Source/Item/Material.cpp: -------------------------------------------------------------------------------- 1 | #include "Material.h" 2 | 3 | const Material Material::NOTHING(ID::Nothing, 0, false, "None"); 4 | const Material Material::GRASS_BLOCK(ID::Grass, 99, true, "Grass Block"); 5 | const Material Material::DIRT_BLOCK(ID::Dirt, 99, true, "Dirt Block"); 6 | const Material Material::STONE_BLOCK(ID::Stone, 99, true, "Stone Block"); 7 | const Material Material::OAK_BARK_BLOCK(ID::OakBark, 99, true, 8 | "Oak Bark Block"); 9 | const Material Material::OAK_LEAF_BLOCK(ID::OakLeaf, 99, true, 10 | "Oak Leaf Block"); 11 | const Material Material::SAND_BLOCK(ID::Sand, 99, true, "Sand Block"); 12 | const Material Material::CACTUS_BLOCK(ID::Cactus, 99, true, "Cactus Block"); 13 | 14 | const Material Material::ROSE(ID::Rose, 99, true, "Rose"); 15 | const Material Material::TALL_GRASS(ID::TallGrass, 99, true, "Tall Grass"); 16 | const Material Material::DEAD_SHRUB(ID::DeadShrub, 99, true, "Dead Shrub"); 17 | 18 | Material::Material(Material::ID id, int maxStack, bool isBlock, 19 | std::string &&name) 20 | : id(id) 21 | , maxStackSize(maxStack) 22 | , isBlock(isBlock) 23 | , name(std::move(name)) 24 | { 25 | } 26 | 27 | BlockId Material::toBlockID() const 28 | { 29 | switch (id) { 30 | case Nothing: 31 | return BlockId::Air; 32 | 33 | case Grass: 34 | return BlockId::Grass; 35 | 36 | case Dirt: 37 | return BlockId::Dirt; 38 | 39 | case Stone: 40 | return BlockId::Stone; 41 | 42 | case OakBark: 43 | return BlockId::OakBark; 44 | 45 | case OakLeaf: 46 | return BlockId::OakLeaf; 47 | 48 | case Sand: 49 | return BlockId::Sand; 50 | 51 | case Cactus: 52 | return BlockId::Cactus; 53 | 54 | case TallGrass: 55 | return BlockId::TallGrass; 56 | 57 | case Rose: 58 | return BlockId::Rose; 59 | 60 | case DeadShrub: 61 | return BlockId::DeadShrub; 62 | 63 | default: 64 | return BlockId::NUM_TYPES; 65 | } 66 | } 67 | 68 | const Material &Material::toMaterial(BlockId id) 69 | { 70 | switch (id) { 71 | case BlockId::Grass: 72 | return GRASS_BLOCK; 73 | 74 | case BlockId::Dirt: 75 | return DIRT_BLOCK; 76 | 77 | case BlockId::Stone: 78 | return STONE_BLOCK; 79 | 80 | case BlockId::OakBark: 81 | return OAK_BARK_BLOCK; 82 | 83 | case BlockId::OakLeaf: 84 | return OAK_LEAF_BLOCK; 85 | 86 | case BlockId::Sand: 87 | return SAND_BLOCK; 88 | 89 | case BlockId::Cactus: 90 | return CACTUS_BLOCK; 91 | 92 | case BlockId::Rose: 93 | return ROSE; 94 | 95 | case BlockId::TallGrass: 96 | return TALL_GRASS; 97 | 98 | case BlockId::DeadShrub: 99 | return DEAD_SHRUB; 100 | 101 | default: 102 | return NOTHING; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Source/Item/Material.h: -------------------------------------------------------------------------------- 1 | #ifndef MATERIAL_H_INCLUDED 2 | #define MATERIAL_H_INCLUDED 3 | 4 | #include "../World/Block/BlockId.h" 5 | #include 6 | 7 | #include "../Util/NonCopyable.h" 8 | 9 | /// @brief Determines case-by-case properties and behaviors of known block types. 10 | struct Material : public NonCopyable { 11 | enum ID { 12 | Nothing, 13 | Grass, 14 | Dirt, 15 | Stone, 16 | OakBark, 17 | OakLeaf, 18 | Sand, 19 | Cactus, 20 | Rose, 21 | TallGrass, 22 | DeadShrub 23 | }; 24 | 25 | const static Material NOTHING, GRASS_BLOCK, DIRT_BLOCK, STONE_BLOCK, 26 | OAK_BARK_BLOCK, OAK_LEAF_BLOCK, SAND_BLOCK, CACTUS_BLOCK, ROSE, 27 | TALL_GRASS, DEAD_SHRUB; 28 | 29 | Material(Material::ID id, int maxStack, bool isBlock, std::string &&name); 30 | 31 | BlockId toBlockID() const; 32 | 33 | static const Material &toMaterial(BlockId id); 34 | 35 | const Material::ID id; 36 | const int maxStackSize; 37 | const bool isBlock; 38 | const std::string name; 39 | }; 40 | 41 | namespace std { 42 | template <> struct hash { 43 | size_t operator()(const Material::ID &id) const 44 | { 45 | std::hash hasher; 46 | 47 | return hasher(id); 48 | } 49 | }; 50 | } // namespace std 51 | 52 | #endif // MATERIAL_H_INCLUDED 53 | -------------------------------------------------------------------------------- /Source/Main.cpp: -------------------------------------------------------------------------------- 1 | #include "Application.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "Config.h" 7 | 8 | #ifdef __WIN32 9 | extern "C" { 10 | // Enable dedicated graphics 11 | __declspec(dllexport) bool NvOptimusEnablement = true; 12 | __declspec(dllexport) bool AmdPowerXpressRequestHighPerformance = true; 13 | } 14 | #endif // __WIN32 15 | 16 | namespace { 17 | void loadConfig(Config &config); 18 | void displayInfo(); 19 | } // namespace 20 | 21 | int main() 22 | { 23 | Config config; 24 | loadConfig(config); 25 | displayInfo(); 26 | 27 | std::cin.ignore(); 28 | std::cout << "Loading game...\n"; 29 | 30 | Application app(config); 31 | app.runLoop(); 32 | } 33 | 34 | namespace { 35 | /// @brief Self declared function that loads in configuration files as needed. 36 | /// @param config 37 | void loadConfig(Config &config) 38 | { 39 | std::ifstream configFile("config.txt"); 40 | std::string key; 41 | 42 | // If the config file is missing or "bad" 43 | if(!configFile.good()) 44 | { 45 | std::cout << "Configuration file invalid,\n"; 46 | std::cout << "writing 'new' configuration." << "\n"; 47 | std::cout << "\n"; 48 | 49 | std::ofstream outfile("config.txt"); 50 | 51 | if(outfile.is_open()) 52 | { 53 | outfile << "renderdistance " << "8"; 54 | outfile << "fullscreen " << "0"; 55 | outfile << "windowsize " << "1600 " << "900"; 56 | outfile << "fov " << "105"; 57 | 58 | outfile.close(); 59 | configFile.close(); // Close so it can be reopened safely. 60 | } 61 | 62 | std::cout << "\n"; 63 | std::cout << "New configuration file created." << "\n"; 64 | } 65 | 66 | try 67 | { 68 | // Open 'new' config file. 69 | if(!configFile.is_open()) 70 | { 71 | configFile.open("config.txt"); 72 | } 73 | 74 | // If the file is still creating errors 75 | if(configFile.fail()) 76 | { 77 | std::cout << "Error: The program failed to load the configuration files." << "\n"; 78 | std::cout << "To understand why this error may have occured,\n"; 79 | std::cout << "please examine your 'config.txt' file. Thank you." << "\n"; 80 | 81 | // Because this is thrown before runtime, no memory needs to be freed. 82 | throw "Unable to load configuration file."; 83 | } 84 | 85 | if (configFile.is_open()) 86 | { 87 | while (configFile >> key) 88 | { 89 | if (key == "renderdistance") { 90 | configFile >> config.renderDistance; 91 | std::cout << "Config: Render Distance: " 92 | << config.renderDistance << '\n'; 93 | } 94 | else if (key == "fullscreen") { 95 | configFile >> config.isFullscreen; 96 | std::cout << "Config: Full screen mode: " << std::boolalpha 97 | << config.isFullscreen << '\n'; 98 | } 99 | else if (key == "windowsize") { 100 | configFile >> config.windowX >> config.windowY; 101 | std::cout << "Config: Window Size: " << config.windowX << " x " 102 | << config.windowY << '\n'; 103 | } 104 | else if (key == "fov") { 105 | configFile >> config.fov; 106 | std::cout << "Config: Field of Vision: " << config.fov << '\n'; 107 | } 108 | } 109 | } 110 | } 111 | catch(const std::exception& e) 112 | { 113 | std::cerr << e.what(); 114 | } 115 | } 116 | 117 | void displayInfo() 118 | { 119 | std::ifstream inFile; 120 | inFile.open("Res/info.txt"); 121 | std::string line; 122 | while (std::getline(inFile, line)) { 123 | std::cout << line << "\n"; 124 | } 125 | } 126 | } // namespace 127 | -------------------------------------------------------------------------------- /Source/Maths/Frustum.cpp: -------------------------------------------------------------------------------- 1 | #include "Frustum.h" 2 | 3 | #include "../Physics/AABB.h" 4 | 5 | enum Planes { 6 | Near, 7 | Far, 8 | Left, 9 | Right, 10 | Top, 11 | Bottom, 12 | }; 13 | 14 | float Plane::distanceToPoint(const glm::vec3 &point) const 15 | { 16 | return glm::dot(point, normal) + distanceToOrigin; 17 | } 18 | 19 | /// @brief Updates the Frustrum relative between player and observed surface. 20 | /// @param mat 21 | void ViewFrustum::update(const glm::mat4 &mat) noexcept 22 | { 23 | // left 24 | m_planes[Planes::Left].normal.x = mat[0][3] + mat[0][0]; 25 | m_planes[Planes::Left].normal.y = mat[1][3] + mat[1][0]; 26 | m_planes[Planes::Left].normal.z = mat[2][3] + mat[2][0]; 27 | m_planes[Planes::Left].distanceToOrigin = mat[3][3] + mat[3][0]; 28 | 29 | // right 30 | m_planes[Planes::Right].normal.x = mat[0][3] - mat[0][0]; 31 | m_planes[Planes::Right].normal.y = mat[1][3] - mat[1][0]; 32 | m_planes[Planes::Right].normal.z = mat[2][3] - mat[2][0]; 33 | m_planes[Planes::Right].distanceToOrigin = mat[3][3] - mat[3][0]; 34 | 35 | // bottom 36 | m_planes[Planes::Bottom].normal.x = mat[0][3] + mat[0][1]; 37 | m_planes[Planes::Bottom].normal.y = mat[1][3] + mat[1][1]; 38 | m_planes[Planes::Bottom].normal.z = mat[2][3] + mat[2][1]; 39 | m_planes[Planes::Bottom].distanceToOrigin = mat[3][3] + mat[3][1]; 40 | 41 | // top 42 | m_planes[Planes::Top].normal.x = mat[0][3] - mat[0][1]; 43 | m_planes[Planes::Top].normal.y = mat[1][3] - mat[1][1]; 44 | m_planes[Planes::Top].normal.z = mat[2][3] - mat[2][1]; 45 | m_planes[Planes::Top].distanceToOrigin = mat[3][3] - mat[3][1]; 46 | 47 | // near 48 | m_planes[Planes::Near].normal.x = mat[0][3] + mat[0][2]; 49 | m_planes[Planes::Near].normal.y = mat[1][3] + mat[1][2]; 50 | m_planes[Planes::Near].normal.z = mat[2][3] + mat[2][2]; 51 | m_planes[Planes::Near].distanceToOrigin = mat[3][3] + mat[3][2]; 52 | 53 | // far 54 | m_planes[Planes::Far].normal.x = mat[0][3] - mat[0][2]; 55 | m_planes[Planes::Far].normal.y = mat[1][3] - mat[1][2]; 56 | m_planes[Planes::Far].normal.z = mat[2][3] - mat[2][2]; 57 | m_planes[Planes::Far].distanceToOrigin = mat[3][3] - mat[3][2]; 58 | 59 | for (auto &plane : m_planes) { 60 | float length = glm::length(plane.normal); 61 | plane.normal /= length; 62 | plane.distanceToOrigin /= length; 63 | } 64 | } 65 | 66 | /// @brief Determines if a collision box is present in the Frustrum. 67 | /// @param box 68 | /// @return result 69 | bool ViewFrustum::isBoxInFrustum(const AABB &box) const noexcept 70 | { 71 | bool result = true; 72 | for (auto &plane : m_planes) { 73 | if (plane.distanceToPoint(box.getVP(plane.normal)) < 0) { 74 | return false; 75 | } 76 | else if (plane.distanceToPoint(box.getVN(plane.normal)) < 0) { 77 | result = true; 78 | } 79 | } 80 | return result; 81 | } 82 | -------------------------------------------------------------------------------- /Source/Maths/Frustum.h: -------------------------------------------------------------------------------- 1 | #ifndef FRUSTUM_H_INCLUDED 2 | #define FRUSTUM_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "glm.h" 7 | 8 | struct AABB; 9 | 10 | /// @brief Vertex based construct, usually flat. 11 | struct Plane { 12 | float distanceToPoint(const glm::vec3 &point) const; 13 | 14 | float distanceToOrigin; 15 | glm::vec3 normal; // Vector3 normals 16 | }; 17 | 18 | class ViewFrustum { 19 | public: 20 | void update(const glm::mat4 &projViewMatrix) noexcept; 21 | 22 | bool isBoxInFrustum(const AABB &box) const noexcept; 23 | 24 | private: 25 | std::array m_planes; 26 | }; 27 | 28 | #endif // FRUSTUM_H_INCLUDED 29 | -------------------------------------------------------------------------------- /Source/Maths/GeneralMaths.cpp: -------------------------------------------------------------------------------- 1 | #include "GeneralMaths.h" 2 | 3 | #include 4 | 5 | float clamp(float x, float lowerlimit, float upperlimit); 6 | 7 | float smoothstep(float edge0, float edge1, float x) 8 | { 9 | // Scale, bias and saturate x to 0..1 range 10 | x = x * x * (3 - 2 * x); 11 | // Evaluate polynomial 12 | return (edge0 * x) + (edge1 * (1 - x)); 13 | } 14 | 15 | /// @brief Clamp function that regulates values between limits. 16 | /// @param x 17 | /// @param lowerlimit 18 | /// @param upperlimit 19 | /// @return x 20 | float clamp(float x, float lowerlimit, float upperlimit) 21 | { 22 | if (x < lowerlimit) 23 | x = lowerlimit; 24 | if (x > upperlimit) 25 | x = upperlimit; 26 | return x; 27 | } 28 | 29 | float smoothInterpolation(float bottomLeft, float topLeft, float bottomRight, 30 | float topRight, float xMin, float xMax, float zMin, 31 | float zMax, float x, float z) 32 | { 33 | float width = xMax - xMin, height = zMax - zMin; 34 | float xValue = 1 - (x - xMin) / width; 35 | float zValue = 1 - (z - zMin) / height; 36 | 37 | // std::cout << xValue << std::endl; 38 | 39 | float a = smoothstep(bottomLeft, bottomRight, xValue); 40 | float b = smoothstep(topLeft, topRight, xValue); 41 | return smoothstep(a, b, zValue); 42 | } 43 | 44 | float bilinearInterpolation(float bottomLeft, float topLeft, float bottomRight, 45 | float topRight, float xMin, float xMax, float zMin, 46 | float zMax, float x, float z) 47 | { 48 | float width = xMax - xMin, height = zMax - zMin, 49 | 50 | xDistanceToMaxValue = xMax - x, zDistanceToMaxValue = zMax - z, 51 | 52 | xDistanceToMinValue = x - xMin, zDistanceToMinValue = z - zMin; 53 | 54 | return 1.0f / (width * height) * 55 | (bottomLeft * xDistanceToMaxValue * zDistanceToMaxValue + 56 | bottomRight * xDistanceToMinValue * zDistanceToMaxValue + 57 | topLeft * xDistanceToMaxValue * zDistanceToMinValue + 58 | topRight * xDistanceToMinValue * zDistanceToMinValue); 59 | } 60 | -------------------------------------------------------------------------------- /Source/Maths/GeneralMaths.h: -------------------------------------------------------------------------------- 1 | #ifndef GENERALMATHS_H_INCLUDED 2 | #define GENERALMATHS_H_INCLUDED 3 | 4 | float bilinearInterpolation(float bottomLeft, float topLeft, float bottomRight, 5 | float topRight, float xMin, float xMax, float zMin, 6 | float zMax, float xToCalc, float yToCalc); 7 | 8 | float smoothInterpolation(float bottomLeft, float topLeft, float bottomRight, 9 | float topRight, float xMin, float xMax, float zMin, 10 | float zMax, float x, float z); 11 | 12 | #endif // GENERALMATHS_H_INCLUDED 13 | -------------------------------------------------------------------------------- /Source/Maths/Matrix.cpp: -------------------------------------------------------------------------------- 1 | #include "Matrix.h" 2 | 3 | #include "../Camera.h" 4 | #include "../Entity.h" 5 | 6 | #include "../Config.h" 7 | 8 | glm::mat4 makeModelMatrix(const Entity &entity) 9 | { 10 | glm::mat4 matrix; 11 | 12 | matrix = glm::rotate(matrix, glm::radians(entity.rotation.x), {1, 0, 0}); 13 | matrix = glm::rotate(matrix, glm::radians(entity.rotation.y), {0, 1, 0}); 14 | matrix = glm::rotate(matrix, glm::radians(entity.rotation.z), {0, 0, 1}); 15 | 16 | matrix = glm::translate(matrix, entity.position); 17 | 18 | return matrix; 19 | } 20 | 21 | glm::mat4 makeViewMatrix(const Camera &camera) 22 | { 23 | glm::mat4 matrix(1.f); 24 | 25 | matrix = glm::rotate(matrix, glm::radians(camera.rotation.x), {1, 0, 0}); 26 | matrix = glm::rotate(matrix, glm::radians(camera.rotation.y), {0, 1, 0}); 27 | matrix = glm::rotate(matrix, glm::radians(camera.rotation.z), {0, 0, 1}); 28 | 29 | matrix = glm::translate(matrix, -camera.position); 30 | 31 | return matrix; 32 | } 33 | 34 | glm::mat4 makeProjectionMatrix(const Config &config) 35 | { 36 | float x = (float)config.windowX; 37 | float y = (float)config.windowY; 38 | float fov = (float)config.fov; 39 | 40 | return glm::perspective(glm::radians(fov), x / y, 0.1f, 2000.0f); 41 | } 42 | -------------------------------------------------------------------------------- /Source/Maths/Matrix.h: -------------------------------------------------------------------------------- 1 | #ifndef MATRIX_H_INCLUDED 2 | #define MATRIX_H_INCLUDED 3 | 4 | #include "glm.h" 5 | 6 | class Camera; 7 | 8 | struct Entity; 9 | struct Config; 10 | 11 | glm::mat4 makeModelMatrix(const Entity &entity); 12 | glm::mat4 makeViewMatrix(const Camera &camera); 13 | glm::mat4 makeProjectionMatrix(const Config &config); 14 | 15 | #endif // MATRIX_H_INCLUDED 16 | -------------------------------------------------------------------------------- /Source/Maths/NoiseGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "NoiseGenerator.h" 2 | 3 | #include "../World/WorldConstants.h" 4 | 5 | #include 6 | 7 | NoiseGenerator::NoiseGenerator(int seed) 8 | : m_seed(seed) 9 | { 10 | m_noiseParameters.octaves = 7; 11 | m_noiseParameters.amplitude = 70; 12 | m_noiseParameters.smoothness = 235; 13 | m_noiseParameters.heightOffset = -5; 14 | m_noiseParameters.roughness = 0.53; 15 | } 16 | 17 | void NoiseGenerator::setParameters(const NoiseParameters ¶ms) noexcept 18 | { 19 | m_noiseParameters = params; 20 | } 21 | 22 | /// @brief Gets Noise through n which acts as a seed number. 23 | /// @param n 24 | /// @return 25 | double NoiseGenerator::getNoise(int n) const noexcept 26 | { 27 | n += m_seed; 28 | n = (n << 13) ^ n; 29 | auto newN = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; 30 | 31 | return 1.0 - ((double)newN / 1073741824.0); 32 | } 33 | 34 | /// @brief Overload of getNoise that takes doubles instead of int n. 35 | /// @param x 36 | /// @param z 37 | /// @return 38 | double NoiseGenerator::getNoise(double x, double z) const noexcept 39 | { 40 | return getNoise(x + z * 57.0); 41 | } 42 | 43 | double NoiseGenerator::lerp(double a, double b, double z) const noexcept 44 | { 45 | double mu2 = (1 - std::cos(z * 3.14)) / 2; 46 | return (a * (1 - mu2) + b * mu2); 47 | } 48 | 49 | double NoiseGenerator::noise(double x, double z) const noexcept 50 | { 51 | auto floorX = (double)(( 52 | int)x); // This is kinda a cheap way to floor a double integer. 53 | auto floorZ = (double)((int)z); 54 | 55 | auto s = 0.0, t = 0.0, u = 0.0, 56 | v = 0.0; // Integer declaration 57 | 58 | s = getNoise(floorX, floorZ); 59 | t = getNoise(floorX + 1, floorZ); 60 | u = getNoise( 61 | floorX, 62 | floorZ + 1); // Get the surrounding values to calculate the transition. 63 | v = getNoise(floorX + 1, floorZ + 1); 64 | 65 | auto rec1 = lerp(s, t, x - floorX); // Interpolate between the values. 66 | auto rec2 = lerp( 67 | u, v, 68 | x - floorX); // Here we use x-floorX, to get 1st dimension. Don't mind 69 | // the x-floorX thingie, it's part of the cosine formula. 70 | auto rec3 = 71 | lerp(rec1, rec2, 72 | z - floorZ); // Here we use y-floorZ, to get the 2nd dimension. 73 | return rec3; 74 | } 75 | 76 | /// @brief Gets the height of the chunk for the sake of Noise Generation. 77 | /// @param x 78 | /// @param z 79 | /// @param chunkX 80 | /// @param chunkZ 81 | /// @return val 82 | double NoiseGenerator::getHeight(int x, int z, int chunkX, int chunkZ) const 83 | noexcept 84 | { 85 | auto newX = (x + (chunkX * CHUNK_SIZE)); 86 | auto newZ = (z + (chunkZ * CHUNK_SIZE)); 87 | 88 | if (newX < 0 || newZ < 0) { 89 | return WATER_LEVEL - 1; 90 | } 91 | 92 | auto totalValue = 0.0; 93 | 94 | for (auto a = 0; a < m_noiseParameters.octaves - 1; 95 | a++) // This loops through the octaves. 96 | { 97 | auto frequency = pow( 98 | 2.0, 99 | a); // This increases the frequency with every loop of the octave. 100 | auto amplitude = pow( 101 | m_noiseParameters.roughness, 102 | a); // This decreases the amplitude with every loop of the octave. 103 | totalValue += 104 | noise(((double)newX) * frequency / m_noiseParameters.smoothness, 105 | ((double)newZ) * frequency / m_noiseParameters.smoothness) * 106 | amplitude; 107 | } 108 | 109 | auto val = (((totalValue / 2.1) + 1.2) * m_noiseParameters.amplitude) + 110 | m_noiseParameters.heightOffset; 111 | 112 | return val > 0 ? val : 1; // Compare if value is greater than 0 113 | } 114 | -------------------------------------------------------------------------------- /Source/Maths/NoiseGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef NOISEGENERATOR_H_INCLUDED 2 | #define NOISEGENERATOR_H_INCLUDED 3 | 4 | struct NoiseParameters { 5 | int octaves; 6 | int amplitude; 7 | int smoothness; 8 | int heightOffset; 9 | 10 | double roughness; 11 | }; 12 | 13 | /// @brief Perlin noise generator used in construction of chunks and chunk blocks. 14 | class NoiseGenerator { 15 | public: 16 | NoiseGenerator(int seed); 17 | 18 | double getHeight(int x, int z, int chunkX, int chunkZ) const noexcept; 19 | 20 | void setParameters(const NoiseParameters ¶ms) noexcept; 21 | 22 | private: 23 | double getNoise(int n) const noexcept; 24 | double getNoise(double x, double z) const noexcept; 25 | 26 | double lerp(double a, double b, double z) const noexcept; 27 | 28 | double noise(double x, double z) const noexcept; 29 | 30 | NoiseParameters m_noiseParameters; 31 | 32 | int m_seed; 33 | }; 34 | 35 | #endif // NOISEGENERATOR_H_INCLUDED 36 | -------------------------------------------------------------------------------- /Source/Maths/Ray.cpp: -------------------------------------------------------------------------------- 1 | #include "Ray.h" 2 | 3 | Ray::Ray(const glm::vec3 &position, const glm::vec3 &direction) 4 | : m_rayStart(position) 5 | , m_rayEnd(position) 6 | , m_direction(direction) 7 | { 8 | } 9 | 10 | void Ray::step(float scale) 11 | { 12 | float yaw = glm::radians(m_direction.y + 90); 13 | float pitch = glm::radians(m_direction.x); 14 | 15 | auto &p = m_rayEnd; 16 | 17 | p.x -= glm::cos(yaw) * scale; 18 | p.z -= glm::sin(yaw) * scale; 19 | p.y -= glm::tan(pitch) * scale; 20 | } 21 | 22 | const glm::vec3 &Ray::getEnd() const 23 | { 24 | return m_rayEnd; 25 | } 26 | 27 | float Ray::getLength() const 28 | { 29 | return glm::distance(m_rayStart, m_rayEnd); 30 | } 31 | -------------------------------------------------------------------------------- /Source/Maths/Ray.h: -------------------------------------------------------------------------------- 1 | #ifndef RAY_H_INCLUDED 2 | #define RAY_H_INCLUDED 3 | 4 | #include "glm.h" 5 | 6 | /// @brief Raycasting class associated with player's line of sight. 7 | class Ray { 8 | public: 9 | Ray(const glm::vec3 &position, const glm::vec3 &direction); 10 | 11 | void step(float scale); 12 | 13 | const glm::vec3 &getEnd() const; 14 | 15 | float getLength() const; 16 | 17 | private: 18 | glm::vec3 m_rayStart; 19 | glm::vec3 m_rayEnd; 20 | glm::vec3 m_direction; 21 | }; 22 | 23 | #endif // RAY_H_INCLUDED 24 | -------------------------------------------------------------------------------- /Source/Maths/Vector2XZ.cpp: -------------------------------------------------------------------------------- 1 | #include "Vector2XZ.h" 2 | 3 | bool operator==(const VectorXZ &left, const VectorXZ &right) noexcept 4 | { 5 | return (left.x == right.x) && (left.z == right.z); 6 | } 7 | -------------------------------------------------------------------------------- /Source/Maths/Vector2XZ.h: -------------------------------------------------------------------------------- 1 | #ifndef VECTOR2XZ_H_INCLUDED 2 | #define VECTOR2XZ_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | struct VectorXZ { 8 | int x, z; 9 | }; 10 | 11 | bool operator==(const VectorXZ &left, const VectorXZ &right) noexcept; 12 | 13 | namespace std { 14 | template <> struct hash { 15 | size_t operator()(const VectorXZ &vect) const noexcept 16 | { 17 | std::hash hasher; 18 | 19 | auto hash1 = hasher(vect.x); 20 | auto hash2 = hasher(vect.z); 21 | 22 | return std::hash{}((hash1 ^ hash2) >> 2); 23 | } 24 | }; 25 | } // namespace std 26 | 27 | namespace std { 28 | template <> struct hash { 29 | size_t operator()(const sf::Vector3i &vect) const noexcept 30 | { 31 | std::hash hasher; 32 | 33 | auto hash1 = hasher(vect.x); 34 | auto hash2 = hasher(vect.y); 35 | auto hash3 = hasher(vect.z); 36 | 37 | return std::hash{}( 38 | (hash1 ^ (hash2 << hash3) ^ hash3)); 39 | } 40 | }; 41 | } // namespace std 42 | 43 | #endif // VECTOR2XZ_H_INCLUDED 44 | -------------------------------------------------------------------------------- /Source/Maths/glm.h: -------------------------------------------------------------------------------- 1 | #ifndef GLM_H_INCLUDED 2 | #define GLM_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #endif // GLM_H_INCLUDED 9 | -------------------------------------------------------------------------------- /Source/Mesh.h: -------------------------------------------------------------------------------- 1 | #ifndef MESH_H_INCLUDED 2 | #define MESH_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | /// @brief Mesh struct used for the purpose of constructing block meshes. 8 | struct Mesh { 9 | std::vector vertexPositions; 10 | std::vector textureCoords; 11 | std::vector indices; 12 | }; 13 | 14 | #endif // MESH_H_INCLUDED 15 | -------------------------------------------------------------------------------- /Source/Model.cpp: -------------------------------------------------------------------------------- 1 | #include "Model.h" 2 | 3 | /// @brief Default constructor. 4 | /// @param mesh 5 | Model::Model(const Mesh &mesh) 6 | { 7 | addData(mesh); 8 | } 9 | 10 | Model::~Model() 11 | { 12 | deleteData(); 13 | } 14 | 15 | /// @brief Copy constructor. 16 | /// @param other 17 | Model::Model(Model &&other) 18 | : m_renderInfo(other.m_renderInfo) 19 | , m_vboCount(other.m_vboCount) 20 | , m_buffers(std::move(other.m_buffers)) 21 | { 22 | other.m_renderInfo.reset(); 23 | other.m_vboCount = 0; 24 | } 25 | 26 | /// @brief Operator function that declares a model can be assigned to another model. 27 | /// @param other 28 | /// @return *this 29 | Model &Model::operator=(Model &&other) 30 | { 31 | m_renderInfo = other.m_renderInfo; 32 | m_vboCount = other.m_vboCount; 33 | m_buffers = std::move(other.m_buffers); 34 | 35 | other.m_renderInfo.reset(); 36 | other.m_vboCount = 0; 37 | 38 | return *this; 39 | } 40 | 41 | void Model::genVAO() 42 | { 43 | if (m_renderInfo.vao != 0) 44 | deleteData(); 45 | 46 | glGenVertexArrays(1, &m_renderInfo.vao); 47 | glBindVertexArray(m_renderInfo.vao); 48 | } 49 | 50 | void Model::bindVAO() const 51 | { 52 | glBindVertexArray(m_renderInfo.vao); 53 | } 54 | 55 | void Model::addData(const Mesh &mesh) 56 | { 57 | genVAO(); 58 | 59 | addVBO(3, mesh.vertexPositions); 60 | addVBO(2, mesh.textureCoords); 61 | addEBO(mesh.indices); 62 | } 63 | 64 | void Model::addVBO(int dimensions, const std::vector &data) 65 | { 66 | GLuint vbo; 67 | glGenBuffers(1, &vbo); 68 | glBindBuffer(GL_ARRAY_BUFFER, vbo); 69 | glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), data.data(), 70 | GL_STATIC_DRAW); 71 | 72 | glVertexAttribPointer(static_cast(m_vboCount), dimensions, GL_FLOAT, 73 | GL_FALSE, 0, (GLvoid *)0); 74 | 75 | glEnableVertexAttribArray(static_cast(m_vboCount++)); 76 | 77 | m_buffers.push_back(vbo); 78 | } 79 | 80 | void Model::addEBO(const std::vector &indices) 81 | { 82 | m_renderInfo.indicesCount = static_cast(indices.size()); 83 | GLuint ebo; 84 | glGenBuffers(1, &ebo); 85 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); 86 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), 87 | indices.data(), GL_STATIC_DRAW); 88 | } 89 | 90 | /// @brief Deletes model data, used to free models from memory. 91 | void Model::deleteData() 92 | { 93 | if (m_renderInfo.vao) 94 | glDeleteVertexArrays(1, &m_renderInfo.vao); 95 | if (m_buffers.size() > 0) 96 | glDeleteBuffers(static_cast(m_buffers.size()), 97 | m_buffers.data()); 98 | 99 | m_buffers.clear(); 100 | 101 | m_vboCount = 0; 102 | m_renderInfo.reset(); 103 | } 104 | 105 | int Model::getIndicesCount() const 106 | { 107 | return m_renderInfo.indicesCount; 108 | } 109 | 110 | const RenderInfo &Model::getRenderInfo() const 111 | { 112 | return m_renderInfo; 113 | } 114 | -------------------------------------------------------------------------------- /Source/Model.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEL_H_INCLUDED 2 | #define MODEL_H_INCLUDED 3 | 4 | #include "Mesh.h" 5 | 6 | #include "Util/NonCopyable.h" 7 | 8 | #include "Renderer/RenderInfo.h" 9 | 10 | /// @brief Models using mesh data to spawn entities for the game world. 11 | class Model : public NonCopyable { 12 | public: 13 | Model() = default; 14 | Model(const Mesh &mesh); 15 | ~Model(); 16 | 17 | Model(Model &&other); 18 | Model &operator=(Model &&other); 19 | 20 | void addData(const Mesh &mesh); 21 | 22 | void deleteData(); 23 | 24 | void genVAO(); 25 | void addEBO(const std::vector &indices); 26 | void addVBO(int dimensions, const std::vector &data); 27 | void bindVAO() const; 28 | 29 | int getIndicesCount() const; 30 | 31 | const RenderInfo &getRenderInfo() const; 32 | 33 | private: 34 | RenderInfo m_renderInfo; 35 | 36 | int m_vboCount = 0; 37 | std::vector m_buffers; 38 | }; 39 | 40 | #endif // MODEL_H_INCLUDED 41 | -------------------------------------------------------------------------------- /Source/Physics/AABB.h: -------------------------------------------------------------------------------- 1 | #ifndef AABB_H_INCLUDED 2 | #define AABB_H_INCLUDED 3 | 4 | #include "../Maths/glm.h" 5 | 6 | /// @brief Collision detection class for 3D environment. 7 | struct AABB { 8 | AABB(const glm::vec3 &dim) 9 | : dimensions(dim) 10 | { 11 | } 12 | 13 | void update(const glm::vec3 &location) 14 | { 15 | position = location; 16 | } 17 | 18 | glm::vec3 getVN(const glm::vec3 &normal) const 19 | { 20 | glm::vec3 res = position; 21 | 22 | if (normal.x < 0) { 23 | res.x += dimensions.x; 24 | } 25 | if (normal.y < 0) { 26 | res.y += dimensions.y; 27 | } 28 | if (normal.z < 0) { 29 | res.z += dimensions.z; 30 | } 31 | 32 | return res; 33 | } 34 | 35 | glm::vec3 getVP(const glm::vec3 &normal) const 36 | { 37 | glm::vec3 res = position; 38 | 39 | if (normal.x > 0) { 40 | res.x += dimensions.x; 41 | } 42 | if (normal.y > 0) { 43 | res.y += dimensions.y; 44 | } 45 | if (normal.z > 0) { 46 | res.z += dimensions.z; 47 | } 48 | 49 | return res; 50 | } 51 | 52 | glm::vec3 position; 53 | const glm::vec3 dimensions; 54 | }; 55 | 56 | #endif // AABB_H_INCLUDED 57 | -------------------------------------------------------------------------------- /Source/Player/Player.cpp: -------------------------------------------------------------------------------- 1 | #include "Player.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../Input/Keyboard.h" 10 | #include "../Renderer/RenderMaster.h" 11 | #include "../World/World.h" 12 | 13 | sf::Font f; 14 | 15 | Player::Player() 16 | : Entity({2500, 125, 2500}, {0.f, 0.f, 0.f}, {0.3f, 1.f, 0.3f}) 17 | , m_itemDown(sf::Keyboard::Down) 18 | , m_itemUp(sf::Keyboard::Up) 19 | , m_flyKey(sf::Keyboard::F) 20 | , m_num1(sf::Keyboard::Num1) 21 | , m_num2(sf::Keyboard::Num2) 22 | , m_num3(sf::Keyboard::Num3) 23 | , m_num4(sf::Keyboard::Num4) 24 | , m_num5(sf::Keyboard::Num5) 25 | , m_slow(sf::Keyboard::LShift) 26 | , m_acceleration(glm::vec3(0.f)) 27 | 28 | { 29 | f.loadFromFile("Res/Fonts/rs.ttf"); 30 | 31 | for (int i = 0; i < 5; i++) { 32 | m_items.emplace_back(Material::NOTHING, 0); 33 | } 34 | 35 | for (float i = 0; i < 5; i++) { 36 | sf::Text t; 37 | t.setFont(f); 38 | t.setOutlineColor(sf::Color::Black); 39 | t.setCharacterSize(25); 40 | t.setPosition({20.0f, 20.0f * i + 100.0f}); 41 | m_itemText.push_back(t); 42 | } 43 | m_posPrint.setFont(f); 44 | m_posPrint.setOutlineColor(sf::Color::Black); 45 | m_posPrint.setCharacterSize(25); 46 | m_posPrint.setPosition(20.0f, 20.0f * 6.0f + 100.0f); 47 | } 48 | 49 | void Player::addItem(const Material &material) 50 | { 51 | Material::ID id = material.id; 52 | 53 | for (unsigned i = 0; i < m_items.size(); i++) { 54 | if (m_items[i].getMaterial().id == id) { 55 | /*int leftOver =*/m_items[i].add(1); 56 | 57 | return; 58 | } 59 | else if (m_items[i].getMaterial().id == Material::ID::Nothing) { 60 | m_items[i] = {material, 1}; 61 | return; 62 | } 63 | } 64 | } 65 | 66 | ItemStack &Player::getHeldItems() 67 | { 68 | return m_items[m_heldItem]; 69 | } 70 | 71 | void Player::handleInput(const sf::Window &window, Keyboard &keyboard) 72 | { 73 | keyboardInput(keyboard); 74 | mouseInput(window); 75 | 76 | if (m_itemDown.isKeyPressed()) { 77 | m_heldItem++; 78 | if (m_heldItem == (int)m_items.size()) { 79 | m_heldItem = 0; 80 | } 81 | } 82 | else if (m_itemUp.isKeyPressed()) { 83 | m_heldItem--; 84 | if (m_heldItem == -1) { 85 | m_heldItem = m_items.size() - 1; 86 | } 87 | } 88 | 89 | if (m_flyKey.isKeyPressed()) { 90 | m_isFlying = !m_isFlying; 91 | } 92 | 93 | if (m_num1.isKeyPressed()) { 94 | m_heldItem = 0; 95 | } 96 | if (m_num2.isKeyPressed()) { 97 | m_heldItem = 1; 98 | } 99 | if (m_num3.isKeyPressed()) { 100 | m_heldItem = 2; 101 | } 102 | if (m_num4.isKeyPressed()) { 103 | m_heldItem = 3; 104 | } 105 | if (m_num5.isKeyPressed()) { 106 | m_heldItem = 4; 107 | } 108 | if (m_slow.isKeyPressed()) 109 | { 110 | m_isSneak = !m_isSneak; 111 | } 112 | } 113 | 114 | void Player::update(float dt, World &world) 115 | { 116 | velocity += m_acceleration; 117 | m_acceleration = {0, 0, 0}; 118 | 119 | if (!m_isFlying) { 120 | if (!m_isOnGround) { 121 | velocity.y -= 40 * dt; 122 | } 123 | m_isOnGround = false; 124 | } 125 | 126 | if (position.y <= 0 && !m_isFlying) { 127 | position.y = 300; 128 | } 129 | 130 | position.x += velocity.x * dt; 131 | collide(world, {velocity.x, 0, 0}, dt); 132 | 133 | position.y += velocity.y * dt; 134 | collide(world, {0, velocity.y, 0}, dt); 135 | 136 | position.z += velocity.z * dt; 137 | collide(world, {0, 0, velocity.z}, dt); 138 | 139 | box.update(position); 140 | velocity.x *= 0.95f; 141 | velocity.z *= 0.95f; 142 | if (m_isFlying) { 143 | velocity.y *= 0.95f; 144 | } 145 | } 146 | 147 | void Player::collide(World &world, const glm::vec3 &vel, float dt) 148 | { 149 | for (int x = position.x - box.dimensions.x; 150 | x < position.x + box.dimensions.x; x++) 151 | for (int y = position.y - box.dimensions.y; y < position.y + 0.7; y++) 152 | for (int z = position.z - box.dimensions.z; 153 | z < position.z + box.dimensions.z; z++) { 154 | auto block = world.getBlock(x, y, z); 155 | 156 | if (block != 0 && block.getData().isCollidable) { 157 | if (vel.y > 0) { 158 | position.y = y - box.dimensions.y; 159 | velocity.y = 0; 160 | } 161 | else if (vel.y < 0) { 162 | m_isOnGround = true; 163 | position.y = y + box.dimensions.y + 1; 164 | velocity.y = 0; 165 | } 166 | 167 | if (vel.x > 0) { 168 | position.x = x - box.dimensions.x; 169 | } 170 | else if (vel.x < 0) { 171 | position.x = x + box.dimensions.x + 1; 172 | } 173 | 174 | if (vel.z > 0) { 175 | position.z = z - box.dimensions.z; 176 | } 177 | else if (vel.z < 0) { 178 | position.z = z + box.dimensions.z + 1; 179 | } 180 | } 181 | } 182 | } 183 | 184 | ///@TODO Move this 185 | float speed = 0.2f; 186 | 187 | void Player::keyboardInput(Keyboard &keyboard) 188 | { 189 | if (keyboard.isKeyDown(sf::Keyboard::W)) { 190 | float s = speed; 191 | if (sf::Keyboard::isKeyPressed(sf::Keyboard::LControl)) 192 | s *= 5; 193 | else if(sf::Keyboard::isKeyPressed(sf::Keyboard::RShift) || 194 | sf::Keyboard::isKeyPressed(sf::Keyboard::LShift)) 195 | s *= 0.35; 196 | m_acceleration.x += -glm::cos(glm::radians(rotation.y + 90)) * s; 197 | m_acceleration.z += -glm::sin(glm::radians(rotation.y + 90)) * s; 198 | } 199 | if (keyboard.isKeyDown(sf::Keyboard::S)) { 200 | m_acceleration.x += glm::cos(glm::radians(rotation.y + 90)) * speed; 201 | m_acceleration.z += glm::sin(glm::radians(rotation.y + 90)) * speed; 202 | } 203 | if (keyboard.isKeyDown(sf::Keyboard::A)) { 204 | m_acceleration.x += -glm::cos(glm::radians(rotation.y)) * speed; 205 | m_acceleration.z += -glm::sin(glm::radians(rotation.y)) * speed; 206 | } 207 | if (keyboard.isKeyDown(sf::Keyboard::D)) { 208 | m_acceleration.x += glm::cos(glm::radians(rotation.y)) * speed; 209 | m_acceleration.z += glm::sin(glm::radians(rotation.y)) * speed; 210 | } 211 | 212 | if (keyboard.isKeyDown(sf::Keyboard::Space)) { 213 | jump(); 214 | } 215 | else if (keyboard.isKeyDown(sf::Keyboard::LShift) && m_isFlying) { 216 | m_acceleration.y -= speed * 3; 217 | } 218 | } 219 | 220 | void Player::mouseInput(const sf::Window &window) 221 | { 222 | static bool useMouse = true; 223 | static ToggleKey useMouseKey(sf::Keyboard::L); 224 | 225 | if (useMouseKey.isKeyPressed()) { 226 | useMouse = !useMouse; 227 | } 228 | 229 | if (!useMouse) { 230 | return; 231 | } 232 | 233 | static float const BOUND = 89.f; 234 | static auto lastMousePosition = sf::Mouse::getPosition(window); 235 | auto change = sf::Mouse::getPosition() - lastMousePosition; 236 | 237 | rotation.y += change.x * 0.05f; 238 | rotation.x += change.y * 0.05f; 239 | 240 | if (rotation.x > BOUND) 241 | rotation.x = BOUND; 242 | else if (rotation.x < -BOUND) 243 | rotation.x = -BOUND; 244 | 245 | if (rotation.y > 360) 246 | rotation.y = 0; 247 | else if (rotation.y < 0) 248 | rotation.y = 360; 249 | 250 | auto cx = static_cast(window.getSize().x / 2); 251 | auto cy = static_cast(window.getSize().y / 2); 252 | 253 | sf::Mouse::setPosition({cx, cy}, window); 254 | 255 | lastMousePosition = sf::Mouse::getPosition(); 256 | } 257 | 258 | void Player::draw(RenderMaster &master) 259 | { 260 | for (unsigned i = 0; i < m_items.size(); i++) { 261 | sf::Text &t = m_itemText[i]; 262 | if (i == (unsigned)m_heldItem) { 263 | t.setFillColor(sf::Color::Red); 264 | } 265 | else { 266 | t.setFillColor(sf::Color::White); 267 | } 268 | t.setString((m_items[i].getMaterial().name) + " " + 269 | std::to_string(m_items[i].getNumInStack()) + " "); 270 | // master.drawSFML(t); 271 | } 272 | std::ostringstream stream; 273 | stream << " X: " << position.x << " Y: " << position.y 274 | << " Z: " << position.z << " Grounded " << std::boolalpha 275 | << m_isOnGround; 276 | 277 | m_posPrint.setString(stream.str()); 278 | } 279 | 280 | void Player::jump() 281 | { 282 | if (!m_isFlying) { 283 | if (m_isOnGround) { 284 | 285 | m_isOnGround = false; 286 | m_acceleration.y += speed * 50; 287 | } 288 | } 289 | else { 290 | m_acceleration.y += speed * 3; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /Source/Player/Player.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_H_INCLUDED 2 | #define PLAYER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../Entity.h" 9 | #include "../Input/ToggleKey.h" 10 | #include "../Item/ItemStack.h" 11 | 12 | class Keyboard; 13 | class World; 14 | class RenderMaster; 15 | 16 | /// @brief Player character, including player movements and world interactions. 17 | class Player : public Entity { 18 | public: 19 | Player(); 20 | 21 | void handleInput(const sf::Window &window, Keyboard &keyboard); 22 | 23 | void update(float dt, World &wolrd); 24 | void collide(World &world, const glm::vec3 &vel, float dt); 25 | 26 | void addItem(const Material &material); 27 | 28 | void draw(RenderMaster &master); 29 | 30 | ItemStack &getHeldItems(); 31 | 32 | private: 33 | void jump(); 34 | 35 | void keyboardInput(Keyboard &keyboard); 36 | void mouseInput(const sf::Window &window); 37 | bool m_isOnGround = false; 38 | bool m_isFlying = false; 39 | bool m_isSneak = false; 40 | 41 | std::vector m_items; 42 | std::vector m_itemText; 43 | sf::Text m_posPrint; 44 | int m_heldItem = 0; 45 | 46 | ToggleKey m_itemDown; 47 | ToggleKey m_itemUp; 48 | ToggleKey m_flyKey; 49 | 50 | ToggleKey m_num1; 51 | ToggleKey m_num2; 52 | ToggleKey m_num3; 53 | ToggleKey m_num4; 54 | ToggleKey m_num5; 55 | 56 | ToggleKey m_slow; 57 | 58 | glm::vec3 m_acceleration; 59 | }; 60 | 61 | #endif // PLAYER_H_INCLUDED 62 | -------------------------------------------------------------------------------- /Source/Renderer/ChunkRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkRenderer.h" 2 | 3 | #include "../World/Block/BlockDatabase.h" 4 | #include "../World/Chunk/ChunkMesh.h" 5 | 6 | #include "../Camera.h" 7 | 8 | #include 9 | 10 | void ChunkRenderer::add(const ChunkMesh &mesh) 11 | { 12 | m_chunks.push_back(&mesh.getModel().getRenderInfo()); 13 | } 14 | 15 | void ChunkRenderer::render(const Camera &camera) 16 | { 17 | if (m_chunks.empty()) { 18 | return; 19 | } 20 | 21 | glDisable(GL_BLEND); 22 | glEnable(GL_CULL_FACE); 23 | 24 | m_shader.useProgram(); 25 | BlockDatabase::get().textureAtlas.bindTexture(); 26 | 27 | m_shader.loadProjectionViewMatrix(camera.getProjectionViewMatrix()); 28 | 29 | for (auto mesh : m_chunks) { 30 | GL::bindVAO(mesh->vao); 31 | GL::drawElements(mesh->indicesCount); 32 | } 33 | 34 | m_chunks.clear(); 35 | } 36 | -------------------------------------------------------------------------------- /Source/Renderer/ChunkRenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKRENDERER_H_INCLUDED 2 | #define CHUNKRENDERER_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "../Shaders/ChunkShader.h" 7 | 8 | struct RenderInfo; 9 | class ChunkMesh; 10 | class Camera; 11 | 12 | /// @brief Block chunk renderer that helps display block data. 13 | class ChunkRenderer { 14 | public: 15 | void add(const ChunkMesh &mesh); 16 | void render(const Camera &camera); 17 | 18 | private: 19 | std::vector m_chunks; 20 | 21 | ChunkShader m_shader; 22 | }; 23 | 24 | #endif // CHUNKRENDERER_H_INCLUDED 25 | -------------------------------------------------------------------------------- /Source/Renderer/FloraRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "FloraRenderer.h" 2 | 3 | #include "../Application.h" 4 | #include "../Camera.h" 5 | #include "../World/Block/BlockDatabase.h" 6 | #include "../World/Chunk/ChunkMesh.h" 7 | 8 | #include 9 | 10 | void FloraRenderer::add(const ChunkMesh &mesh) 11 | { 12 | m_chunks.push_back(&mesh.getModel().getRenderInfo()); 13 | } 14 | 15 | void FloraRenderer::render(const Camera &camera) 16 | { 17 | if (m_chunks.empty()) { 18 | return; 19 | } 20 | 21 | glDisable(GL_BLEND); 22 | glDisable(GL_CULL_FACE); 23 | m_shader.useProgram(); 24 | 25 | m_shader.loadProjectionViewMatrix(camera.getProjectionViewMatrix()); 26 | m_shader.loadTime(g_timeElapsed); 27 | 28 | for (auto mesh : m_chunks) { 29 | GL::bindVAO(mesh->vao); 30 | GL::drawElements(mesh->indicesCount); 31 | } 32 | 33 | m_chunks.clear(); 34 | } 35 | -------------------------------------------------------------------------------- /Source/Renderer/FloraRenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef FLORARENDERER_H_INCLUDED 2 | #define FLORARENDERER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "../Shaders/FloraShader.h" 8 | 9 | struct RenderInfo; 10 | class ChunkMesh; 11 | class Camera; 12 | 13 | /// @brief Renderer handling 'flora' based entities that are not true blocks. 14 | class FloraRenderer { 15 | public: 16 | void add(const ChunkMesh &mesh); 17 | void render(const Camera &camera); 18 | 19 | private: 20 | std::vector m_chunks; 21 | 22 | FloraShader m_shader; 23 | }; 24 | 25 | #endif // FLORARENDERER_H_INCLUDED 26 | -------------------------------------------------------------------------------- /Source/Renderer/RenderInfo.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERINFO_H_INCLUDED 2 | #define RENDERINFO_H_INCLUDED 3 | 4 | struct RenderInfo { 5 | GLuint vao = 0; 6 | GLuint indicesCount = 0; 7 | 8 | inline void reset() 9 | { 10 | vao = 0; 11 | indicesCount = 0; 12 | } 13 | }; 14 | 15 | #endif // RENDERINFO_H_INCLUDED 16 | -------------------------------------------------------------------------------- /Source/Renderer/RenderMaster.cpp: -------------------------------------------------------------------------------- 1 | #include "RenderMaster.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../Application.h" 7 | #include "../Context.h" 8 | #include "../World/Chunk/ChunkMesh.h" 9 | #include "../World/Chunk/ChunkSection.h" 10 | 11 | void RenderMaster::drawChunk(const ChunkSection &chunk) 12 | { 13 | const auto &solidMesh = chunk.getMeshes().solidMesh; 14 | const auto &waterMesh = chunk.getMeshes().waterMesh; 15 | const auto &floraMesh = chunk.getMeshes().floraMesh; 16 | 17 | if (solidMesh.faces > 0) 18 | m_chunkRenderer.add(solidMesh); 19 | 20 | if (waterMesh.faces > 0) 21 | m_waterRenderer.add(waterMesh); 22 | 23 | if (floraMesh.faces > 0) 24 | m_floraRenderer.add(floraMesh); 25 | } 26 | 27 | void RenderMaster::drawSky() 28 | { 29 | m_drawBox = true; 30 | } 31 | 32 | void RenderMaster::finishRender(sf::Window &window, const Camera &camera) 33 | { 34 | glClearColor(0.0, 0.0, 0.0, 1.0); 35 | glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); 36 | 37 | glEnable(GL_DEPTH_TEST); 38 | glEnable(GL_CULL_FACE); 39 | m_chunkRenderer.render(camera); 40 | m_waterRenderer.render(camera); 41 | m_floraRenderer.render(camera); 42 | 43 | if (m_drawBox) { 44 | glDisable(GL_CULL_FACE); 45 | m_skyboxRenderer.render(camera); 46 | m_drawBox = false; 47 | } 48 | 49 | window.display(); 50 | } 51 | -------------------------------------------------------------------------------- /Source/Renderer/RenderMaster.h: -------------------------------------------------------------------------------- 1 | #ifndef RENDERMASTER_H_INCLUDED 2 | #define RENDERMASTER_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "../Config.h" 7 | #include "ChunkRenderer.h" 8 | #include "FloraRenderer.h" 9 | #include "SkyboxRenderer.h" 10 | #include "WaterRenderer.h" 11 | 12 | class Camera; 13 | class ChunkSection; 14 | 15 | /// @brief Master rendering class that handles the sum of drawn in-game objects. 16 | class RenderMaster { 17 | public: 18 | void drawChunk(const ChunkSection &chunk); 19 | void drawSky(); 20 | 21 | void finishRender(sf::Window &window, const Camera &camera); 22 | 23 | private: 24 | // Chunks 25 | ChunkRenderer m_chunkRenderer; 26 | WaterRenderer m_waterRenderer; 27 | FloraRenderer m_floraRenderer; 28 | 29 | // Detail 30 | SkyboxRenderer m_skyboxRenderer; 31 | 32 | bool m_drawBox = false; 33 | }; 34 | 35 | #endif // RENDERMASTER_H_INCLUDED 36 | -------------------------------------------------------------------------------- /Source/Renderer/SkyboxRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "SkyboxRenderer.h" 2 | 3 | #include "../Camera.h" 4 | 5 | #include 6 | 7 | SkyboxRenderer::SkyboxRenderer() 8 | { 9 | constexpr GLfloat SIZE = 500; 10 | std::vector vertexCoords{ 11 | // Back 12 | SIZE, 13 | -SIZE, 14 | -SIZE, 15 | -SIZE, 16 | -SIZE, 17 | -SIZE, 18 | -SIZE, 19 | SIZE, 20 | -SIZE, 21 | SIZE, 22 | SIZE, 23 | -SIZE, 24 | 25 | // Front 26 | -SIZE, 27 | -SIZE, 28 | SIZE, 29 | SIZE, 30 | -SIZE, 31 | SIZE, 32 | SIZE, 33 | SIZE, 34 | SIZE, 35 | -SIZE, 36 | SIZE, 37 | SIZE, 38 | 39 | // Right 40 | SIZE, 41 | -SIZE, 42 | SIZE, 43 | SIZE, 44 | -SIZE, 45 | -SIZE, 46 | SIZE, 47 | SIZE, 48 | -SIZE, 49 | SIZE, 50 | SIZE, 51 | SIZE, 52 | 53 | // Left 54 | -SIZE, 55 | -SIZE, 56 | -SIZE, 57 | -SIZE, 58 | -SIZE, 59 | SIZE, 60 | -SIZE, 61 | SIZE, 62 | SIZE, 63 | -SIZE, 64 | SIZE, 65 | -SIZE, 66 | 67 | // Top 68 | -SIZE, 69 | SIZE, 70 | SIZE, 71 | SIZE, 72 | SIZE, 73 | SIZE, 74 | SIZE, 75 | SIZE, 76 | -SIZE, 77 | -SIZE, 78 | SIZE, 79 | -SIZE, 80 | 81 | // Bottom 82 | -SIZE, 83 | -SIZE, 84 | -SIZE, 85 | SIZE, 86 | -SIZE, 87 | -SIZE, 88 | SIZE, 89 | -SIZE, 90 | SIZE, 91 | -SIZE, 92 | -SIZE, 93 | SIZE, 94 | }; 95 | 96 | std::vector indices{0, 1, 2, 2, 3, 0, 97 | 98 | 4, 5, 6, 6, 7, 4, 99 | 100 | 8, 9, 10, 10, 11, 8, 101 | 102 | 12, 13, 14, 14, 15, 12, 103 | 104 | 16, 17, 18, 18, 19, 16, 105 | 106 | 20, 21, 22, 22, 23, 20}; 107 | 108 | m_skyCube.genVAO(); 109 | m_skyCube.addVBO(3, vertexCoords); 110 | m_skyCube.addEBO(indices); 111 | 112 | m_cubeTexture.loadFromFiles({ 113 | "dm", 114 | "dm", 115 | "dt", 116 | "db", 117 | "dm", 118 | "dm", 119 | }); 120 | } 121 | 122 | void SkyboxRenderer::render(const Camera &camera) 123 | { 124 | m_shader.useProgram(); 125 | m_skyCube.bindVAO(); 126 | m_cubeTexture.bindTexture(); 127 | 128 | m_shader.loadViewMatrix(camera.getViewMatrix()); 129 | m_shader.loadProjectionMatrix(camera.getProjMatrix()); 130 | 131 | GL::drawElements(m_skyCube.getIndicesCount()); 132 | } 133 | -------------------------------------------------------------------------------- /Source/Renderer/SkyboxRenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef SKYBOXRENDERER_H_INCLUDED 2 | #define SKYBOXRENDERER_H_INCLUDED 3 | 4 | #include "../Model.h" 5 | #include "../Shaders/SkyboxShader.h" 6 | #include "../Texture/CubeTexture.h" 7 | 8 | class Camera; 9 | 10 | /// @brief Renderer that specifically draws the skybox and entities outside player reach. 11 | class SkyboxRenderer { 12 | public: 13 | SkyboxRenderer(); 14 | 15 | void render(const Camera &camera); 16 | 17 | private: 18 | Model m_skyCube; 19 | SkyboxShader m_shader; 20 | CubeTexture m_cubeTexture; 21 | }; 22 | 23 | #endif // SKYBOXRENDERER_H_INCLUDED 24 | -------------------------------------------------------------------------------- /Source/Renderer/WaterRenderer.cpp: -------------------------------------------------------------------------------- 1 | #include "WaterRenderer.h" 2 | 3 | #include "../Application.h" 4 | #include "../Camera.h" 5 | #include "../World/Block/BlockDatabase.h" 6 | #include "../World/Chunk/ChunkMesh.h" 7 | 8 | #include 9 | 10 | void WaterRenderer::add(const ChunkMesh &mesh) 11 | { 12 | m_chunks.push_back(&mesh.getModel().getRenderInfo()); 13 | } 14 | 15 | void WaterRenderer::render(const Camera &camera) 16 | { 17 | if (m_chunks.empty()) { 18 | return; 19 | } 20 | 21 | glEnable(GL_BLEND); 22 | glDisable(GL_CULL_FACE); 23 | m_shader.useProgram(); 24 | 25 | m_shader.loadProjectionViewMatrix(camera.getProjectionViewMatrix()); 26 | m_shader.loadTime(g_timeElapsed); 27 | 28 | for (auto mesh : m_chunks) { 29 | GL::bindVAO(mesh->vao); 30 | GL::drawElements(mesh->indicesCount); 31 | } 32 | 33 | m_chunks.clear(); 34 | } 35 | -------------------------------------------------------------------------------- /Source/Renderer/WaterRenderer.h: -------------------------------------------------------------------------------- 1 | #ifndef WATERRENDERER_H_INCLUDED 2 | #define WATERRENDERER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "../Shaders/WaterShader.h" 8 | 9 | struct RenderInfo; 10 | class ChunkMesh; 11 | class Camera; 12 | 13 | /// @brief Renderer specifically targeting water and handling shader behaviors. 14 | class WaterRenderer { 15 | public: 16 | void add(const ChunkMesh &mesh); 17 | void render(const Camera &camera); 18 | 19 | private: 20 | std::vector m_chunks; 21 | 22 | WaterShader m_shader; 23 | }; 24 | 25 | #endif // WATERRENDERER_H_INCLUDED 26 | -------------------------------------------------------------------------------- /Source/Shaders/BasicShader.cpp: -------------------------------------------------------------------------------- 1 | #include "BasicShader.h" 2 | 3 | BasicShader::BasicShader(const std::string &vertexFile, 4 | const std::string &fragmentFile) 5 | : Shader(vertexFile, fragmentFile) 6 | { 7 | getUniforms(); 8 | } 9 | 10 | void BasicShader::loadProjectionViewMatrix(const glm::mat4 &pvMatrix) 11 | { 12 | loadMatrix4(m_locationProjectionViewMatrix, pvMatrix); 13 | } 14 | 15 | void BasicShader::loadModelMatrix(const glm::mat4 &matrix) 16 | { 17 | loadMatrix4(m_locationModelMatrix, matrix); 18 | } 19 | 20 | void BasicShader::getUniforms() 21 | { 22 | useProgram(); 23 | m_locationProjectionViewMatrix = 24 | glGetUniformLocation(m_id, "projViewMatrix"); 25 | m_locationModelMatrix = glGetUniformLocation(m_id, "modelMatrix"); 26 | } 27 | -------------------------------------------------------------------------------- /Source/Shaders/BasicShader.h: -------------------------------------------------------------------------------- 1 | #ifndef BASICSHADER_H_INCLUDED 2 | #define BASICSHADER_H_INCLUDED 3 | 4 | #include "Shader.h" 5 | 6 | class BasicShader : public Shader { 7 | public: 8 | BasicShader(const std::string &vertexFile = "Basic", 9 | const std::string &fragmentFile = "Basic"); 10 | 11 | void loadProjectionViewMatrix(const glm::mat4 &pvMatrix); 12 | void loadModelMatrix(const glm::mat4 &matrix); 13 | 14 | protected: 15 | virtual void getUniforms() override; 16 | 17 | private: 18 | GLuint m_locationProjectionViewMatrix; 19 | GLuint m_locationModelMatrix; 20 | }; 21 | 22 | #endif // BASICSHADER_H_INCLUDED 23 | -------------------------------------------------------------------------------- /Source/Shaders/ChunkShader.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkShader.h" 2 | 3 | ChunkShader::ChunkShader() 4 | : BasicShader("Chunk", "Chunk") 5 | { 6 | getUniforms(); 7 | } 8 | 9 | void ChunkShader::getUniforms() 10 | { 11 | BasicShader::getUniforms(); 12 | } 13 | -------------------------------------------------------------------------------- /Source/Shaders/ChunkShader.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKSHADER_H_INCLUDED 2 | #define CHUNKSHADER_H_INCLUDED 3 | 4 | #include "BasicShader.h" 5 | 6 | class ChunkShader : public BasicShader { 7 | public: 8 | ChunkShader(); 9 | 10 | private: 11 | void getUniforms() override; 12 | }; 13 | 14 | #endif // CHUNKSHADER_H_INCLUDED 15 | -------------------------------------------------------------------------------- /Source/Shaders/FloraShader.cpp: -------------------------------------------------------------------------------- 1 | #include "FloraShader.h" 2 | 3 | FloraShader::FloraShader() 4 | : BasicShader("Flora", "Chunk") 5 | { 6 | getUniforms(); 7 | } 8 | 9 | void FloraShader::loadTime(const float &time) 10 | { 11 | loadFloat(m_time, time); 12 | } 13 | 14 | void FloraShader::getUniforms() 15 | { 16 | BasicShader::getUniforms(); 17 | m_time = glGetUniformLocation(m_id, "globalTime"); 18 | } 19 | -------------------------------------------------------------------------------- /Source/Shaders/FloraShader.h: -------------------------------------------------------------------------------- 1 | #ifndef FLORASHADER_H_INCLUDED 2 | #define FLORASHADER_H_INCLUDED 3 | 4 | #include "BasicShader.h" 5 | 6 | class FloraShader : public BasicShader { 7 | public: 8 | FloraShader(); 9 | void loadTime(const float &time); 10 | 11 | private: 12 | void getUniforms() override; 13 | GLuint m_time; 14 | }; 15 | 16 | #endif // FLORASHADER_H_INCLUDED 17 | -------------------------------------------------------------------------------- /Source/Shaders/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.h" 2 | 3 | #include "ShaderLoader.h" 4 | 5 | Shader::Shader(const std::string &vertexFile, const std::string &fragmentFile) 6 | : m_id(loadShaders(vertexFile, fragmentFile)) 7 | { 8 | useProgram(); 9 | } 10 | 11 | void Shader::loadInt(GLuint location, int value) 12 | { 13 | glUniform1i(location, value); 14 | } 15 | 16 | void Shader::loadFloat(GLuint location, float value) 17 | { 18 | glUniform1f(location, value); 19 | } 20 | 21 | void Shader::loadVector2(GLuint location, const glm::vec2 &vect) 22 | { 23 | glUniform2f(location, vect.x, vect.y); 24 | } 25 | 26 | void Shader::loadVector3(GLuint location, const glm::vec3 &vect) 27 | { 28 | glUniform3f(location, vect.x, vect.y, vect.z); 29 | } 30 | 31 | void Shader::loadVector4(GLuint location, const glm::vec4 &vect) 32 | { 33 | glUniform4f(location, vect.x, vect.y, vect.z, vect.w); 34 | } 35 | 36 | void Shader::loadMatrix4(GLuint location, const glm::mat4 &matrix) 37 | { 38 | glUniformMatrix4fv(location, 1, GL_FALSE, glm::value_ptr(matrix)); 39 | } 40 | 41 | Shader::~Shader() 42 | { 43 | glDeleteProgram(m_id); 44 | } 45 | 46 | void Shader::useProgram() const 47 | { 48 | glUseProgram(m_id); 49 | } 50 | -------------------------------------------------------------------------------- /Source/Shaders/Shader.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADER_H_INCLUDED 2 | #define SHADER_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "../GL/GLFunctions.h" 7 | #include "../Maths/glm.h" 8 | #include "../Util/NonCopyable.h" 9 | 10 | class Shader : NonCopyable { 11 | public: 12 | Shader(const std::string &vertexFile, const std::string &fragmentFile); 13 | virtual ~Shader(); 14 | 15 | void useProgram() const; 16 | 17 | void loadInt(GLuint location, int value); 18 | void loadFloat(GLuint location, float value); 19 | 20 | void loadVector2(GLuint location, const glm::vec2 &vect); 21 | void loadVector3(GLuint location, const glm::vec3 &vect); 22 | void loadVector4(GLuint location, const glm::vec4 &vect); 23 | 24 | void loadMatrix4(GLuint location, const glm::mat4 &matrix); 25 | 26 | protected: 27 | virtual void getUniforms() = 0; 28 | GLuint m_id; 29 | }; 30 | 31 | #endif // SHADER_H_INCLUDED 32 | -------------------------------------------------------------------------------- /Source/Shaders/ShaderLoader.cpp: -------------------------------------------------------------------------------- 1 | #include "ShaderLoader.h" 2 | 3 | #include "../Util/FileUtil.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | namespace { 10 | GLuint compileShader(const GLchar *source, GLenum shaderType) 11 | { 12 | auto shaderID = glCreateShader(shaderType); 13 | 14 | glShaderSource(shaderID, 1, &source, nullptr); 15 | glCompileShader(shaderID); 16 | 17 | GLint isSuccess = 0; 18 | GLchar infoLog[512]; 19 | 20 | glGetShaderiv(shaderID, GL_COMPILE_STATUS, &isSuccess); 21 | if (!isSuccess) { 22 | glGetShaderInfoLog(shaderID, 512, nullptr, infoLog); 23 | throw std::runtime_error("Unable to load a shader: " + 24 | std::string(infoLog)); 25 | } 26 | 27 | return shaderID; 28 | } 29 | 30 | GLuint linkProgram(GLuint vertexShaderID, GLuint fragmentShaderID) 31 | { 32 | auto id = glCreateProgram(); 33 | 34 | glAttachShader(id, vertexShaderID); 35 | glAttachShader(id, fragmentShaderID); 36 | 37 | glLinkProgram(id); 38 | 39 | return id; 40 | } 41 | } // namespace 42 | 43 | GLuint loadShaders(const std::string &vertexShader, 44 | const std::string &fragmentShader) 45 | { 46 | auto vertexSource = getFileContents("Shaders/" + vertexShader + ".vert"); 47 | auto fragmentSource = 48 | getFileContents("Shaders/" + fragmentShader + ".frag"); 49 | 50 | auto vertexShaderID = compileShader(vertexSource.c_str(), GL_VERTEX_SHADER); 51 | auto fragmentShaderID = 52 | compileShader(fragmentSource.c_str(), GL_FRAGMENT_SHADER); 53 | 54 | auto shaderID = linkProgram(vertexShaderID, fragmentShaderID); 55 | 56 | glDeleteShader(vertexShaderID); 57 | glDeleteShader(fragmentShaderID); 58 | 59 | return shaderID; 60 | } 61 | -------------------------------------------------------------------------------- /Source/Shaders/ShaderLoader.h: -------------------------------------------------------------------------------- 1 | #ifndef SHADERLOADER_H_INCLUDED 2 | #define SHADERLOADER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | GLuint loadShaders(const std::string &vertexShader, 8 | const std::string &fragmentShader); 9 | 10 | #endif // SHADERLOADER_H_INCLUDED 11 | -------------------------------------------------------------------------------- /Source/Shaders/SkyboxShader.cpp: -------------------------------------------------------------------------------- 1 | #include "SkyboxShader.h" 2 | 3 | SkyboxShader::SkyboxShader() 4 | : Shader("Skybox", "Skybox") 5 | { 6 | getUniforms(); 7 | } 8 | 9 | void SkyboxShader::loadViewMatrix(glm::mat4 viewMatrix) 10 | { 11 | viewMatrix[3][0] = 0; 12 | viewMatrix[3][1] = 0; 13 | viewMatrix[3][2] = 0; 14 | Shader::loadMatrix4(m_locationView, viewMatrix); 15 | } 16 | 17 | void SkyboxShader::loadProjectionMatrix(const glm::mat4 &proj) 18 | { 19 | Shader::loadMatrix4(m_locationProjection, proj); 20 | } 21 | 22 | void SkyboxShader::getUniforms() 23 | { 24 | m_locationProjection = glGetUniformLocation(m_id, "projectionMatrix"); 25 | m_locationView = glGetUniformLocation(m_id, "viewMatrix"); 26 | } 27 | -------------------------------------------------------------------------------- /Source/Shaders/SkyboxShader.h: -------------------------------------------------------------------------------- 1 | #ifndef SKYBOXSHADER_H_INCLUDED 2 | #define SKYBOXSHADER_H_INCLUDED 3 | 4 | #include "Shader.h" 5 | 6 | class SkyboxShader : public Shader { 7 | public: 8 | SkyboxShader(); 9 | 10 | void loadViewMatrix(glm::mat4 viewMatrix); 11 | void loadProjectionMatrix(const glm::mat4 &proj); 12 | 13 | private: 14 | void getUniforms() override; 15 | 16 | GLuint m_locationProjection; 17 | GLuint m_locationView; 18 | }; 19 | 20 | #endif // SKYBOXSHADER_H_INCLUDED 21 | -------------------------------------------------------------------------------- /Source/Shaders/WaterShader.cpp: -------------------------------------------------------------------------------- 1 | #include "WaterShader.h" 2 | 3 | WaterShader::WaterShader() 4 | : BasicShader("Water", "Chunk") 5 | { 6 | getUniforms(); 7 | } 8 | 9 | void WaterShader::loadTime(const float &time) 10 | { 11 | loadFloat(m_time, time); 12 | } 13 | 14 | void WaterShader::getUniforms() 15 | { 16 | BasicShader::getUniforms(); 17 | m_time = glGetUniformLocation(m_id, "globalTime"); 18 | } 19 | -------------------------------------------------------------------------------- /Source/Shaders/WaterShader.h: -------------------------------------------------------------------------------- 1 | #ifndef WATERSHADER_H_INCLUDED 2 | #define WATERSHADER_H_INCLUDED 3 | 4 | #include "BasicShader.h" 5 | 6 | /// @brief Shader affecting water blocks specifically. 7 | class WaterShader : public BasicShader { 8 | public: 9 | WaterShader(); 10 | void loadTime(const float &time); 11 | 12 | private: 13 | void getUniforms() override; 14 | GLuint m_time; 15 | }; 16 | 17 | #endif // WATERSHADER_H_INCLUDED 18 | -------------------------------------------------------------------------------- /Source/States/PlayState.cpp: -------------------------------------------------------------------------------- 1 | #include "PlayState.h" 2 | 3 | #include "../Application.h" 4 | #include "../Maths/Ray.h" 5 | #include "../Renderer/RenderMaster.h" 6 | #include "../World/Event/PlayerDigEvent.h" 7 | 8 | #include 9 | 10 | StatePlay::StatePlay(Application &app, const Config &config) 11 | : StateBase(app) 12 | , m_world(app.getCamera(), config, m_player) 13 | { 14 | app.getCamera().hookEntity(m_player); 15 | } 16 | 17 | void StatePlay::handleEvent(sf::Event e) 18 | { 19 | m_keyboard.update(e); 20 | } 21 | 22 | void StatePlay::handleInput() 23 | { 24 | m_player.handleInput(m_pApplication->getWindow(), m_keyboard); 25 | 26 | static sf::Clock timer; 27 | glm::vec3 lastPosition; 28 | 29 | // Ray is cast as player's 'vision' 30 | for (Ray ray({m_player.position.x, m_player.position.y + 0.6f, 31 | m_player.position.z}, 32 | m_player.rotation); // Corrected for camera offset 33 | ray.getLength() < 6; ray.step(0.05f)) { 34 | int x = static_cast(ray.getEnd().x); 35 | int y = static_cast(ray.getEnd().y); 36 | int z = static_cast(ray.getEnd().z); 37 | 38 | auto block = m_world.getBlock(x, y, z); 39 | auto id = (BlockId)block.id; 40 | 41 | if (id != BlockId::Air && id != BlockId::Water) { 42 | if (timer.getElapsedTime().asSeconds() > 0.2) { 43 | if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) { 44 | timer.restart(); 45 | // The player "digs" the block up 46 | m_world.addEvent(sf::Mouse::Left, 47 | ray.getEnd(), m_player); 48 | break; 49 | } 50 | else if (sf::Mouse::isButtonPressed(sf::Mouse::Right)) { 51 | timer.restart(); 52 | // Block is placed by player 53 | m_world.addEvent(sf::Mouse::Right, 54 | lastPosition, m_player); 55 | break; 56 | } 57 | } 58 | } 59 | lastPosition = ray.getEnd(); 60 | } 61 | } 62 | 63 | void StatePlay::update(float deltaTime) 64 | { 65 | if (m_player.position.x < 0) 66 | m_player.position.x = 0; 67 | if (m_player.position.z < 0) 68 | m_player.position.z = 0; 69 | 70 | m_fpsCounter.update(); 71 | m_player.update(deltaTime, m_world); 72 | m_world.update(m_pApplication->getCamera()); 73 | } 74 | 75 | void StatePlay::render(RenderMaster &renderer) 76 | { 77 | static sf::Clock dt; 78 | 79 | static bool drawGUI = false; 80 | static ToggleKey drawKey(sf::Keyboard::F3); 81 | 82 | if (drawKey.isKeyPressed()) { 83 | drawGUI = !drawGUI; 84 | } 85 | 86 | if (drawGUI) { 87 | m_fpsCounter.draw(renderer); 88 | m_player.draw(renderer); 89 | } 90 | 91 | m_world.renderWorld(renderer, m_pApplication->getCamera()); 92 | } 93 | 94 | void StatePlay::onOpen() 95 | { 96 | m_pApplication->turnOffMouse(); 97 | } 98 | -------------------------------------------------------------------------------- /Source/States/PlayState.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYSTATE_H_INCLUDED 2 | #define PLAYSTATE_H_INCLUDED 3 | 4 | #include "../Player/Player.h" 5 | #include "StateBase.h" 6 | 7 | #include "../Input/Keyboard.h" 8 | #include "../Util/FPSCounter.h" 9 | #include "../World/Chunk/Chunk.h" 10 | #include "../World/World.h" 11 | 12 | /// @brief Active game playing state, not associated with game menus. 13 | class StatePlay : public StateBase { 14 | public: 15 | StatePlay(Application &app, const Config &config); 16 | 17 | void handleEvent(sf::Event e) override; 18 | void handleInput() override; 19 | 20 | void update(float deltaTime) override; 21 | 22 | void render(RenderMaster &renderer) override; 23 | 24 | void onOpen() override; 25 | 26 | private: 27 | Keyboard m_keyboard; 28 | Player m_player; 29 | World m_world; 30 | 31 | FPSCounter m_fpsCounter; 32 | }; 33 | 34 | #endif // PlayState_H_INCLUDED 35 | -------------------------------------------------------------------------------- /Source/States/StateBase.h: -------------------------------------------------------------------------------- 1 | #ifndef STATEBASE_H_INCLUDED 2 | #define STATEBASE_H_INCLUDED 3 | 4 | #include 5 | 6 | // This base class for all States, used more as a template than a functional class. 7 | 8 | class RenderMaster; 9 | class Application; 10 | 11 | /// @brief Base state that determines how other states should normally behave. 12 | class StateBase { 13 | public: 14 | StateBase(Application &app) 15 | : m_pApplication(&app) 16 | { 17 | } 18 | 19 | virtual ~StateBase() = default; 20 | 21 | virtual void handleEvent(sf::Event e) = 0; 22 | virtual void handleInput() = 0; 23 | 24 | virtual void update(float deltaTime) = 0; 25 | 26 | virtual void render(RenderMaster &renderer) = 0; 27 | 28 | virtual void onOpen() = 0; 29 | 30 | protected: 31 | Application *m_pApplication; 32 | }; 33 | 34 | #endif // STATEBASE_H_INCLUDED 35 | -------------------------------------------------------------------------------- /Source/Texture/BasicTexture.cpp: -------------------------------------------------------------------------------- 1 | #include "BasicTexture.h" 2 | 3 | BasicTexture::BasicTexture(const std::string &file) 4 | { 5 | loadFromFile(file); 6 | } 7 | 8 | void BasicTexture::loadFromImage(const sf::Image &i) 9 | { 10 | glGenTextures(1, &m_id); 11 | glActiveTexture(GL_TEXTURE0); 12 | glBindTexture(GL_TEXTURE_2D, m_id); 13 | 14 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, i.getSize().x, i.getSize().y, 0, 15 | GL_RGBA, GL_UNSIGNED_BYTE, i.getPixelsPtr()); 16 | 17 | glGenerateMipmap(GL_TEXTURE_2D); 18 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 19 | GL_NEAREST_MIPMAP_NEAREST); 20 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 21 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 22 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 23 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1); 24 | } 25 | void BasicTexture::loadFromFile(const std::string &file) 26 | { 27 | sf::Image i; 28 | if (!i.loadFromFile("Res/Textures/" + file + ".png")) { 29 | throw std::runtime_error("Unable to load BasicTexture: " + file); 30 | } 31 | 32 | loadFromImage(i); 33 | } 34 | 35 | BasicTexture::~BasicTexture() 36 | { 37 | glDeleteTextures(1, &m_id); 38 | } 39 | 40 | void BasicTexture::bindTexture() const 41 | { 42 | glBindTexture(GL_TEXTURE_2D, m_id); 43 | } 44 | -------------------------------------------------------------------------------- /Source/Texture/BasicTexture.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTURE_H_INCLUDED 2 | #define TEXTURE_H_INCLUDED 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "../Util/NonCopyable.h" 10 | 11 | /// @brief Standard texture that will be mapped to objects. 12 | class BasicTexture : public NonCopyable { 13 | public: 14 | BasicTexture() = default; 15 | BasicTexture(const std::string &file); 16 | 17 | ~BasicTexture(); 18 | 19 | void loadFromImage(const sf::Image &image); 20 | void loadFromFile(const std::string &file); 21 | 22 | void bindTexture() const; 23 | 24 | private: 25 | GLuint m_id; 26 | }; 27 | 28 | #endif // TEXTURE_H_INCLUDED 29 | -------------------------------------------------------------------------------- /Source/Texture/CubeTexture.cpp: -------------------------------------------------------------------------------- 1 | #include "CubeTexture.h" 2 | 3 | CubeTexture::CubeTexture(const std::array &files) 4 | { 5 | loadFromFiles(files); 6 | } 7 | 8 | CubeTexture::~CubeTexture() 9 | { 10 | glDeleteTextures(1, &m_texId); 11 | } 12 | 13 | void CubeTexture::loadFromFiles(const std::array &files) 14 | { 15 | glGenTextures(1, &m_texId); 16 | glActiveTexture(GL_TEXTURE0); 17 | glBindTexture(GL_TEXTURE_CUBE_MAP, m_texId); 18 | 19 | for (int i = 0; i < 6; i++) { 20 | auto &str = files[i]; 21 | sf::Image image; 22 | if (!image.loadFromFile("Res/Textures/" + str + ".png")) { 23 | throw std::runtime_error("Unable to load CubeTexture Part: " + str); 24 | } 25 | 26 | auto param = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; 27 | auto width = image.getSize().x; 28 | auto height = image.getSize().y; 29 | 30 | glTexImage2D(param, 0, GL_RGBA, width, height, 0, GL_RGBA, 31 | GL_UNSIGNED_BYTE, image.getPixelsPtr()); 32 | } 33 | 34 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 35 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 36 | 37 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 38 | glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 39 | } 40 | 41 | void CubeTexture::bindTexture() const 42 | { 43 | glBindTexture(GL_TEXTURE_CUBE_MAP, m_texId); 44 | } 45 | -------------------------------------------------------------------------------- /Source/Texture/CubeTexture.h: -------------------------------------------------------------------------------- 1 | #ifndef CUBETEXTURE_H_INCLUDED 2 | #define CUBETEXTURE_H_INCLUDED 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../Util/NonCopyable.h" 11 | 12 | /// @brief Complex texture class that applies textures to all sides of a cube. 13 | class CubeTexture : public NonCopyable { 14 | public: 15 | CubeTexture() = default; 16 | CubeTexture(const std::array &files); 17 | 18 | ~CubeTexture(); 19 | 20 | /** 21 | MUST BE IN THIS ORDER: 22 | -right 23 | -left 24 | -top 25 | -bottom 26 | -back 27 | -front 28 | */ 29 | void loadFromFiles(const std::array &files); 30 | 31 | void bindTexture() const; 32 | 33 | private: 34 | GLuint m_texId; 35 | }; 36 | 37 | #endif // CUBETEXTURE_H_INCLUDED 38 | -------------------------------------------------------------------------------- /Source/Texture/TextureAtlas.cpp: -------------------------------------------------------------------------------- 1 | #include "TextureAtlas.h" 2 | #include 3 | 4 | TextureAtlas::TextureAtlas(const std::string &textureFileName) 5 | { 6 | sf::Image i; 7 | if (!i.loadFromFile("Res/Textures/" + textureFileName + ".png")) { 8 | throw std::runtime_error("Unable to open image: " + textureFileName); 9 | } 10 | loadFromImage(i); 11 | 12 | m_imageSize = 256; 13 | m_individualTextureSize = 16; 14 | } 15 | 16 | std::array TextureAtlas::getTexture(const sf::Vector2i &coords) 17 | { 18 | static const GLfloat TEX_PER_ROW = 19 | (GLfloat)m_imageSize / (GLfloat)m_individualTextureSize; 20 | static const GLfloat INDV_TEX_SIZE = 1.0f / TEX_PER_ROW; 21 | static const GLfloat PIXEL_SIZE = 1.0f / (float)m_imageSize; 22 | 23 | GLfloat xMin = (coords.x * INDV_TEX_SIZE) + 0.5f * PIXEL_SIZE; 24 | GLfloat yMin = (coords.y * INDV_TEX_SIZE) + 0.5f * PIXEL_SIZE; 25 | 26 | GLfloat xMax = (xMin + INDV_TEX_SIZE) - PIXEL_SIZE; 27 | GLfloat yMax = (yMin + INDV_TEX_SIZE) - PIXEL_SIZE; 28 | 29 | return {xMax, yMax, xMin, yMax, xMin, yMin, xMax, yMin}; 30 | } 31 | -------------------------------------------------------------------------------- /Source/Texture/TextureAtlas.h: -------------------------------------------------------------------------------- 1 | #ifndef TEXTUREATLAS_H_INCLUDED 2 | #define TEXTUREATLAS_H_INCLUDED 3 | 4 | #include "BasicTexture.h" 5 | 6 | /// @brief Texture atlas that pulls texture data from existing files and maps them appropraitely. 7 | class TextureAtlas : public BasicTexture { 8 | public: 9 | TextureAtlas(const std::string &textureFileName); 10 | 11 | std::array getTexture(const sf::Vector2i &coords); 12 | 13 | private: 14 | int m_imageSize; 15 | int m_individualTextureSize; 16 | }; 17 | 18 | #endif // TEXTUREATLAS_H_INCLUDED 19 | -------------------------------------------------------------------------------- /Source/Util/Array2D.h: -------------------------------------------------------------------------------- 1 | #ifndef ARRAY2D_H_INCLUDED 2 | #define ARRAY2D_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | /// @brief Array template used in mathematical calculations. 8 | /// @tparam T 9 | /// @tparam WIDTH 10 | template class Array2D { 11 | using Array = std::array; 12 | 13 | public: 14 | T &get(int x, int z) 15 | { 16 | return m_array[x * WIDTH + z]; 17 | } 18 | 19 | const T &get(int x, int z) const 20 | { 21 | return m_array[x * WIDTH + z]; 22 | } 23 | 24 | T &getMaxValue() 25 | { 26 | return *std::max_element(m_array.begin(), m_array.end()); 27 | } 28 | 29 | void setAll(T val) 30 | { 31 | m_array.fill(val); 32 | } 33 | 34 | private: 35 | Array m_array; 36 | }; 37 | 38 | #endif // ARRAY2D_H_INCLUDED 39 | -------------------------------------------------------------------------------- /Source/Util/FPSCounter.cpp: -------------------------------------------------------------------------------- 1 | #include "FPSCounter.h" 2 | 3 | #include "../Renderer/RenderMaster.h" 4 | 5 | #include 6 | 7 | FPSCounter::FPSCounter() 8 | { 9 | enabled = true; 10 | debugging = false; 11 | 12 | // m_text.setPosition(sf::Vector2f(10.f,10.f)); 13 | m_text.move(10, 10); 14 | m_text.setOutlineColor(sf::Color::Black); 15 | m_text.setOutlineThickness(2); 16 | 17 | m_font.loadFromFile("Res/Fonts/rs.ttf"); 18 | m_text.setFont(m_font); 19 | m_text.setCharacterSize(25); 20 | } 21 | 22 | void FPSCounter::update() 23 | { 24 | m_frameCount++; 25 | 26 | if(enabled) 27 | { 28 | if (m_delayTimer.getElapsedTime().asSeconds() > 0.5) 29 | { 30 | m_fps = m_frameCount / m_fpsTimer.restart().asSeconds(); 31 | m_frameCount = 0; 32 | m_delayTimer.restart(); 33 | 34 | // Only show this output in debug mode 35 | if(debugging) 36 | { 37 | std::cout << m_fps << '\n'; 38 | } 39 | } 40 | } 41 | } 42 | 43 | void FPSCounter::draw(RenderMaster &renderer) 44 | { 45 | m_text.setString("FPS: " + std::to_string(m_fps)); 46 | } 47 | -------------------------------------------------------------------------------- /Source/Util/FPSCounter.h: -------------------------------------------------------------------------------- 1 | #ifndef FPSCOUNTER_H_INCLUDED 2 | #define FPSCOUNTER_H_INCLUDED 3 | 4 | #include 5 | 6 | class RenderMaster; 7 | 8 | /// @brief Generally obsolete FPS counter associated with SFML. 9 | class FPSCounter { 10 | public: 11 | FPSCounter(); 12 | 13 | void update(); 14 | 15 | void draw(RenderMaster &renderer); 16 | 17 | private: 18 | bool enabled = false; 19 | bool debugging; 20 | 21 | sf::Text m_text; 22 | sf::Font m_font; 23 | 24 | sf::Clock m_delayTimer; 25 | sf::Clock m_fpsTimer; 26 | 27 | float m_fps = 0; 28 | 29 | int m_frameCount = 0; 30 | }; 31 | 32 | #endif // FPSCOUNTER_H_INCLUDED 33 | -------------------------------------------------------------------------------- /Source/Util/FileUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "FileUtil.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | std::string getFileContents(const std::string &filePath) 8 | { 9 | std::ifstream inFile(filePath); 10 | if (!inFile.is_open()) { 11 | throw std::runtime_error("Unable to open file: " + filePath); 12 | } 13 | 14 | std::stringstream stream; 15 | 16 | stream << inFile.rdbuf(); 17 | return stream.str(); 18 | } 19 | -------------------------------------------------------------------------------- /Source/Util/FileUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef FILEUTIL_H_INCLUDED 2 | #define FILEUTIL_H_INCLUDED 3 | 4 | #include 5 | 6 | std::string getFileContents(const std::string &filePath); 7 | 8 | #endif // FILEUTIL_H_INCLUDED 9 | -------------------------------------------------------------------------------- /Source/Util/NonCopyable.h: -------------------------------------------------------------------------------- 1 | #ifndef NONCOPYABLE_H_INCLUDED 2 | #define NONCOPYABLE_H_INCLUDED 3 | 4 | /// @brief Struct used to define entities that should not be duplicated. 5 | struct NonCopyable { 6 | NonCopyable() = default; 7 | NonCopyable(const NonCopyable &) = delete; 8 | NonCopyable &operator=(const NonCopyable &) = delete; 9 | }; 10 | 11 | #endif // NONCOPYABLE_H_INCLUDED 12 | -------------------------------------------------------------------------------- /Source/Util/NonMovable.h: -------------------------------------------------------------------------------- 1 | #ifndef NON_MOVE 2 | #define NON_MOVE 3 | 4 | /// @brief Class that identifies entities not designed for physical movement. 5 | class NonMovable { 6 | public: 7 | NonMovable(NonMovable &&) = delete; 8 | 9 | NonMovable &operator=(NonMovable &&) = delete; 10 | 11 | protected: 12 | NonMovable() = default; 13 | }; 14 | 15 | #endif // NON_MOVE 16 | -------------------------------------------------------------------------------- /Source/Util/Random.cpp: -------------------------------------------------------------------------------- 1 | #include "Random.h" 2 | 3 | RandomSingleton &RandomSingleton::get() 4 | { 5 | static RandomSingleton r; 6 | return r; 7 | } 8 | 9 | RandomSingleton::RandomSingleton() 10 | { 11 | m_randomEngine.seed(static_cast(std::time(nullptr))); 12 | for (int i = 0; i < 5; i++) { 13 | intInRange(i, i * 5); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Source/Util/Random.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOM_H_INCLUDED 2 | #define RANDOM_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "Singleton.h" 8 | 9 | /// @brief Singleton class that increases randomness (such as with seeds) 10 | class RandomSingleton : public Singleton { 11 | public: 12 | static RandomSingleton &get(); 13 | 14 | template T intInRange(T low, T high) 15 | { 16 | static_assert(std::is_integral::value, "Not integral type!"); 17 | std::uniform_int_distribution dist(low, high); 18 | return dist(m_randomEngine); 19 | } 20 | 21 | private: 22 | RandomSingleton(); 23 | 24 | std::mt19937 m_randomEngine; 25 | }; 26 | 27 | template class Random { 28 | public: 29 | Random(int n = std::time(nullptr)) 30 | { 31 | m_randomEngine.seed(n); 32 | for (int i = 0; i < 5; i++) 33 | intInRange(i, i * 5); 34 | } 35 | 36 | template T intInRange(T low, T high) 37 | { 38 | static_assert(std::is_integral::value, "Not integral type!"); 39 | std::uniform_int_distribution dist(low, high); 40 | return dist(m_randomEngine); 41 | } 42 | 43 | void setSeed(int seed) 44 | { 45 | m_randomEngine.seed(seed); 46 | } 47 | 48 | private: 49 | REngine m_randomEngine; 50 | }; 51 | 52 | #endif // RANDOM_H_INCLUDED 53 | -------------------------------------------------------------------------------- /Source/Util/Singleton.h: -------------------------------------------------------------------------------- 1 | #ifndef SINGLETON_H_INCLUDED 2 | #define SINGLETON_H_INCLUDED 3 | 4 | #include "NonCopyable.h" 5 | #include "NonMovable.h" 6 | 7 | class Singleton : public NonMovable, public NonCopyable { 8 | }; 9 | 10 | #endif // SINGLETON_H_INCLUDED 11 | -------------------------------------------------------------------------------- /Source/World/Block/BlockData.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockData.h" 2 | 3 | #include 4 | 5 | BlockData::BlockData(const std::string &fileName) 6 | { 7 | std::ifstream inFile("Res/Blocks/" + fileName + ".block"); 8 | 9 | if (!inFile.is_open()) { 10 | throw std::runtime_error("Unable to open block file: " + fileName + 11 | "!"); 12 | } 13 | 14 | /* BlockData parses through text strings and applies valid attributes. 15 | 16 | Textures are applied first, then Block IDs, opacity data, collision data, 17 | mesh data, and shader data. 18 | 19 | Essentially, blocks being constructed by the renderer depend on this 20 | file data being correctly imported and read by the program.*/ 21 | 22 | std::string line; 23 | while (std::getline(inFile, line)) { 24 | if (line == "TexTop") { 25 | int x, y; 26 | inFile >> x >> y; 27 | m_data.texTopCoord = {x, y}; 28 | } 29 | else if (line == "TexSide") { 30 | int x, y; 31 | inFile >> x >> y; 32 | m_data.texSideCoord = {x, y}; 33 | } 34 | else if (line == "TexBottom") { 35 | int x, y; 36 | inFile >> x >> y; 37 | m_data.texBottomCoord = {x, y}; 38 | } 39 | else if (line == "TexAll") { 40 | int x, y; 41 | inFile >> x >> y; 42 | m_data.texTopCoord = {x, y}; 43 | m_data.texSideCoord = {x, y}; 44 | m_data.texBottomCoord = {x, y}; 45 | } 46 | else if (line == "Id") { 47 | int id; 48 | inFile >> id; 49 | m_data.id = static_cast(id); 50 | } 51 | else if (line == "Opaque") { 52 | inFile >> m_data.isOpaque; 53 | } 54 | else if (line == "Collidable") { 55 | inFile >> m_data.isCollidable; 56 | } 57 | else if (line == "MeshType") { 58 | int id; 59 | inFile >> id; 60 | m_data.meshType = static_cast(id); 61 | } 62 | else if (line == "ShaderType") { 63 | int id; 64 | inFile >> id; 65 | m_data.shaderType = static_cast(id); 66 | } 67 | } 68 | } 69 | 70 | const BlockDataHolder &BlockData::getBlockData() const 71 | { 72 | return m_data; 73 | } 74 | -------------------------------------------------------------------------------- /Source/World/Block/BlockData.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCKDATA_H_INCLUDED 2 | #define BLOCKDATA_H_INCLUDED 3 | 4 | #include "../../Util/NonCopyable.h" 5 | #include "BlockId.h" 6 | #include 7 | 8 | /// @brief Allocates meshes to cubes and non-cube entities. 9 | enum class BlockMeshType { 10 | Cube = 0, 11 | X = 1, 12 | }; 13 | 14 | /// @brief Allocates shader behavior to groups of blocks. 15 | enum class BlockShaderType { 16 | Chunk = 0, 17 | Liquid = 1, 18 | Flora = 2, 19 | }; 20 | 21 | /// @brief Struct designed to hold geometric and tangibility data for each individual block. 22 | struct BlockDataHolder : public NonCopyable { 23 | BlockId id; 24 | sf::Vector2i texTopCoord; 25 | sf::Vector2i texSideCoord; 26 | sf::Vector2i texBottomCoord; 27 | 28 | BlockMeshType meshType; 29 | BlockShaderType shaderType; 30 | 31 | bool isOpaque; 32 | bool isCollidable; 33 | }; 34 | 35 | class BlockData : public NonCopyable { 36 | public: 37 | BlockData(const std::string &fileName); 38 | 39 | const BlockDataHolder &getBlockData() const; 40 | 41 | private: 42 | BlockDataHolder m_data; 43 | }; 44 | 45 | #endif // BLOCKDATA_H_INCLUDED 46 | -------------------------------------------------------------------------------- /Source/World/Block/BlockDatabase.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockDatabase.h" 2 | 3 | // Block Database initializes to first pack, not the second. 4 | BlockDatabase::BlockDatabase() 5 | : textureAtlas("DefaultPack") 6 | { 7 | m_blocks[(int)BlockId::Air] = std::make_unique("Air"); 8 | m_blocks[(int)BlockId::Grass] = std::make_unique("Grass"); 9 | m_blocks[(int)BlockId::Dirt] = std::make_unique("Dirt"); 10 | m_blocks[(int)BlockId::Stone] = std::make_unique("Stone"); 11 | m_blocks[(int)BlockId::OakBark] = std::make_unique("OakBark"); 12 | m_blocks[(int)BlockId::OakLeaf] = std::make_unique("OakLeaf"); 13 | m_blocks[(int)BlockId::Sand] = std::make_unique("Sand"); 14 | m_blocks[(int)BlockId::Water] = std::make_unique("Water"); 15 | m_blocks[(int)BlockId::Cactus] = std::make_unique("Cactus"); 16 | m_blocks[(int)BlockId::TallGrass] = 17 | std::make_unique("TallGrass"); 18 | m_blocks[(int)BlockId::Rose] = std::make_unique("Rose"); 19 | m_blocks[(int)BlockId::DeadShrub] = 20 | std::make_unique("DeadShrub"); 21 | } 22 | 23 | BlockDatabase &BlockDatabase::get() 24 | { 25 | static BlockDatabase d; 26 | return d; 27 | } 28 | 29 | const BlockType &BlockDatabase::getBlock(BlockId id) const 30 | { 31 | return *m_blocks[(int)id]; 32 | } 33 | 34 | const BlockData &BlockDatabase::getData(BlockId id) const 35 | { 36 | return m_blocks[(int)id]->getData(); 37 | } 38 | -------------------------------------------------------------------------------- /Source/World/Block/BlockDatabase.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCKDATABASE_H_INCLUDED 2 | #define BLOCKDATABASE_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "../../Util/Singleton.h" 8 | 9 | #include "BlockId.h" 10 | #include "BlockTypes/BlockType.h" 11 | 12 | #include "../../Texture/TextureAtlas.h" 13 | 14 | /// @brief Singleton class that determines status and ID of blocks as a whole. 15 | class BlockDatabase : public Singleton { 16 | public: 17 | static BlockDatabase &get(); 18 | 19 | const BlockType &getBlock(BlockId id) const; 20 | const BlockData &getData(BlockId id) const; 21 | 22 | TextureAtlas textureAtlas; 23 | 24 | private: 25 | BlockDatabase(); 26 | 27 | std::array, (unsigned)BlockId::NUM_TYPES> 28 | m_blocks; 29 | }; 30 | 31 | #endif // BLOCKDATABASE_H_INCLUDED 32 | -------------------------------------------------------------------------------- /Source/World/Block/BlockId.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCKID_H_INCLUDED 2 | #define BLOCKID_H_INCLUDED 3 | 4 | #include 5 | 6 | using Block_t = uint8_t; 7 | 8 | /// @brief Known block ID types used in game. 9 | enum class BlockId : Block_t { 10 | Air = 0, 11 | Grass = 1, 12 | Dirt = 2, 13 | Stone = 3, 14 | OakBark = 4, 15 | OakLeaf = 5, 16 | Sand = 6, 17 | Water = 7, 18 | Cactus = 8, 19 | Rose = 9, 20 | TallGrass = 10, 21 | DeadShrub = 11, 22 | 23 | NUM_TYPES 24 | }; 25 | 26 | #endif // BLOCKID_H_INCLUDED 27 | -------------------------------------------------------------------------------- /Source/World/Block/BlockTypes/BlockType.cpp: -------------------------------------------------------------------------------- 1 | #include "BlockType.h" 2 | 3 | /// @brief Reads block data from an existing file. 4 | /// @param fileName 5 | BlockType::BlockType(const std::string &fileName) 6 | : m_data(fileName) 7 | { 8 | } 9 | 10 | const BlockData &BlockType::getData() const 11 | { 12 | return m_data; 13 | } 14 | -------------------------------------------------------------------------------- /Source/World/Block/BlockTypes/BlockType.h: -------------------------------------------------------------------------------- 1 | #ifndef BLOCKTYPE_H_INCLUDED 2 | #define BLOCKTYPE_H_INCLUDED 3 | 4 | #include "../BlockData.h" 5 | 6 | class BlockType : public NonCopyable { 7 | public: 8 | BlockType(const std::string &fileName); 9 | virtual ~BlockType() = default; 10 | 11 | const BlockData &getData() const; 12 | 13 | private: 14 | BlockData m_data; 15 | }; 16 | 17 | class DefaultBlock : public BlockType { 18 | public: 19 | DefaultBlock(const std::string &fileName) 20 | : BlockType(fileName) 21 | { 22 | } 23 | }; 24 | 25 | #endif // BLOCKTYPE_H_INCLUDED 26 | -------------------------------------------------------------------------------- /Source/World/Block/ChunkBlock.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkBlock.h" 2 | 3 | #include "BlockDatabase.h" 4 | 5 | ChunkBlock::ChunkBlock(Block_t id) 6 | : id(id) 7 | { 8 | } 9 | 10 | ChunkBlock::ChunkBlock(BlockId id) 11 | : id(static_cast(id)) 12 | { 13 | } 14 | 15 | const BlockDataHolder &ChunkBlock::getData() const 16 | { 17 | return BlockDatabase::get().getData((BlockId)id).getBlockData(); 18 | } 19 | 20 | const BlockType &ChunkBlock::getType() const 21 | { 22 | return BlockDatabase::get().getBlock((BlockId)id); 23 | } 24 | -------------------------------------------------------------------------------- /Source/World/Block/ChunkBlock.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKBLOCK_H_INCLUDED 2 | #define CHUNKBLOCK_H_INCLUDED 3 | 4 | #include "BlockId.h" 5 | 6 | struct BlockDataHolder; 7 | class BlockType; 8 | 9 | struct ChunkBlock { 10 | ChunkBlock() = default; 11 | 12 | ChunkBlock(Block_t id); 13 | ChunkBlock(BlockId id); 14 | 15 | const BlockDataHolder &getData() const; 16 | const BlockType &getType() const; 17 | 18 | bool operator==(ChunkBlock other) const 19 | { 20 | return id == other.id; 21 | } 22 | 23 | bool operator!=(ChunkBlock other) const 24 | { 25 | return id != other.id; 26 | } 27 | 28 | Block_t id = 0; 29 | }; 30 | 31 | #endif // CHUNKBLOCK_H_INCLUDED 32 | -------------------------------------------------------------------------------- /Source/World/Chunk/Chunk.cpp: -------------------------------------------------------------------------------- 1 | #include "Chunk.h" 2 | 3 | #include "../../Camera.h" 4 | #include "../../Maths/NoiseGenerator.h" 5 | #include "../../Renderer/RenderMaster.h" 6 | #include "../../Util/Random.h" 7 | #include "../Generation/Terrain/TerrainGenerator.h" 8 | #include "../World.h" 9 | 10 | Chunk::Chunk(World &world, const sf::Vector2i &location) 11 | : m_location(location) 12 | , m_pWorld(&world) 13 | { 14 | m_highestBlocks.setAll(0); 15 | } 16 | 17 | bool Chunk::makeMesh(const Camera &camera) 18 | { 19 | for (auto &chunk : m_chunks) { 20 | if (!chunk.hasMesh() && 21 | camera.getFrustum().isBoxInFrustum(chunk.m_aabb)) { 22 | chunk.makeMesh(); 23 | return true; 24 | } 25 | } 26 | return false; 27 | } 28 | 29 | void Chunk::setBlock(int x, int y, int z, ChunkBlock block) 30 | { 31 | addSectionsBlockTarget(y); 32 | if (outOfBound(x, y, z)) 33 | return; 34 | 35 | int bY = y % CHUNK_SIZE; 36 | m_chunks[y / CHUNK_SIZE].setBlock(x, bY, z, block); 37 | 38 | if (y == m_highestBlocks.get(x, z)) { 39 | auto highBlock = getBlock(x, y--, z); 40 | while (!highBlock.getData().isOpaque) { 41 | highBlock = getBlock(x, y--, z); 42 | } 43 | } 44 | else if (y > m_highestBlocks.get(x, z)) { 45 | m_highestBlocks.get(x, z) = y; 46 | } 47 | 48 | if (m_isLoaded) { 49 | // m_pWorld->updateChunk(x, y, z); 50 | } 51 | } 52 | 53 | // Chunk block to SECTION BLOCK positions 54 | ChunkBlock Chunk::getBlock(int x, int y, int z) const noexcept 55 | { 56 | if (outOfBound(x, y, z)) { 57 | return BlockId::Air; 58 | } 59 | 60 | int bY = y % CHUNK_SIZE; 61 | 62 | return m_chunks[y / CHUNK_SIZE].getBlock(x, bY, z); 63 | } 64 | 65 | int Chunk::getHeightAt(int x, int z) 66 | { 67 | return m_highestBlocks.get(x, z); 68 | } 69 | 70 | bool Chunk::outOfBound(int x, int y, int z) const noexcept 71 | { 72 | if (x >= CHUNK_SIZE) 73 | return true; 74 | if (z >= CHUNK_SIZE) 75 | return true; 76 | 77 | if (x < 0) 78 | return true; 79 | if (y < 0) 80 | return true; 81 | if (z < 0) 82 | return true; 83 | 84 | if (y >= (int)m_chunks.size() * CHUNK_SIZE) { 85 | return true; 86 | } 87 | 88 | return false; 89 | } 90 | 91 | void Chunk::drawChunks(RenderMaster &renderer, const Camera &camera) 92 | { 93 | for (auto &chunk : m_chunks) { 94 | if (chunk.hasMesh()) { 95 | if (!chunk.hasBuffered()) { 96 | chunk.bufferMesh(); 97 | } 98 | 99 | if (camera.getFrustum().isBoxInFrustum(chunk.m_aabb)) 100 | renderer.drawChunk(chunk); 101 | } 102 | } 103 | } 104 | 105 | bool Chunk::hasLoaded() const noexcept 106 | { 107 | return m_isLoaded; 108 | } 109 | 110 | void Chunk::load(TerrainGenerator &generator) 111 | { 112 | if (hasLoaded()) 113 | return; 114 | 115 | generator.generateTerrainFor(*this); 116 | m_isLoaded = true; 117 | } 118 | 119 | ChunkSection &Chunk::getSection(int index) 120 | { 121 | static ChunkSection errorSection({444, 444, 444}, *m_pWorld); 122 | 123 | if (index >= (int)m_chunks.size() || index < 0) 124 | return errorSection; 125 | 126 | return m_chunks[index]; 127 | } 128 | 129 | void Chunk::deleteMeshes() 130 | { 131 | for (unsigned i = 0; i < m_chunks.size(); i++) { 132 | m_chunks[i].deleteMeshes(); 133 | } 134 | } 135 | 136 | void Chunk::addSection() 137 | { 138 | int y = m_chunks.size(); 139 | m_chunks.emplace_back(sf::Vector3i(m_location.x, y, m_location.y), 140 | *m_pWorld); 141 | } 142 | 143 | void Chunk::addSectionsBlockTarget(int blockY) 144 | { 145 | int index = blockY / CHUNK_SIZE; 146 | addSectionsIndexTarget(index); 147 | } 148 | 149 | void Chunk::addSectionsIndexTarget(int index) 150 | { 151 | while ((int)m_chunks.size() < index + 1) { 152 | addSection(); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /Source/World/Chunk/Chunk.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNK_H_INCLUDED 2 | #define CHUNK_H_INCLUDED 3 | 4 | #include "../../Util/Array2D.h" 5 | #include "../../Util/NonCopyable.h" 6 | #include "ChunkSection.h" 7 | #include 8 | 9 | class RenderMaster; 10 | class Camera; 11 | class TerrainGenerator; 12 | 13 | /// @brief A chunk, in other words, a large arrangement of blocks. 14 | class Chunk : public IChunk { 15 | public: 16 | Chunk() = default; 17 | Chunk(World &world, const sf::Vector2i &location); 18 | 19 | bool makeMesh(const Camera &camera); 20 | 21 | void setBlock(int x, int y, int z, ChunkBlock block) override; 22 | ChunkBlock getBlock(int x, int y, int z) const noexcept override; 23 | int getHeightAt(int x, int z); 24 | 25 | void drawChunks(RenderMaster &renderer, const Camera &camera); 26 | 27 | bool hasLoaded() const noexcept; 28 | void load(TerrainGenerator &generator); 29 | 30 | ChunkSection &getSection(int index); 31 | 32 | const sf::Vector2i &getLocation() const 33 | { 34 | return m_location; 35 | } 36 | 37 | void deleteMeshes(); 38 | 39 | private: 40 | void addSection(); 41 | void addSectionsBlockTarget(int blockY); 42 | void addSectionsIndexTarget(int index); 43 | 44 | bool outOfBound(int x, int y, int z) const noexcept; 45 | 46 | std::vector m_chunks; 47 | Array2D m_highestBlocks; 48 | sf::Vector2i m_location; 49 | 50 | World *m_pWorld; 51 | 52 | bool m_isLoaded = false; 53 | }; 54 | 55 | #endif // CHUNK_H_INCLUDED 56 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkManager.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkManager.h" 2 | 3 | #include 4 | 5 | #include "../Generation/Terrain/ClassicOverWorldGenerator.h" 6 | #include "../Generation/Terrain/SuperFlatGenerator.h" 7 | 8 | ChunkManager::ChunkManager(World &world) 9 | : m_world(&world) 10 | { 11 | m_terrainGenerator = std::make_unique(); 12 | } 13 | 14 | Chunk &ChunkManager::getChunk(int x, int z) 15 | { 16 | VectorXZ key{x, z}; 17 | if (!chunkExistsAt(x, z)) { 18 | Chunk chunk{*m_world, {x, z}}; 19 | m_chunks.emplace(key, std::move(chunk)); 20 | } 21 | 22 | return m_chunks[key]; 23 | } 24 | 25 | ChunkMap &ChunkManager::getChunks() 26 | { 27 | return m_chunks; 28 | } 29 | 30 | bool ChunkManager::makeMesh(int x, int z, const Camera &camera) 31 | { 32 | for (int nx = -1; nx <= 1; nx++) 33 | for (int nz = -1; nz <= 1; nz++) { 34 | loadChunk( 35 | x + nx, 36 | z + nz); // getChunk(x + nx, z + nz).load(*m_terrainGenerator); 37 | } 38 | 39 | return getChunk(x, z).makeMesh(camera); 40 | } 41 | 42 | bool ChunkManager::chunkLoadedAt(int x, int z) const 43 | { 44 | if (!chunkExistsAt(x, z)) 45 | return false; 46 | 47 | return m_chunks.at({x, z}).hasLoaded(); 48 | } 49 | 50 | bool ChunkManager::chunkExistsAt(int x, int z) const 51 | { 52 | return m_chunks.find({x, z}) != m_chunks.end(); 53 | } 54 | 55 | void ChunkManager::loadChunk(int x, int z) 56 | { 57 | getChunk(x, z).load(*m_terrainGenerator); 58 | } 59 | 60 | void ChunkManager::deleteMeshes() 61 | { 62 | for (auto &chunk : m_chunks) { 63 | chunk.second.deleteMeshes(); 64 | } 65 | } 66 | 67 | const TerrainGenerator &ChunkManager::getTerrainGenerator() const noexcept 68 | { 69 | return *m_terrainGenerator; 70 | } 71 | 72 | void ChunkManager::unloadChunk(int x, int z) 73 | { 74 | ///@TODO Save chunk to file ? 75 | if (chunkExistsAt(x, z)) 76 | m_chunks.erase({x, z}); 77 | } 78 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkManager.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKMANAGER_H_INCLUDED 2 | #define CHUNKMANAGER_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "../../Maths/Vector2XZ.h" 9 | #include "../Generation/Terrain/TerrainGenerator.h" 10 | #include "Chunk.h" 11 | 12 | class World; 13 | 14 | using ChunkMap = std::unordered_map; 15 | 16 | /// @brief Dynamic chunk manager that affects chunk and block placement. 17 | class ChunkManager { 18 | public: 19 | ChunkManager(World &world); 20 | 21 | Chunk &getChunk(int x, int z); 22 | ChunkMap &getChunks(); 23 | 24 | bool makeMesh(int x, int z, const Camera &camera); 25 | 26 | bool chunkLoadedAt(int x, int z) const; 27 | bool chunkExistsAt(int x, int z) const; 28 | 29 | void loadChunk(int x, int z); 30 | void unloadChunk(int x, int z); 31 | 32 | void deleteMeshes(); 33 | 34 | const TerrainGenerator &getTerrainGenerator() const noexcept; 35 | 36 | private: 37 | ChunkMap m_chunks; 38 | std::unique_ptr m_terrainGenerator; 39 | 40 | World *m_world; 41 | }; 42 | 43 | #endif // CHUNKMANAGER_H_INCLUDED 44 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkMesh.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkMesh.h" 2 | 3 | #include "../WorldConstants.h" 4 | 5 | #include 6 | 7 | void ChunkMesh::addFace(const std::array &blockFace, 8 | const std::array &textureCoords, 9 | const sf::Vector3i &chunkPosition, 10 | const sf::Vector3i &blockPosition, 11 | GLfloat cardinalLight) 12 | { 13 | faces++; 14 | auto &verticies = m_mesh.vertexPositions; 15 | auto &texCoords = m_mesh.textureCoords; 16 | auto &indices = m_mesh.indices; 17 | 18 | texCoords.insert(texCoords.end(), textureCoords.begin(), 19 | textureCoords.end()); 20 | 21 | /// Vertex: The current vertex in the "blockFace" vector, 4 vertex in total 22 | /// hence "< 4" Index: X, Y, Z 23 | for (int i = 0, index = 0; i < 4; ++i) { 24 | verticies.push_back(blockFace[index++] + chunkPosition.x * CHUNK_SIZE + 25 | blockPosition.x); 26 | verticies.push_back(blockFace[index++] + chunkPosition.y * CHUNK_SIZE + 27 | blockPosition.y); 28 | verticies.push_back(blockFace[index++] + chunkPosition.z * CHUNK_SIZE + 29 | blockPosition.z); 30 | m_light.push_back(cardinalLight); 31 | } 32 | 33 | indices.insert(indices.end(), 34 | {m_indexIndex, m_indexIndex + 1, m_indexIndex + 2, 35 | 36 | m_indexIndex + 2, m_indexIndex + 3, m_indexIndex}); 37 | m_indexIndex += 4; 38 | } 39 | 40 | void ChunkMesh::bufferMesh() 41 | { 42 | m_model.addData(m_mesh); 43 | m_model.addVBO(1, m_light); 44 | 45 | m_mesh.vertexPositions.clear(); 46 | m_mesh.textureCoords.clear(); 47 | m_mesh.indices.clear(); 48 | m_light.clear(); 49 | 50 | m_mesh.vertexPositions.shrink_to_fit(); 51 | m_mesh.textureCoords.shrink_to_fit(); 52 | m_mesh.indices.shrink_to_fit(); 53 | m_light.shrink_to_fit(); 54 | 55 | m_indexIndex = 0; 56 | } 57 | 58 | void ChunkMesh::deleteData() 59 | { 60 | m_model.deleteData(); 61 | } 62 | 63 | const Model &ChunkMesh::getModel() const 64 | { 65 | return m_model; 66 | } 67 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkMesh.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKMESH_H_INCLUDED 2 | #define CHUNKMESH_H_INCLUDED 3 | 4 | #include "../../Model.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class ChunkMesh { 11 | public: 12 | ChunkMesh() = default; 13 | 14 | void addFace(const std::array &blockFace, 15 | const std::array &textureCoords, 16 | const sf::Vector3i &chunkPosition, 17 | const sf::Vector3i &blockPosition, GLfloat cardinalLight); 18 | 19 | void bufferMesh(); 20 | 21 | const Model &getModel() const; 22 | 23 | void deleteData(); 24 | 25 | int faces = 0; 26 | 27 | private: 28 | Mesh m_mesh; 29 | Model m_model; 30 | std::vector m_light; 31 | GLuint m_indexIndex = 0; 32 | }; 33 | 34 | struct ChunkMeshCollection { 35 | ChunkMesh solidMesh; 36 | ChunkMesh waterMesh; 37 | ChunkMesh floraMesh; 38 | }; 39 | 40 | #endif // CHUNKMESH_H_INCLUDED 41 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkMeshBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkMeshBuilder.h" 2 | 3 | #include "ChunkMesh.h" 4 | #include "ChunkSection.h" 5 | 6 | #include "../Block/BlockData.h" 7 | #include "../Block/BlockDatabase.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace { 15 | const std::array frontFace{ 16 | 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 17 | }; 18 | 19 | const std::array backFace{ 20 | 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 21 | }; 22 | 23 | const std::array leftFace{ 24 | 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 25 | }; 26 | 27 | const std::array rightFace{ 28 | 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 29 | }; 30 | 31 | const std::array topFace{ 32 | 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 33 | }; 34 | 35 | const std::array bottomFace{0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1}; 36 | 37 | const std::array xFace1{ 38 | 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 39 | }; 40 | 41 | const std::array xFace2{ 42 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 43 | }; 44 | 45 | constexpr GLfloat LIGHT_TOP = 1.0f; 46 | constexpr GLfloat LIGHT_X = 0.8f; 47 | constexpr GLfloat LIGHT_Z = 0.6f; 48 | constexpr GLfloat LIGHT_BOT = 0.4f; 49 | 50 | } // namespace 51 | 52 | ChunkMeshBuilder::ChunkMeshBuilder(ChunkSection &chunk, 53 | ChunkMeshCollection &mesh) 54 | : m_pChunk(&chunk) 55 | , m_pMeshes(&mesh) 56 | { 57 | } 58 | 59 | struct AdjacentBlockPositions { 60 | void update(int x, int y, int z) 61 | { 62 | up = {x, y + 1, z}; 63 | down = {x, y - 1, z}; 64 | left = {x - 1, y, z}; 65 | right = {x + 1, y, z}; 66 | front = {x, y, z + 1}; 67 | back = {x, y, z - 1}; 68 | } 69 | 70 | sf::Vector3i up; 71 | sf::Vector3i down; 72 | sf::Vector3i left; 73 | sf::Vector3i right; 74 | sf::Vector3i front; 75 | sf::Vector3i back; 76 | }; 77 | 78 | int faces; 79 | void ChunkMeshBuilder::buildMesh() 80 | { 81 | AdjacentBlockPositions directions; 82 | m_pBlockPtr = m_pChunk->begin(); 83 | faces = 0; 84 | sf::Clock timer; 85 | for (int16_t i = 0; i < CHUNK_VOLUME; i++) { 86 | uint8_t x = i % CHUNK_SIZE; 87 | uint8_t y = i / (CHUNK_SIZE * CHUNK_SIZE); 88 | uint8_t z = (i / CHUNK_SIZE) % CHUNK_SIZE; 89 | 90 | if (!shouldMakeLayer(y)) { 91 | continue; 92 | } 93 | 94 | ChunkBlock block = *m_pBlockPtr; 95 | m_pBlockPtr++; 96 | 97 | sf::Vector3i position(x, y, z); 98 | setActiveMesh(block); 99 | 100 | if (block == BlockId::Air) { 101 | continue; 102 | } 103 | 104 | m_pBlockData = &block.getData(); 105 | auto &data = *m_pBlockData; 106 | 107 | if (data.meshType == BlockMeshType::X) { 108 | addXBlockToMesh(data.texTopCoord, position); 109 | continue; 110 | } 111 | 112 | directions.update(x, y, z); 113 | 114 | // Up/ Down 115 | if ((m_pChunk->getLocation().y != 0) || y != 0) 116 | tryAddFaceToMesh(bottomFace, data.texBottomCoord, position, 117 | directions.down, LIGHT_BOT); 118 | tryAddFaceToMesh(topFace, data.texTopCoord, position, directions.up, 119 | LIGHT_TOP); 120 | 121 | // Left/ Right 122 | tryAddFaceToMesh(leftFace, data.texSideCoord, position, directions.left, 123 | LIGHT_X); 124 | tryAddFaceToMesh(rightFace, data.texSideCoord, position, 125 | directions.right, LIGHT_X); 126 | 127 | // Front/ Back 128 | tryAddFaceToMesh(frontFace, data.texSideCoord, position, 129 | directions.front, LIGHT_Z); 130 | tryAddFaceToMesh(backFace, data.texSideCoord, position, directions.back, 131 | LIGHT_Z); 132 | } 133 | } 134 | 135 | void ChunkMeshBuilder::setActiveMesh(ChunkBlock block) 136 | { 137 | switch (block.getData().shaderType) { 138 | case BlockShaderType::Chunk: 139 | m_pActiveMesh = &m_pMeshes->solidMesh; 140 | break; 141 | 142 | case BlockShaderType::Liquid: 143 | m_pActiveMesh = &m_pMeshes->waterMesh; 144 | break; 145 | 146 | case BlockShaderType::Flora: 147 | m_pActiveMesh = &m_pMeshes->floraMesh; 148 | break; 149 | } 150 | } 151 | 152 | void ChunkMeshBuilder::addXBlockToMesh(const sf::Vector2i &textureCoords, 153 | const sf::Vector3i &blockPosition) 154 | { 155 | faces++; 156 | auto texCoords = 157 | BlockDatabase::get().textureAtlas.getTexture(textureCoords); 158 | 159 | m_pActiveMesh->addFace(xFace1, texCoords, m_pChunk->getLocation(), 160 | blockPosition, LIGHT_X); 161 | 162 | m_pActiveMesh->addFace(xFace2, texCoords, m_pChunk->getLocation(), 163 | blockPosition, LIGHT_X); 164 | } 165 | 166 | void ChunkMeshBuilder::tryAddFaceToMesh( 167 | const std::array &blockFace, const sf::Vector2i &textureCoords, 168 | const sf::Vector3i &blockPosition, const sf::Vector3i &blockFacing, 169 | GLfloat cardinalLight) 170 | { 171 | if (shouldMakeFace(blockFacing, *m_pBlockData)) { 172 | faces++; 173 | auto texCoords = 174 | BlockDatabase::get().textureAtlas.getTexture(textureCoords); 175 | 176 | m_pActiveMesh->addFace(blockFace, texCoords, m_pChunk->getLocation(), 177 | blockPosition, cardinalLight); 178 | } 179 | } 180 | 181 | bool ChunkMeshBuilder::shouldMakeFace(const sf::Vector3i &adjBlock, 182 | const BlockDataHolder &blockData) 183 | { 184 | auto block = m_pChunk->getBlock(adjBlock.x, adjBlock.y, adjBlock.z); 185 | auto &data = block.getData(); 186 | 187 | if (block == BlockId::Air) { 188 | return true; 189 | } 190 | else if ((!data.isOpaque) && (data.id != m_pBlockData->id)) { 191 | return true; 192 | } 193 | return false; 194 | } 195 | 196 | bool ChunkMeshBuilder::shouldMakeLayer(int y) 197 | { 198 | auto adjIsSolid = [&](int dx, int dz) { 199 | const ChunkSection § = m_pChunk->getAdjacent(dx, dz); 200 | return sect.getLayer(y).isAllSolid(); 201 | }; 202 | 203 | return (!m_pChunk->getLayer(y).isAllSolid()) || 204 | (!m_pChunk->getLayer(y + 1).isAllSolid()) || 205 | (!m_pChunk->getLayer(y - 1).isAllSolid()) || 206 | 207 | (!adjIsSolid(1, 0)) || (!adjIsSolid(0, 1)) || (!adjIsSolid(-1, 0)) || 208 | (!adjIsSolid(0, -1)); 209 | } 210 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkMeshBuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKMESHBUILDER_H_INCLUDED 2 | #define CHUNKMESHBUILDER_H_INCLUDED 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "../Block/ChunkBlock.h" 10 | 11 | class ChunkSection; 12 | class ChunkMesh; 13 | class BlockData; 14 | 15 | struct ChunkMeshCollection; 16 | struct BlockDataHolder; 17 | 18 | class ChunkMeshBuilder { 19 | public: 20 | ChunkMeshBuilder(ChunkSection &chunk, ChunkMeshCollection &meshes); 21 | 22 | void buildMesh(); 23 | 24 | private: 25 | void setActiveMesh(ChunkBlock block); 26 | 27 | void addXBlockToMesh(const sf::Vector2i &textureCoords, 28 | const sf::Vector3i &blockPosition); 29 | 30 | void tryAddFaceToMesh(const std::array &blockFace, 31 | const sf::Vector2i &textureCoords, 32 | const sf::Vector3i &blockPosition, 33 | const sf::Vector3i &blockFacing, 34 | GLfloat cardinalLight); 35 | 36 | bool shouldMakeFace(const sf::Vector3i &blockPosition, 37 | const BlockDataHolder &blockData); 38 | 39 | bool shouldMakeLayer(int y); 40 | 41 | const ChunkBlock *m_pBlockPtr = nullptr; 42 | ChunkSection *m_pChunk = nullptr; 43 | ChunkMeshCollection *m_pMeshes = nullptr; 44 | ChunkMesh *m_pActiveMesh = nullptr; 45 | const BlockDataHolder *m_pBlockData = nullptr; 46 | }; 47 | 48 | #endif // CHUNKMESHBUILDER_H_INCLUDED 49 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkSection.cpp: -------------------------------------------------------------------------------- 1 | #include "ChunkSection.h" 2 | 3 | #include "../Block/BlockId.h" 4 | 5 | #include "../World.h" 6 | #include "ChunkMeshBuilder.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | ChunkSection::ChunkSection(const sf::Vector3i &location, World &world) 13 | : m_aabb({CHUNK_SIZE, CHUNK_SIZE, CHUNK_SIZE}) 14 | , m_location(location) 15 | , m_pWorld(&world) 16 | { 17 | m_aabb.update({location.x * CHUNK_SIZE, location.y * CHUNK_SIZE, 18 | location.z * CHUNK_SIZE}); 19 | } 20 | 21 | void ChunkSection::setBlock(int x, int y, int z, ChunkBlock block) 22 | { 23 | if (outOfBounds(x) || outOfBounds(y) || outOfBounds(z)) { 24 | auto location = toWorldPosition(x, y, z); 25 | m_pWorld->setBlock(location.x, location.y, location.z, block); 26 | return; 27 | } 28 | 29 | m_layers[y].update(block); 30 | 31 | m_blocks[getIndex(x, y, z)] = block; 32 | } 33 | 34 | ChunkBlock ChunkSection::getBlock(int x, int y, int z) const 35 | { 36 | if (outOfBounds(x) || outOfBounds(y) || outOfBounds(z)) { 37 | auto location = toWorldPosition(x, y, z); 38 | return m_pWorld->getBlock(location.x, location.y, location.z); 39 | } 40 | 41 | return m_blocks[getIndex(x, y, z)]; 42 | } 43 | 44 | const sf::Vector3i ChunkSection::getLocation() const 45 | { 46 | return m_location; 47 | } 48 | 49 | bool ChunkSection::hasMesh() const 50 | { 51 | return m_hasMesh; 52 | } 53 | 54 | bool ChunkSection::hasBuffered() const 55 | { 56 | return m_hasBufferedMesh; 57 | } 58 | 59 | sf::Vector3i ChunkSection::toWorldPosition(int x, int y, int z) const 60 | { 61 | return {m_location.x * CHUNK_SIZE + x, m_location.y * CHUNK_SIZE + y, 62 | m_location.z * CHUNK_SIZE + z}; 63 | } 64 | 65 | void ChunkSection::makeMesh() 66 | { 67 | ChunkMeshBuilder(*this, m_meshes).buildMesh(); 68 | m_hasMesh = true; 69 | m_hasBufferedMesh = false; 70 | } 71 | 72 | void ChunkSection::bufferMesh() 73 | { 74 | m_meshes.solidMesh.bufferMesh(); 75 | m_meshes.waterMesh.bufferMesh(); 76 | m_meshes.floraMesh.bufferMesh(); 77 | m_hasBufferedMesh = true; 78 | } 79 | 80 | const ChunkSection::Layer &ChunkSection::getLayer(int y) const 81 | { 82 | if (y == -1) { 83 | return m_pWorld->getChunkManager() 84 | .getChunk(m_location.x, m_location.z) 85 | .getSection(m_location.y - 1) 86 | .getLayer(CHUNK_SIZE - 1); 87 | } 88 | else if (y == CHUNK_SIZE) { 89 | return m_pWorld->getChunkManager() 90 | .getChunk(m_location.x, m_location.z) 91 | .getSection(m_location.y + 1) 92 | .getLayer(0); 93 | } 94 | else { 95 | return m_layers[y]; 96 | } 97 | } 98 | 99 | void ChunkSection::deleteMeshes() 100 | { 101 | if (m_hasMesh) { 102 | m_hasBufferedMesh = false; 103 | m_hasMesh = false; 104 | m_meshes.solidMesh.deleteData(); 105 | m_meshes.waterMesh.deleteData(); 106 | m_meshes.floraMesh.deleteData(); 107 | } 108 | } 109 | 110 | ChunkSection &ChunkSection::getAdjacent(int dx, int dz) 111 | { 112 | int newX = m_location.x + dx; 113 | int newZ = m_location.z + dz; 114 | 115 | return m_pWorld->getChunkManager() 116 | .getChunk(newX, newZ) 117 | .getSection(m_location.y); 118 | } 119 | 120 | bool ChunkSection::outOfBounds(int value) 121 | { 122 | return value >= CHUNK_SIZE || value < 0; 123 | } 124 | 125 | int ChunkSection::getIndex(int x, int y, int z) 126 | { 127 | return y * CHUNK_AREA + z * CHUNK_SIZE + x; 128 | } 129 | -------------------------------------------------------------------------------- /Source/World/Chunk/ChunkSection.h: -------------------------------------------------------------------------------- 1 | #ifndef CHUNKSECTION_H_INCLUDED 2 | #define CHUNKSECTION_H_INCLUDED 3 | 4 | #include 5 | #include 6 | 7 | #include "../Block/ChunkBlock.h" 8 | #include "../WorldConstants.h" 9 | #include "ChunkMesh.h" 10 | #include "IChunk.h" 11 | 12 | #include "../../Physics/AABB.h" 13 | #include "../Block/BlockData.h" 14 | 15 | class World; 16 | 17 | class ChunkSection : public IChunk { 18 | friend class Chunk; 19 | 20 | class Layer { 21 | public: 22 | void update(ChunkBlock c) 23 | { 24 | if (c.getData().isOpaque) { 25 | m_solidBlockCount--; 26 | } 27 | else { 28 | m_solidBlockCount++; 29 | } 30 | } 31 | 32 | bool isAllSolid() const 33 | { 34 | return m_solidBlockCount == CHUNK_AREA; 35 | } 36 | 37 | private: 38 | int m_solidBlockCount = 0; 39 | }; 40 | 41 | public: 42 | ChunkSection(const sf::Vector3i &position, World &world); 43 | 44 | void setBlock(int x, int y, int z, ChunkBlock block) override; 45 | ChunkBlock getBlock(int x, int y, int z) const override; 46 | 47 | const sf::Vector3i getLocation() const; 48 | 49 | bool hasMesh() const; 50 | bool hasBuffered() const; 51 | 52 | void makeMesh(); 53 | void bufferMesh(); 54 | 55 | const Layer &getLayer(int y) const; 56 | ChunkSection &getAdjacent(int dx, int dz); 57 | 58 | const ChunkMeshCollection &getMeshes() const 59 | { 60 | return m_meshes; 61 | } 62 | 63 | void deleteMeshes(); 64 | 65 | const ChunkBlock *begin() 66 | { 67 | return &m_blocks[0]; 68 | } 69 | 70 | private: 71 | sf::Vector3i toWorldPosition(int x, int y, int z) const; 72 | 73 | static bool outOfBounds(int value); 74 | static int getIndex(int x, int y, int z); 75 | 76 | std::array m_blocks; 77 | std::array m_layers; 78 | 79 | ChunkMeshCollection m_meshes; 80 | AABB m_aabb; 81 | sf::Vector3i m_location; 82 | 83 | World *m_pWorld; 84 | 85 | bool m_hasMesh = false; 86 | bool m_hasBufferedMesh = false; 87 | }; 88 | 89 | #endif // CHUNKSECTION_H_INCLUDED 90 | -------------------------------------------------------------------------------- /Source/World/Chunk/IChunk.h: -------------------------------------------------------------------------------- 1 | #ifndef ICHUNK_H_INCLUDED 2 | #define ICHUNK_H_INCLUDED 3 | 4 | struct IChunk { 5 | virtual ~IChunk() = default; 6 | 7 | virtual ChunkBlock getBlock(int x, int y, int z) const = 0; 8 | virtual void setBlock(int x, int y, int z, ChunkBlock block) = 0; 9 | }; 10 | 11 | #endif // ICHUNK_H_INCLUDED 12 | -------------------------------------------------------------------------------- /Source/World/Event/IWorldEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef IWORLDEVENT_H_INCLUDED 2 | #define IWORLDEVENT_H_INCLUDED 3 | 4 | class World; 5 | 6 | struct IWorldEvent { 7 | virtual ~IWorldEvent() = default; 8 | virtual void handle(World &world) = 0; 9 | }; 10 | 11 | #endif // IWORLDEVENT_H_INCLUDED 12 | -------------------------------------------------------------------------------- /Source/World/Event/PlayerDigEvent.cpp: -------------------------------------------------------------------------------- 1 | #include "PlayerDigEvent.h" 2 | 3 | #include "../../Item/Material.h" 4 | #include "../../Player/Player.h" 5 | #include "../World.h" 6 | 7 | PlayerDigEvent::PlayerDigEvent(sf::Mouse::Button button, 8 | const glm::vec3 &location, Player &player) 9 | : m_buttonPress(button) 10 | , m_digSpot(location) 11 | , m_pPlayer(&player) 12 | { 13 | } 14 | 15 | void PlayerDigEvent::handle(World &world) 16 | { 17 | auto chunkLocation = World::getChunkXZ(static_cast(m_digSpot.x), 18 | static_cast(m_digSpot.z)); 19 | 20 | if (world.getChunkManager().chunkLoadedAt(chunkLocation.x, 21 | chunkLocation.z)) { 22 | dig(world); 23 | } 24 | } 25 | 26 | void PlayerDigEvent::dig(World &world) 27 | { 28 | int x = static_cast(m_digSpot.x); 29 | int y = static_cast(m_digSpot.y); 30 | int z = static_cast(m_digSpot.z); 31 | switch (m_buttonPress) { 32 | case sf::Mouse::Button::Left: { 33 | auto block = world.getBlock(x, y, z); 34 | const auto &material = Material::toMaterial((BlockId)block.id); 35 | m_pPlayer->addItem(material); 36 | /* 37 | auto r = 1; 38 | for (int y = -r; y < r; y++) 39 | for (int x = -r; x < r;x++) 40 | for (int z = -r; z < r; z++) 41 | { 42 | int newX = m_digSpot.x + x; 43 | int newY = m_digSpot.y + y; 44 | int newZ = m_digSpot.z + z; 45 | world.updateChunk (newX, newY, newZ); 46 | world.setBlock (newX, newY, newZ, 0); 47 | */ 48 | world.updateChunk(x, y, z); 49 | world.setBlock(x, y, z, 0); 50 | //} 51 | break; 52 | } 53 | 54 | case sf::Mouse::Button::Right: { 55 | auto &stack = m_pPlayer->getHeldItems(); 56 | auto &material = stack.getMaterial(); 57 | 58 | if (material.id == Material::ID::Nothing) { 59 | return; 60 | } 61 | else { 62 | stack.remove(); 63 | world.updateChunk(x, y, z); 64 | world.setBlock(x, y, z, material.toBlockID()); 65 | break; 66 | } 67 | } 68 | default: 69 | break; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Source/World/Event/PlayerDigEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef PLAYERDIGEVENT_H_INCLUDED 2 | #define PLAYERDIGEVENT_H_INCLUDED 3 | 4 | #include 5 | 6 | #include "../../Maths/glm.h" 7 | #include "IWorldEvent.h" 8 | 9 | class Player; 10 | 11 | /// @brief Event class that handles what happens to a block in a world as a player interacts. 12 | class PlayerDigEvent : public IWorldEvent { 13 | public: 14 | PlayerDigEvent(sf::Mouse::Button button, const glm::vec3 &location, 15 | Player &player); 16 | 17 | void handle(World &world); 18 | 19 | private: 20 | void dig(World &world); 21 | 22 | sf::Mouse::Button m_buttonPress; 23 | glm::vec3 m_digSpot; 24 | Player *m_pPlayer; 25 | }; 26 | 27 | #endif // PLAYERDIGEVENT_H_INCLUDED 28 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/Biome.cpp: -------------------------------------------------------------------------------- 1 | #include "Biome.h" 2 | 3 | Biome::Biome(const NoiseParameters ¶meters, int treeFreq, int plantFreq, 4 | int seed) 5 | : m_heightGenerator(seed) 6 | , m_treeFreq(treeFreq) 7 | , m_plantFreq(plantFreq) 8 | { 9 | m_heightGenerator.setParameters(parameters); 10 | } 11 | 12 | ChunkBlock Biome::getBeachBlock(Rand &rand) const 13 | { 14 | return BlockId::Sand; 15 | } 16 | 17 | int Biome::getHeight(int x, int z, int chunkX, int chunkZ) const 18 | { 19 | return m_heightGenerator.getHeight(x, z, chunkX, chunkZ); 20 | } 21 | 22 | int Biome::getTreeFrequency() const noexcept 23 | { 24 | return m_treeFreq; 25 | } 26 | 27 | int Biome::getPlantFrequency() const noexcept 28 | { 29 | return m_plantFreq; 30 | } 31 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/Biome.h: -------------------------------------------------------------------------------- 1 | #ifndef BIOME_H_INCLUDED 2 | #define BIOME_H_INCLUDED 3 | 4 | #include "../../../Maths/NoiseGenerator.h" 5 | #include "../../../Util/Random.h" 6 | #include "../../Block/ChunkBlock.h" 7 | 8 | using Rand = Random; 9 | 10 | class Chunk; 11 | 12 | struct Biome { 13 | public: 14 | Biome(const NoiseParameters ¶meters, int treeFreq, int plantFreq, 15 | int seed); 16 | virtual ~Biome() = default; 17 | 18 | virtual ChunkBlock getPlant(Rand &rand) const = 0; 19 | virtual ChunkBlock getTopBlock(Rand &rand) const = 0; 20 | virtual ChunkBlock getUnderWaterBlock(Rand &rand) const = 0; 21 | virtual ChunkBlock getBeachBlock(Rand &rand) const; 22 | virtual void makeTree(Rand &rand, Chunk &chunk, int x, int y, 23 | int z) const = 0; 24 | 25 | int getHeight(int x, int z, int chunkX, int chunkZ) const; 26 | int getTreeFrequency() const noexcept; 27 | int getPlantFrequency() const noexcept; 28 | 29 | protected: 30 | virtual NoiseParameters getNoiseParameters() = 0; 31 | 32 | private: 33 | NoiseGenerator m_heightGenerator; 34 | int m_treeFreq; 35 | int m_plantFreq; 36 | }; 37 | 38 | #endif // BIOME_H_INCLUDED 39 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/DesertBiome.cpp: -------------------------------------------------------------------------------- 1 | #include "DesertBiome.h" 2 | 3 | #include "../../WorldConstants.h" 4 | #include "../Structures/TreeGenerator.h" 5 | 6 | DesertBiome::DesertBiome(int seed) 7 | : Biome(getNoiseParameters(), 1350, 500, seed) 8 | { 9 | } 10 | 11 | ChunkBlock DesertBiome::getTopBlock(Rand &rand) const 12 | { 13 | return BlockId::Sand; 14 | } 15 | 16 | ChunkBlock DesertBiome::getUnderWaterBlock(Rand &rand) const 17 | { 18 | return BlockId::Sand; 19 | } 20 | 21 | void DesertBiome::makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const 22 | { 23 | if (y < WATER_LEVEL + 15) { 24 | if (rand.intInRange(0, 100) > 75) { 25 | makePalmTree(chunk, rand, x, y, z); 26 | } 27 | else { 28 | makeCactus(chunk, rand, x, y, z); 29 | } 30 | } 31 | else { 32 | makeCactus(chunk, rand, x, y, z); 33 | } 34 | } 35 | 36 | NoiseParameters DesertBiome::getNoiseParameters() 37 | { 38 | NoiseParameters heightParams; 39 | heightParams.octaves = 9; 40 | heightParams.amplitude = 80; 41 | heightParams.smoothness = 335; 42 | heightParams.heightOffset = -7; 43 | heightParams.roughness = 0.56; 44 | 45 | return heightParams; 46 | } 47 | 48 | ChunkBlock DesertBiome::getPlant(Rand &rand) const 49 | { 50 | return BlockId::DeadShrub; 51 | } 52 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/DesertBiome.h: -------------------------------------------------------------------------------- 1 | #ifndef DESERTBIOME_H_INCLUDED 2 | #define DESERTBIOME_H_INCLUDED 3 | 4 | #include "Biome.h" 5 | 6 | class DesertBiome : public Biome { 7 | public: 8 | DesertBiome(int seed); 9 | 10 | ChunkBlock getPlant(Rand &rand) const override; 11 | ChunkBlock getTopBlock(Rand &rand) const override; 12 | ChunkBlock getUnderWaterBlock(Rand &rand) const override; 13 | void makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const override; 14 | 15 | private: 16 | NoiseParameters getNoiseParameters() override; 17 | }; 18 | 19 | #endif // DESERTBIOME_H_INCLUDED 20 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/GrasslandBiome.cpp: -------------------------------------------------------------------------------- 1 | #include "GrasslandBiome.h" 2 | 3 | #include "../Structures/TreeGenerator.h" 4 | 5 | GrasslandBiome::GrasslandBiome(int seed) 6 | : Biome(getNoiseParameters(), 1000, 20, seed) 7 | { 8 | } 9 | 10 | ChunkBlock GrasslandBiome::getTopBlock(Rand &rand) const 11 | { 12 | return BlockId::Grass; 13 | } 14 | 15 | ChunkBlock GrasslandBiome::getUnderWaterBlock(Rand &rand) const 16 | { 17 | return rand.intInRange(0, 10) > 8 ? BlockId::Dirt : BlockId::Sand; 18 | } 19 | 20 | ChunkBlock GrasslandBiome::getBeachBlock(Rand &rand) const 21 | { 22 | return rand.intInRange(0, 10) > 2 ? BlockId::Grass : BlockId::Dirt; 23 | } 24 | 25 | void GrasslandBiome::makeTree(Rand &rand, Chunk &chunk, int x, int y, 26 | int z) const 27 | { 28 | makeOakTree(chunk, rand, x, y, z); 29 | } 30 | 31 | NoiseParameters GrasslandBiome::getNoiseParameters() 32 | { 33 | NoiseParameters heightParams; 34 | heightParams.octaves = 9; 35 | heightParams.amplitude = 85; 36 | heightParams.smoothness = 235; 37 | heightParams.heightOffset = -20; 38 | heightParams.roughness = 0.51; 39 | 40 | return heightParams; 41 | } 42 | 43 | ChunkBlock GrasslandBiome::getPlant(Rand &rand) const 44 | { 45 | return rand.intInRange(0, 10) > 6 ? BlockId::Rose : BlockId::TallGrass; 46 | } 47 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/GrasslandBiome.h: -------------------------------------------------------------------------------- 1 | #ifndef GRASSLANDBIOME_H_INCLUDED 2 | #define GRASSLANDBIOME_H_INCLUDED 3 | 4 | #include "Biome.h" 5 | 6 | class GrasslandBiome : public Biome { 7 | public: 8 | GrasslandBiome(int seed); 9 | 10 | ChunkBlock getBeachBlock(Rand &rand) const override; 11 | ChunkBlock getPlant(Rand &rand) const override; 12 | ChunkBlock getTopBlock(Rand &rand) const override; 13 | ChunkBlock getUnderWaterBlock(Rand &rand) const override; 14 | void makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const override; 15 | 16 | private: 17 | NoiseParameters getNoiseParameters() override; 18 | }; 19 | 20 | #endif // GRASSLANDBIOME_H_INCLUDED 21 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/LightForest.cpp: -------------------------------------------------------------------------------- 1 | #include "LightForest.h" 2 | 3 | #include "../Structures/TreeGenerator.h" 4 | 5 | LightForest::LightForest(int seed) 6 | : Biome(getNoiseParameters(), 60, 80, seed) 7 | { 8 | } 9 | 10 | ChunkBlock LightForest::getTopBlock(Rand &rand) const 11 | { 12 | return BlockId::Grass; 13 | } 14 | 15 | ChunkBlock LightForest::getUnderWaterBlock(Rand &rand) const 16 | { 17 | return rand.intInRange(0, 10) > 9 ? BlockId::Sand : BlockId::Dirt; 18 | } 19 | 20 | void LightForest::makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const 21 | { 22 | makeOakTree(chunk, rand, x, y, z); 23 | } 24 | 25 | NoiseParameters LightForest::getNoiseParameters() 26 | { 27 | NoiseParameters heightParams; 28 | heightParams.octaves = 5; 29 | heightParams.amplitude = 100; 30 | heightParams.smoothness = 195; // 195 31 | heightParams.heightOffset = -32; 32 | heightParams.roughness = 0.52; 33 | 34 | return heightParams; 35 | } 36 | 37 | ChunkBlock LightForest::getPlant(Rand &rand) const 38 | { 39 | return rand.intInRange(0, 10) > 8 ? BlockId::Rose : BlockId::TallGrass; 40 | } 41 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/LightForest.h: -------------------------------------------------------------------------------- 1 | #ifndef LIGHTFOREST_H_INCLUDED 2 | #define LIGHTFOREST_H_INCLUDED 3 | 4 | #include "Biome.h" 5 | 6 | class LightForest : public Biome { 7 | public: 8 | LightForest(int seed); 9 | 10 | ChunkBlock getPlant(Rand &rand) const override; 11 | ChunkBlock getTopBlock(Rand &rand) const override; 12 | ChunkBlock getUnderWaterBlock(Rand &rand) const override; 13 | void makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const override; 14 | 15 | private: 16 | NoiseParameters getNoiseParameters() override; 17 | }; 18 | 19 | #endif // LIGHTFOREST_H_INCLUDED 20 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/OceanBiome.cpp: -------------------------------------------------------------------------------- 1 | #include "OceanBiome.h" 2 | 3 | #include "../Structures/TreeGenerator.h" 4 | 5 | OceanBiome::OceanBiome(int seed) 6 | : Biome(getNoiseParameters(), 50, 100, seed) 7 | { 8 | } 9 | 10 | ChunkBlock OceanBiome::getTopBlock(Rand &rand) const 11 | { 12 | return BlockId::Grass; 13 | } 14 | 15 | ChunkBlock OceanBiome::getUnderWaterBlock(Rand &rand) const 16 | { 17 | return BlockId::Sand; 18 | } 19 | 20 | void OceanBiome::makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const 21 | { 22 | rand.intInRange(0, 5) < 3 ? makePalmTree(chunk, rand, x, y, z) 23 | : makeOakTree(chunk, rand, x, y, z); 24 | } 25 | 26 | NoiseParameters OceanBiome::getNoiseParameters() 27 | { 28 | NoiseParameters heightParams; 29 | heightParams.octaves = 7; 30 | heightParams.amplitude = 43; 31 | heightParams.smoothness = 55; 32 | heightParams.heightOffset = 0; 33 | heightParams.roughness = 0.50; 34 | 35 | return heightParams; 36 | } 37 | 38 | ChunkBlock OceanBiome::getPlant(Rand &rand) const 39 | { 40 | return rand.intInRange(0, 10) > 6 ? BlockId::Rose : BlockId::TallGrass; 41 | } 42 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/OceanBiome.h: -------------------------------------------------------------------------------- 1 | #ifndef OCEANBIOME_H_INCLUDED 2 | #define OCEANBIOME_H_INCLUDED 3 | 4 | #include "Biome.h" 5 | 6 | class OceanBiome : public Biome { 7 | public: 8 | OceanBiome(int seed); 9 | 10 | ChunkBlock getPlant(Rand &rand) const override; 11 | ChunkBlock getTopBlock(Rand &rand) const override; 12 | ChunkBlock getUnderWaterBlock(Rand &rand) const override; 13 | void makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const override; 14 | 15 | private: 16 | NoiseParameters getNoiseParameters() override; 17 | }; 18 | 19 | #endif // OCEANBIOME_H_INCLUDED 20 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/TemperateForestBiome.cpp: -------------------------------------------------------------------------------- 1 | #include "TemperateForestBiome.h" 2 | 3 | #include "../Structures/TreeGenerator.h" 4 | 5 | TemperateForestBiome::TemperateForestBiome(int seed) 6 | : Biome(getNoiseParameters(), 55, 75, seed) 7 | { 8 | } 9 | 10 | ChunkBlock TemperateForestBiome::getTopBlock(Rand &rand) const 11 | { 12 | return rand.intInRange(0, 10) < 8 ? BlockId::Grass : BlockId::Dirt; 13 | } 14 | 15 | ChunkBlock TemperateForestBiome::getUnderWaterBlock(Rand &rand) const 16 | { 17 | return rand.intInRange(0, 10) > 8 ? BlockId::Dirt : BlockId::Sand; 18 | } 19 | 20 | void TemperateForestBiome::makeTree(Rand &rand, Chunk &chunk, int x, int y, 21 | int z) const 22 | { 23 | makeOakTree(chunk, rand, x, y, z); 24 | } 25 | 26 | NoiseParameters TemperateForestBiome::getNoiseParameters() 27 | { 28 | NoiseParameters heightParams; 29 | heightParams.octaves = 5; 30 | heightParams.amplitude = 100; 31 | heightParams.smoothness = 195; 32 | heightParams.heightOffset = -30; 33 | heightParams.roughness = 0.52; 34 | 35 | return heightParams; 36 | } 37 | 38 | ChunkBlock TemperateForestBiome::getPlant(Rand &rand) const 39 | { 40 | return BlockId::TallGrass; 41 | } 42 | -------------------------------------------------------------------------------- /Source/World/Generation/Biome/TemperateForestBiome.h: -------------------------------------------------------------------------------- 1 | #ifndef TEMPERATEFORESTBIOME_H_INCLUDED 2 | #define TEMPERATEFORESTBIOME_H_INCLUDED 3 | 4 | #include "Biome.h" 5 | 6 | class TemperateForestBiome : public Biome { 7 | public: 8 | TemperateForestBiome(int seed); 9 | 10 | ChunkBlock getPlant(Rand &rand) const override; 11 | ChunkBlock getTopBlock(Rand &rand) const override; 12 | ChunkBlock getUnderWaterBlock(Rand &rand) const override; 13 | void makeTree(Rand &rand, Chunk &chunk, int x, int y, int z) const override; 14 | 15 | private: 16 | NoiseParameters getNoiseParameters(); 17 | }; 18 | 19 | #endif // TEMPERATEFORESTBIOME_H_INCLUDED 20 | -------------------------------------------------------------------------------- /Source/World/Generation/Structures/StructureBuilder.cpp: -------------------------------------------------------------------------------- 1 | #include "StructureBuilder.h" 2 | 3 | #include "../../Chunk/Chunk.h" 4 | 5 | #include 6 | 7 | void StructureBuilder::build(Chunk &chunk) 8 | { 9 | for (auto &block : m_blocks) { 10 | chunk.setBlock(block.x, block.y, block.z, block.id); 11 | } 12 | } 13 | 14 | void StructureBuilder::makeColumn(int x, int z, int yStart, int height, 15 | BlockId block) 16 | { 17 | for (int y = yStart; y < yStart + height; y++) { 18 | addBlock(x, y, z, block); 19 | } 20 | } 21 | 22 | void StructureBuilder::makeRowX(int xStart, int xEnd, int y, int z, 23 | BlockId block) 24 | { 25 | for (int x = xStart; x <= xEnd; ++x) { 26 | addBlock(x, y, z, block); 27 | } 28 | } 29 | 30 | void StructureBuilder::makeRowZ(int zStart, int zEnd, int x, int y, 31 | BlockId block) 32 | { 33 | for (int z = zStart; z <= zEnd; ++z) { 34 | addBlock(x, y, z, block); 35 | } 36 | } 37 | 38 | void StructureBuilder::fill(int y, int xStart, int xEnd, int zStart, int zEnd, 39 | BlockId block) 40 | { 41 | for (int x = xStart; x < xEnd; ++x) 42 | for (int z = zStart; z < zEnd; ++z) { 43 | addBlock(x, y, z, block); 44 | } 45 | } 46 | 47 | void StructureBuilder::addBlock(int x, int y, int z, BlockId block) 48 | { 49 | m_blocks.emplace_back(block, x, y, z); 50 | } 51 | -------------------------------------------------------------------------------- /Source/World/Generation/Structures/StructureBuilder.h: -------------------------------------------------------------------------------- 1 | #ifndef STRUCTUREBUILDER_H_INCLUDED 2 | #define STRUCTUREBUILDER_H_INCLUDED 3 | 4 | #include "../../Block/BlockId.h" 5 | #include 6 | 7 | class Chunk; 8 | 9 | class StructureBuilder { 10 | struct Block { 11 | Block(BlockId id, int x, int y, int z) 12 | : id(id) 13 | , x(x) 14 | , y(y) 15 | , z(z) 16 | { 17 | } 18 | 19 | BlockId id; 20 | int x, y, z; 21 | }; 22 | 23 | public: 24 | void build(Chunk &chunk); 25 | 26 | void makeColumn(int x, int z, int yStart, int height, BlockId block); 27 | void makeRowX(int xStart, int xEnd, int y, int z, BlockId block); 28 | void makeRowZ(int zStart, int zEnd, int x, int y, BlockId block); 29 | 30 | void fill(int y, int xStart, int xEnd, int zStart, int zEnd, BlockId block); 31 | 32 | void addBlock(int x, int y, int z, BlockId block); 33 | 34 | private: 35 | std::vector m_blocks; 36 | }; 37 | 38 | #endif // STRUCTUREBUILDER_H_INCLUDED 39 | -------------------------------------------------------------------------------- /Source/World/Generation/Structures/TreeGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "TreeGenerator.h" 2 | 3 | #include "../../Chunk/Chunk.h" 4 | #include "StructureBuilder.h" 5 | 6 | constexpr BlockId CACTUS = BlockId::Cactus; 7 | 8 | namespace { 9 | void makeCactus1(Chunk &chunk, Random &rand, int x, int y, 10 | int z) 11 | { 12 | StructureBuilder builder; 13 | builder.makeColumn(x, z, y, rand.intInRange(4, 7), CACTUS); 14 | builder.build(chunk); 15 | } 16 | 17 | void makeCactus2(Chunk &chunk, Random &rand, int x, int y, 18 | int z) 19 | { 20 | StructureBuilder builder; 21 | int height = rand.intInRange(6, 8); 22 | builder.makeColumn(x, z, y, height, CACTUS); 23 | 24 | int stem = height / 2; 25 | 26 | builder.makeRowX(x - 2, x + 2, stem + y, z, CACTUS); 27 | builder.addBlock(x - 2, stem + y + 1, z, CACTUS); 28 | builder.addBlock(x - 2, stem + y + 2, z, CACTUS); 29 | builder.addBlock(x + 2, stem + y + 1, z, CACTUS); 30 | 31 | builder.build(chunk); 32 | } 33 | 34 | void makeCactus3(Chunk &chunk, Random &rand, int x, int y, 35 | int z) 36 | { 37 | StructureBuilder builder; 38 | int height = rand.intInRange(6, 8); 39 | builder.makeColumn(x, z, y, height, CACTUS); 40 | 41 | int stem = height / 2; 42 | 43 | builder.makeRowZ(z - 2, z + 2, x, stem + y, CACTUS); 44 | builder.addBlock(x, stem + y + 1, z - 2, CACTUS); 45 | builder.addBlock(x, stem + y + 2, z - 2, CACTUS); 46 | builder.addBlock(x, stem + y + 1, z + 2, CACTUS); 47 | 48 | builder.build(chunk); 49 | } 50 | } // namespace 51 | 52 | void makeOakTree(Chunk &chunk, Random &rand, int x, int y, 53 | int z) 54 | { 55 | StructureBuilder builder; 56 | 57 | int h = rand.intInRange(4, 7); 58 | int leafSize = 2; 59 | 60 | int newY = h + y; 61 | builder.fill(newY, x - leafSize, x + leafSize, z - leafSize, z + leafSize, 62 | BlockId::OakLeaf); 63 | builder.fill(newY - 1, x - leafSize, x + leafSize, z - leafSize, 64 | z + leafSize, BlockId::OakLeaf); 65 | 66 | for (int32_t zLeaf = -leafSize + 1; zLeaf <= leafSize - 1; zLeaf++) { 67 | builder.addBlock(x, newY + 1, z + zLeaf, BlockId::OakLeaf); 68 | } 69 | 70 | for (int32_t xLeaf = -leafSize + 1; xLeaf <= leafSize - 1; xLeaf++) { 71 | builder.addBlock(x + xLeaf, newY + 1, z, BlockId::OakLeaf); 72 | } 73 | 74 | builder.makeColumn(x, z, y, h, BlockId::OakBark); 75 | builder.build(chunk); 76 | } 77 | 78 | void makePalmTree(Chunk &chunk, Random &rand, int x, int y, 79 | int z) 80 | { 81 | StructureBuilder builder; 82 | 83 | int height = rand.intInRange(7, 9); 84 | int diameter = rand.intInRange(4, 6); 85 | 86 | for (int xLeaf = -diameter; xLeaf < diameter; xLeaf++) { 87 | builder.addBlock(xLeaf + x, y + height, z, BlockId::OakLeaf); 88 | } 89 | for (int zLeaf = -diameter; zLeaf < diameter; zLeaf++) { 90 | builder.addBlock(x, y + height, zLeaf + z, BlockId::OakLeaf); 91 | } 92 | 93 | builder.addBlock(x, y + height - 1, z + diameter, BlockId::OakLeaf); 94 | builder.addBlock(x, y + height - 1, z - diameter, BlockId::OakLeaf); 95 | builder.addBlock(x + diameter, y + height - 1, z, BlockId::OakLeaf); 96 | builder.addBlock(x - diameter, y + height - 1, z, BlockId::OakLeaf); 97 | builder.addBlock(x, y + height + 1, z, BlockId::OakLeaf); 98 | 99 | builder.makeColumn(x, z, y, height, BlockId::OakBark); 100 | builder.build(chunk); 101 | } 102 | 103 | void makeCactus(Chunk &chunk, Random &rand, int x, int y, 104 | int z) 105 | { 106 | int cac = rand.intInRange(0, 2); 107 | 108 | switch (cac) { 109 | case 0: 110 | makeCactus1(chunk, rand, x, y, z); 111 | break; 112 | 113 | case 1: 114 | makeCactus2(chunk, rand, x, y, z); 115 | break; 116 | 117 | case 2: 118 | makeCactus3(chunk, rand, x, y, z); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Source/World/Generation/Structures/TreeGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef TREEGENERATOR_H_INCLUDED 2 | #define TREEGENERATOR_H_INCLUDED 3 | 4 | #include "../../../Util/Random.h" 5 | 6 | class Chunk; 7 | 8 | void makeOakTree(Chunk &chunk, Random &rand, int x, int y, 9 | int z); 10 | void makePalmTree(Chunk &chunk, Random &rand, int x, int y, 11 | int z); 12 | 13 | void makeCactus(Chunk &chunk, Random &rand, int x, int y, 14 | int z); 15 | 16 | #endif // TREEGENERATOR_H_INCLUDED 17 | -------------------------------------------------------------------------------- /Source/World/Generation/Terrain/ClassicOverWorldGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "ClassicOverWorldGenerator.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../../../Maths/GeneralMaths.h" 7 | #include "../../../Util/Random.h" 8 | #include "../../Chunk/Chunk.h" 9 | 10 | #include "../Structures/TreeGenerator.h" 11 | 12 | namespace { 13 | const int seed = RandomSingleton::get().intInRange(424, 325322); 14 | } 15 | 16 | NoiseGenerator ClassicOverWorldGenerator::m_biomeNoiseGen(seed * 2); 17 | 18 | ClassicOverWorldGenerator::ClassicOverWorldGenerator() 19 | : m_grassBiome(seed) 20 | , m_temperateForest(seed) 21 | , m_desertBiome(seed) 22 | , m_oceanBiome(seed) 23 | , m_lightForest(seed) 24 | { 25 | setUpNoise(); 26 | } 27 | 28 | void ClassicOverWorldGenerator::setUpNoise() 29 | { 30 | std::cout << "Seed: " << seed << '\n'; 31 | static bool noiseGen = false; 32 | if (!noiseGen) { 33 | std::cout << "making noise\n"; 34 | noiseGen = true; 35 | 36 | NoiseParameters biomeParmams; 37 | biomeParmams.octaves = 5; 38 | biomeParmams.amplitude = 120; 39 | biomeParmams.smoothness = 1035; 40 | biomeParmams.heightOffset = 0; 41 | biomeParmams.roughness = 0.75; 42 | 43 | m_biomeNoiseGen.setParameters(biomeParmams); 44 | } 45 | } 46 | 47 | void ClassicOverWorldGenerator::generateTerrainFor(Chunk &chunk) 48 | { 49 | m_pChunk = &chunk; 50 | 51 | auto location = chunk.getLocation(); 52 | m_random.setSeed((location.x ^ location.y) << 2); 53 | 54 | getBiomeMap(); 55 | getHeightMap(); 56 | 57 | auto maxHeight = m_heightMap.getMaxValue(); 58 | 59 | maxHeight = std::max(maxHeight, WATER_LEVEL); 60 | setBlocks(maxHeight); 61 | } 62 | 63 | int ClassicOverWorldGenerator::getMinimumSpawnHeight() const noexcept 64 | { 65 | return WATER_LEVEL; 66 | } 67 | 68 | void ClassicOverWorldGenerator::getHeightIn(int xMin, int zMin, int xMax, 69 | int zMax) 70 | { 71 | 72 | auto getHeightAt = [&](int x, int z) { 73 | const Biome &biome = getBiome(x, z); 74 | 75 | return biome.getHeight(x, z, m_pChunk->getLocation().x, 76 | m_pChunk->getLocation().y); 77 | }; 78 | 79 | float bottomLeft = static_cast(getHeightAt(xMin, zMin)); 80 | float bottomRight = static_cast(getHeightAt(xMax, zMin)); 81 | float topLeft = static_cast(getHeightAt(xMin, zMax)); 82 | float topRight = static_cast(getHeightAt(xMax, zMax)); 83 | 84 | for (int x = xMin; x < xMax; ++x) 85 | for (int z = zMin; z < zMax; ++z) { 86 | if (x == CHUNK_SIZE) 87 | continue; 88 | if (z == CHUNK_SIZE) 89 | continue; 90 | 91 | float h = smoothInterpolation( 92 | bottomLeft, topLeft, bottomRight, topRight, 93 | static_cast(xMin), static_cast(xMax), 94 | static_cast(zMin), static_cast(zMax), 95 | static_cast(x), static_cast(z)); 96 | 97 | m_heightMap.get(x, z) = static_cast(h); 98 | } 99 | } 100 | 101 | void ClassicOverWorldGenerator::getHeightMap() 102 | { 103 | constexpr static auto HALF_CHUNK = CHUNK_SIZE / 2; 104 | constexpr static auto CHUNK = CHUNK_SIZE; 105 | 106 | getHeightIn(0, 0, HALF_CHUNK, HALF_CHUNK); 107 | getHeightIn(HALF_CHUNK, 0, CHUNK, HALF_CHUNK); 108 | getHeightIn(0, HALF_CHUNK, HALF_CHUNK, CHUNK); 109 | getHeightIn(HALF_CHUNK, HALF_CHUNK, CHUNK, CHUNK); 110 | } 111 | 112 | void ClassicOverWorldGenerator::getBiomeMap() 113 | { 114 | auto location = m_pChunk->getLocation(); 115 | 116 | for (int x = 0; x < CHUNK_SIZE + 1; x++) 117 | for (int z = 0; z < CHUNK_SIZE + 1; z++) { 118 | double h = m_biomeNoiseGen.getHeight(x, z, location.x + 10, 119 | location.y + 10); 120 | m_biomeMap.get(x, z) = static_cast(h); 121 | } 122 | } 123 | 124 | void ClassicOverWorldGenerator::setBlocks(int maxHeight) 125 | { 126 | std::vector trees; 127 | std::vector plants; 128 | 129 | for (int y = 0; y < maxHeight + 1; y++) 130 | for (int x = 0; x < CHUNK_SIZE; x++) 131 | for (int z = 0; z < CHUNK_SIZE; z++) { 132 | int height = m_heightMap.get(x, z); 133 | auto &biome = getBiome(x, z); 134 | 135 | if (y > height) { 136 | if (y <= WATER_LEVEL) { 137 | m_pChunk->setBlock(x, y, z, BlockId::Water); 138 | } 139 | continue; 140 | } 141 | else if (y == height) { 142 | if (y >= WATER_LEVEL) { 143 | if (y < WATER_LEVEL + 4) { 144 | m_pChunk->setBlock(x, y, z, 145 | biome.getBeachBlock(m_random)); 146 | continue; 147 | } 148 | 149 | if (m_random.intInRange(0, biome.getTreeFrequency()) == 150 | 5) { 151 | trees.emplace_back(x, y + 1, z); 152 | } 153 | if (m_random.intInRange(0, biome.getPlantFrequency()) == 154 | 5) { 155 | plants.emplace_back(x, y + 1, z); 156 | } 157 | m_pChunk->setBlock( 158 | x, y, z, getBiome(x, z).getTopBlock(m_random)); 159 | } 160 | else { 161 | m_pChunk->setBlock(x, y, z, 162 | biome.getUnderWaterBlock(m_random)); 163 | } 164 | } 165 | else if (y > height - 3) { 166 | m_pChunk->setBlock(x, y, z, BlockId::Dirt); 167 | } 168 | else { 169 | m_pChunk->setBlock(x, y, z, BlockId::Stone); 170 | } 171 | } 172 | 173 | for (auto &plant : plants) { 174 | int x = plant.x; 175 | int z = plant.z; 176 | 177 | auto block = getBiome(x, z).getPlant(m_random); 178 | m_pChunk->setBlock(x, plant.y, z, block); 179 | } 180 | 181 | for (auto &tree : trees) { 182 | int x = tree.x; 183 | int z = tree.z; 184 | 185 | getBiome(x, z).makeTree(m_random, *m_pChunk, x, tree.y, z); 186 | } 187 | } 188 | 189 | const Biome &ClassicOverWorldGenerator::getBiome(int x, int z) const 190 | { 191 | int biomeValue = m_biomeMap.get(x, z); 192 | 193 | if (biomeValue > 160) { 194 | return m_oceanBiome; 195 | } 196 | else if (biomeValue > 150) { 197 | return m_grassBiome; 198 | } 199 | else if (biomeValue > 130) { 200 | return m_lightForest; 201 | } 202 | else if (biomeValue > 120) { 203 | return m_temperateForest; 204 | } 205 | else if (biomeValue > 110) { 206 | return m_lightForest; 207 | } 208 | else if (biomeValue > 100) { 209 | return m_grassBiome; 210 | } 211 | else { 212 | return m_desertBiome; 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /Source/World/Generation/Terrain/ClassicOverWorldGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef CLASSICOVERWORLDGENERATOR_H_INCLUDED 2 | #define CLASSICOVERWORLDGENERATOR_H_INCLUDED 3 | 4 | #include "TerrainGenerator.h" 5 | 6 | #include "../../../Util/Array2D.h" 7 | #include "../../../Util/Random.h" 8 | 9 | #include "../../../Maths/NoiseGenerator.h" 10 | #include "../../WorldConstants.h" 11 | 12 | #include "../Biome/DesertBiome.h" 13 | #include "../Biome/GrasslandBiome.h" 14 | #include "../Biome/LightForest.h" 15 | #include "../Biome/OceanBiome.h" 16 | #include "../Biome/TemperateForestBiome.h" 17 | 18 | class Chunk; 19 | 20 | /// @brief Generates chunks based on perlin noise and recognizable MC parameters. 21 | class ClassicOverWorldGenerator : public TerrainGenerator { 22 | public: 23 | ClassicOverWorldGenerator(); 24 | 25 | void generateTerrainFor(Chunk &chunk) override; 26 | int getMinimumSpawnHeight() const noexcept override; 27 | 28 | private: 29 | static void setUpNoise(); 30 | 31 | void setBlocks(int maxHeight); 32 | 33 | void getHeightIn(int xMin, int zMin, int xMax, int zMax); 34 | void getHeightMap(); 35 | void getBiomeMap(); 36 | 37 | const Biome &getBiome(int x, int z) const; 38 | 39 | Array2D m_heightMap; 40 | Array2D m_biomeMap; 41 | 42 | Random m_random; 43 | 44 | static NoiseGenerator m_biomeNoiseGen; 45 | 46 | GrasslandBiome m_grassBiome; 47 | TemperateForestBiome m_temperateForest; 48 | DesertBiome m_desertBiome; 49 | OceanBiome m_oceanBiome; 50 | LightForest m_lightForest; 51 | 52 | Chunk *m_pChunk = nullptr; 53 | }; 54 | 55 | #endif // CLASSICOVERWORLDGENERATOR_H_INCLUDED 56 | -------------------------------------------------------------------------------- /Source/World/Generation/Terrain/SuperFlatGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "SuperFlatGenerator.h" 2 | 3 | #include 4 | 5 | #include "../../Chunk/Chunk.h" 6 | #include "../../WorldConstants.h" 7 | 8 | void SuperFlatGenerator::generateTerrainFor(Chunk &chunk) 9 | { 10 | for (int x = 0; x < CHUNK_SIZE; ++x) 11 | for (int z = 0; z < CHUNK_SIZE; ++z) { 12 | chunk.setBlock(x, 0, z, BlockId::Stone); 13 | chunk.setBlock(x, 1, z, BlockId::Dirt); 14 | chunk.setBlock(x, 2, z, BlockId::Dirt); 15 | chunk.setBlock(x, 3, z, BlockId::Dirt); 16 | chunk.setBlock(x, 4, z, BlockId::Grass); 17 | } 18 | } 19 | 20 | int SuperFlatGenerator::getMinimumSpawnHeight() const noexcept 21 | { 22 | return 1; 23 | } 24 | -------------------------------------------------------------------------------- /Source/World/Generation/Terrain/SuperFlatGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef SUPERFLATGENERATOR_H_INCLUDED 2 | #define SUPERFLATGENERATOR_H_INCLUDED 3 | 4 | #include "TerrainGenerator.h" 5 | 6 | class SuperFlatGenerator : public TerrainGenerator { 7 | public: 8 | void generateTerrainFor(Chunk &chunk) override; 9 | int getMinimumSpawnHeight() const noexcept override; 10 | }; 11 | 12 | #endif // SUPERFLATGENERATOR_H_INCLUDED 13 | -------------------------------------------------------------------------------- /Source/World/Generation/Terrain/TerrainGenerator.h: -------------------------------------------------------------------------------- 1 | #ifndef TERRAINGENERATOR_H_INCLUDED 2 | #define TERRAINGENERATOR_H_INCLUDED 3 | 4 | class Chunk; 5 | 6 | class TerrainGenerator { 7 | public: 8 | virtual void generateTerrainFor(Chunk &chunk) = 0; 9 | virtual int getMinimumSpawnHeight() const noexcept = 0; 10 | 11 | virtual ~TerrainGenerator() = default; 12 | }; 13 | 14 | #endif // TERRAINGENERATOR_H_INCLUDED 15 | -------------------------------------------------------------------------------- /Source/World/World.cpp: -------------------------------------------------------------------------------- 1 | #include "World.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "../Camera.h" 7 | #include "../Input/ToggleKey.h" 8 | #include "../Maths/Vector2XZ.h" 9 | #include "../Player/Player.h" 10 | #include "../Renderer/RenderMaster.h" 11 | #include "../Util/Random.h" 12 | 13 | World::World(const Camera &camera, const Config &config, Player &player) 14 | : m_chunkManager(*this) 15 | , m_renderDistance(config.renderDistance) 16 | { 17 | setSpawnPoint(); 18 | player.position = m_playerSpawnPoint; 19 | 20 | for (int i = 0; i < 1; i++) { 21 | std::this_thread::sleep_for(std::chrono::milliseconds(200)); 22 | m_chunkLoadThreads.emplace_back([&]() { loadChunks(camera); }); 23 | } 24 | } 25 | 26 | World::~World() 27 | { 28 | m_isRunning = false; 29 | for (auto &thread : m_chunkLoadThreads) { 30 | thread.join(); 31 | } 32 | } 33 | 34 | // world coords into chunk column coords 35 | ChunkBlock World::getBlock(int x, int y, int z) 36 | { 37 | auto bp = getBlockXZ(x, z); 38 | auto chunkPosition = getChunkXZ(x, z); 39 | 40 | return m_chunkManager.getChunk(chunkPosition.x, chunkPosition.z) 41 | .getBlock(bp.x, y, bp.z); 42 | } 43 | 44 | void World::setBlock(int x, int y, int z, ChunkBlock block) 45 | { 46 | if (y <= 0) 47 | return; 48 | 49 | auto bp = getBlockXZ(x, z); 50 | auto chunkPosition = getChunkXZ(x, z); 51 | 52 | m_chunkManager.getChunk(chunkPosition.x, chunkPosition.z) 53 | .setBlock(bp.x, y, bp.z, block); 54 | } 55 | 56 | // loads chunks 57 | // make chunk meshes 58 | void World::update(const Camera &camera) 59 | { 60 | static ToggleKey key(sf::Keyboard::C); 61 | 62 | if (key.isKeyPressed()) { 63 | std::unique_lock lock(m_mainMutex); 64 | m_chunkManager.deleteMeshes(); 65 | m_loadDistance = 2; 66 | } 67 | 68 | for (auto &event : m_events) { 69 | event->handle(*this); 70 | } 71 | m_events.clear(); 72 | 73 | updateChunks(); 74 | } 75 | 76 | ///@TODO 77 | /// Optimize for chunkPositionU usage :thinking: 78 | void World::loadChunks(const Camera &camera) 79 | { 80 | while (m_isRunning) { 81 | bool isMeshMade = false; 82 | int cameraX = camera.position.x / CHUNK_SIZE; 83 | int cameraZ = camera.position.z / CHUNK_SIZE; 84 | 85 | for (int i = 0; i < m_loadDistance; i++) { 86 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 87 | int minX = std::max(cameraX - i, 0); 88 | int minZ = std::max(cameraZ - i, 0); 89 | int maxX = cameraX + i; 90 | int maxZ = cameraZ + i; 91 | 92 | for (int x = minX; x < maxX; ++x) { 93 | for (int z = minZ; z < maxZ; ++z) { 94 | std::unique_lock lock(m_mainMutex); 95 | isMeshMade = m_chunkManager.makeMesh(x, z, camera); 96 | } 97 | // if (isMeshMade) 98 | // break; 99 | } 100 | 101 | if (isMeshMade) 102 | break; 103 | } 104 | 105 | if (!isMeshMade) { 106 | m_loadDistance++; 107 | } 108 | if (m_loadDistance >= m_renderDistance) { 109 | m_loadDistance = 2; 110 | } 111 | } 112 | } 113 | 114 | void World::updateChunk(int blockX, int blockY, int blockZ) 115 | { 116 | std::unique_lock lock(m_mainMutex); 117 | 118 | auto addChunkToUpdateBatch = [&](const sf::Vector3i &key, 119 | ChunkSection §ion) { 120 | m_chunkUpdates.emplace(key, §ion); 121 | }; 122 | 123 | auto chunkPosition = getChunkXZ(blockX, blockZ); 124 | auto chunkSectionY = blockY / CHUNK_SIZE; 125 | 126 | sf::Vector3i key(chunkPosition.x, chunkSectionY, chunkPosition.z); 127 | addChunkToUpdateBatch( 128 | key, m_chunkManager.getChunk(chunkPosition.x, chunkPosition.z) 129 | .getSection(chunkSectionY)); 130 | 131 | auto sectionBlockXZ = getBlockXZ(blockX, blockZ); 132 | auto sectionBlockY = blockY % CHUNK_SIZE; 133 | 134 | if (sectionBlockXZ.x == 0) { 135 | sf::Vector3i newKey(chunkPosition.x - 1, chunkSectionY, 136 | chunkPosition.z); 137 | addChunkToUpdateBatch( 138 | newKey, 139 | m_chunkManager.getChunk(newKey.x, newKey.z).getSection(newKey.y)); 140 | } 141 | else if (sectionBlockXZ.x == CHUNK_SIZE - 1) { 142 | sf::Vector3i newKey(chunkPosition.x + 1, chunkSectionY, 143 | chunkPosition.z); 144 | addChunkToUpdateBatch( 145 | newKey, 146 | m_chunkManager.getChunk(newKey.x, newKey.z).getSection(newKey.y)); 147 | } 148 | 149 | if (sectionBlockY == 0) { 150 | sf::Vector3i newKey(chunkPosition.x, chunkSectionY - 1, 151 | chunkPosition.z); 152 | addChunkToUpdateBatch( 153 | newKey, 154 | m_chunkManager.getChunk(newKey.x, newKey.z).getSection(newKey.y)); 155 | } 156 | else if (sectionBlockY == CHUNK_SIZE - 1) { 157 | sf::Vector3i newKey(chunkPosition.x, chunkSectionY + 1, 158 | chunkPosition.z); 159 | addChunkToUpdateBatch( 160 | newKey, 161 | m_chunkManager.getChunk(newKey.x, newKey.z).getSection(newKey.y)); 162 | } 163 | 164 | if (sectionBlockXZ.z == 0) { 165 | sf::Vector3i newKey(chunkPosition.x, chunkSectionY, 166 | chunkPosition.z - 1); 167 | addChunkToUpdateBatch( 168 | newKey, 169 | m_chunkManager.getChunk(newKey.x, newKey.z).getSection(newKey.y)); 170 | } 171 | else if (sectionBlockXZ.z == CHUNK_SIZE - 1) { 172 | sf::Vector3i newKey(chunkPosition.x, chunkSectionY, 173 | chunkPosition.z + 1); 174 | addChunkToUpdateBatch( 175 | newKey, 176 | m_chunkManager.getChunk(newKey.x, newKey.z).getSection(newKey.y)); 177 | } 178 | } 179 | 180 | void World::renderWorld(RenderMaster &renderer, const Camera &camera) 181 | { 182 | std::unique_lock lock(m_mainMutex); 183 | renderer.drawSky(); 184 | 185 | auto &chunkMap = m_chunkManager.getChunks(); 186 | for (auto itr = chunkMap.begin(); itr != chunkMap.end();) { 187 | Chunk &chunk = itr->second; 188 | 189 | int cameraX = camera.position.x; 190 | int cameraZ = camera.position.z; 191 | 192 | int minX = (cameraX / CHUNK_SIZE) - m_renderDistance; 193 | int minZ = (cameraZ / CHUNK_SIZE) - m_renderDistance; 194 | int maxX = (cameraX / CHUNK_SIZE) + m_renderDistance; 195 | int maxZ = (cameraZ / CHUNK_SIZE) + m_renderDistance; 196 | 197 | auto location = chunk.getLocation(); 198 | 199 | if (minX > location.x || minZ > location.y || maxZ < location.y || 200 | maxX < location.x) { 201 | itr = chunkMap.erase(itr); 202 | continue; 203 | } 204 | else { 205 | chunk.drawChunks(renderer, camera); 206 | itr++; 207 | } 208 | } 209 | } 210 | 211 | ChunkManager &World::getChunkManager() 212 | { 213 | return m_chunkManager; 214 | } 215 | 216 | VectorXZ World::getBlockXZ(int x, int z) 217 | { 218 | return {x % CHUNK_SIZE, z % CHUNK_SIZE}; 219 | } 220 | 221 | VectorXZ World::getChunkXZ(int x, int z) 222 | { 223 | return {x / CHUNK_SIZE, z / CHUNK_SIZE}; 224 | } 225 | 226 | void World::updateChunks() 227 | { 228 | std::unique_lock lock(m_mainMutex); 229 | for (auto &c : m_chunkUpdates) { 230 | ChunkSection &s = *c.second; 231 | s.makeMesh(); 232 | } 233 | m_chunkUpdates.clear(); 234 | } 235 | 236 | void World::setSpawnPoint() 237 | { 238 | sf::Clock timer; 239 | std::cout << "Searching for spawn...\n"; 240 | int attempts = 0; 241 | int chunkX = -1; 242 | int chunkZ = -1; 243 | int blockX = 0; 244 | int blockZ = 0; 245 | int blockY = 0; 246 | 247 | auto h = m_chunkManager.getTerrainGenerator().getMinimumSpawnHeight(); 248 | 249 | while (blockY <= h) { 250 | m_chunkManager.unloadChunk(chunkX, chunkZ); 251 | 252 | chunkX = RandomSingleton::get().intInRange(100, 200); 253 | chunkZ = RandomSingleton::get().intInRange(100, 200); 254 | blockX = RandomSingleton::get().intInRange(0, 15); 255 | blockZ = RandomSingleton::get().intInRange(0, 15); 256 | 257 | m_chunkManager.loadChunk(chunkX, chunkZ); 258 | blockY = 259 | m_chunkManager.getChunk(chunkX, chunkZ).getHeightAt(blockX, blockZ); 260 | attempts++; 261 | } 262 | 263 | int worldX = chunkX * CHUNK_SIZE + blockX; 264 | int worldZ = chunkZ * CHUNK_SIZE + blockZ; 265 | 266 | m_playerSpawnPoint = {worldX, blockY, worldZ}; 267 | 268 | for (int x = worldX - 1; x <= worldX + 1; ++x) { 269 | for (int z = worldZ - 1; z < worldZ + 1; ++z) { 270 | std::unique_lock lock(m_mainMutex); 271 | m_chunkManager.loadChunk(x, z); 272 | } 273 | }; 274 | 275 | std::cout << "Spawn found! Attempts: " << attempts 276 | << " Time Taken: " << timer.getElapsedTime().asSeconds() 277 | << " seconds\n"; 278 | } 279 | -------------------------------------------------------------------------------- /Source/World/World.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_H_INCLUDED 2 | #define WORLD_H_INCLUDED 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "../Util/NonCopyable.h" 11 | #include "Chunk/Chunk.h" 12 | #include "Chunk/ChunkManager.h" 13 | 14 | #include "Event/IWorldEvent.h" 15 | 16 | #include "../Config.h" 17 | 18 | class RenderMaster; 19 | class Camera; 20 | class Player; 21 | 22 | struct Entity; 23 | 24 | /// @brief Massive class designed to hold multiple chunks, the player, and most game aspects. 25 | class World : public NonCopyable { 26 | public: 27 | World(const Camera &camera, const Config &config, Player &player); 28 | ~World(); 29 | 30 | ChunkBlock getBlock(int x, int y, int z); 31 | void setBlock(int x, int y, int z, ChunkBlock block); 32 | 33 | void update(const Camera &camera); 34 | void updateChunk(int blockX, int blockY, int blockZ); 35 | 36 | void renderWorld(RenderMaster &master, const Camera &camera); 37 | 38 | ChunkManager &getChunkManager(); 39 | 40 | static VectorXZ getBlockXZ(int x, int z); 41 | static VectorXZ getChunkXZ(int x, int z); 42 | 43 | // void collisionTest(Entity &entity); 44 | 45 | template void addEvent(Args &&... args) 46 | { 47 | m_events.push_back(std::make_unique(std::forward(args)...)); 48 | } 49 | 50 | private: 51 | void loadChunks(const Camera &camera); 52 | void updateChunks(); 53 | void setSpawnPoint(); 54 | 55 | ChunkManager m_chunkManager; 56 | 57 | std::vector> m_events; 58 | std::unordered_map m_chunkUpdates; 59 | 60 | std::atomic m_isRunning{true}; 61 | std::vector m_chunkLoadThreads; 62 | 63 | // Mutex classes invoked to protect data from shared threads 64 | 65 | std::mutex m_mainMutex; 66 | std::mutex m_genMutex; 67 | 68 | int m_loadDistance = 2; 69 | const int m_renderDistance; 70 | 71 | glm::vec3 m_playerSpawnPoint; 72 | }; 73 | 74 | #endif // WORLD_H_INCLUDED 75 | -------------------------------------------------------------------------------- /Source/World/WorldConstants.h: -------------------------------------------------------------------------------- 1 | #ifndef WORLDCONSTANTS_H_INCLUDED 2 | #define WORLDCONSTANTS_H_INCLUDED 3 | 4 | // Defines the most basic rules for chunk generation in any given world. 5 | 6 | constexpr int CHUNK_SIZE = 16, CHUNK_AREA = CHUNK_SIZE * CHUNK_SIZE, 7 | CHUNK_VOLUME = CHUNK_SIZE * CHUNK_SIZE * CHUNK_SIZE, 8 | 9 | WATER_LEVEL = 64; 10 | 11 | #endif // WORLDCONSTANTS_H_INCLUDED 12 | -------------------------------------------------------------------------------- /config.txt: -------------------------------------------------------------------------------- 1 | renderdistance 32 2 | fullscreen 0 3 | windowsize 1600 900 4 | fov 105 -------------------------------------------------------------------------------- /deps/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(glad) 2 | add_subdirectory(imgui_sfml) -------------------------------------------------------------------------------- /deps/glad/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library( 2 | glad 3 | glad.c 4 | ) 5 | 6 | target_compile_options(glad PRIVATE -fPIE) -------------------------------------------------------------------------------- /mc-one-week-challenge.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.7.34202.233 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mc-one-week-challenge", "mc-one-week-challenge.vcxproj", "{A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Debug|x64.ActiveCfg = Debug|x64 17 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Debug|x64.Build.0 = Debug|x64 18 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Debug|x86.ActiveCfg = Debug|Win32 19 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Debug|x86.Build.0 = Debug|Win32 20 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Release|x64.ActiveCfg = Release|x64 21 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Release|x64.Build.0 = Release|x64 22 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Release|x86.ActiveCfg = Release|Win32 23 | {A8F1DCC1-7E1A-43FD-96C4-E6BD9C626405}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {E184B4C6-D809-4290-B622-E2DAB8C2F379} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | target_release() { 4 | cd release 5 | cmake -DCMAKE_BUILD_TYPE=Release ../.. 6 | make 7 | echo "Built target in build/release/" 8 | cd ../.. 9 | } 10 | 11 | target_debug() { 12 | cd debug 13 | cmake -DCMAKE_BUILD_TYPE=Debug ../.. 14 | make 15 | echo "Built target in build/debug/" 16 | cd ../.. 17 | } 18 | 19 | # Create folder for distribution 20 | if [ "$1" = "release" ] 21 | then 22 | if [ -d "$mc-one-week-challenge" ] 23 | then 24 | rm -rf -d mc-one-week-challenge 25 | fi 26 | 27 | mkdir -p mc-one-week-challenge 28 | fi 29 | 30 | # Creates the folder for the buildaries 31 | mkdir -p mc-one-week-challenge 32 | mkdir -p mc-one-week-challenge/assets 33 | mkdir -p build 34 | mkdir -p build/release 35 | mkdir -p build/debug 36 | cd build 37 | 38 | # Builds target 39 | if [ "$1" = "release" ] 40 | then 41 | target_release 42 | cp build/release/mc-one-week-challenge mc-one-week-challenge/mc-one-week-challenge 43 | else 44 | target_debug 45 | fi 46 | 47 | cp -R assets mc-one-week-challenge/ 48 | -------------------------------------------------------------------------------- /scripts/debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ./scripts/build.sh 4 | gdb ./build/debug/mc-one-week-challenge -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ "$1" = "release" ] 4 | then 5 | ./build/release/mc-one-week-challenge 6 | else 7 | ./build/debug/mc-one-week-challenge 8 | fi -------------------------------------------------------------------------------- /vcpkg-configuration.json: -------------------------------------------------------------------------------- 1 | { 2 | "default-registry": { 3 | "kind": "git", 4 | "baseline": "cacf5994341f27e9a14a7b8724b0634b138ecb30", 5 | "repository": "https://github.com/microsoft/vcpkg" 6 | }, 7 | "registries": [ 8 | { 9 | "kind": "artifact", 10 | "location": "https://github.com/microsoft/vcpkg-ce-catalog/archive/refs/heads/main.zip", 11 | "name": "microsoft" 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": ["glm", "sfml"] 3 | } 4 | --------------------------------------------------------------------------------