├── game ├── assets │ ├── shaders │ │ ├── depth.frag │ │ ├── quad.vert │ │ ├── quad.frag │ │ ├── depth.vert │ │ ├── chunkDeferred.frag │ │ ├── blurMaskPass.frag │ │ ├── chunkDeferred.vert │ │ ├── chunkForward.vert │ │ ├── blurPassVertical.frag │ │ ├── depth.geom │ │ ├── blurPassHoritzontal.frag │ │ ├── light.frag │ │ ├── cubeLight.frag │ │ ├── chunkForward.frag │ │ └── ambientPass.frag │ ├── debugFont.png │ └── textures │ │ └── blocks8.png ├── commons.cpp ├── SceneMain │ ├── Manager.cpp │ ├── world │ │ ├── generator │ │ │ ├── Dec.hpp │ │ │ ├── Noise2D.hpp │ │ │ ├── Noise3D.hpp │ │ │ ├── Biome.hpp │ │ │ ├── Biome.cpp │ │ │ ├── TaskPool.hpp │ │ │ ├── Noise2D.cpp │ │ │ ├── Noise3D.cpp │ │ │ ├── ColumnGenerator.hpp │ │ │ ├── DecTrees.hpp │ │ │ ├── terrainFunctions.hpp │ │ │ ├── biomeFunctions.hpp │ │ │ └── ColumnGenerator.cpp │ │ ├── DeferredCubeLight.hpp │ │ ├── Sun.hpp │ │ ├── Column.hpp │ │ ├── Column.cpp │ │ ├── Cube.hpp │ │ ├── Chunk.hpp │ │ ├── World.hpp │ │ ├── Sun.cpp │ │ ├── DeferredCubeLight.cpp │ │ ├── Cube.cpp │ │ └── Chunk.cpp │ ├── SceneMain.hpp │ ├── Debugger.hpp │ ├── DeferredLight.hpp │ ├── Entity.hpp │ ├── Hitbox.hpp │ ├── BlurContainer.hpp │ ├── Player.hpp │ ├── DeferredContainer.hpp │ ├── Hitbox.cpp │ ├── Manager.hpp │ ├── Debugger.cpp │ ├── DeferredLight.cpp │ ├── Entity.cpp │ ├── BlurContainer.cpp │ ├── SceneMain.cpp │ ├── Player.cpp │ └── DeferredContainer.cpp ├── main.cpp ├── commons.hpp └── game.pro ├── .gitignore ├── .syntastic_cpp_config_backup ├── .gitmodules ├── VoxelGame.pro ├── debug.sh ├── README.md ├── run.sh ├── compile.sh └── .ycm_extra_conf.py /game/assets/shaders/depth.frag: -------------------------------------------------------------------------------- 1 | #version 420 core 2 | 3 | void main() { 4 | } 5 | -------------------------------------------------------------------------------- /game/commons.cpp: -------------------------------------------------------------------------------- 1 | #include "commons.hpp" 2 | 3 | bool USE_CPU_VISIBILITY = false; 4 | -------------------------------------------------------------------------------- /game/assets/debugFont.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Towerthousand/VBE-VoxelGame/HEAD/game/assets/debugFont.png -------------------------------------------------------------------------------- /game/assets/textures/blocks8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Towerthousand/VBE-VoxelGame/HEAD/game/assets/textures/blocks8.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.pro.user* 3 | Makefile 4 | *~ 5 | *.autosave 6 | VBE-VoxelGame 7 | log.txt 8 | *.pyc 9 | build/ 10 | build-debug/ 11 | -------------------------------------------------------------------------------- /.syntastic_cpp_config_backup: -------------------------------------------------------------------------------- 1 | -std=c++11 2 | -I game/ 3 | -I VBE/include 4 | -I VBE-Scenegraph/include 5 | -I VBE-Profiler/include 6 | -Wall 7 | -W 8 | -fPIE 9 | -g 10 | -------------------------------------------------------------------------------- /game/assets/shaders/quad.vert: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform mat4 MVP; 4 | 5 | in vec3 a_position; 6 | 7 | void main() { 8 | gl_Position = MVP * vec4(a_position, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /game/SceneMain/Manager.cpp: -------------------------------------------------------------------------------- 1 | #include "Manager.hpp" 2 | 3 | Manager Textures2D; 4 | Manager Meshes; 5 | Manager Programs; 6 | Manager Textures3D; 7 | Manager Textures2DArrays; 8 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Dec.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEC_HPP 2 | #define DEC_HPP 3 | #include "ColumnGenerator.hpp" 4 | 5 | class Dec { //abstract 6 | public: 7 | Dec() {} 8 | virtual ~Dec() {} 9 | 10 | virtual void decorate(ColumnGenerator::ColumnData* col) = 0; 11 | }; 12 | 13 | #endif // DEC_HPP 14 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "VBE"] 2 | path = VBE 3 | url = https://github.com/Towerthousand/VBE.git 4 | [submodule "VBE-Scenegraph"] 5 | path = VBE-Scenegraph 6 | url = https://github.com/Towerthousand/VBE-Scenegraph.git 7 | [submodule "VBE-Profiler"] 8 | path = VBE-Profiler 9 | url = https://github.com/Towerthousand/VBE-Profiler.git 10 | -------------------------------------------------------------------------------- /game/assets/shaders/quad.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D tex1; 4 | uniform sampler2D tex2; 5 | uniform vec2 invResolution; 6 | 7 | out vec4 finalColor; 8 | 9 | void main(void) { 10 | vec2 vTexCoord = gl_FragCoord.xy*invResolution; 11 | finalColor = vec4(texture(tex1,vTexCoord).xyz + texture(tex2,vTexCoord).xyz*0.5,1.0); 12 | } 13 | -------------------------------------------------------------------------------- /game/SceneMain/SceneMain.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SCENEMAIN_HPP 2 | #define SCENEMAIN_HPP 3 | #include "commons.hpp" 4 | 5 | class Chunk; 6 | class SceneMain : public GameObject { 7 | public: 8 | SceneMain(); 9 | ~SceneMain(); 10 | void update(float deltaTime); 11 | 12 | private: 13 | void loadResources(); 14 | }; 15 | 16 | #endif // SCENEMAIN_HPP 17 | -------------------------------------------------------------------------------- /game/main.cpp: -------------------------------------------------------------------------------- 1 | #include "SceneMain/SceneMain.hpp" 2 | 3 | int main() { 4 | //Log::setFlags(Log::Timestamp); 5 | ContextSettings s; 6 | s.versionMajor = 4; 7 | s.versionMinor = 3; 8 | s.requestSRGB = true; 9 | Game* game = new Game(Window::getFullscreenModes()[0], s); 10 | game->setFixedUpdateRate(20); 11 | SceneMain* sc = new SceneMain(); 12 | sc->addTo(game); 13 | game->run(); 14 | delete game; 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /game/assets/shaders/depth.vert: -------------------------------------------------------------------------------- 1 | #version 420 core 2 | 3 | in vec3 a_position; 4 | in int a_normal; 5 | in uint draw_index; 6 | 7 | out uint g_draw_index; 8 | 9 | const vec3[6] normals = { 10 | vec3(0,0,1), 11 | vec3(0,0,-1), 12 | vec3(1,0,0), 13 | vec3(-1,0,0), 14 | vec3(0,-1,0), 15 | vec3(0,1,0) 16 | }; 17 | 18 | void main(void) { 19 | g_draw_index = draw_index; 20 | gl_Position = vec4(a_position-normals[a_normal]*0.0002f,1.0); 21 | } 22 | -------------------------------------------------------------------------------- /game/SceneMain/Debugger.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEBUGGER_HPP 2 | #define DEBUGGER_HPP 3 | #include "commons.hpp" 4 | 5 | class Debugger final : public Profiler { 6 | public: 7 | Debugger(); 8 | ~Debugger(); 9 | 10 | static int numChunksDrawn; 11 | static int numChunksSkipped; 12 | static int numChunksDrawnShadow; 13 | static int numChunksSkippedShadow; 14 | private: 15 | void renderCustomInterface() const override; 16 | }; 17 | 18 | #endif // DEBUGGER_HPP 19 | -------------------------------------------------------------------------------- /game/assets/shaders/chunkDeferred.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D diffuseTex; 4 | 5 | in vec2 v_texCoord; 6 | in vec3 v_normal; 7 | in float v_light; 8 | 9 | layout(location = 0) out vec3 color0; 10 | layout(location = 1) out vec4 color1; 11 | 12 | void main(void) { 13 | color0 = texture(diffuseTex, v_texCoord).xyz; 14 | vec3 normal = normalize(v_normal); 15 | float p = sqrt(normal.z*8+8); 16 | vec2 encodedNormal = normal.xy/p; 17 | color1 = vec4(encodedNormal, v_light, 0.001); 18 | } 19 | -------------------------------------------------------------------------------- /VoxelGame.pro: -------------------------------------------------------------------------------- 1 | 2 | TEMPLATE = subdirs 3 | # Needed to ensure that things are built right, which you have to do yourself :( 4 | CONFIG += ordered 5 | 6 | # All the projects in your application are sub-projects of your solution 7 | SUBDIRS = VBE \ 8 | VBE-Scenegraph \ 9 | VBE-Profiler \ 10 | game 11 | 12 | 13 | # Use .depends to specify that a project depends on another. 14 | VBE-Scenegraph.depends = VBE 15 | VBE-Profiler.depends = VBE-Scenegraph VBE 16 | game.depends = VBE VBE-Scenegraph VBE-Profiler 17 | -------------------------------------------------------------------------------- /game/assets/shaders/blurMaskPass.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D color0; 4 | 5 | uniform vec2 invResolution; 6 | 7 | out vec4 finalColor; 8 | 9 | void main(void) { 10 | vec2 vTexCoord = gl_FragCoord.xy * invResolution; 11 | vec4 valColor0 = texture(color0, vTexCoord); //xyz = color 12 | float threshold = 0.99; 13 | if(valColor0.x > threshold || valColor0.y > threshold || valColor0.z > threshold) 14 | finalColor = vec4(valColor0.xyz, 1.0); 15 | else 16 | finalColor = vec4(vec3(0.0),1); 17 | } 18 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Noise2D.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOISE2D_HPP 2 | #define NOISE2D_HPP 3 | #include "commons.hpp" 4 | 5 | class Noise2D { 6 | public: 7 | Noise2D(std::mt19937* generator); 8 | ~Noise2D(); 9 | 10 | float get(float x, float y) const; 11 | float octavedGet(float x, float y, unsigned int octaves) const; 12 | 13 | private: 14 | int fastfloor(const float x) const; 15 | float dot(const int* g, const float x, const float y) const; 16 | 17 | std::vector perm; 18 | static const int grad3[12][3]; 19 | }; 20 | 21 | #endif // NOISE2D_HPP 22 | -------------------------------------------------------------------------------- /game/SceneMain/DeferredLight.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEFERREDLIGHT_HPP 2 | #define DEFERREDLIGHT_HPP 3 | #include "commons.hpp" 4 | 5 | class DeferredContainer; 6 | class DeferredLight : public GameObject{ 7 | public: 8 | DeferredLight(); 9 | virtual ~DeferredLight(); 10 | 11 | virtual void update(float deltaTime); 12 | void draw() const; 13 | vec3f pos = vec3f(0.0f); 14 | vec3f color = vec3f(1.0f); 15 | float radius = 30.0f; 16 | private: 17 | DeferredContainer* renderer = nullptr; 18 | MeshBase* quad = nullptr; 19 | }; 20 | 21 | #endif // DEFERREDLIGHT_HPP 22 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Noise3D.hpp: -------------------------------------------------------------------------------- 1 | #ifndef NOISE3D_HPP 2 | #define NOISE3D_HPP 3 | #include "commons.hpp" 4 | 5 | class Noise3D { 6 | public: 7 | Noise3D(std::mt19937* generator); 8 | ~Noise3D(); 9 | 10 | float get(float x, float y, float z) const; 11 | float octavedGet(float x, float y, float z, unsigned int octaves) const; 12 | 13 | private: 14 | int fastfloor(const float x) const; 15 | float dot(const int* g, const float x, const float y, const float z) const; 16 | 17 | std::vector perm; 18 | static const int grad3[12][3]; 19 | }; 20 | 21 | #endif // NOISE2D_HPP 22 | -------------------------------------------------------------------------------- /debug.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SCRIPT CONFIG 4 | EXECUTABLE_NAME='VoxelGame' 5 | 6 | set -e 7 | bold=$(tput bold) 8 | normal=$(tput sgr0) 9 | 10 | function prim { # PRint IMportant 11 | IFS="%" 12 | echo $bold$@$normal 13 | unset IFS 14 | } 15 | 16 | function usage { 17 | prim 'Usage:' 18 | echo ' Run after building with compile.sh -d.' 19 | } 20 | 21 | BUILD_DIR='build-debug' 22 | 23 | if [ ! -x $BUILD_DIR/game/$EXECUTABLE_NAME ]; then 24 | echo 'Cannot find an executable at '$PWD'/'$BUILD_DIR'/game/'$EXECUTABLE_NAME'.' 25 | echo 'Are you sure you have built it?' 26 | exit 1 27 | fi 28 | cd $BUILD_DIR/game 29 | gdb $EXECUTABLE_NAME 30 | -------------------------------------------------------------------------------- /game/SceneMain/Entity.hpp: -------------------------------------------------------------------------------- 1 | #ifndef ENTITY_HPP 2 | #define ENTITY_HPP 3 | #include "commons.hpp" 4 | #include "Hitbox.hpp" 5 | 6 | class Entity : public GameObject { 7 | public: 8 | Entity(); 9 | virtual ~Entity(); 10 | 11 | vec3f getPosition() const { return pos;} 12 | 13 | protected: 14 | virtual void draw() const; 15 | virtual void update(float deltaTime); 16 | virtual void movePos(float deltaTime); 17 | 18 | vec3f disp = vec3f(0.0f); 19 | vec3f acc = vec3f(0.0f); 20 | Hitbox* hitbox = nullptr; 21 | vec3f scale = vec3f(1.0f); 22 | vec3f pos = vec3f(0.0f); 23 | }; 24 | 25 | #endif // ENTITY_HPP 26 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Biome.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BIOME_HPP 2 | #define BIOME_HPP 3 | #include 4 | 5 | enum Biome { 6 | OCEAN = 0, 7 | PLAINS, 8 | NUM_BIOMES 9 | }; 10 | 11 | enum BiomeParam { 12 | SIMPLEX_LOW = 0, 13 | SIMPLEX_HIGH, 14 | LOW_SURFACE_DEPTH, 15 | SURFACE_DEPTH, 16 | NUM_BIOME_PARAMS 17 | }; 18 | 19 | enum BiomeIntParam { 20 | MAIN_TERRAIN = 0, 21 | LOW_SURFACE, 22 | SURFACE, 23 | TREE_MIN_GRID, 24 | TREE_MAX_GRID, 25 | TREE_CUTOFF, 26 | TREE_DROP_CHANCE, 27 | SEA_LEVEL, 28 | }; 29 | 30 | extern const std::valarray BIOME_PARAMS[NUM_BIOMES]; 31 | extern const std::valarray BIOME_INT_PARAMS[NUM_BIOMES]; 32 | 33 | #endif // BIOME_HPP 34 | -------------------------------------------------------------------------------- /game/assets/shaders/chunkDeferred.vert: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform ivec3 transforms[400]; 4 | uniform mat4 V; 5 | uniform mat4 VP; 6 | 7 | in vec3 a_position; 8 | in int a_normal; 9 | in vec2 a_texCoord; 10 | in float a_light; 11 | in uint draw_index; 12 | 13 | out vec3 v_normal; 14 | out vec2 v_texCoord; 15 | out float v_light; 16 | 17 | const vec3[6] normals = { 18 | vec3(0,0,1), 19 | vec3(0,0,-1), 20 | vec3(1,0,0), 21 | vec3(-1,0,0), 22 | vec3(0,-1,0), 23 | vec3(0,1,0) 24 | }; 25 | 26 | void main(void) { 27 | v_normal = vec3(V*vec4(normals[a_normal],0.0)); 28 | v_texCoord = a_texCoord/512; 29 | v_light = a_light; 30 | gl_Position = VP * vec4(a_position+transforms[draw_index], 1.0); 31 | } 32 | -------------------------------------------------------------------------------- /game/assets/shaders/chunkForward.vert: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform ivec3 transforms[400]; 4 | uniform mat4 V; 5 | uniform mat4 VP; 6 | 7 | in vec3 a_position; 8 | in int a_normal; 9 | in vec2 a_texCoord; 10 | in float a_light; 11 | in uint draw_index; 12 | 13 | out vec2 v_texCoord; 14 | out vec3 v_normal; 15 | out float v_light; 16 | 17 | const vec3[6] normals = { 18 | vec3(0,0,1), 19 | vec3(0,0,-1), 20 | vec3(1,0,0), 21 | vec3(-1,0,0), 22 | vec3(0,-1,0), 23 | vec3(0,1,0) 24 | }; 25 | 26 | void main(void) { 27 | v_normal = vec3(V*vec4(normals[a_normal],0.0)); 28 | v_texCoord = a_texCoord/512; 29 | v_light = a_light; 30 | gl_Position = VP * vec4(a_position+transforms[draw_index], 1.0); 31 | } 32 | -------------------------------------------------------------------------------- /game/SceneMain/Hitbox.hpp: -------------------------------------------------------------------------------- 1 | #ifndef HITBOX_HPP 2 | #define HITBOX_HPP 3 | #include "commons.hpp" 4 | 5 | class Hitbox : public GameObject { 6 | public: 7 | enum hitboxType { 8 | BOX = 0, 9 | POINT 10 | }; 11 | 12 | Hitbox(hitboxType type, const vec3f &radius = vec3f(0,0,0)); 13 | ~Hitbox(); 14 | 15 | bool collidesWithWorld( const vec3f &offset = vec3f(0,0,0)) const; 16 | bool collidesWithHitbox(const Hitbox& hitbox, const vec3f &offset = vec3f(0,0,0)) const; 17 | 18 | vec3f getWorldPos() const; 19 | 20 | vec3f radius = vec3f(0.0f); 21 | hitboxType type = BOX; 22 | private: 23 | bool pointCollidesWithWorld(const vec3f& point) const; 24 | }; 25 | 26 | #endif // HITBOX_HPP 27 | -------------------------------------------------------------------------------- /game/SceneMain/BlurContainer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BLURCONTAINER_HPP 2 | #define BLURCONTAINER_HPP 3 | #include "commons.hpp" 4 | 5 | class BlurContainer : public ContainerObject { 6 | public: 7 | BlurContainer(); 8 | ~BlurContainer(); 9 | 10 | private: 11 | void draw() const; 12 | void update(float deltaTime); 13 | void makeTarget(); 14 | 15 | RenderTarget noBlur; 16 | RenderBuffer noBlurDepth; 17 | Texture2D noBlurColor0; 18 | 19 | RenderTarget blurMask; 20 | Texture2D blurMaskColor0; 21 | 22 | RenderTarget horitzontalBlurred; 23 | Texture2D horitzontalBlurredColor0; 24 | 25 | RenderTarget blurred; 26 | Texture2D blurredColor0; 27 | 28 | mutable MeshBase* quad = nullptr; 29 | }; 30 | 31 | #endif // BLURCONTAINER_HPP 32 | -------------------------------------------------------------------------------- /game/SceneMain/Player.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PLAYER_HPP 2 | #define PLAYER_HPP 3 | #include "Entity.hpp" 4 | 5 | class Camera; 6 | class Player : public Entity { 7 | public: 8 | Player(); 9 | ~Player(); 10 | 11 | void update(float deltaTime); 12 | void fixedUpdate(float deltaTime); 13 | Camera* getCam() const { return cam; } 14 | vec2f getCamFov() const { return fov;} 15 | 16 | private: 17 | void processKeys(float deltaTime); 18 | void traceView(); 19 | 20 | Camera* cam = nullptr; 21 | unsigned int selectedID = 1; //current blockID, used to place blocks 22 | vec3i targetedBlock = vec3i(0); 23 | vec3i targetedBlockEnter = vec3i(0); 24 | float xRot = 0.0f; 25 | bool onFloor = true; 26 | bool isJumping = false; 27 | bool targetsBlock = false; 28 | vec3f lastPos = vec3f(0.0f); 29 | vec2f fov; 30 | }; 31 | 32 | #endif // PLAYER_HPP 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VBE-VoxelGame 2 | 3 | Voxel Game 4 | 5 | ## Requirements 6 | 7 | This demo uses OpenGL 4.3. Appropriate drivers should be installed. It also uses [GLEW](http://glew.sourceforge.net/) (`libglew-dev`, tested with >=1.10) and [SDL 2](https://www.libsdl.org/) (`libsdl2-dev`, tested with >=2.0.1) for window managing and input/audio devices. 8 | 9 | To build these projects you will need both `make` and `qmake` installed. On Ubuntu at least, qmake requires the `qt5-default` package to be installed too. 10 | 11 | ## Setup 12 | 13 | After cloning initially from `https://github.com/Towethousand/VBE-VoxelGame.git` make sure to run: 14 | 15 | git submodule update --init --recursive 16 | 17 | ## Building 18 | 19 | You can build this project with the `compile.sh` script. For build options, see `./compile.sh -h`. 20 | 21 | Builds directories are `./build/` for the release build and `./build-debug/` for the debug build. 22 | 23 | ## Running 24 | 25 | Run the demo with the `run.sh` script once you've built it successfully. Use `-d` to run the debug build. 26 | -------------------------------------------------------------------------------- /game/SceneMain/world/DeferredCubeLight.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEFERREDCUBELIGHT_HPP 2 | #define DEFERREDCUBELIGHT_HPP 3 | #include "commons.hpp" 4 | 5 | #define LIGHTSIZE 16 6 | 7 | class World; 8 | class DeferredContainer; 9 | class DeferredCubeLight : public GameObject { 10 | public: 11 | DeferredCubeLight(const vec3f& pos, const vec3f& color); 12 | ~DeferredCubeLight(); 13 | 14 | void calcLight(int cx = 0, int cy = 0, int cz = 0); 15 | const vec3f& getPosition() const {return pos;} 16 | private: 17 | void update(float deltaTime); 18 | void draw() const; 19 | 20 | void count(std::pair& pr, vec3i p, vec3i c); 21 | void calcQuadrant(int cx, int cy, int cz, int dx, int dy, int dz); 22 | float light[LIGHTSIZE*2][LIGHTSIZE*2][LIGHTSIZE*2]; 23 | unsigned char data[LIGHTSIZE*2][LIGHTSIZE*2][LIGHTSIZE*2]; 24 | vec3f pos = vec3f(0.0f); 25 | vec3f color = vec3f(1.0f); 26 | MeshBase* quad = nullptr; 27 | Texture3D tex; 28 | DeferredContainer* renderer = nullptr; 29 | World* world = nullptr; 30 | 31 | }; 32 | 33 | #endif // DEFERREDCUBELIGHT_HPP 34 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Biome.cpp: -------------------------------------------------------------------------------- 1 | #include "Biome.hpp" 2 | 3 | const std::valarray BIOME_PARAMS[NUM_BIOMES] = { 4 | //OCEAN 5 | { 6 | 145.0f, // SIMPLEX_LOW 7 | 155.0f, // SIMPLEX_HIGH 8 | 4.0f, // LOW_SURFACE_DEPTH 9 | 1.0f, // SURFACE_DEPTH 10 | }, 11 | 12 | //PLAINS 13 | { 14 | 160.0f, // SIMPLEX_LOW 15 | 170.0f, // SIMPLEX_HIGH 16 | 4.0f, // LOW_SURFACE_DEPTH 17 | 1.0f, // SURFACE_DEPTH 18 | } 19 | }; 20 | 21 | const std::valarray BIOME_INT_PARAMS[NUM_BIOMES] = { 22 | //OCEAN 23 | { 24 | 2, // MAIN_TERRAIN 25 | 8, // LOW_SURFACE 26 | 8, // SURFACE 27 | 2, // TREE_MIN_GRID 28 | 5, // TREE_MAX_GRID 29 | 4, // TREE_CUTOFF 30 | 100, // TREE_DROP_CHANCE 31 | 155, // SEA_LEVEL 32 | }, 33 | //PLAINS 34 | { 35 | 2, // MAIN_TERRAIN 36 | 1, // LOW_SURFACE 37 | 3, // SURFACE 38 | 2, // TREE_MIN_GRID 39 | 5, // TREE_MAX_GRID 40 | 4, // TREE_CUTOFF 41 | 0, // TREE_DROP_CHANCE 42 | 0, // SEA_LEVEL 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SCRIPT CONFIG 4 | EXECUTABLE_NAME='VoxelGame' 5 | 6 | set -e 7 | bold=$(tput bold) 8 | normal=$(tput sgr0) 9 | 10 | function prim { # PRint IMportant 11 | IFS="%" 12 | echo $bold$@$normal 13 | unset IFS 14 | } 15 | 16 | function usage { 17 | prim 'Usage:' 18 | echo ' Run after building with compile.sh.' 19 | prim ' Options:' 20 | prim ' -d' 21 | echo ' Build in debug the debug build.' 22 | } 23 | 24 | BUILD_DIR='build' 25 | DEBUG=false 26 | 27 | while getopts ":dh" opt; do 28 | case $opt in 29 | d) 30 | DEBUG=true 31 | BUILD_DIR='build-debug' 32 | ;; 33 | h) 34 | usage 35 | exit 1 36 | ;; 37 | \?) 38 | echo "Invalid option: -$OPTARG" >&2 39 | usage 40 | exit 1 41 | ;; 42 | :) 43 | echo "Option -$OPTARG requires an argument." >&2 44 | usage 45 | exit 1 46 | ;; 47 | esac 48 | done 49 | if [ ! -x $BUILD_DIR/game/$EXECUTABLE_NAME ]; then 50 | echo 'Cannot find an executable at '$PWD'/'$BUILD_DIR'/game/'$EXECUTABLE_NAME'.' 51 | echo 'Are you sure you have built it?' 52 | exit 1 53 | fi 54 | cd $BUILD_DIR/game 55 | ./$EXECUTABLE_NAME 56 | -------------------------------------------------------------------------------- /game/SceneMain/world/Sun.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SUN_HPP 2 | #define SUN_HPP 3 | #include "commons.hpp" 4 | 5 | #define NUM_SUN_CASCADES 4 6 | 7 | class Sun : public GameObject { 8 | public: 9 | Sun(); 10 | ~Sun(); 11 | 12 | void update(float deltaTime); 13 | void updateCameras(); 14 | 15 | vec3f getDirection() const {return vec3f(cos(angle), -sin(angle), 0.25);} 16 | float getAngle() const {return angle;} 17 | 18 | const Camera* getCam(unsigned int i) const {return cameras[i];} 19 | const Camera* getGlobalCam() const {return cameras[NUM_SUN_CASCADES];} 20 | const std::vector& getDepthPlanes() const {return maxZ;} 21 | const std::vector& getVPMatrices() const {return VP;} 22 | 23 | private: 24 | void extendFrustums(); 25 | void extend(std::vector index, const AABB& occludedBox, const Camera* pCam); 26 | 27 | float angle = 45.0f; //sun always moves on the x-y plane (z never changes) 28 | Camera* cameras[NUM_SUN_CASCADES+1]; 29 | AABB aabbs[NUM_SUN_CASCADES+1]; 30 | std::vector minZ; 31 | std::vector maxZ; 32 | std::vector VP; 33 | }; 34 | 35 | #endif // SUN_HPP 36 | -------------------------------------------------------------------------------- /game/commons.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COMMONS_HPP 2 | #define COMMONS_HPP 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | constexpr int CHUNKSIZE_POW2 = 4; 20 | constexpr int CHUNKSIZE = 16; //in voxels 21 | constexpr int CHUNKSIZE_MASK = CHUNKSIZE-1; 22 | constexpr int WORLDSIZE = 512/CHUNKSIZE; 23 | constexpr int GENERATIONHEIGHT = 256/CHUNKSIZE; 24 | constexpr int WORLDSIZE_MASK = WORLDSIZE-1; 25 | extern bool USE_CPU_VISIBILITY; 26 | 27 | namespace Utils { 28 | template 29 | inline int manhattanDistance(const glm::tvec3& a, const glm::tvec3& b) { 30 | return std::abs(a.x-b.x)+std::abs(a.y-b.y)+std::abs(a.z-b.z); 31 | } 32 | template 33 | std::string toString(T arg) { 34 | std::ostringstream temp; 35 | temp << arg; 36 | return temp.str(); 37 | } 38 | } 39 | 40 | #endif // COMMONS_HPP 41 | -------------------------------------------------------------------------------- /game/SceneMain/world/Column.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COLUMN_HPP 2 | #define COLUMN_HPP 3 | #include "commons.hpp" 4 | 5 | class Chunk; 6 | class Column { 7 | public: 8 | Column(int x, int z); 9 | ~Column(); 10 | 11 | unsigned int getCube(unsigned int x, unsigned int y, unsigned int z) const; 12 | inline vec3i getAbolutePos() const { //in cubes 13 | return vec3i(XPOS*CHUNKSIZE,0,ZPOS*CHUNKSIZE); 14 | } 15 | inline int getX() const { return XPOS; } 16 | inline int getZ() const { return ZPOS; } 17 | inline unsigned int getChunkCount() const { return chunks.size(); } 18 | Chunk* getChunk(int y) const { 19 | int realY = y >> CHUNKSIZE_POW2; 20 | return (realY < 0 || realY >= (int)chunks.size()) ? nullptr : chunks[realY]; 21 | } 22 | inline Chunk* getChunkCC(int y) const { 23 | return (y < 0 || y >= (int)chunks.size()) ? nullptr : chunks[y]; 24 | } 25 | 26 | void rebuildAllMeshes(); 27 | void setCube(unsigned int x, unsigned int y, unsigned int z, unsigned int cube); 28 | void rebuildMesh(int y); 29 | void rebuildMeshCC(int y); 30 | private: 31 | int XPOS = 0; //in chunks 32 | int ZPOS = 0; //in chunks 33 | std::vector chunks; 34 | 35 | friend class ColumnGenerator; 36 | }; 37 | 38 | #endif // COLUMN_HPP 39 | -------------------------------------------------------------------------------- /game/SceneMain/DeferredContainer.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DEFERREDCONTAINER_HPP 2 | #define DEFERREDCONTAINER_HPP 3 | #include "commons.hpp" 4 | 5 | class DeferredContainer : public ContainerObject { 6 | public: 7 | 8 | enum DrawMode { 9 | Deferred = 0, 10 | Light, 11 | ShadowMap, 12 | TransShadowMap, 13 | Forward 14 | }; 15 | 16 | DeferredContainer(); 17 | ~DeferredContainer(); 18 | 19 | void update(float deltaTime); 20 | void draw() const; 21 | DrawMode getMode() const; 22 | Texture2D* getColor0() const; 23 | Texture2D* getColor1() const; 24 | Texture2D* getDepth() const; 25 | Texture2DArray* getTransSunDepth() const; 26 | Texture2DArray* getSunDepth() const; 27 | 28 | private: 29 | void makeTarget(); 30 | 31 | RenderTarget gBuffer; 32 | 33 | //the textures for the gBuffer 34 | Texture2D GBDepth; 35 | Texture2D GBColor0; 36 | Texture2D GBColor1; 37 | 38 | RenderTargetLayered sunTarget; 39 | RenderTargetLayered sunTargetTrans; 40 | 41 | //the texture for the sun target 42 | Texture2DArray SDepth; 43 | Texture2DArray SDepthTrans; 44 | 45 | mutable DrawMode drawMode = Deferred; 46 | mutable MeshBase* quad = nullptr; 47 | }; 48 | 49 | #endif // DEFERREDCONTAINER_HPP 50 | -------------------------------------------------------------------------------- /game/assets/shaders/blurPassVertical.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D RTBlurH; // this should hold the texture rendered by the horizontal blur pass 4 | uniform vec2 invResolution; 5 | 6 | out vec4 color; 7 | 8 | void main(void) { 9 | float blurSize = invResolution.y; 10 | vec2 texCoord = gl_FragCoord.xy * invResolution; 11 | vec4 sum = vec4(0.0); 12 | 13 | // blur in y (vertical) 14 | // take nine samples, with the distance blurSize between them 15 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y - 5.0 * blurSize)) * 0.02655802906; 16 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y - 4.0 * blurSize)) * 0.04505334779; 17 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y - 3.0 * blurSize)) * 0.06281566324; 18 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y - 2.0 * blurSize)) * 0.09528487833; 19 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y - blurSize)) * 0.12234820561; 20 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y)) * 0.13298076013; 21 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y + blurSize)) * 0.12234820561; 22 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y + 2.0 * blurSize)) * 0.09528487833; 23 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y + 3.0 * blurSize)) * 0.06281566324; 24 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y + 4.0 * blurSize)) * 0.04505334779; 25 | sum += texture2D(RTBlurH, vec2(texCoord.x, texCoord.y + 5.0 * blurSize)) * 0.02655802906; 26 | 27 | color = sum; 28 | } 29 | -------------------------------------------------------------------------------- /game/assets/shaders/depth.geom: -------------------------------------------------------------------------------- 1 | #version 440 core 2 | 3 | uniform mat4 VP[4]; 4 | uniform ivec3 transforms[400]; 5 | 6 | in uint g_draw_index[]; 7 | 8 | layout(invocations = 4, triangles) in; 9 | layout(triangle_strip, max_vertices = 3) out; 10 | 11 | vec4 positions[4]; 12 | vec4 positionsTransformed[4]; 13 | 14 | // return 1 if v inside the box from (-1,-1) to (1,1), return 0 otherwise 15 | float insideBox(vec2 v) { 16 | vec2 s = step(vec2(-1,-1), v) - step(vec2(1,1), v); 17 | return s.x * s.y; 18 | } 19 | 20 | void main(void) { 21 | //manual clipping reduces further throughput. 22 | positions[0] = VP[gl_InvocationID] * (gl_in[0].gl_Position+vec4(transforms[g_draw_index[0]], 0)); 23 | positions[1] = VP[gl_InvocationID] * (gl_in[1].gl_Position+vec4(transforms[g_draw_index[1]], 0)); 24 | positions[2] = VP[gl_InvocationID] * (gl_in[2].gl_Position+vec4(transforms[g_draw_index[2]], 0)); 25 | positionsTransformed[0] = positions[0]/positions[0].w; 26 | positionsTransformed[1] = positions[0]/positions[1].w; 27 | positionsTransformed[2] = positions[0]/positions[2].w; 28 | 29 | if(!(insideBox(positionsTransformed[0].xy) > 0.0 || insideBox(positionsTransformed[1].xy) > 0.0 || insideBox(positionsTransformed[2].xy) > 0.0)) return; 30 | 31 | //actually emit geometry 32 | gl_Layer = gl_InvocationID; 33 | gl_Position = positions[0]; 34 | EmitVertex(); 35 | gl_Position = positions[1]; 36 | EmitVertex(); 37 | gl_Position = positions[2]; 38 | EmitVertex(); 39 | EndPrimitive(); 40 | } 41 | -------------------------------------------------------------------------------- /game/SceneMain/world/Column.cpp: -------------------------------------------------------------------------------- 1 | #include "Column.hpp" 2 | #include "Chunk.hpp" 3 | 4 | Column::Column(int x, int z) : XPOS(x), ZPOS(z) { 5 | } 6 | 7 | Column::~Column() { 8 | for(unsigned int i = 0; i < chunks.size(); ++i) 9 | if(chunks[i] != nullptr) 10 | delete chunks[i]; 11 | chunks.resize(0); 12 | } 13 | 14 | unsigned int Column::getCube(unsigned int x, unsigned int y, unsigned int z) const { 15 | VBE_ASSERT(int(x) < CHUNKSIZE && int(z) < CHUNKSIZE, "Invalid Column::getCube() parameters"); 16 | Chunk* c = getChunk(y); 17 | return (c != nullptr? c->cubes[x][y&CHUNKSIZE_MASK][z] : 0); 18 | } 19 | 20 | void Column::setCube(unsigned int x, unsigned int y, unsigned int z, unsigned int cube) { 21 | VBE_ASSERT(int(x) < CHUNKSIZE && int(z) < CHUNKSIZE, "Invalid Column::setCube() parameters"); 22 | int chunk = y >> CHUNKSIZE_POW2; 23 | if((int)chunks.size() <= chunk) 24 | chunks.resize(chunk+1, nullptr); 25 | if(chunks[chunk] == nullptr) 26 | chunks[chunk] = new Chunk(XPOS, chunk, ZPOS); 27 | chunks[chunk]->cubes[x][y&CHUNKSIZE_MASK][z] = cube; 28 | } 29 | 30 | void Column::rebuildAllMeshes() { 31 | for(unsigned int i = 0; i < chunks.size(); ++i) 32 | if(chunks[i] != nullptr) 33 | chunks[i]->needsMeshRebuild = true; 34 | } 35 | 36 | void Column::rebuildMesh(int y) { 37 | Chunk* c = getChunk(y); 38 | if(c) c->needsMeshRebuild = true; 39 | } 40 | 41 | void Column::rebuildMeshCC(int y) { 42 | Chunk* c = getChunkCC(y); 43 | if(c) c->needsMeshRebuild = true; 44 | } 45 | -------------------------------------------------------------------------------- /game/SceneMain/Hitbox.cpp: -------------------------------------------------------------------------------- 1 | #include "Hitbox.hpp" 2 | #include "world/World.hpp" 3 | 4 | Hitbox::Hitbox(hitboxType type, const vec3f &radius) : radius(radius), type(type) { 5 | } 6 | 7 | Hitbox::~Hitbox() { 8 | } 9 | 10 | bool Hitbox::collidesWithWorld(const vec3f &offset) const { 11 | switch (type) { 12 | case (BOX): { 13 | vec3f newPos = getWorldPos()+offset; 14 | for(float x = -radius.x; x <= radius.x+0.3; x += 0.3) 15 | for(float y = -radius.y; y <= radius.y+0.3; y += 0.3) 16 | for(float z = -radius.z; z <= radius.z+0.3; z += 0.3) { 17 | vec3f point(std::fmin(x, radius.x), std::fmin(y, radius.y),std::fmin(z, radius.z)); 18 | if(pointCollidesWithWorld(point+newPos)) { 19 | return true; 20 | } 21 | } 22 | break; 23 | } 24 | case (POINT): 25 | return pointCollidesWithWorld(getWorldPos()+offset); 26 | break; 27 | default: 28 | break; 29 | } 30 | return false; 31 | } 32 | 33 | bool Hitbox::collidesWithHitbox(const Hitbox &hitbox, const vec3f &offset) const { 34 | return false; //TODO 35 | } 36 | 37 | vec3f Hitbox::getWorldPos() const { 38 | return vec3f(fullTransform*vec4f(0,0,0,1)); 39 | } 40 | 41 | bool Hitbox::pointCollidesWithWorld(const vec3f& point) const { 42 | World* world = (World*)getGame()->getObjectByName("world"); 43 | if(world->getCube(floor(point.x),floor(point.y),floor(point.z)) != 0) 44 | return true; 45 | return false; 46 | } 47 | -------------------------------------------------------------------------------- /game/assets/shaders/blurPassHoritzontal.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D RTScene; // the texture with the scene you want to blur 4 | uniform vec2 invResolution; 5 | 6 | out vec4 color; 7 | 8 | void main(void) { 9 | float blurSize = invResolution.x; // I've chosen this size because this will result in that every step will be one pixel wide if the RTScene texture is of size 512x512 10 | vec2 texCoord = gl_FragCoord.xy * invResolution; 11 | vec4 sum = vec4(0.0); 12 | // blur in y (vertical) 13 | // take eleven samples, with the distance blurSize between them 0. 14 | sum += texture2D(RTScene, vec2(texCoord.x - 5.0*blurSize, texCoord.y)) * 0.02655802906; 15 | sum += texture2D(RTScene, vec2(texCoord.x - 4.0*blurSize, texCoord.y)) * 0.04505334779; 16 | sum += texture2D(RTScene, vec2(texCoord.x - 3.0*blurSize, texCoord.y)) * 0.06281566324; 17 | sum += texture2D(RTScene, vec2(texCoord.x - 2.0*blurSize, texCoord.y)) * 0.09528487833; 18 | sum += texture2D(RTScene, vec2(texCoord.x - blurSize, texCoord.y)) * 0.12234820561; 19 | sum += texture2D(RTScene, vec2(texCoord.x, texCoord.y)) * 0.13298076013; 20 | sum += texture2D(RTScene, vec2(texCoord.x + blurSize, texCoord.y)) * 0.12234820561; 21 | sum += texture2D(RTScene, vec2(texCoord.x + 2.0*blurSize, texCoord.y)) * 0.09528487833; 22 | sum += texture2D(RTScene, vec2(texCoord.x + 3.0*blurSize, texCoord.y)) * 0.06281566324; 23 | sum += texture2D(RTScene, vec2(texCoord.x + 4.0*blurSize, texCoord.y)) * 0.04505334779; 24 | sum += texture2D(RTScene, vec2(texCoord.x + 5.0*blurSize, texCoord.y)) * 0.02655802906; 25 | 26 | color = vec4(sum.xyz, 1.0); 27 | } 28 | -------------------------------------------------------------------------------- /game/SceneMain/Manager.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MANAGER_HPP 2 | #define MANAGER_HPP 3 | #include "commons.hpp" 4 | 5 | template 6 | class Manager : NonCopyable { 7 | public: 8 | Manager() {} 9 | virtual ~Manager() {} 10 | 11 | void add (const std::string& resID, T resource) { 12 | VBE_ASSERT(resources.find(resID) == resources.end(), "Failed to add resource. resource " << resID << " already exists"); 13 | VBE_LOG("* Adding resource with ID " << resID); 14 | resources.insert(std::pair(resID, std::move(resource))); 15 | } 16 | T& get (const std::string& resID) const { 17 | VBE_ASSERT(resources.find(resID) != resources.end(), "Failed to get resource. resource " << resID << " doesn't exist"); 18 | return resources.at(resID); 19 | } 20 | 21 | void erase(const std::string& resID) { 22 | VBE_ASSERT(resources.find(resID) != resources.end(), "Failed to delete resource. resource " << resID << " doesn't exist"); 23 | VBE_LOG("* Deleting resource with ID " << resID ); 24 | resources.erase(resID); 25 | } 26 | 27 | bool exists(const std::string& resID) { 28 | return (resources.find(resID) != resources.end()); 29 | } 30 | 31 | void clear() { 32 | resources.clear(); 33 | } 34 | private: 35 | mutable std::map resources; 36 | }; 37 | 38 | //default Managers 39 | extern Manager Textures2D; 40 | extern Manager Meshes; 41 | extern Manager Programs; 42 | extern Manager Textures2DArrays; 43 | extern Manager Textures3D; 44 | 45 | #endif // MANAGER_HPP 46 | -------------------------------------------------------------------------------- /game/assets/shaders/light.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D depth; 4 | uniform sampler2D color0; 5 | uniform sampler2D color1; 6 | uniform vec3 lightPos; 7 | uniform vec3 lightColor; 8 | 9 | uniform mat4 invProj; 10 | uniform float lightRadius; 11 | uniform vec2 invResolution; 12 | 13 | out vec4 color; 14 | 15 | vec3 getFragPos(vec2 vTexCoord) { 16 | vec4 sPos = vec4(vTexCoord * 2 - 1, texture(depth, vTexCoord).x * 2 - 1, 1.0); 17 | sPos = invProj * sPos; 18 | return sPos.xyz / sPos.w; 19 | } 20 | 21 | vec3 decodeNormal(vec2 enc) { 22 | //Decode normal 23 | vec2 fenc = enc * 4; 24 | float f = dot(fenc, fenc); 25 | float g = sqrt(1 - f / 4); 26 | return vec3(fenc * g, 1 - f / 2); 27 | } 28 | 29 | void main(void) { 30 | vec2 vTexCoord = gl_FragCoord.xy * invResolution; 31 | vec3 fragmentPos = getFragPos(vTexCoord); //view space 32 | if(length(fragmentPos - lightPos) > lightRadius) 33 | discard; 34 | 35 | vec4 valColor0 = texture(color0, vTexCoord); 36 | vec4 valColor1 = texture(color1, vTexCoord); 37 | 38 | //material properties 39 | vec3 matDiffuseColor = valColor0.xyz; 40 | vec3 matSpecularColor = vec3(valColor1.w); 41 | 42 | //fragment light parameters 43 | vec3 lightVector = normalize(lightPos - fragmentPos); //view space 44 | vec3 normalVector = decodeNormal(valColor1.xy); //view space 45 | 46 | //Blinn-Phong shading 47 | vec3 E = normalize(-fragmentPos); 48 | vec3 H = normalize(lightVector + E); 49 | float cosAlpha = clamp(dot(normalVector, H), 0.0f, 1.0f); 50 | float cosTheta = max(dot(normalVector, lightVector), 0.0f); 51 | float attenuationFactor = max(0.0, 1 - length(fragmentPos-lightPos) / lightRadius); 52 | 53 | gl_FragDepth = texture(depth, vTexCoord).x; 54 | color = vec4(matDiffuseColor * lightColor * cosTheta * attenuationFactor + 55 | matSpecularColor * lightColor * pow(cosAlpha, 1000) * cosTheta * attenuationFactor, 1.0f); 56 | } 57 | -------------------------------------------------------------------------------- /compile.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | #SCRIPT CONFIG 4 | PROJECT_FILE_RELATIVE_PATH="../VoxelGame.pro" 5 | 6 | set -e 7 | set -o pipefail 8 | bold=$(tput bold) 9 | normal=$(tput sgr0) 10 | 11 | function prim { # PRint IMportant 12 | IFS="%" 13 | echo $bold$@$normal 14 | unset IFS 15 | } 16 | 17 | function usage { 18 | prim "Usage: " 19 | echo " Configure the script accordingly and then run it from the project root directory." 20 | prim " Options:" 21 | prim " -d" 22 | echo " Build in debug mode. This will enable consistency checks and debug logs." 23 | prim " -r" 24 | echo " Rebuild all. Will remove the previous build directory" 25 | echo " and recreate the symbolic links." 26 | } 27 | 28 | REBUILD=false 29 | DEBUG=false 30 | BUILD_DIR="build" 31 | 32 | while getopts ":rdh" opt; do 33 | case $opt in 34 | r) 35 | REBUILD=true 36 | ;; 37 | d) 38 | DEBUG=true 39 | BUILD_DIR="build-debug" 40 | ;; 41 | h) 42 | usage 43 | exit 1 44 | ;; 45 | \?) 46 | echo "Invalid option: -$OPTARG" >&2 47 | usage 48 | exit 1 49 | ;; 50 | :) 51 | echo "Option -$OPTARG requires an argument." >&2 52 | usage 53 | exit 1 54 | ;; 55 | esac 56 | done 57 | if [ $REBUILD = true ]; then 58 | prim "Rebuilding all projects" 59 | prim "Removing previous build..." 60 | rm -rf $BUILD_DIR 61 | fi 62 | mkdir -p $BUILD_DIR 63 | cd $BUILD_DIR 64 | prim "Running qmake..." 65 | 66 | if [ $DEBUG = true ]; then 67 | prim "DEBUG BUILD" 68 | qmake $PROJECT_FILE_RELATIVE_PATH -r -spec linux-g++-64 2>&1 CONFIG+=debug 69 | else 70 | prim "RELEASE BUILD" 71 | qmake $PROJECT_FILE_RELATIVE_PATH -r -spec linux-g++-64 2>&1 72 | fi 73 | 74 | if [ ! -L "game/assets" ]; then 75 | prim "Creating symbolic link for assets folder..." 76 | ln -s $PWD/../game/assets game/assets 77 | fi 78 | prim "Building project..." 79 | if hash colormake 2>/dev/null; then 80 | colormake -j8 2>&1 81 | else 82 | make -j8 2>&1 83 | fi 84 | -------------------------------------------------------------------------------- /game/assets/shaders/cubeLight.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D depth; 4 | uniform sampler2D color0; 5 | uniform sampler2D color1; 6 | uniform vec3 lightPos; 7 | uniform vec3 lightColor; 8 | 9 | uniform mat4 invProj; 10 | uniform mat4 invView; 11 | uniform float lightRadius; 12 | uniform vec2 invResolution; 13 | 14 | uniform sampler3D tex; 15 | 16 | out vec4 color; 17 | 18 | vec3 getFragPos(vec2 vTexCoord) { 19 | vec4 sPos = vec4(vTexCoord * 2 - 1, texture(depth, vTexCoord).x * 2 - 1, 1.0); 20 | sPos = invProj * sPos; 21 | return sPos.xyz / sPos.w; 22 | } 23 | 24 | vec3 decodeNormal(vec2 enc) { 25 | //Decode normal 26 | vec2 fenc = enc * 4; 27 | float f = dot(fenc, fenc); 28 | float g = sqrt(1 - f / 4); 29 | return vec3(fenc * g, 1 - f / 2); 30 | } 31 | 32 | void main(void) { 33 | vec2 vTexCoord = gl_FragCoord.xy * invResolution; 34 | vec3 fragmentPos = getFragPos(vTexCoord); //view space 35 | 36 | //fragment light parameters 37 | vec3 L = lightPos - fragmentPos; //view space 38 | float lightDist = length(L); 39 | L = normalize(L); 40 | 41 | if(lightDist > lightRadius) 42 | discard; 43 | 44 | vec4 valColor0 = texture(color0, vTexCoord); 45 | vec4 valColor1 = texture(color1, vTexCoord); 46 | 47 | //material properties 48 | vec3 matDiffuseColor = valColor0.xyz; 49 | vec3 matSpecularColor = vec3(valColor1.w); 50 | vec3 N = decodeNormal(valColor1.xy); //view space 51 | 52 | vec3 posRelativeToLight = (invView * vec4(fragmentPos+N*0.1 - lightPos, 0.0f)).xyz; 53 | float blockLight = texture(tex, (posRelativeToLight+0.5)/(2*lightRadius) + 0.5f).r; 54 | if(blockLight < 0.01) 55 | discard; 56 | 57 | //Blinn-Phong shading 58 | vec3 E = normalize(-fragmentPos); 59 | vec3 H = normalize(L + E); 60 | vec3 R = reflect(-L, N); 61 | float cosAlpha = max(dot(R, E), 0.0f); 62 | float cosTheta = max(dot(L, N), 0.0f); 63 | float attenuationFactor = 1 - lightDist / lightRadius; 64 | 65 | gl_FragDepth = texture(depth, vTexCoord).x; 66 | 67 | color = vec4((matDiffuseColor * lightColor * cosTheta * attenuationFactor * blockLight + (matSpecularColor * pow(cosAlpha, 1000) * blockLight * attenuationFactor))*10.0f, 1.0f); 68 | } 69 | -------------------------------------------------------------------------------- /game/SceneMain/Debugger.cpp: -------------------------------------------------------------------------------- 1 | #include "Debugger.hpp" 2 | 3 | int Debugger::numChunksDrawn = 0; 4 | int Debugger::numChunksSkipped = 0; 5 | int Debugger::numChunksDrawnShadow = 0; 6 | int Debugger::numChunksSkippedShadow = 0; 7 | 8 | Debugger::Debugger() { 9 | } 10 | 11 | Debugger::~Debugger() { 12 | } 13 | 14 | void Debugger::renderCustomInterface() const { 15 | ImGui::Begin("Controls", nullptr, ImVec2(0.18f*Window::getInstance()->getSize().x, 0.21f*Window::getInstance()->getSize().y), 0.9f, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); 16 | ImGui::SetWindowPos(ImVec2(0.78f*Window::getInstance()->getSize().x, 0.05f*Window::getInstance()->getSize().y), ImGuiCond_FirstUseEver); 17 | ImGui::Text("%s", std::string("ChunksDrawnDeferred: " + Utils::toString(numChunksDrawn)).c_str()); 18 | ImGui::Text("%s", std::string("ChunksSkippedDeferred: " + Utils::toString(numChunksSkipped)).c_str()); 19 | ImGui::Text("%s", std::string("ChunksDrawnShadow: " + Utils::toString(numChunksDrawnShadow)).c_str()); 20 | ImGui::Text("%s", std::string("ChunksSkippedShadow: " + Utils::toString(numChunksSkippedShadow)).c_str()); 21 | ImGui::Separator(); 22 | ImGui::Columns(2, nullptr, true); 23 | ImGui::SetColumnOffset(0,0); 24 | ImGui::SetColumnOffset(1,0.7f*0.18f*Window::getInstance()->getSize().x); 25 | ImGui::Text("Move"); ImGui::NextColumn(); 26 | ImGui::Text("W,A,S,D"); ImGui::NextColumn(); 27 | ImGui::Text("Look Around"); ImGui::NextColumn(); 28 | ImGui::Text("Mouse"); ImGui::NextColumn(); 29 | ImGui::Text("Jump"); ImGui::NextColumn(); 30 | ImGui::Text("Space"); ImGui::NextColumn(); 31 | ImGui::Text("Place a light"); ImGui::NextColumn(); 32 | ImGui::Text("L"); ImGui::NextColumn(); 33 | ImGui::Text("Toggle interface"); ImGui::NextColumn(); 34 | ImGui::Text("F1"); ImGui::NextColumn(); 35 | ImGui::Text("Toggle sun camera"); ImGui::NextColumn(); 36 | ImGui::Text("Q"); ImGui::NextColumn(); 37 | ImGui::Text("Decrease sun angle"); ImGui::NextColumn(); 38 | ImGui::Text("Z"); ImGui::NextColumn(); 39 | ImGui::Text("Increase sun angle"); ImGui::NextColumn(); 40 | ImGui::Text("X"); ImGui::NextColumn(); 41 | ImGui::Columns(1); 42 | ImGui::Separator(); 43 | ImGui::Text("While showing the interface,\nthe mouse will be visible.\nHover the profiler graphs for\nmore info"); 44 | ImGui::End(); 45 | } 46 | -------------------------------------------------------------------------------- /game/SceneMain/DeferredLight.cpp: -------------------------------------------------------------------------------- 1 | #include "DeferredLight.hpp" 2 | #include "DeferredContainer.hpp" 3 | #include "Manager.hpp" 4 | 5 | DeferredLight::DeferredLight() : renderer((DeferredContainer*)getGame()->getObjectByName("deferred")) { 6 | quad = &Meshes.get("quad"); 7 | } 8 | 9 | DeferredLight::~DeferredLight() { 10 | } 11 | 12 | void DeferredLight::update(float deltaTime) { 13 | (void) deltaTime; 14 | } 15 | 16 | void DeferredLight::draw() const{ 17 | if(renderer->getMode() != DeferredContainer::Light) return; 18 | Camera* cam = (Camera*)getGame()->getObjectByName("playerCam"); 19 | vec3f posWorldSpace = vec3f(fullTransform*vec4f(0,0,0,1)); 20 | vec3f posViewSpace = vec3f(cam->getView()*vec4f(posWorldSpace,1.0)); 21 | 22 | mat4f t(1.0); 23 | if(glm::length(posViewSpace) > radius) { 24 | vec3f front = cam->getWorldPos()-posWorldSpace; 25 | front = glm::normalize(front); 26 | vec3f dummyUp(0, 1, 0); 27 | vec3f right = glm::cross(dummyUp, front); 28 | right = glm::normalize(right); 29 | vec3f up = glm::cross(front, right); 30 | up = glm::normalize(up); 31 | mat4f rot(right.x, right.y, right.z, 0, 32 | up.x , up.y , up.z , 0, 33 | front.x, front.y, front.z, 0, 34 | 0 , 0 , 0 , 1); 35 | t = glm::scale(rot, vec3f(radius)); 36 | t = glm::translate(t, vec3f(0, 0, 1)); 37 | Programs.get("deferredLight").uniform("MVP")->set(cam->projection*cam->getView()*fullTransform*t); 38 | } 39 | else 40 | Programs.get("deferredLight").uniform("MVP")->set(t); 41 | 42 | Programs.get("deferredLight").uniform("invResolution")->set(vec2f(1.0f/Window::getInstance()->getSize().x, 1.0f/Window::getInstance()->getSize().y)); 43 | Programs.get("deferredLight").uniform("color0")->set(renderer->getColor0()); 44 | Programs.get("deferredLight").uniform("color1")->set(renderer->getColor1()); 45 | Programs.get("deferredLight").uniform("depth")->set(renderer->getDepth()); 46 | Programs.get("deferredLight").uniform("lightPos")->set(posViewSpace); 47 | Programs.get("deferredLight").uniform("invProj")->set(glm::inverse(cam->projection)); 48 | Programs.get("deferredLight").uniform("lightColor")->set(color); 49 | Programs.get("deferredLight").uniform("lightRadius")->set(radius); 50 | quad->draw(Programs.get("deferredLight")); 51 | } 52 | -------------------------------------------------------------------------------- /game/game.pro: -------------------------------------------------------------------------------- 1 | QT -= gui 2 | 3 | TARGET = VoxelGame 4 | CONFIG -= app_bundle 5 | 6 | TEMPLATE = app 7 | 8 | include(../VBE-Scenegraph/VBE-Scenegraph.pri) 9 | include(../VBE-Profiler/VBE-Profiler.pri) 10 | include(../VBE/VBE.pri) 11 | 12 | LIBS += -lGLEW -lGL -lSDL2 13 | QMAKE_CXXFLAGS += -std=c++0x -fno-exceptions 14 | 15 | INCLUDEPATH += . 16 | 17 | SOURCES += main.cpp \ 18 | commons.cpp \ 19 | SceneMain/SceneMain.cpp \ 20 | SceneMain/DeferredContainer.cpp \ 21 | SceneMain/DeferredLight.cpp \ 22 | SceneMain/world/World.cpp \ 23 | SceneMain/BlurContainer.cpp \ 24 | SceneMain/world/Column.cpp \ 25 | SceneMain/world/Chunk.cpp \ 26 | SceneMain/world/Cube.cpp \ 27 | SceneMain/world/generator/Noise3D.cpp \ 28 | SceneMain/world/generator/Noise2D.cpp \ 29 | SceneMain/world/generator/Biome.cpp \ 30 | SceneMain/world/generator/ColumnGenerator.cpp \ 31 | SceneMain/Entity.cpp \ 32 | SceneMain/Player.cpp \ 33 | SceneMain/Hitbox.cpp \ 34 | SceneMain/world/DeferredCubeLight.cpp \ 35 | SceneMain/world/Sun.cpp \ 36 | SceneMain/Manager.cpp \ 37 | SceneMain/Debugger.cpp 38 | 39 | HEADERS += \ 40 | commons.hpp \ 41 | SceneMain/SceneMain.hpp \ 42 | SceneMain/DeferredContainer.hpp \ 43 | SceneMain/DeferredLight.hpp \ 44 | SceneMain/world/World.hpp \ 45 | SceneMain/BlurContainer.hpp \ 46 | SceneMain/world/Column.hpp \ 47 | SceneMain/world/Chunk.hpp \ 48 | SceneMain/world/Cube.hpp \ 49 | SceneMain/world/generator/Noise3D.hpp \ 50 | SceneMain/world/generator/Noise2D.hpp \ 51 | SceneMain/world/generator/Biome.hpp \ 52 | SceneMain/world/generator/ColumnGenerator.hpp \ 53 | SceneMain/Entity.hpp \ 54 | SceneMain/Player.hpp \ 55 | SceneMain/Hitbox.hpp \ 56 | SceneMain/world/DeferredCubeLight.hpp \ 57 | SceneMain/world/generator/TaskPool.hpp \ 58 | SceneMain/world/Sun.hpp \ 59 | SceneMain/Manager.hpp \ 60 | SceneMain/Debugger.hpp 61 | 62 | OTHER_FILES += \ 63 | assets/shaders/quad.vert \ 64 | assets/shaders/light.frag \ 65 | assets/shaders/ambientPass.frag \ 66 | assets/shaders/blurPassVertical.frag \ 67 | assets/shaders/blurPassHoritzontal.frag \ 68 | assets/shaders/quad.frag \ 69 | assets/shaders/blurMaskPass.frag \ 70 | assets/shaders/depth.frag \ 71 | assets/shaders/depth.vert \ 72 | assets/shaders/chunkDeferred.vert \ 73 | assets/shaders/chunkDeferred.frag \ 74 | assets/shaders/chunkForward.vert \ 75 | assets/shaders/chunkForward.frag \ 76 | assets/shaders/cubeLight.frag \ 77 | assets/shaders/depth.geom 78 | -------------------------------------------------------------------------------- /game/SceneMain/world/Cube.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CUBE_HPP 2 | #define CUBE_HPP 3 | #include "commons.hpp" 4 | 5 | class Chunk; 6 | 7 | namespace Cube { 8 | 9 | struct __attribute__((packed)) Vert { 10 | Vert(unsigned char vx = 0, unsigned char vy = 0, unsigned char vz = 0, 11 | unsigned char n = 0, 12 | unsigned short tx = 0, unsigned short ty = 0, unsigned char l = 0) : 13 | vx(vx), vy(vy), vz(vz), 14 | n(n), 15 | tx(tx), ty(ty), 16 | l(l) {} 17 | unsigned char vx,vy,vz,n; 18 | unsigned short tx,ty; 19 | unsigned char l; 20 | }; 21 | 22 | enum Type { 23 | AIR = 0, 24 | DIRT = 1, 25 | STONE = 2, 26 | GRASS = 3, 27 | LIGHT = 4, 28 | COBBLE = 5, 29 | LOG = 6, 30 | PLANKS = 7, 31 | SAND = 8, 32 | LEAVES = 9, 33 | WATER = 10, 34 | NUM_TYPES, 35 | }; 36 | 37 | enum PropertyFlag { 38 | TRANSPARENT = (1 << 0), 39 | SOLID = (1 << 1), 40 | }; 41 | 42 | constexpr unsigned int PROPERTIES[NUM_TYPES] = { 43 | TRANSPARENT, // 0 AIR 44 | SOLID, // 1 DIRT 45 | SOLID, // 2 STONE 46 | SOLID, // 3 GRASS 47 | SOLID, // 4 LIGHT 48 | SOLID, // 5 COBBLE 49 | SOLID, // 6 LOG 50 | SOLID, // 7 PLANKS 51 | SOLID, // 8 SAND 52 | SOLID, // 9 LEAVES 53 | TRANSPARENT, // 10 WATER 54 | }; 55 | 56 | constexpr unsigned char OPACITY[NUM_TYPES] = { 57 | 0, // 0 AIR 58 | 0, // 1 DIRT 59 | 0, // 2 STONE 60 | 0, // 3 GRASS 61 | 0, // 4 LIGHT 62 | 0, // 5 COBBLE 63 | 0, // 6 LOG 64 | 0, // 7 PLANKS 65 | 0, // 8 SAND 66 | 0, // 9 LEAVES 67 | 128, // 10 WATER 68 | }; 69 | 70 | constexpr unsigned int TEXTURE_INDEXES[NUM_TYPES][6] = { //order is front, back, left, right, bottom, top 71 | {0, 0, 0, 0, 0, 0}, //0 AIR (empty, will never be used) 72 | {2, 2, 2, 2, 2, 2}, //1 DIRT 73 | {3, 3, 3, 3, 3, 3}, //2 STONE 74 | {0, 0, 0, 0, 2, 1}, //3 GRASS 75 | {4, 4, 4, 4, 4, 4}, //4 LIGHT 76 | {5, 5, 5, 5, 5, 5}, //5 COBBLE 77 | {6, 6, 6, 6, 7, 7}, //6 LOG 78 | {8, 8, 8, 8, 8, 8}, //7 PLANKS 79 | {9, 9, 9, 9, 9, 9}, //8 SAND 80 | {10, 10, 10, 10, 10, 10}, //9 LEAVES 81 | {11, 11, 11, 11, 11, 11}, //10 WATER 82 | }; 83 | 84 | constexpr int TEXSIZE = 8; 85 | constexpr int ATLAS_SIZE = 512; 86 | 87 | inline bool getFlag(Type t, PropertyFlag f) { 88 | return bool(PROPERTIES[t] & f); 89 | }; 90 | 91 | void pushCubeToArray(short x, short y, short z, Cube::Type cubeID, const Chunk* c, std::vector& renderData); 92 | 93 | } // namespace Cube 94 | 95 | #endif // CUBE_HPP 96 | -------------------------------------------------------------------------------- /game/SceneMain/Entity.cpp: -------------------------------------------------------------------------------- 1 | #include "Entity.hpp" 2 | 3 | Entity::Entity() : hitbox(new Hitbox(Hitbox::POINT)) { 4 | hitbox->addTo(this); 5 | } 6 | 7 | Entity::~Entity() { 8 | } 9 | 10 | void Entity::update(float deltaTime) { 11 | (void) deltaTime; 12 | } 13 | 14 | void Entity::draw() const { 15 | } 16 | 17 | void Entity::movePos(float deltaTime) { //collisons 18 | disp += acc*deltaTime; //a = const, dist = vt + at2 19 | vec3f dist = disp; 20 | if(hitbox->collidesWithWorld(vec3f(dist.x,0,0))) { 21 | if ((!hitbox->collidesWithWorld(vec3f(dist.x,1.01,0)) || !hitbox->collidesWithWorld(vec3f(dist.x,0.5,0))) && 22 | hitbox->collidesWithWorld(vec3f(0,-2,0))) { 23 | //this is for auto climbing. 24 | //First check: So that it can go through underground staircases. 25 | //Second check: So it won't climb if it's in the air. 26 | pos.y += 15*deltaTime; 27 | disp.y += 2*deltaTime; 28 | } 29 | float min = 0; 30 | float max = 1; 31 | while(max-min > 0.001) { //search for the maximum distance you can move 32 | float m = (max+min)/2; 33 | if(hitbox->collidesWithWorld(vec3f(dist.x*m,0,0))) 34 | max = m; 35 | else 36 | min = m; 37 | } 38 | disp.x = 0; 39 | dist.x *= min; 40 | } 41 | 42 | if(hitbox->collidesWithWorld(vec3f(0,0,dist.z))) { 43 | if ((!hitbox->collidesWithWorld(vec3f(0,1.01,dist.z)) || !hitbox->collidesWithWorld(vec3f(0,0.5,dist.z))) && 44 | hitbox->collidesWithWorld(vec3f(0,-2,0))) { 45 | //this is for auto climbing. 46 | //First check: So that it can go through underground staircases. 47 | //Second check: So it won't climb if it's in the air. 48 | pos.y += 15*deltaTime;; 49 | disp.y += 2*deltaTime;; 50 | } 51 | float min = 0; 52 | float max = 1; 53 | while(max-min > 0.001) { //search for the maximum distance you can move 54 | float m = (max+min)/2; 55 | if(hitbox->collidesWithWorld(vec3f(0,0,dist.z*m))) 56 | max = m; 57 | else 58 | min = m; 59 | } 60 | disp.z = 0; 61 | dist.z *= min; 62 | } 63 | 64 | dist.y = disp.y * deltaTime; //corrected displacement for the climbing feature 65 | 66 | if(hitbox->collidesWithWorld(vec3f(0,dist.y,0))) { 67 | float min = 0; 68 | float max = 1; 69 | while(max-min > 0.001) { //search for the maximum distance you can move 70 | float m = (max+min)/2; 71 | if(hitbox->collidesWithWorld(vec3f(0,dist.y*m,0))) 72 | max = m; 73 | else 74 | min = m; 75 | } 76 | disp.y = 0; 77 | dist.y *= min; 78 | } 79 | 80 | pos += dist; 81 | } 82 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/TaskPool.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TASKPOOL_HPP 2 | #define TASKPOOL_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "commons.hpp" 14 | 15 | 16 | class TaskPool { 17 | public: 18 | TaskPool(size_t threads, std::function = [](){}); 19 | ~TaskPool(); 20 | 21 | template 22 | auto enqueue(F&& f, Args&&... args) 23 | -> std::future::type>; 24 | 25 | void discard(); 26 | int size(); 27 | 28 | private: 29 | std::vector< std::thread > workers; 30 | std::queue< std::function > tasks; 31 | 32 | std::mutex queue_mutex; 33 | std::condition_variable condition; 34 | bool stop; 35 | }; 36 | 37 | inline TaskPool::TaskPool(size_t threads, std::function init) : stop(false) { 38 | for(size_t i = 0;i lock(this->queue_mutex); 43 | while(!this->stop && this->tasks.empty()) 44 | this->condition.wait(lock); 45 | if(this->stop && this->tasks.empty()) 46 | return; 47 | std::function task(this->tasks.front()); 48 | this->tasks.pop(); 49 | lock.unlock(); 50 | task(); 51 | } 52 | }); 53 | } 54 | 55 | inline TaskPool::~TaskPool() { 56 | { 57 | std::unique_lock lock(queue_mutex); 58 | stop = true; 59 | } 60 | condition.notify_all(); 61 | for(size_t i = 0; i < workers.size(); ++i) 62 | workers[i].join(); 63 | } 64 | 65 | 66 | template 67 | auto TaskPool::enqueue(F&& f, Args&&... args) 68 | -> std::future::type> { 69 | typedef typename std::result_of::type return_type; 70 | 71 | VBE_ASSERT(!stop, "enqueue on stopped ThreadPool"); 72 | auto task = std::make_shared< std::packaged_task >( 73 | std::bind(std::forward(f), std::forward(args)...) 74 | ); 75 | std::future res = task->get_future(); 76 | 77 | { 78 | std::unique_lock lock(queue_mutex); 79 | tasks.push([task](){ (*task)(); }); 80 | } 81 | 82 | condition.notify_one(); 83 | return res; 84 | } 85 | 86 | inline void TaskPool::discard() { 87 | std::unique_lock lock(queue_mutex); 88 | tasks = std::queue< std::function >(); 89 | } 90 | 91 | int TaskPool::size() { 92 | std::unique_lock lock(queue_mutex); 93 | return tasks.size(); 94 | } 95 | 96 | #endif //TASKPOOL_HPP 97 | -------------------------------------------------------------------------------- /game/SceneMain/world/Chunk.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CHUNK_HPP 2 | #define CHUNK_HPP 3 | #include "commons.hpp" 4 | #include "World.hpp" 5 | 6 | #define AO_MAX_RAD 9 7 | 8 | class DeferredContainer; 9 | class Chunk { 10 | public: 11 | enum Face { 12 | MINX = 0, 13 | MAXX = 1, 14 | MINY = 2, 15 | MAXY = 3, 16 | MINZ = 4, 17 | MAXZ = 5, 18 | ALL_FACES = 6 19 | }; 20 | 21 | Chunk(int x, unsigned int y, int z); 22 | ~Chunk(); 23 | 24 | static void initStructures(); 25 | static Face getOppositeFace(Face f); 26 | 27 | void update(float deltaTime); 28 | void draw() const; 29 | void rebuildMesh(); 30 | 31 | inline unsigned int getCube(int x, int y, int z) const { 32 | //local coords, (0,0,0) is (XPOS*CS,YPOS*CS,ZPOS*CS) in absolute 33 | if(x >= 0 && x < CHUNKSIZE && y >= 0 && y < CHUNKSIZE && z >= 0 && z < CHUNKSIZE) 34 | return cubes[x][y][z]; 35 | return world->getCube(x+(XPOS*CHUNKSIZE), y+(YPOS*CHUNKSIZE), z+(ZPOS*CHUNKSIZE)); //in another chunk 36 | } 37 | inline int getX() const { return XPOS; } 38 | inline unsigned int getY() const { return YPOS; } 39 | inline int getZ() const { return ZPOS; } 40 | inline vec3i getAbsolutePos() const { //in cubes 41 | return vec3i(XPOS*CHUNKSIZE, YPOS*CHUNKSIZE, ZPOS*CHUNKSIZE); 42 | } 43 | inline AABB getWorldSpaceBoundingBox() const { 44 | vec3f absPos = vec3f(getAbsolutePos()); 45 | return AABB(boundingBox.getMin()+absPos, boundingBox.getMax()+absPos); 46 | } 47 | bool visibilityTest(Chunk::Face exit) const; 48 | inline bool isEmpty() const { return (boundingBox.getDimensions() == vec3f(0)); } 49 | bool isSurrounded() const; 50 | inline bool hasMesh() const { return hasVertices; } 51 | inline bool wasDrawedByPlayer() const { return drawedByPlayer; } 52 | unsigned char calcLight(int x, int y, int z, int dx, int dy, int dz) const; 53 | 54 | std::bitset<6> facesVisited = std::bitset<6>(0); 55 | 56 | private: 57 | void calcLightSum(); 58 | int sumRect(int x1, int y1, int z1, int x2, int y2, int z2) const; 59 | float calcSubLight(int x, int y, int z, int dx, int dy, int dz, int d) const; 60 | 61 | static inline int getVisibilityIndex(int a, int b) { 62 | if(a >= b && a > 0) a--; 63 | return a+b*5; 64 | } 65 | 66 | void initMesh(); 67 | void rebuildVisibilityGraph(); 68 | 69 | unsigned int cubes[CHUNKSIZE][CHUNKSIZE][CHUNKSIZE]; 70 | const int XPOS = 0; //in chunks 71 | const unsigned int YPOS = 0; //in chunks 72 | const int ZPOS = 0; //in chunks 73 | mutable bool drawedByPlayer = false; //has been seen by a player this frame? 74 | bool needsMeshRebuild = true; //does it need rebuilding? 75 | bool hasVertices = false; //is there any face touching air? 76 | std::bitset<30> visibilityGraph = std::bitset<30>(0); 77 | AABB boundingBox = AABB(vec3f(0), vec3f(0)); 78 | MeshBatched* terrainModel = nullptr; 79 | MeshBatched* transModel = nullptr; 80 | MeshIndexed* boundingBoxModel = nullptr; 81 | World* world = nullptr; 82 | DeferredContainer* renderer = nullptr; 83 | 84 | static std::vector visibilityNodes; 85 | static vec3c d[6]; 86 | 87 | friend class ColumnGenerator; 88 | friend class Column; 89 | }; 90 | 91 | #endif // CHUNK_HPP 92 | -------------------------------------------------------------------------------- /game/assets/shaders/chunkForward.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D diffuseTex; 4 | 5 | uniform sampler2DArrayShadow sunDepth; 6 | uniform sampler2DArrayShadow sunDepthTrans; 7 | uniform mat4 depthMVP[4]; 8 | uniform float depthPlanes[4]; 9 | uniform mat4 invCamProj; 10 | uniform mat4 invCamView; 11 | uniform mat4 camMV; 12 | uniform int worldsize; 13 | uniform vec2 invResolution; 14 | uniform vec3 lightDir; 15 | 16 | in vec2 v_texCoord; 17 | in vec3 v_normal; 18 | in float v_light; 19 | 20 | out vec4 finalColor; 21 | 22 | vec2 poissonDisk[16] = { 23 | vec2( -0.94201624, -0.39906216 ), 24 | vec2( 0.94558609, -0.76890725 ), 25 | vec2( -0.094184101, -0.92938870 ), 26 | vec2( 0.34495938, 0.29387760 ), 27 | vec2( -0.91588581, 0.45771432 ), 28 | vec2( -0.81544232, -0.87912464 ), 29 | vec2( -0.38277543, 0.27676845 ), 30 | vec2( 0.97484398, 0.75648379 ), 31 | vec2( 0.44323325, -0.97511554 ), 32 | vec2( 0.53742981, -0.47373420 ), 33 | vec2( -0.26496911, -0.41893023 ), 34 | vec2( 0.79197514, 0.19090188 ), 35 | vec2( -0.24188840, 0.99706507 ), 36 | vec2( -0.81409955, 0.91437590 ), 37 | vec2( 0.19984126, 0.78641367 ), 38 | vec2( 0.14383161, -0.14100790 ) 39 | }; 40 | 41 | vec4 skyColor = vec4(50.0f/255.0f,90.0f/255.0f,255.0f/255.0f,1); 42 | 43 | vec3 getFragPos(vec2 texCoord) { 44 | vec4 sPos = vec4(texCoord * 2 - 1, gl_FragCoord.z * 2 - 1, 1.0); 45 | sPos = invCamProj * sPos; 46 | return sPos.xyz / sPos.w; 47 | } 48 | 49 | void main(void) { 50 | vec2 vTexCoord = gl_FragCoord.xy * invResolution; 51 | 52 | vec3 fragmentViewPos = getFragPos(vTexCoord); //view space 53 | float fragmentWorldZ = abs(fragmentViewPos.z); 54 | int shadowIndex = -1; 55 | for(int i = 0; i < 4; ++i) { 56 | if(abs(fragmentViewPos.z) < depthPlanes[i]) { 57 | shadowIndex = i; break; 58 | } 59 | } 60 | vec3 fragmentWorldPos = vec4(invCamView*vec4(fragmentViewPos, 1.0)).xyz; //world space 61 | vec4 shadowCoord = vec4(depthMVP[shadowIndex]*vec4(fragmentWorldPos, 1.0)); //texture space (for shadow tex), not clip space 62 | 63 | vec3 normalVector = v_normal; //view space 64 | vec3 lightVector = normalize(vec4(camMV*vec4(lightDir,0.0)).xyz); //view space 65 | 66 | float cosTheta = max(-dot(lightVector, normalVector), 0.0f); 67 | 68 | // Compute visibility. Sample the shadow map 16 times 69 | float visibilitySolid = 1.0; 70 | float visibilityTrans = 1.0; 71 | float visibility = 1.0; 72 | float bias = 0.0025f; 73 | float shadowZ = shadowCoord.z-bias; 74 | float sampleNum = 16.0f; 75 | 76 | if(shadowIndex != -1 && fragmentWorldZ < depthPlanes[3]) { 77 | for (int i=0; i < sampleNum; i++) { 78 | visibilitySolid -= (1.0f/sampleNum)*(texture(sunDepth,vec4(shadowCoord.xy + poissonDisk[i]/1000.0, shadowIndex, shadowZ))); 79 | visibilityTrans -= (0.8f/sampleNum)*(texture(sunDepthTrans,vec4(shadowCoord.xy + poissonDisk[i]/1000.0, shadowIndex, shadowZ))); 80 | } 81 | visibility = min(visibilitySolid, visibilityTrans); 82 | if(fragmentWorldZ > depthPlanes[3]-10) 83 | visibility = mix(visibility, 1.0, (fragmentWorldZ - (depthPlanes[3]-10.0))/10.0); 84 | } 85 | 86 | // Abs distance to player: 87 | vec4 camPos = invCamView * vec4(vec3(0.0), 1.0); 88 | 89 | float fog = clamp(length(fragmentWorldPos - camPos.xyz)/((worldsize*8)-48), 0.0, 1.0); 90 | fog = pow(fog, 5); 91 | vec4 albedo = texture(diffuseTex, v_texCoord); 92 | float light = v_light; 93 | light += (1-min(1.0f,light))*(visibility*0.2f); 94 | finalColor = vec4(vec3(albedo.rgb*light*0.05f+albedo.rgb*light*(1.0f*(visibility*cosTheta*0.6)))*(1-fog) + skyColor.xyz*fog, albedo.a); 95 | } 96 | -------------------------------------------------------------------------------- /game/SceneMain/world/World.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WORLD_HPP 2 | #define WORLD_HPP 3 | #include "commons.hpp" 4 | #include "generator/ColumnGenerator.hpp" 5 | #include "Column.hpp" 6 | 7 | class Camera; 8 | class DeferredContainer; 9 | class Sun; 10 | class World : public GameObject { 11 | public: 12 | World(); 13 | ~World(); 14 | 15 | inline bool outOfBounds(int x, int y, int z) const { 16 | Column* c = columns[(x >> CHUNKSIZE_POW2) & WORLDSIZE_MASK][(z >> CHUNKSIZE_POW2) & WORLDSIZE_MASK]; 17 | return (c == nullptr || c->getX() != (x >> CHUNKSIZE_POW2) || c->getZ() != (z >> CHUNKSIZE_POW2) || y < 0); 18 | } 19 | inline bool outOfBounds(vec3i pos) const {return outOfBounds(pos.x,pos.y,pos.z);} 20 | inline unsigned int getCube(int x, int y, int z) const { 21 | Column* c = getColumn(x,y,z); 22 | return (c == nullptr)? 0 : c->getCube(x & CHUNKSIZE_MASK,y,z & CHUNKSIZE_MASK); 23 | } 24 | inline unsigned int getCube(vec3i pos) const {return getCube(pos.x,pos.y,pos.z);} 25 | inline Column* getColumn(int x, int y, int z) const { 26 | Column* c = columns[(x >> CHUNKSIZE_POW2) & WORLDSIZE_MASK][(z >> CHUNKSIZE_POW2) & WORLDSIZE_MASK]; 27 | return (c == nullptr || c->getX() != (x >> CHUNKSIZE_POW2) || c->getZ() != (z >> CHUNKSIZE_POW2) || y < 0)? nullptr:c; 28 | } 29 | inline Column* getColumn(vec3i pos) const {return getColumn(pos.x,pos.y,pos.z);} 30 | inline Chunk* getChunk(int x, int y, int z) const { 31 | Column* c = getColumn(x,y,z); 32 | return c == nullptr ? nullptr : c->getChunk(y); 33 | } 34 | inline Chunk* getChunk(vec3i pos) const {return getChunk(pos.x,pos.y,pos.z);} 35 | inline Column* getColumnCC(int x, int y, int z) const { 36 | Column* c = columns[x & WORLDSIZE_MASK][z & WORLDSIZE_MASK]; 37 | return (c == nullptr || c->getX() != x || c->getZ() != z || y < 0)? nullptr:c; 38 | } 39 | inline Column* getColumnCC(vec3i pos) const {return getColumnCC(pos.x,pos.y,pos.z);} 40 | inline Chunk* getChunkCC(int x, int y, int z) const { 41 | Column* c = getColumnCC(x,y,z); 42 | return c == nullptr ? nullptr : c->getChunkCC(y); 43 | } 44 | inline Chunk* getChunkCC(vec3i pos) const {return getChunkCC(pos.x,pos.y,pos.z);} 45 | void setCube(int x, int y, int z, unsigned int cube); 46 | void setCubeRange(int x, int y, int z, unsigned int sx, unsigned int sy, unsigned int sz, unsigned int cube); 47 | inline void setCube(vec3i pos, unsigned int cube) {setCube(pos.x, pos.y, pos.z, cube);} 48 | inline void setCubeRange(vec3i pos, vec3i size, unsigned int cube) { 49 | setCubeRange(pos.x, pos.y, pos.z, size.x, size.y, size.z, cube); 50 | } 51 | 52 | inline void recalc(int x, int y, int z) { 53 | Column* c = getColumn(x,y,z); 54 | if(c) c->rebuildMesh(y); 55 | } 56 | inline void recalc(vec3i pos) {recalc(pos.x, pos.y, pos.z);} 57 | private: 58 | void update(float deltaTime); 59 | void fixedUpdate(float deltaTime); 60 | void draw() const; 61 | std::list getSunCamChunks(const Camera* cam) const; 62 | std::list getPerspectiveCamChunks(const Camera* cam) const; 63 | unsigned int sendDrawCommands(const std::list& chunksToDraw, const ShaderProgram& program) const; 64 | bool batchChunk(Chunk* c, std::vector& transforms, const int batchCount, const Sun* sun) const; 65 | 66 | inline void setCubeData(int x, int y, int z, unsigned int cube) { 67 | Column* c = getColumn(x,y,z); 68 | if(c) c->setCube(x & CHUNKSIZE_MASK, y, z & CHUNKSIZE_MASK, cube); 69 | } 70 | 71 | friend class Sun; 72 | vec3i maxLoadedCoords, minLoadedCoords; 73 | bool chunksExist = false; 74 | Column* columns[WORLDSIZE][WORLDSIZE]; 75 | ColumnGenerator generator; 76 | DeferredContainer* renderer = nullptr; 77 | }; 78 | 79 | #endif // WORLD_HPP 80 | -------------------------------------------------------------------------------- /game/assets/shaders/ambientPass.frag: -------------------------------------------------------------------------------- 1 | #version 420 2 | 3 | uniform sampler2D color0; 4 | uniform sampler2D color1; 5 | uniform sampler2DArrayShadow sunDepth; 6 | uniform sampler2DArrayShadow sunDepthTrans; 7 | uniform sampler2D depth; 8 | uniform mat4 depthMVP[4]; 9 | uniform float depthPlanes[4]; 10 | uniform mat4 invCamProj; 11 | uniform mat4 invCamView; 12 | uniform mat4 camMV; 13 | uniform int worldsize; 14 | uniform vec2 invResolution; 15 | uniform vec3 lightDir; 16 | 17 | out vec4 finalColor; 18 | 19 | vec2 poissonDisk[16] = { 20 | vec2( -0.94201624, -0.39906216 ), 21 | vec2( 0.94558609, -0.76890725 ), 22 | vec2( -0.094184101, -0.92938870 ), 23 | vec2( 0.34495938, 0.29387760 ), 24 | vec2( -0.91588581, 0.45771432 ), 25 | vec2( -0.81544232, -0.87912464 ), 26 | vec2( -0.38277543, 0.27676845 ), 27 | vec2( 0.97484398, 0.75648379 ), 28 | vec2( 0.44323325, -0.97511554 ), 29 | vec2( 0.53742981, -0.47373420 ), 30 | vec2( -0.26496911, -0.41893023 ), 31 | vec2( 0.79197514, 0.19090188 ), 32 | vec2( -0.24188840, 0.99706507 ), 33 | vec2( -0.81409955, 0.91437590 ), 34 | vec2( 0.19984126, 0.78641367 ), 35 | vec2( 0.14383161, -0.14100790 ) 36 | }; 37 | 38 | vec4 skyColor = vec4(50.0f/255.0f,90.0f/255.0f,255.0f/255.0f,1); 39 | 40 | vec3 getFragPos(vec2 texCoord) { 41 | vec4 sPos = vec4(texCoord * 2 - 1, texture(depth, texCoord).x * 2 - 1, 1.0); 42 | sPos = invCamProj * sPos; 43 | return sPos.xyz / sPos.w; 44 | } 45 | 46 | vec3 decodeNormal(vec2 enc) { 47 | //Decode normal 48 | vec2 fenc = enc * 4; 49 | float f = dot(fenc, fenc); 50 | float g = sqrt(1 - f / 4); 51 | return vec3(fenc * g, 1 - f / 2); 52 | } 53 | 54 | void main(void) { 55 | vec2 vTexCoord = gl_FragCoord.xy * invResolution; 56 | gl_FragDepth = texture(depth, vTexCoord).x; 57 | vec4 valColor0 = texture(color0, vTexCoord); 58 | vec4 valColor1 = texture(color1, vTexCoord); 59 | 60 | vec3 fragmentViewPos = getFragPos(vTexCoord); //view space 61 | float fragmentWorldZ = abs(fragmentViewPos.z); 62 | int shadowIndex = -1; 63 | for(int i = 0; i < 4; ++i) { 64 | if(abs(fragmentViewPos.z) < depthPlanes[i]) { 65 | shadowIndex = i; break; 66 | } 67 | } 68 | vec3 fragmentWorldPos = vec4(invCamView*vec4(fragmentViewPos,1.0)).xyz; //world space 69 | vec4 shadowCoord = vec4(depthMVP[shadowIndex]*vec4(fragmentWorldPos,1.0)); //texture space (for shadow tex), not clip space 70 | 71 | vec3 normalVector = normalize(decodeNormal(valColor1.xy)); //view space 72 | vec3 lightVector = normalize(vec4(camMV*vec4(lightDir,0.0)).xyz); //view space 73 | 74 | float cosTheta = max(-dot(lightVector, normalVector), 0.0f); 75 | 76 | // Compute visibility. Sample the shadow map 16 times 77 | float visibilitySolid = 1.0; 78 | float visibilityTrans = 1.0; 79 | float visibility = 1.0; 80 | float bias = 0.0025f; 81 | float shadowZ = shadowCoord.z-bias; 82 | float sampleNum = 16.0f; 83 | 84 | if(shadowIndex != -1 && fragmentWorldZ < depthPlanes[3]) { 85 | for (int i=0; i < sampleNum; i++) { 86 | visibilitySolid -= (1.0f/sampleNum)*(texture(sunDepth,vec4(shadowCoord.xy + poissonDisk[i]/1000.0, shadowIndex, shadowZ))); 87 | visibilityTrans -= (0.8f/sampleNum)*(texture(sunDepthTrans,vec4(shadowCoord.xy + poissonDisk[i]/1000.0, shadowIndex, shadowZ))); 88 | } 89 | visibility = min(visibilitySolid, visibilityTrans); 90 | if(fragmentWorldZ > depthPlanes[3]-10) 91 | visibility = mix(visibility, 1.0, (fragmentWorldZ - (depthPlanes[3]-10.0))/10.0); 92 | } 93 | 94 | 95 | // Abs distance to player: 96 | vec4 camPos = invCamView * vec4(vec3(0.0), 1.0); 97 | 98 | //Compute fog percentage 99 | float fog = clamp(length(fragmentWorldPos - camPos.xyz)/((worldsize*8)-48), 0.0, 1.0); 100 | fog = pow(fog, 5); 101 | valColor1.z += (1-min(1.0f,valColor1.z))*(visibility*0.2f); 102 | finalColor = vec4(vec3(valColor0.xyz*valColor1.z*0.05f+valColor0.xyz*valColor1.z*(+1.0f*(visibility*cosTheta*0.6)))*(1-fog) + skyColor.xyz*fog, 1.0); 103 | } 104 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Noise2D.cpp: -------------------------------------------------------------------------------- 1 | #include "Noise2D.hpp" 2 | 3 | const int Noise2D::grad3[12][3] = { 4 | {1,1,0}, {-1,1,0}, {1,-1,0}, {-1,-1,0}, 5 | {1,0,1}, {-1,0,1}, {1,0,-1}, {-1,0,-1}, 6 | {0,1,1}, {0,-1,1}, {0,1,-1}, {0,-1,-1} 7 | }; 8 | 9 | Noise2D::Noise2D(std::mt19937* generator) { 10 | //generate permutation 11 | std::uniform_int_distribution distribution(0,255); 12 | perm.resize(256); 13 | for (int i = 0; i < 256; i++) 14 | perm[i] = i; 15 | for (int i = 0; i < 256; i++) { 16 | int j = distribution(*generator); 17 | //Swap perm[i] and perm[j] 18 | int aux = perm[i]; 19 | perm[i] = perm[j]; 20 | perm[j] = aux; 21 | } 22 | perm.resize(512); 23 | for (int i = 0; i < 256; i++) 24 | perm[i+256] = perm[i]; 25 | } 26 | 27 | Noise2D::~Noise2D() { 28 | } 29 | 30 | float Noise2D::get(float x, float y) const { 31 | // Noise contributions from the three corners 32 | float n0, n1, n2; 33 | 34 | // Skew the input space to determine which simplex cell we're in 35 | float F2 = 0.5 * (sqrtf(3.0) - 1.0); 36 | // Hairy factor for 2D 37 | float s = (x + y) * F2; 38 | int i = fastfloor( x + s ); 39 | int j = fastfloor( y + s ); 40 | 41 | float G2 = (3.0 - sqrtf(3.0)) / 6.0; 42 | float t = (i + j) * G2; 43 | // Unskew the cell origin back to (x,y) space 44 | float X0 = i-t; 45 | float Y0 = j-t; 46 | // The x,y distances from the cell origin 47 | float x0 = x-X0; 48 | float y0 = y-Y0; 49 | 50 | // For the 2D case, the simplex shape is an equilateral triangle. 51 | // Determine which simplex we are in. 52 | int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords 53 | if(x0>y0) {i1=1; j1=0;} // lower triangle, XY order: (0,0)->(1,0)->(1,1) 54 | else {i1=0; j1=1;} // upper triangle, YX order: (0,0)->(0,1)->(1,1) 55 | 56 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and 57 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where 58 | // c = (3-sqrt(3))/6 59 | float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords 60 | float y1 = y0 - j1 + G2; 61 | float x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords 62 | float y2 = y0 - 1.0 + 2.0 * G2; 63 | 64 | // Work out the hashed gradient indices of the three simplex corners 65 | int ii = i & 255; 66 | int jj = j & 255; 67 | int gi0 = perm[ii+perm[jj]] % 12; 68 | int gi1 = perm[ii+i1+perm[jj+j1]] % 12; 69 | int gi2 = perm[ii+1+perm[jj+1]] % 12; 70 | 71 | // Calculate the contribution from the three corners 72 | float t0 = 0.5 - x0*x0-y0*y0; 73 | if(t0<0) n0 = 0.0; 74 | else { 75 | t0 *= t0; 76 | n0 = t0 * t0 * dot(grad3[gi0], x0, y0); // (x,y) of grad3 used for 2D gradient 77 | } 78 | 79 | float t1 = 0.5 - x1*x1-y1*y1; 80 | if(t1<0) n1 = 0.0; 81 | else { 82 | t1 *= t1; 83 | n1 = t1 * t1 * dot(grad3[gi1], x1, y1); 84 | } 85 | 86 | float t2 = 0.5 - x2*x2-y2*y2; 87 | if(t2<0) n2 = 0.0; 88 | else { 89 | t2 *= t2; 90 | n2 = t2 * t2 * dot(grad3[gi2], x2, y2); 91 | } 92 | 93 | // Add contributions from each corner to get the final noise value. 94 | // The result is scaled to return values in the interval [-1,1]. 95 | float result = 70.0 * (n0 + n1 + n2); 96 | //scale the result 97 | result += 1; //in [0,2] 98 | result *= 0.5; // in [0,1] 99 | return result; 100 | } 101 | 102 | float Noise2D::octavedGet(float x, float y, unsigned int octaves) const { 103 | float currScale = 1.0f; 104 | float val = 0.0f; 105 | float numParts = float(1 << octaves) - 1; 106 | for(unsigned int i = 0; i < octaves; ++i) { 107 | float importance = float(1 << (octaves-i-1))/numParts; 108 | val += get(x/currScale, y/currScale)*importance; 109 | currScale *= 0.5f; 110 | } 111 | return val; 112 | } 113 | 114 | int Noise2D::fastfloor( const float x ) const { return x > 0 ? (int) x : (int) x - 1; } 115 | float Noise2D::dot( const int* g, const float x, const float y) const { return g[0]*x + g[1]*y; } 116 | -------------------------------------------------------------------------------- /game/SceneMain/BlurContainer.cpp: -------------------------------------------------------------------------------- 1 | #include "BlurContainer.hpp" 2 | #include "Manager.hpp" 3 | #include "Debugger.hpp" 4 | 5 | BlurContainer::BlurContainer() { 6 | quad = &Meshes.get("quad"); 7 | } 8 | 9 | BlurContainer::~BlurContainer() { 10 | } 11 | 12 | void BlurContainer::update(float deltaTime) { 13 | makeTarget(); 14 | ContainerObject::update(deltaTime); 15 | } 16 | 17 | void BlurContainer::makeTarget() { 18 | if(Window::getInstance()->getSize() == noBlur.getSize()) return; 19 | vec2ui size = Window::getInstance()->getSize(); 20 | 21 | noBlurDepth= RenderBuffer(size, TextureFormat::DEPTH_COMPONENT32); 22 | noBlurColor0 = Texture2D(size, TextureFormat::RGBA16F); 23 | noBlurColor0.setFilter(GL_NEAREST, GL_NEAREST); 24 | noBlurColor0.setWrap(GL_CLAMP_TO_EDGE); 25 | noBlur = RenderTarget(size.x, size.y); 26 | noBlur.setBuffer(RenderTargetBase::DEPTH, &noBlurDepth); 27 | noBlur.setTexture(RenderTargetBase::COLOR0, &noBlurColor0); 28 | 29 | float blurSize = 1; 30 | float blurSizeDivisor = std::pow(2,blurSize); 31 | 32 | blurMaskColor0 = Texture2D(size, TextureFormat::RGBA16F); 33 | blurMaskColor0.setFilter(GL_LINEAR, GL_LINEAR); 34 | blurMaskColor0.setWrap(GL_CLAMP_TO_EDGE); 35 | blurMask = RenderTarget(size.x, size.y); 36 | blurMask.setTexture(RenderTargetBase::COLOR0, &blurMaskColor0); 37 | 38 | horitzontalBlurredColor0 = Texture2D(vec2ui(vec2f(size)/blurSizeDivisor), TextureFormat::RGBA16F); 39 | horitzontalBlurredColor0.setFilter(GL_LINEAR, GL_LINEAR); 40 | horitzontalBlurredColor0.setWrap(GL_CLAMP_TO_EDGE); 41 | horitzontalBlurred = RenderTarget(size.x/blurSizeDivisor, size.y/blurSizeDivisor); 42 | horitzontalBlurred.setTexture(RenderTargetBase::COLOR0, &horitzontalBlurredColor0); 43 | 44 | blurredColor0 = Texture2D(vec2ui(vec2f(size)/blurSizeDivisor), TextureFormat::RGBA16F); 45 | blurredColor0.setFilter(GL_LINEAR, GL_LINEAR); 46 | blurredColor0.setWrap(GL_CLAMP_TO_EDGE); 47 | blurred = RenderTarget(size.x/blurSizeDivisor, size.y/blurSizeDivisor); 48 | blurred.setTexture(RenderTargetBase::COLOR0, &blurredColor0); 49 | } 50 | 51 | void BlurContainer::draw() const { 52 | RenderTargetBase::bind(noBlur); 53 | 54 | ContainerObject::draw(); 55 | 56 | Debugger::pushMark("Blur Pass", "Time spent rendering the post-process blur"); 57 | 58 | GL_ASSERT(glBlendFunc(GL_ONE,GL_ONE)); 59 | 60 | //BLUR MASK BUILDING 61 | RenderTargetBase::bind(blurMask); 62 | GL_ASSERT(glClear(GL_COLOR_BUFFER_BIT)); 63 | Programs.get("blurMaskPass").uniform("MVP")->set(mat4f(1.0f)); 64 | Programs.get("blurMaskPass").uniform("color0")->set(noBlur.getTexture(RenderTargetBase::COLOR0)); 65 | Programs.get("blurMaskPass").uniform("invResolution")->set(vec2f(1.0f/blurMask.getSize().x, 1.0f/blurMask.getSize().y)); 66 | quad->draw(Programs.get("blurMaskPass")); 67 | 68 | //BLUR 69 | RenderTargetBase::bind(horitzontalBlurred); 70 | GL_ASSERT(glClear(GL_COLOR_BUFFER_BIT)); 71 | if(!Keyboard::pressed(Keyboard::B)) { 72 | Programs.get("blurPassHoritzontal").uniform("MVP")->set(mat4f(1.0f)); 73 | Programs.get("blurPassHoritzontal").uniform("RTScene")->set(blurMask.getTexture(RenderTargetBase::COLOR0)); 74 | Programs.get("blurPassHoritzontal").uniform("invResolution")->set(vec2f(1.0f/horitzontalBlurred.getSize().x, 1.0f/horitzontalBlurred.getSize().y)); 75 | quad->draw(Programs.get("blurPassHoritzontal")); 76 | } 77 | 78 | RenderTargetBase::bind(blurred); 79 | GL_ASSERT(glClear(GL_COLOR_BUFFER_BIT)); 80 | if(!Keyboard::pressed(Keyboard::B)) { 81 | Programs.get("blurPassVertical").uniform("MVP")->set(mat4f(1.0f)); 82 | Programs.get("blurPassVertical").uniform("RTBlurH")->set(horitzontalBlurred.getTexture(RenderTargetBase::COLOR0)); 83 | Programs.get("blurPassVertical").uniform("invResolution")->set(vec2f(1.0f/blurred.getSize().x, 1.0f/blurred.getSize().y)); 84 | quad->draw(Programs.get("blurPassVertical")); 85 | } 86 | 87 | //BLUR + SCENE 88 | RenderTargetBase::bind(nullptr); 89 | GL_ASSERT(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); 90 | Programs.get("textureToScreen").uniform("MVP")->set(mat4f(1.0f)); 91 | Programs.get("textureToScreen").uniform("tex1")->set(noBlur.getTexture(RenderTargetBase::COLOR0)); 92 | Programs.get("textureToScreen").uniform("tex2")->set(blurred.getTexture(RenderTargetBase::COLOR0)); 93 | Programs.get("textureToScreen").uniform("invResolution")->set(vec2f(1.0f/(Window::getInstance()->getSize().x), 1.0f/(Window::getInstance()->getSize().y))); 94 | quad->draw(Programs.get("textureToScreen")); 95 | 96 | GL_ASSERT(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); 97 | 98 | Debugger::popMark(); //blur 99 | } 100 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/Noise3D.cpp: -------------------------------------------------------------------------------- 1 | #include "Noise3D.hpp" 2 | 3 | const int Noise3D::grad3[12][3] = { 4 | {1,1,0}, {-1,1,0}, {1,-1,0}, {-1,-1,0}, 5 | {1,0,1}, {-1,0,1}, {1,0,-1}, {-1,0,-1}, 6 | {0,1,1}, {0,-1,1}, {0,1,-1}, {0,-1,-1} 7 | }; 8 | 9 | Noise3D::Noise3D(std::mt19937* generator) { 10 | //generate permutation 11 | std::uniform_int_distribution distribution(0,255); 12 | perm.resize(256); 13 | for (int i = 0; i < 256; i++) 14 | perm[i] = i; 15 | for (int i = 0; i < 256; i++) { 16 | int j = distribution(*generator); 17 | //Swap perm[i] and perm[j] 18 | int aux = perm[i]; 19 | perm[i] = perm[j]; 20 | perm[j] = aux; 21 | } 22 | perm.resize(512); 23 | for (int i = 0; i < 256; i++) 24 | perm[i+256] = perm[i]; 25 | } 26 | 27 | Noise3D::~Noise3D() { 28 | } 29 | 30 | float Noise3D::get(float x, float y, float z) const { 31 | float n0, n1, n2, n3; // Noise contributions from the four corners 32 | 33 | // Skew the input space to determine which simplex cell we're in 34 | float F3 = 1.0f/3.0f; 35 | float s = (x+y+z)*F3; // Very nice and simple skew factor for 3D 36 | int i = fastfloor(x+s); 37 | int j = fastfloor(y+s); 38 | int k = fastfloor(z+s); 39 | 40 | float G3 = 1.0f/6.0f; // Very nice and simple unskew factor, too 41 | float t = (i+j+k)*G3; 42 | float X0 = i-t; // Unskew the cell origin back to (x,y,z) space 43 | float Y0 = j-t; 44 | float Z0 = k-t; 45 | float x0 = x-X0; // The x,y,z distances from the cell origin 46 | float y0 = y-Y0; 47 | float z0 = z-Z0; 48 | 49 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron. 50 | // Determine which simplex we are in. 51 | int i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords 52 | int i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords 53 | 54 | if(x0>=y0) { 55 | if(y0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=1; k2=0; } // X Y Z order 56 | else if(x0>=z0) { i1=1; j1=0; k1=0; i2=1; j2=0; k2=1; } // X Z Y order 57 | else { i1=0; j1=0; k1=1; i2=1; j2=0; k2=1; } // Z X Y order 58 | } 59 | else { // x0 0 ? (int) x : (int) x - 1; } 139 | float Noise3D::dot( const int* g, const float x, const float y, const float z ) const { return g[0]*x + g[1]*y + g[2]*z; } 140 | -------------------------------------------------------------------------------- /game/SceneMain/world/Sun.cpp: -------------------------------------------------------------------------------- 1 | #include "Sun.hpp" 2 | #include "World.hpp" 3 | #include "Column.hpp" 4 | #include "Chunk.hpp" 5 | #include "../Player.hpp" 6 | 7 | Sun::Sun() { 8 | setName("sun"); 9 | minZ = std::vector(NUM_SUN_CASCADES, 0.0f); 10 | maxZ = std::vector(NUM_SUN_CASCADES, 0.0f); 11 | float zFar = std::min(WORLDSIZE*CHUNKSIZE, 256); 12 | float numParts = (1 << (NUM_SUN_CASCADES)) - 1; 13 | float lastMax = 0.0f; 14 | for(int i = 0; i < NUM_SUN_CASCADES; ++i) { 15 | cameras[i] = new Camera(); 16 | cameras[i]->addTo(this); 17 | minZ[i] = lastMax; 18 | maxZ[i] = zFar*(float(1 << i)/float(numParts)); 19 | lastMax = maxZ[i]; 20 | } 21 | cameras[NUM_SUN_CASCADES] = new Camera(); 22 | cameras[NUM_SUN_CASCADES]->projection = glm::ortho(-10,10,-10,10,-100,100); 23 | cameras[NUM_SUN_CASCADES]->addTo(this); 24 | //TODO: figure out best partitioning instead of hard-coding this 25 | minZ[0] = -10000; 26 | maxZ[0] = 8.0f; 27 | minZ[1] = 8.0f; 28 | maxZ[1] = 32.0f; 29 | minZ[2] = 32.0f; 30 | maxZ[2] = 128.0f; 31 | minZ[3] = 128.0f; 32 | maxZ[3] = 256.0f; 33 | VP = std::vector(NUM_SUN_CASCADES, mat4f(1.0f)); 34 | } 35 | 36 | Sun::~Sun() { 37 | } 38 | 39 | void Sun::update(float deltaTime) { 40 | (void) deltaTime; 41 | if(Keyboard::pressed(Keyboard::Z)) angle -= 1.0f*deltaTime; 42 | if(Keyboard::pressed(Keyboard::X)) angle += 1.0f*deltaTime; 43 | } 44 | 45 | void Sun::updateCameras() { 46 | Camera* playerCam = (Camera*)this->getGame()->getObjectByName("playerCam"); 47 | for(int i = 0; i < NUM_SUN_CASCADES+1; ++i) { 48 | cameras[i]->pos = vec3f(0.0f); 49 | cameras[i]->lookInDir(getDirection()); 50 | cameras[i]->projection = glm::ortho(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); 51 | aabbs[i] = AABB(); //sun view space bounding box for the player-drawn geometry 52 | } 53 | //extend the view-space bounding box to enclose the user-seen chunks 54 | extendFrustums(); 55 | for(int i = 0; i < NUM_SUN_CASCADES+1; ++i) { 56 | //transform from player view coords to sun cam view coords 57 | AABB trueBB = AABB(); 58 | for(int i2 = 0; i2 < 2; ++i2) 59 | for(int j = 0; j < 2; ++j) 60 | for(int k = 0; k < 2; ++k) { 61 | vec3f p = aabbs[i].getMin() + aabbs[i].getDimensions()*vec3f(i2, j, k); 62 | if(false && i != NUM_SUN_CASCADES) 63 | p = {p.x, p.y, glm::min(glm::max(minZ[i], p.z), maxZ[i])}; 64 | p = vec3f(glm::inverse(playerCam->getView()) * vec4f(p, 1.0)); 65 | p = vec3f(cameras[i]->getView()*vec4f(p, 1.0)); 66 | trueBB.extend(p); 67 | } 68 | aabbs[i] = trueBB; 69 | //unfold the view space AABB into worldspace and position the camera in it's center 70 | vec3f worldCenter = vec3f(glm::inverse(cameras[i]->getView())*vec4f(aabbs[i].getCenter(), 1.0f)); 71 | cameras[i]->pos = worldCenter; 72 | aabbs[i] = AABB(aabbs[i].getMin() - aabbs[i].getCenter(), aabbs[i].getMax() - aabbs[i].getCenter()); 73 | //build actual frustum 74 | cameras[i]->projection = glm::ortho(aabbs[i].getMin().x, aabbs[i].getMax().x, aabbs[i].getMin().y, aabbs[i].getMax().y, aabbs[i].getMin().z, aabbs[i].getMax().z); 75 | cameras[i]->recalculateFrustum(); 76 | } 77 | for(int i = 0; i < NUM_SUN_CASCADES; ++i) 78 | VP[i] = cameras[i]->projection*cameras[i]->getView(); 79 | } 80 | 81 | void Sun::extendFrustums() { 82 | Camera* pCam = (Camera*)getGame()->getObjectByName("playerCam"); 83 | World* w = (World*)getGame()->getObjectByName("world"); 84 | for(int x = 0; x < WORLDSIZE; ++x) 85 | for(int z = 0; z < WORLDSIZE; ++z) { 86 | Column* col = w->columns[x][z]; 87 | if(col == nullptr) continue; 88 | for(unsigned int y = 0; y < col->getChunkCount(); ++y) { 89 | Chunk* actual = col->getChunkCC(y); 90 | if(actual != nullptr && actual->wasDrawedByPlayer()) { 91 | std::vector indexes; 92 | AABB bbox = actual->getWorldSpaceBoundingBox(); 93 | for(int i = 0; i < NUM_SUN_CASCADES; ++i) { 94 | float dist = glm::abs(vec3f(pCam->getView() * vec4f(bbox.getCenter(), 1.0f)).z); 95 | if(dist < maxZ[i]+bbox.getRadius() && dist > minZ[i]-bbox.getRadius()) 96 | indexes.push_back(i); 97 | } 98 | if(!indexes.empty()) { 99 | indexes.push_back(NUM_SUN_CASCADES); 100 | extend(indexes, actual->getWorldSpaceBoundingBox(), pCam); 101 | } 102 | } 103 | } 104 | } 105 | } 106 | 107 | void Sun::extend(std::vector index, const AABB& occludedBox, const Camera* pCam) { 108 | for(int i = 0; i < 2; ++i) 109 | for(int j = 0; j < 2; ++j) 110 | for(int k = 0; k < 2; ++k) { 111 | //project this corner onto the view plane. view matrix is the same 112 | //for all cameras at this point so we'll just multiply once 113 | vec3f p = vec3f( 114 | pCam->getView() * 115 | vec4f( 116 | occludedBox.getMin() + occludedBox.getDimensions()*vec3f(i, j, k), 117 | 1.0f 118 | ) 119 | ); 120 | for(unsigned int j : index) aabbs[j].extend(p); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /game/SceneMain/SceneMain.cpp: -------------------------------------------------------------------------------- 1 | #include "SceneMain.hpp" 2 | #include "Player.hpp" 3 | #include "DeferredContainer.hpp" 4 | #include "BlurContainer.hpp" 5 | #include "world/World.hpp" 6 | #include "world/DeferredCubeLight.hpp" 7 | #include "Manager.hpp" 8 | #include "Debugger.hpp" 9 | 10 | SceneMain::SceneMain() { 11 | this->setName("SCENE"); 12 | 13 | Window::getInstance()->setTitle("VoxelGame"); 14 | Window::getInstance()->setVsync(Window::DisabledVsync); 15 | Mouse::setGrab(false); 16 | Mouse::setRelativeMode(true); 17 | 18 | loadResources(); 19 | 20 | srand(Clock::getSeconds()*1000); 21 | 22 | //GL stuff..: 23 | GL_ASSERT(glClearColor(0, 0, 0, 1)); 24 | GL_ASSERT(glEnable(GL_DEPTH_TEST)); 25 | GL_ASSERT(glEnable(GL_BLEND)); 26 | GL_ASSERT(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); 27 | GL_ASSERT(glDepthFunc(GL_LEQUAL)); 28 | GL_ASSERT(glEnable(GL_CULL_FACE)); //enable backface culling 29 | GL_ASSERT(glCullFace(GL_BACK)); 30 | GL_ASSERT(glEnable(GL_FRAMEBUFFER_SRGB)); 31 | BlurContainer* blur = new BlurContainer(); 32 | blur->addTo(this); 33 | 34 | DeferredContainer* renderer = new DeferredContainer(); 35 | renderer->addTo(blur); 36 | 37 | World* world = new World(); 38 | world->addTo(renderer); 39 | 40 | Player* player = new Player(); 41 | player->addTo(this); 42 | 43 | Debugger* debug = new Debugger(); 44 | debug->addTo(this); 45 | } 46 | 47 | SceneMain::~SceneMain() { 48 | Textures2D.clear(); 49 | Meshes.clear(); 50 | Programs.clear(); 51 | } 52 | 53 | void SceneMain::loadResources() { 54 | //meshes 55 | std::vector elems = { 56 | Vertex::Attribute("a_position", Vertex::Attribute::Float, 3) 57 | }; 58 | std::vector data = { 59 | vec3f(1, -1, 0), vec3f(1, 1, 0), vec3f(-1, 1, 0), 60 | vec3f(-1, -1, 0) 61 | }; 62 | std::vector indexes = { 63 | 0, 1, 2, 3, 0, 2 64 | }; 65 | MeshIndexed quad = MeshIndexed(Vertex::Format(elems)); 66 | quad.setVertexData(&data[0], 6); 67 | quad.setIndexData(&indexes[0], 6); 68 | quad.setPrimitiveType(Mesh::TRIANGLES); 69 | Meshes.add("quad", std::move(quad)); 70 | 71 | std::vector cubeVertices = { 72 | vec3f(0.0, 0.0, 1.0), 73 | vec3f(1.0, 0.0, 1.0), 74 | vec3f(0.0, 1.0, 1.0), 75 | vec3f(1.0, 1.0, 1.0), 76 | vec3f(0.0, 0.0, 0.0), 77 | vec3f(1.0, 0.0, 0.0), 78 | vec3f(0.0, 1.0, 0.0), 79 | vec3f(1.0, 1.0, 0.0), 80 | }; 81 | 82 | std::vector cubeIndices = { 83 | 0, 1, 2, 3, 7, 1, 5, 4, 7, 6, 2, 4, 0, 1 84 | }; 85 | 86 | MeshIndexed cube = MeshIndexed(Vertex::Format(elems)); 87 | cube.setPrimitiveType(Mesh::TRIANGLE_STRIP); 88 | cube.setVertexData(&cubeVertices[0],cubeVertices.size()); 89 | cube.setIndexData(&cubeIndices[0],cubeIndices.size()); 90 | Meshes.add("1x1Cube", std::move(cube)); 91 | 92 | //textures 93 | char pixels[4] = {char(200), char(20), char(20), char(255)}; 94 | Texture2D nullRed(vec2ui(1)); 95 | nullRed.setData(pixels); 96 | Textures2D.add("nullRed", std::move(nullRed)); 97 | char pixels2[4] = {char(20), char(200), char(20), char(255)}; 98 | Texture2D nullGreen = Texture2D(vec2ui(1)); 99 | nullRed.setData(pixels2); 100 | Textures2D.add("nullGreen", std::move(nullGreen)); 101 | char pixels3[4] = {char(20), char(20), char(200), char(255)}; 102 | Texture2D nullBlue = Texture2D(vec2ui(1)); 103 | nullRed.setData(pixels3); 104 | Textures2D.add("nullBlue", std::move(nullBlue)); 105 | char pixels4[4] = {char(70), char(30), char(80), char(255)}; 106 | Texture2D nullBlack = Texture2D(vec2ui(1)); 107 | nullRed.setData(pixels4); 108 | Textures2D.add("nullBlack", std::move(nullBlack)); 109 | char pixels5[4] = {char(255), char(255), char(255), char(255)}; 110 | Texture2D nullWhite = Texture2D(vec2ui(1)); 111 | nullRed.setData(pixels5); 112 | Textures2D.add("nullWhite", std::move(nullWhite)); 113 | Textures2D.add("blocks", Texture2D::load(Storage::openAsset("textures/blocks8.png"), TextureFormat::SRGBA8)); 114 | Textures2D.get("blocks").setFilter(GL_NEAREST,GL_NEAREST); 115 | 116 | //program 117 | Programs.add("deferredLight", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/light.frag"))); 118 | Programs.add("deferredCubeLight", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/cubeLight.frag"))); 119 | Programs.add("ambientPass", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/ambientPass.frag"))); 120 | Programs.add("blurPassVertical", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/blurPassVertical.frag"))); 121 | Programs.add("blurPassHoritzontal", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/blurPassHoritzontal.frag"))); 122 | Programs.add("textureToScreen", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/quad.frag"))); 123 | Programs.add("blurMaskPass", ShaderProgram(Storage::openAsset("shaders/quad.vert"), Storage::openAsset("shaders/blurMaskPass.frag"))); 124 | Programs.add("depthShader", ShaderProgram(Storage::openAsset("shaders/depth.vert"), Storage::openAsset("shaders/depth.geom"), Storage::openAsset("shaders/depth.frag"))); 125 | Programs.add("deferredChunk", ShaderProgram(Storage::openAsset("shaders/chunkDeferred.vert"), Storage::openAsset("shaders/chunkDeferred.frag"))); 126 | Programs.add("forwardChunk", ShaderProgram(Storage::openAsset("shaders/chunkForward.vert"), Storage::openAsset("shaders/chunkForward.frag"))); 127 | } 128 | 129 | void SceneMain::update(float deltaTime) { 130 | (void) deltaTime; 131 | if(Keyboard::pressed(Keyboard::Escape) || Window::getInstance()->isClosing()) getGame()->isRunning = false; 132 | if(Keyboard::justPressed(Keyboard::K)) USE_CPU_VISIBILITY = !USE_CPU_VISIBILITY; 133 | } 134 | -------------------------------------------------------------------------------- /game/SceneMain/world/DeferredCubeLight.cpp: -------------------------------------------------------------------------------- 1 | #include "DeferredCubeLight.hpp" 2 | #include "World.hpp" 3 | #include "../DeferredContainer.hpp" 4 | #include "../Manager.hpp" 5 | #include "Cube.hpp" 6 | 7 | DeferredCubeLight::DeferredCubeLight(const vec3f& pos, const vec3f& color) : pos(pos), color(color) { 8 | renderer = (DeferredContainer*)getGame()->getObjectByName("deferred"); 9 | world = (World*)getGame()->getObjectByName("world"); 10 | tex = Texture3D(vec3ui(LIGHTSIZE*2), TextureFormat::RED); 11 | 12 | int x0 = int(floor(pos.x)); 13 | int y0 = int(floor(pos.y)); 14 | int z0 = int(floor(pos.z)); 15 | calcLight(x0, y0, z0); 16 | 17 | quad = &Meshes.get("quad"); 18 | } 19 | 20 | DeferredCubeLight::~DeferredCubeLight() { 21 | } 22 | 23 | void DeferredCubeLight::count(std::pair& pr, vec3i p, vec3i c) { 24 | float c1 = glm::dot(vec3f(p), vec3f(c)); 25 | float c2 = glm::dot(vec3f(p), vec3f(p)); 26 | vec3f b = vec3f(p) * (c1 / c2); 27 | 28 | float d = glm::distance(b, vec3f(c)); 29 | 30 | float w = 1-d; 31 | if(w < 0) w = 0; 32 | 33 | if(w == 0) return; 34 | 35 | pr.first += light[c.x+LIGHTSIZE][c.y+LIGHTSIZE][c.z+LIGHTSIZE]*w; 36 | pr.second += w; 37 | } 38 | 39 | inline int sign(int n) { 40 | return n<0?-1:1; 41 | } 42 | 43 | void DeferredCubeLight::calcQuadrant(int cx, int cy, int cz, int dx, int dy, int dz) { 44 | if(cx*dx < 0 || cy*dy < 0 || cz*dz < 0) 45 | return; 46 | 47 | int x0 = int(floor(pos.x)); 48 | int y0 = int(floor(pos.y)); 49 | int z0 = int(floor(pos.z)); 50 | 51 | for(int x = cx; x >= -LIGHTSIZE && x < LIGHTSIZE; x+=dx) { 52 | for(int y = cy; y >= -LIGHTSIZE && y < LIGHTSIZE; y+=dy) { 53 | for(int z = cz; z >= -LIGHTSIZE && z < LIGHTSIZE; z+=dz) { 54 | 55 | if((x == 0 && y == 0 && z == 0) || sqrt(x*x + y*y + z*z) > LIGHTSIZE) continue; 56 | 57 | if(!Cube::getFlag((Cube::Type)world->getCube(x+x0, y+y0, z+z0), Cube::TRANSPARENT)) 58 | light[x+LIGHTSIZE][y+LIGHTSIZE][z+LIGHTSIZE] = 0; 59 | else 60 | { 61 | std::pair p (0, 0); 62 | count(p, vec3i(x, y, z), vec3i(x-dx, y, z)); 63 | count(p, vec3i(x, y, z), vec3i(x, y-dy, z)); 64 | count(p, vec3i(x, y, z), vec3i(x, y, z-dz)); 65 | count(p, vec3i(x, y, z), vec3i(x-dx, y-dy, z)); 66 | count(p, vec3i(x, y, z), vec3i(x-dx, y, z-dz)); 67 | count(p, vec3i(x, y, z), vec3i(x, y-dy, z-dz)); 68 | count(p, vec3i(x, y, z), vec3i(x-dx, y-dy, z-dz)); 69 | 70 | p.first /= p.second; 71 | 72 | light[x+LIGHTSIZE][y+LIGHTSIZE][z+LIGHTSIZE] = p.first; 73 | } 74 | data[z+LIGHTSIZE][y+LIGHTSIZE][x+LIGHTSIZE] = (unsigned char)(light[x+LIGHTSIZE][y+LIGHTSIZE][z+LIGHTSIZE]*255); 75 | } 76 | } 77 | } 78 | } 79 | 80 | void DeferredCubeLight::calcLight(int cx, int cy, int cz) { 81 | if(glm::length(vec3f(cx,cy,cz) - this->pos) > LIGHTSIZE+1) return; 82 | 83 | light[LIGHTSIZE][LIGHTSIZE][LIGHTSIZE] = 1; 84 | data[LIGHTSIZE][LIGHTSIZE][LIGHTSIZE] = (unsigned char)(light[LIGHTSIZE][LIGHTSIZE][LIGHTSIZE]*255); 85 | 86 | int x0 = int(floor(pos.x)); 87 | int y0 = int(floor(pos.y)); 88 | int z0 = int(floor(pos.z)); 89 | 90 | cx -= x0; 91 | cy -= y0; 92 | cz -= z0; 93 | 94 | calcQuadrant(cx, cy, cz, -1, -1, -1); 95 | calcQuadrant(cx, cy, cz, -1, -1, 1); 96 | calcQuadrant(cx, cy, cz, -1, 1, -1); 97 | calcQuadrant(cx, cy, cz, -1, 1, 1); 98 | calcQuadrant(cx, cy, cz, 1, -1, -1); 99 | calcQuadrant(cx, cy, cz, 1, -1, 1); 100 | calcQuadrant(cx, cy, cz, 1, 1, -1); 101 | calcQuadrant(cx, cy, cz, 1, 1, 1); 102 | tex.setData(data, TextureFormat::RED, TextureFormat::UNSIGNED_BYTE); 103 | tex.setFilter(GL_LINEAR,GL_LINEAR); 104 | tex.setWrap(GL_CLAMP_TO_BORDER); 105 | } 106 | 107 | void DeferredCubeLight::update(float deltaTime) { 108 | (void) deltaTime; 109 | transform = glm::translate(mat4f(1.0f), pos); 110 | } 111 | 112 | void DeferredCubeLight::draw() const { 113 | if(renderer->getMode() != DeferredContainer::Light) return; 114 | Camera* cam = (Camera*)getGame()->getObjectByName("playerCam"); 115 | vec3f posWorldSpace = vec3f(fullTransform*vec4f(0,0,0,1)); 116 | vec3f posViewSpace = vec3f(cam->getView()*vec4f(posWorldSpace,1.0)); 117 | 118 | mat4f t(1.0); 119 | if(glm::length(posViewSpace) > LIGHTSIZE) { 120 | vec3f front = cam->getWorldPos()-posWorldSpace; 121 | front = glm::normalize(front); 122 | vec3f dummyUp(0, 1, 0); 123 | vec3f right = glm::cross(dummyUp, front); 124 | right = glm::normalize(right); 125 | vec3f up = glm::cross(front, right); 126 | up = glm::normalize(up); 127 | mat4f rot(right.x, right.y, right.z, 0, 128 | up.x , up.y , up.z , 0, 129 | front.x, front.y, front.z, 0, 130 | 0 , 0 , 0 , 1); 131 | t = glm::scale(rot, vec3f(LIGHTSIZE)); 132 | t = glm::translate(t, vec3f(0, 0, 1)); 133 | Programs.get("deferredCubeLight").uniform("MVP")->set(cam->projection*cam->getView()*fullTransform*t); 134 | } 135 | else 136 | Programs.get("deferredCubeLight").uniform("MVP")->set(t); 137 | 138 | Programs.get("deferredCubeLight").uniform("invResolution")->set(vec2f(1.0f/Window::getInstance()->getSize().x, 1.0f/Window::getInstance()->getSize().y)); 139 | Programs.get("deferredCubeLight").uniform("color0")->set(renderer->getColor0()); 140 | Programs.get("deferredCubeLight").uniform("color1")->set(renderer->getColor1()); 141 | Programs.get("deferredCubeLight").uniform("depth")->set(renderer->getDepth()); 142 | Programs.get("deferredCubeLight").uniform("lightPos")->set(posViewSpace); 143 | Programs.get("deferredCubeLight").uniform("invProj")->set(glm::inverse(cam->projection)); 144 | Programs.get("deferredCubeLight").uniform("invView")->set(glm::inverse(cam->getView())); 145 | Programs.get("deferredCubeLight").uniform("lightColor")->set(color); 146 | Programs.get("deferredCubeLight").uniform("lightRadius")->set(float(LIGHTSIZE)); 147 | Programs.get("deferredCubeLight").uniform("tex")->set(tex); 148 | quad->draw(Programs.get("deferredCubeLight")); 149 | } 150 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/ColumnGenerator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef WORLDGENERATOR_HPP 2 | #define WORLDGENERATOR_HPP 3 | #include "commons.hpp" 4 | #include "terrainFunctions.hpp" 5 | #include "biomeFunctions.hpp" 6 | 7 | #define BIOME_MATRIX_MARGIN 32 8 | #define BIOME_MATRIX_SIZE (CHUNKSIZE+BIOME_MATRIX_MARGIN*2) 9 | 10 | class TaskPool; 11 | class Column; 12 | class Dec; 13 | class ColumnGenerator { 14 | public: 15 | ColumnGenerator(int seed); 16 | ~ColumnGenerator(); 17 | 18 | void queueLoad(vec2i colPos); 19 | void discardGenerateTasks(); 20 | bool locked() const; 21 | void lock(); 22 | void unlock(); 23 | void unloadColumn(Column* col); 24 | void setRelevantArea(const vec2i& min, const vec2i&max); 25 | Column* pullDone(); 26 | void update(); 27 | 28 | struct ColumnData { 29 | struct PairComp { 30 | bool operator()(const std::pair& a, const std::pair& b) { 31 | if(a.first.x != b.first.x) return a.first.x < b.first.x; 32 | if(a.first.y != b.first.y) return a.first.y < b.first.y; 33 | if(a.first.z != b.first.z) return a.first.z < b.first.z; 34 | if(a.second != b.second) return a.second < b.second; 35 | return false; 36 | } 37 | }; 38 | 39 | enum State { 40 | Loading = 0, 41 | Raw, 42 | Decorating, 43 | Decorated, 44 | Building, 45 | Built, 46 | Deleting, 47 | Deleted 48 | }; 49 | 50 | bool canDelete() const { 51 | return refCount == 0 && ( 52 | state == Built || 53 | state == Raw 54 | ); 55 | } 56 | 57 | inline void setDecorationWC(vec3i v, unsigned char layer, unsigned int val) { 58 | setDecorationRC(v.x - pos.x*CHUNKSIZE, v.y, v.z - pos.y*CHUNKSIZE, layer, val); 59 | } 60 | 61 | inline void setDecorationWC(int x, int y, int z, unsigned char layer, unsigned int val) { 62 | setDecorationRC(x - pos.x*CHUNKSIZE, y, z - pos.y*CHUNKSIZE, layer, val); 63 | } 64 | 65 | inline void setDecorationRC(vec3s v, unsigned char layer, unsigned int val) { 66 | setDecorationRC(v.x, v.y, v.z, layer, val); 67 | } 68 | 69 | void setDecorationRC(short x, short y, short z, unsigned char layer, unsigned int val) { 70 | int x1 = (x+16) >> CHUNKSIZE_POW2; 71 | int z1 = (z+16) >> CHUNKSIZE_POW2; 72 | VBE_ASSERT_SIMPLE(z1 >= 0 && z1 <= 2 && z1 >= 0 && z1 <= 2); 73 | VBE_ASSERT_SIMPLE(y >= 0); 74 | auto r = decIn[x1][z1].insert( 75 | std::make_pair( 76 | std::make_pair( 77 | vec3us(x & CHUNKSIZE_MASK, y, z & CHUNKSIZE_MASK), 78 | layer 79 | ), 80 | val 81 | ) 82 | ); 83 | (void) r; 84 | } 85 | 86 | typedef std::map, unsigned int, PairComp> ChunkDecoration; 87 | 88 | // Raw column block data. May be empty if the column is 89 | // loaded into a Column object (so this->col != nullptr) 90 | const unsigned int* raw = nullptr; 91 | // Raw biome data. Idem. 92 | Biome* biomes = nullptr; 93 | // Finished column, with decorations and entities. 94 | // Will be nullptr if still being loaded, decorated, etc 95 | // in which case this->raw != nullptr 96 | Column* col = nullptr; 97 | ChunkDecoration decIn[3][3]; 98 | ChunkDecoration decOut; 99 | State state = State::Loading; 100 | int refCount = 0; 101 | vec2i pos = {0, 0}; 102 | }; 103 | 104 | private: 105 | void queueBuild(vec2i colPos); 106 | void queueDecorate(vec2i colPos); 107 | void queueDelete(vec2i colPos); 108 | bool inPlayerArea(const vec2i& colPos) const; 109 | 110 | struct Comp { 111 | bool operator()(const vec2i& a, const vec2i& b) { 112 | if(a.x != b.x) return a.x < b.x; 113 | if(a.y != b.y) return a.y < b.y; 114 | return false; 115 | } 116 | }; 117 | 118 | 119 | // All deleteable chunks outside this range will be deleted 120 | vec2i relevantMin = {0, 0}; 121 | vec2i relevantMax = {0, 0}; 122 | 123 | // ____ _ __ ______ 124 | // / __ \ / | / // ____/ Do you have a moment 125 | // / /_/ // |/ // / __ to talk about our 126 | // / _, _// /| // /_/ / lord and savior, 127 | // /_/ |_|/_/ |_/ \____/ RNG? 128 | // 129 | std::mt19937 generator; 130 | 131 | // Terrain generation entrypoint 132 | FunctionTerrain* terrainEntry = nullptr; 133 | // Terrain decorators 134 | std::vector decorators; 135 | // Biome generation entrypoint 136 | FunctionBiome* biomeEntry = nullptr; 137 | 138 | // All loaded Columns. May be missing decorations 139 | std::unique_lock loadedLock; 140 | std::mutex loadedMutex; 141 | std::map loaded; 142 | 143 | // Done and ready to be pulled into the game 144 | std::unique_lock doneLock; 145 | std::mutex doneMutex; 146 | std::queue done; 147 | 148 | // Jobs that try to load/generate a ColumnData 149 | TaskPool* generatePool = nullptr; 150 | 151 | // Jobs that decorate a newly generated ColumnData 152 | TaskPool* decoratePool = nullptr; 153 | 154 | // Jobs grab a finished ColumnData, 155 | // builds the Column* object if necessary and computes 156 | // the initial 3D model for all it's chunks 157 | TaskPool* buildPool= nullptr; 158 | 159 | // Jobs that unload a ColumnData 160 | TaskPool* killPool = nullptr; 161 | }; 162 | 163 | #endif // WORLDGENERATOR_HPP 164 | -------------------------------------------------------------------------------- /game/SceneMain/Player.cpp: -------------------------------------------------------------------------------- 1 | #include "Player.hpp" 2 | #include "world/World.hpp" 3 | #include "world/DeferredCubeLight.hpp" 4 | #include "DeferredContainer.hpp" 5 | #include "Debugger.hpp" 6 | 7 | Player::Player() { 8 | setName("player"); 9 | cam = new Camera("playerCam", vec3f(0,1.5,0)); 10 | float fovy = glm::radians(60.0f); 11 | float ratio = float(Window::getInstance()->getSize().x)/float(Window::getInstance()->getSize().y); 12 | cam->projection = glm::perspective(fovy, ratio, 0.01f, WORLDSIZE*CHUNKSIZE*0.5f); 13 | fov = vec2f(2.0f*glm::atan(glm::tan(fovy*0.5f)*ratio), fovy); 14 | cam->addTo(this); 15 | acc = vec3f(0,-10,0); 16 | pos = vec3f(0,256,0); 17 | hitbox->type = Hitbox::BOX; 18 | hitbox->radius = vec3f(0.6*scale.x,1.6*scale.y,0.6*scale.z); 19 | } 20 | 21 | Player::~Player() { 22 | } 23 | 24 | void Player::update(float deltaTime) { 25 | //take input 26 | if(!Debugger::isShown()) processKeys(deltaTime); 27 | 28 | //transform coordinates for camera and other children 29 | float p = getGame()->getTimeSinceFixed()/getGame()->getFixedUpdateTime(); 30 | transform = glm::translate(mat4f(1.0), lastPos*(1-p) + pos*p); 31 | 32 | //trace view 33 | traceView(); 34 | } 35 | 36 | void Player::fixedUpdate(float deltaTime) { 37 | transform = glm::translate(mat4f(1.0), pos); 38 | this->propragateTransforms(); 39 | 40 | lastPos = pos; 41 | 42 | //move and update camera position 43 | if(!Debugger::isShown()) movePos(deltaTime); //this handles collisions 44 | 45 | //Limit movement 46 | disp.x = 0; // Player only accelerates vertically, so disp.x doesn't carry 47 | disp.y = std::fmax(-70, disp.y); 48 | disp.z = 0; // Player only accelerates vertically, so disp.z doesn't carry 49 | 50 | //feedback to be used by the scene 51 | onFloor = hitbox->collidesWithWorld(vec3f(0,-0.1,0)); 52 | isJumping = (disp.y > 0); 53 | } 54 | 55 | void Player::processKeys(float deltaTime) { 56 | World* w = (World*)getGame()->getObjectByName("world"); 57 | //Move player 58 | const float speedKeys = 30.0f*deltaTime; 59 | vec2f speedPad = vec2f(Gamepad::axis(0, Gamepad::AxisLeftX), Gamepad::axis(0, Gamepad::AxisLeftY)); 60 | vec2f dir = vec2f(cam->getForward().x,cam->getForward().z); 61 | dir = (dir == vec2f(0.0f))? vec2f(1.0f,0.0f) : glm::normalize(dir); 62 | if(Keyboard::pressed(Keyboard::W)) { 63 | disp.x += dir.x*speedKeys; 64 | disp.z += dir.y*speedKeys; 65 | } 66 | if(Keyboard::pressed(Keyboard::S)) { 67 | disp.x += -dir.x*speedKeys; 68 | disp.z += -dir.y*speedKeys; 69 | } 70 | if(Keyboard::pressed(Keyboard::A)) { 71 | disp.x += dir.y*speedKeys; 72 | disp.z += -dir.x*speedKeys; 73 | } 74 | if(Keyboard::pressed(Keyboard::D)) { 75 | disp.x += -dir.y*speedKeys; 76 | disp.z += dir.x*speedKeys; 77 | } 78 | if(glm::length(speedPad) > 0.3f) { 79 | disp.x += -dir.x*speedPad.y*speedKeys; 80 | disp.z += -dir.y*speedPad.y*speedKeys; 81 | disp.x += -dir.y*speedPad.x*speedKeys; 82 | disp.z += dir.x*speedPad.x*speedKeys; 83 | } 84 | if(Keyboard::pressed(Keyboard::Space) || Gamepad::pressed(0, Gamepad::ButtonA)) 85 | //if (onFloor && !isJumping) 86 | disp.y = 15; 87 | 88 | //look around 89 | vec2f displacement = glm::radians(vec2f(Mouse::movement())*0.1f); 90 | vec2f padDisplacement = vec2f(Gamepad::axis(0, Gamepad::AxisRightX), Gamepad::axis(0, Gamepad::AxisRightY)); 91 | if(glm::length(padDisplacement) > 0.3f) //death zone 92 | displacement += glm::radians(padDisplacement * 2.0f); 93 | 94 | cam->rotateGlobal(displacement.x, vec3f(0,1,0)); 95 | //limit x rotation 96 | if(glm::degrees(std::abs(xRot+displacement.y)) < 90.0f) { 97 | cam->rotateLocal(displacement.y, vec3f(1,0,0)); 98 | xRot += displacement.y; 99 | } 100 | 101 | //take block 102 | if(Mouse::justPressed(Mouse::Left) && targetsBlock) 103 | w->setCubeRange(targetedBlock-vec3i(5), vec3i(11), 0); 104 | 105 | //put block 106 | if(Mouse::justPressed(Mouse::Right) && targetsBlock) { 107 | w->setCube(targetedBlockEnter.x,targetedBlockEnter.y,targetedBlockEnter.z,4); 108 | vec3f pos = vec3f(targetedBlockEnter)+vec3f(0.5f); 109 | DeferredCubeLight* l = new DeferredCubeLight(pos, glm::abs(glm::sphericalRand(1.0f))); 110 | l->addTo(w); 111 | } 112 | 113 | if(Keyboard::justPressed(Keyboard::R) && targetsBlock) { 114 | w->setCube(targetedBlockEnter.x,targetedBlockEnter.y,targetedBlockEnter.z,4); 115 | vec3f pos = vec3f(targetedBlockEnter)+vec3f(0.5f); 116 | DeferredCubeLight* l = new DeferredCubeLight(pos, vec3f(1,0,0)); 117 | l->addTo(w); 118 | } 119 | 120 | if(Keyboard::justPressed(Keyboard::B) && targetsBlock) { 121 | w->setCube(targetedBlockEnter.x,targetedBlockEnter.y,targetedBlockEnter.z,4); 122 | vec3f pos = vec3f(targetedBlockEnter)+vec3f(0.5f); 123 | DeferredCubeLight* l = new DeferredCubeLight(pos, vec3f(0,0,1)); 124 | l->addTo(w); 125 | } 126 | } 127 | 128 | void Player::traceView() { 129 | World* w = (World*)getGame()->getObjectByName("world"); 130 | float tMax = 10; //View radius 131 | vec3f cpos(cam->getWorldPos()), 132 | dir(cam->getForward()), 133 | vox(floor(cpos.x), floor(cpos.y), floor(cpos.z)), 134 | step(0,0,0), 135 | next(0,0,0), 136 | tMaxc(tMax,tMax,tMax), 137 | tDelta(tMax,tMax,tMax); 138 | 139 | if (!w->outOfBounds(cpos.x,cpos.y,cpos.z) && 140 | w->getCube(cpos.x,cpos.y,cpos.z) != 0) { 141 | targetsBlock = true; 142 | targetedBlock = vec3f(floor(cpos.x),floor(cpos.y),floor(cpos.z)); 143 | return; 144 | } 145 | 146 | if (dir.x < 0) step.x = -1; 147 | else step.x = 1; 148 | if (dir.y < 0) step.y = -1; 149 | else step.y = 1; 150 | if (dir.z < 0) step.z = -1; 151 | else step.z = 1; 152 | 153 | next.x = vox.x + (step.x > 0 ? 1 : 0); 154 | next.y = vox.y + (step.y > 0 ? 1 : 0); 155 | next.z = vox.z + (step.z > 0 ? 1 : 0); 156 | 157 | if (dir.x != 0) { 158 | tDelta.x = step.x/dir.x; 159 | tMaxc.x = (next.x - cpos.x)/dir.x; 160 | } 161 | if (dir.y != 0) { 162 | tDelta.y = step.y/dir.y; 163 | tMaxc.y = (next.y - cpos.y)/dir.y; 164 | } 165 | if (dir.z != 0) { 166 | tDelta.z = step.z/dir.z; 167 | tMaxc.z = (next.z - cpos.z)/dir.z; 168 | } 169 | 170 | float tCurr = 0; 171 | while (tCurr < tMax) { 172 | targetedBlockEnter = vox; 173 | if(tMaxc.x < tMaxc.y) { 174 | if(tMaxc.x < tMaxc.z) { 175 | tCurr = tMaxc.x; 176 | tMaxc.x = tMaxc.x + tDelta.x; 177 | vox.x = vox.x + step.x; 178 | } 179 | else { 180 | tCurr = tMaxc.z; 181 | vox.z = vox.z + step.z; 182 | tMaxc.z = tMaxc.z + tDelta.z; 183 | } 184 | } 185 | else { 186 | if(tMaxc.y < tMaxc.z) { 187 | tCurr = tMaxc.y; 188 | vox.y = vox.y + step.y; 189 | tMaxc.y = tMaxc.y + tDelta.y; 190 | } 191 | else { 192 | tCurr = tMaxc.z; 193 | vox.z = vox.z + step.z; 194 | tMaxc.z= tMaxc.z + tDelta.z; 195 | } 196 | } 197 | if(!w->outOfBounds(vox.x,vox.y,vox.z) && w->getCube(vox.x,vox.y,vox.z) != 0) { 198 | targetsBlock = true; 199 | targetedBlock = vox; 200 | return; 201 | } 202 | } 203 | targetsBlock = false; 204 | } 205 | -------------------------------------------------------------------------------- /.ycm_extra_conf.py: -------------------------------------------------------------------------------- 1 | # This file is NOT licensed under the GPLv3, which is the license for the rest 2 | # of YouCompleteMe. 3 | # 4 | # Here's the license text for this file: 5 | # 6 | # This is free and unencumbered software released into the public domain. 7 | # 8 | # Anyone is free to copy, modify, publish, use, compile, sell, or 9 | # distribute this software, either in source code form or as a compiled 10 | # binary, for any purpose, commercial or non-commercial, and by any 11 | # means. 12 | # 13 | # In jurisdictions that recognize copyright laws, the author or authors 14 | # of this software dedicate any and all copyright interest in the 15 | # software to the public domain. We make this dedication for the benefit 16 | # of the public at large and to the detriment of our heirs and 17 | # successors. We intend this dedication to be an overt act of 18 | # relinquishment in perpetuity of all present and future rights to this 19 | # software under copyright law. 20 | # 21 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 22 | # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 24 | # IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 25 | # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 26 | # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 27 | # OTHER DEALINGS IN THE SOFTWARE. 28 | # 29 | # For more information, please refer to 30 | 31 | import os 32 | import ycm_core 33 | 34 | # These are the compilation flags that will be used in case there's no 35 | # compilation database set (by default, one is not set). 36 | # CHANGE THIS LIST OF FLAGS. YES, THIS IS THE DROID YOU HAVE BEEN LOOKING FOR. 37 | flags = [ 38 | '-Wall', 39 | # '-Wextra', 40 | # '-Werror', 41 | # '-Wc++98-compat', 42 | # '-Wno-long-long', 43 | # '-Wno-variadic-macros', 44 | '-fno-exceptions', 45 | '-fPIC', 46 | '-W', 47 | '-g', 48 | # '-DNDEBUG', 49 | # You 100% do NOT need -DUSE_CLANG_COMPLETER in your flags; only the YCM 50 | # source code needs it. 51 | '-DUSE_CLANG_COMPLETER', 52 | # THIS IS IMPORTANT! Without a "-std=" flag, clang won't know whic 53 | # language to use when compiling headers. So it will guess. Badly. So C++ 54 | # headers will be compiled as C headers. You don't want that so ALWAYS specif 55 | # a "-std=". 56 | # For a C project, you would set this to something like 'c99' instead of 57 | # 'c++11'. 58 | '-std=c++11', 59 | # ...and the same thing goes for the magic -x option which specifies the 60 | # language that the files to be compiled are written in. This is mostly 61 | # relevant for c++ headers. 62 | # For a C project, you would set this to 'c' instead of 'c++'. 63 | '-x', 64 | 'c++', 65 | '-isystem', 66 | '../BoostParts', 67 | '-isystem', 68 | # This path will only work on OS X, but extra paths that don't exist are not 69 | # harmful 70 | '/System/Library/Frameworks/Python.framework/Headers', 71 | '-isystem', 72 | '../llvm/include', 73 | '-isystem', 74 | '../llvm/tools/clang/include', 75 | '-I', 76 | '.', 77 | '-I', 78 | './ClangCompleter', 79 | '-isystem', 80 | './tests/gmock/gtest', 81 | '-isystem', 82 | './tests/gmock/gtest/include', 83 | '-isystem', 84 | './tests/gmock', 85 | '-isystem', 86 | './tests/gmock/include', 87 | # VOXELGAME 88 | '-I', 89 | 'game/', 90 | '-I', 91 | 'VBE/include', 92 | '-I', 93 | 'VBE-Scenegraph/include', 94 | '-I', 95 | 'VBE-Profiler/include', 96 | ] 97 | 98 | 99 | # Set this to the absolute path to the folder (NOT the file!) containing the 100 | # compile_commands.json file to use that instead of 'flags'. See here for 101 | # more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html 102 | # 103 | # You can get CMake to generate this file for you by adding: 104 | # set( CMAKE_EXPORT_COMPILE_COMMANDS 1 ) 105 | # to your CMakeLists.txt file. 106 | # 107 | # Most projects will NOT need to set this to anything; you can just change the 108 | # 'flags' list of compilation flags. Notice that YCM itself uses that approach. 109 | compilation_database_folder = '' 110 | 111 | if os.path.exists(compilation_database_folder): 112 | database = ycm_core.CompilationDatabase(compilation_database_folder) 113 | else: 114 | database = None 115 | 116 | SOURCE_EXTENSIONS = ['.cpp', '.cxx', '.cc', '.c', '.m', '.mm'] 117 | 118 | 119 | def DirectoryOfThisScript(): 120 | return os.path.dirname(os.path.abspath(__file__)) 121 | 122 | 123 | def MakeRelativePathsInFlagsAbsolute(flags, working_directory): 124 | if not working_directory: 125 | return list(flags) 126 | new_flags = [] 127 | make_next_absolute = False 128 | path_flags = ['-isystem', '-I', '-iquote', '--sysroot='] 129 | for flag in flags: 130 | new_flag = flag 131 | 132 | if make_next_absolute: 133 | make_next_absolute = False 134 | if not flag.startswith('/'): 135 | new_flag = os.path.join(working_directory, flag) 136 | 137 | for path_flag in path_flags: 138 | if flag == path_flag: 139 | make_next_absolute = True 140 | break 141 | 142 | if flag.startswith(path_flag): 143 | path = flag[len(path_flag):] 144 | new_flag = path_flag + os.path.join(working_directory, path) 145 | break 146 | 147 | if new_flag: 148 | new_flags.append(new_flag) 149 | return new_flags 150 | 151 | 152 | def IsHeaderFile(filename): 153 | extension = os.path.splitext(filename)[1] 154 | return extension in ['.h', '.hxx', '.hpp', '.hh'] 155 | 156 | 157 | def GetCompilationInfoForFile(filename): 158 | # The compilation_commands.json file generated by CMake does not have entr 159 | # for header files. So we do our best by asking the db for flags for a 160 | # corresponding source file, if any. If one exists, the flags for that file 161 | # should be good enough. 162 | if IsHeaderFile(filename): 163 | basename = os.path.splitext(filename)[0] 164 | for extension in SOURCE_EXTENSIONS: 165 | replacement_file = basename + extension 166 | if os.path.exists(replacement_file): 167 | compilation_info = database.GetCompilationInfoForFile( 168 | replacement_file) 169 | if compilation_info.compiler_flags_: 170 | return compilation_info 171 | return None 172 | return database.GetCompilationInfoForFile(filename) 173 | 174 | 175 | def FlagsForFile(filename, **kwargs): 176 | if database: 177 | # Bear in mind that compilation_info.compiler_flags_ does NOT return a 178 | # python list, but a "list-like" StringVec object 179 | compilation_info = GetCompilationInfoForFile(filename) 180 | if not compilation_info: 181 | return None 182 | 183 | final_flags = MakeRelativePathsInFlagsAbsolute( 184 | compilation_info.compiler_flags_, 185 | compilation_info.compiler_working_dir_) 186 | 187 | # NOTE: This is just for YouCompleteMe; it's highly likely that your 188 | # does NOT need to remove the stdlib flag. DO NOT USE THIS IN YOUR 189 | # ycm_extra_conf IF YOU'RE NOT 100% SURE YOU NEED IT. 190 | try: 191 | final_flags.remove('-stdlib=libc++') 192 | except ValueError: 193 | pass 194 | else: 195 | relative_to = DirectoryOfThisScript() 196 | final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to) 197 | 198 | return { 199 | 'flags': final_flags, 200 | 'do_cache': True 201 | } 202 | -------------------------------------------------------------------------------- /game/SceneMain/DeferredContainer.cpp: -------------------------------------------------------------------------------- 1 | #include "DeferredContainer.hpp" 2 | #include "BlurContainer.hpp" 3 | #include "world/World.hpp" 4 | #include "world/Chunk.hpp" 5 | #include "world/Column.hpp" 6 | #include "world/Sun.hpp" 7 | #include "Manager.hpp" 8 | #include "Debugger.hpp" 9 | 10 | DeferredContainer::DeferredContainer() { 11 | setName("deferred"); 12 | makeTarget(); 13 | quad = &Meshes.get("quad"); 14 | } 15 | 16 | DeferredContainer::~DeferredContainer() { 17 | } 18 | 19 | void DeferredContainer::update(float deltaTime) { 20 | makeTarget(); 21 | ContainerObject::update(deltaTime); 22 | } 23 | 24 | void DeferredContainer::draw() const { 25 | //"The Screen". It may not be actually the screen since a upper container might be postprocessing 26 | const RenderTargetBase* screen = RenderTargetBase::getCurrent(); 27 | 28 | GL_ASSERT(glEnable(GL_DEPTH_TEST)); 29 | GL_ASSERT(glDisable(GL_BLEND)); 30 | 31 | //Deferred pass 32 | Debugger::pushMark("Deferred Pass", "Time spent rendering geometry to the g-buffer"); 33 | drawMode = Deferred; 34 | RenderTargetBase::bind(gBuffer); 35 | GL_ASSERT(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); 36 | ContainerObject::draw(); 37 | Debugger::popMark(); //deferred 38 | 39 | //Shadowmap pass 40 | Debugger::pushMark("Shadowmap Pass", "Time spent rendering geometry to the layered shadowmap"); 41 | glEnable(GL_DEPTH_CLAMP); 42 | drawMode = ShadowMap; 43 | RenderTargetBase::bind(sunTarget); 44 | GL_ASSERT(glClear(GL_DEPTH_BUFFER_BIT)); 45 | ContainerObject::draw(); 46 | glDisable(GL_DEPTH_CLAMP); 47 | Debugger::popMark(); //shadow 48 | 49 | //Transparent shadowmap pass 50 | Debugger::pushMark("Transparent ShadowMap Pass", "Time spent rendering transparent geometry to the layered shadowmap"); 51 | glEnable(GL_DEPTH_CLAMP); 52 | drawMode = TransShadowMap; 53 | RenderTargetBase::bind(sunTargetTrans); 54 | GL_ASSERT(glClear(GL_DEPTH_BUFFER_BIT)); 55 | ContainerObject::draw(); 56 | glDisable(GL_DEPTH_CLAMP); 57 | Debugger::popMark(); //transparent shadow 58 | 59 | Debugger::pushMark("Light Pass", "Time spent rendering deferred lights"); 60 | //bind output texture (screen) 61 | RenderTargetBase::bind(screen); 62 | GL_ASSERT(glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT)); 63 | 64 | //Light pass 65 | GL_ASSERT(glEnable(GL_BLEND)); 66 | GL_ASSERT(glBlendFunc(GL_ONE, GL_ONE)); //additive 67 | GL_ASSERT(glDepthMask(GL_FALSE)); 68 | GL_ASSERT(glDepthFunc(GL_ALWAYS)); 69 | drawMode = Light; 70 | ContainerObject::draw(); 71 | Debugger::popMark(); //lights 72 | 73 | //Ambient+Visibility pass 74 | Debugger::pushMark("Ambient+Visibility Pass", "Time spent rendering ambient light and sunlight contribution to the scene"); 75 | GL_ASSERT(glDepthMask(GL_TRUE)); 76 | const Camera* cam = (Camera*)getGame()->getObjectByName("playerCam"); 77 | Sun* sun = (Sun*)getGame()->getObjectByName("sun"); 78 | glm::mat4 biasMatrix( //gets coords from [-1..1] to [0..1] 79 | 0.5, 0.0, 0.0, 0.0, 80 | 0.0, 0.5, 0.0, 0.0, 81 | 0.0, 0.0, 0.5, 0.0, 82 | 0.5, 0.5, 0.5, 1.0 83 | ); 84 | //compute each of the cascaded cameras's matrices 85 | std::vector depthMVP(NUM_SUN_CASCADES); 86 | for(int i = 0; i < NUM_SUN_CASCADES; ++i) 87 | depthMVP[i] = biasMatrix*(sun->getVPMatrices()[i]*fullTransform); 88 | Programs.get("ambientPass").uniform("MVP")->set(mat4f(1.0f)); 89 | Programs.get("ambientPass").uniform("camMV")->set(cam->getView()*fullTransform); 90 | Programs.get("ambientPass").uniform("color0")->set(getColor0()); 91 | Programs.get("ambientPass").uniform("color1")->set(getColor1()); 92 | Programs.get("ambientPass").uniform("invResolution")->set(vec2f(1.0f/screen->getSize().x, 1.0f/screen->getSize().y)); 93 | Programs.get("ambientPass").uniform("invCamProj")->set(glm::inverse(cam->projection)); 94 | Programs.get("ambientPass").uniform("invCamView")->set(glm::inverse(cam->getView())); 95 | Programs.get("ambientPass").uniform("lightDir")->set(sun->getCam(0)->getForward()); 96 | Programs.get("ambientPass").uniform("worldsize")->set(WORLDSIZE); 97 | Programs.get("ambientPass").uniform("depthMVP")->set(depthMVP); 98 | Programs.get("ambientPass").uniform("depthPlanes")->set(sun->getDepthPlanes()); 99 | Programs.get("ambientPass").uniform("depth")->set(gBuffer.getTexture(RenderTargetBase::DEPTH)); 100 | Programs.get("ambientPass").uniform("sunDepth")->set(sunTarget.getTexture(RenderTargetBase::DEPTH)); 101 | Programs.get("ambientPass").uniform("sunDepthTrans")->set(sunTargetTrans.getTexture(RenderTargetBase::DEPTH)); 102 | quad->draw(Programs.get("ambientPass")); 103 | Debugger::popMark(); //ambient+shadowmap 104 | 105 | //Forward pass 106 | Debugger::pushMark("Forward Pass", "Time spent rendering forward-render stuff"); 107 | GL_ASSERT(glDepthMask(GL_TRUE)); 108 | GL_ASSERT(glDepthFunc(GL_LEQUAL)); 109 | GL_ASSERT(glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)); //forward rendering blending 110 | drawMode = Forward; 111 | ContainerObject::draw(); 112 | Debugger::popMark(); 113 | } 114 | 115 | DeferredContainer::DrawMode DeferredContainer::getMode() const { 116 | return drawMode; 117 | } 118 | 119 | Texture2D *DeferredContainer::getColor0() const { 120 | return gBuffer.getTexture(RenderTargetBase::COLOR0); 121 | } 122 | 123 | Texture2D *DeferredContainer::getColor1() const { 124 | return gBuffer.getTexture(RenderTargetBase::COLOR1); 125 | } 126 | 127 | Texture2D* DeferredContainer::getDepth() const { 128 | return gBuffer.getTexture(RenderTargetBase::DEPTH); 129 | } 130 | 131 | Texture2DArray* DeferredContainer::getTransSunDepth() const { 132 | return sunTargetTrans.getTexture(RenderTargetBase::DEPTH); 133 | } 134 | 135 | Texture2DArray* DeferredContainer::getSunDepth() const { 136 | return sunTarget.getTexture(RenderTargetBase::DEPTH); 137 | } 138 | 139 | void DeferredContainer::makeTarget() { 140 | if(Window::getInstance()->getSize() == gBuffer.getSize()) return; 141 | vec2ui size = Window::getInstance()->getSize(); 142 | GBDepth = Texture2D(size, TextureFormat::DEPTH_COMPONENT32F); 143 | GBDepth.setFilter(GL_NEAREST, GL_NEAREST); 144 | GBColor0 = Texture2D(size, TextureFormat::RGBA16F); 145 | GBColor0.setFilter(GL_NEAREST, GL_NEAREST); 146 | GBColor1 = Texture2D(size, TextureFormat::RGBA16F); 147 | GBColor1.setFilter(GL_NEAREST, GL_NEAREST); 148 | gBuffer = RenderTarget(size.x, size.y); 149 | gBuffer.setTexture(RenderTargetBase::DEPTH, &GBDepth); //Z-BUFFER 150 | gBuffer.setTexture(RenderTargetBase::COLOR0, &GBColor0); //COLOR 151 | gBuffer.setTexture(RenderTargetBase::COLOR1, &GBColor1); //NORMAL, BRIGHTNESS, SPECULAR FACTOR 152 | if(sunTarget.getSize() == vec2ui(0)) { //invalid (this is the first makeTarget() ) 153 | SDepth = Texture2DArray(vec3ui(4096,4096, NUM_SUN_CASCADES), TextureFormat::DEPTH_COMPONENT32F); 154 | SDepth.setFilter(GL_LINEAR, GL_LINEAR); 155 | SDepth.setComparison(GL_GREATER); 156 | sunTarget = RenderTargetLayered(4096, 4096, NUM_SUN_CASCADES); 157 | sunTarget.setTexture(RenderTargetBase::DEPTH, &SDepth); //Z-BUFFER 158 | 159 | SDepthTrans = Texture2DArray(vec3ui(4096,4096, NUM_SUN_CASCADES), TextureFormat::DEPTH_COMPONENT32F); 160 | SDepthTrans.setFilter(GL_LINEAR, GL_LINEAR); 161 | SDepthTrans.setComparison(GL_GREATER); 162 | sunTargetTrans = RenderTargetLayered(4096, 4096, NUM_SUN_CASCADES); 163 | sunTargetTrans.setTexture(RenderTargetBase::DEPTH, &SDepthTrans); //Z-BUFFER 164 | } 165 | } 166 | 167 | -------------------------------------------------------------------------------- /game/SceneMain/world/Cube.cpp: -------------------------------------------------------------------------------- 1 | #include "Cube.hpp" 2 | #include "Chunk.hpp" 3 | 4 | inline bool shouldDraw(Cube::Type c, Cube::Type n) { 5 | VBE_ASSERT_SIMPLE(c != Cube::AIR); 6 | return (c != n && (Cube::getFlag(c, Cube::TRANSPARENT) || Cube::getFlag(n, Cube::TRANSPARENT))); 7 | } 8 | 9 | void Cube::pushCubeToArray(short x, short y, short z, Cube::Type cubeID, const Chunk* c, std::vector& renderData) { 10 | short texY, texX; 11 | std::vector v(6); 12 | 13 | if(shouldDraw(cubeID, (Cube::Type) c->getCube(x, y, z+1))) { // front face 14 | texX = (Cube::TEXTURE_INDEXES[cubeID][0] % (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; // Cube::ATLAS_SIZE/Cube::TEXSIZE = number of textures/row 15 | texY = (Cube::TEXTURE_INDEXES[cubeID][0] / (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; // Cube::ATLAS_SIZE/Cube::TEXSIZE = number of textures/row 16 | 17 | v[0] = Cube::Vert(x+1, y+1, z+1, 0, texX , texY , c->calcLight(x+1, y+1, z+1, 0, 0, 1)); 18 | v[1] = Cube::Vert(x , y+1, z+1, 0, texX+Cube::TEXSIZE, texY , c->calcLight(x , y+1, z+1, 0, 0, 1)); 19 | v[2] = Cube::Vert(x+1, y , z+1, 0, texX , texY+Cube::TEXSIZE, c->calcLight(x+1, y , z+1, 0, 0, 1)); 20 | 21 | v[3] = Cube::Vert(x , y , z+1, 0, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x , y , z+1, 0, 0, 1)); 22 | v[4] = Cube::Vert(x+1, y , z+1, 0, texX , texY+Cube::TEXSIZE, c->calcLight(x+1, y , z+1, 0, 0, 1)); 23 | v[5] = Cube::Vert(x , y+1, z+1, 0, texX+Cube::TEXSIZE, texY , c->calcLight(x , y+1, z+1, 0, 0, 1)); 24 | 25 | if((v[1].l + v[2].l) < (v[0].l + v[3].l)) { 26 | v[2] = v[3]; 27 | v[5] = v[0]; 28 | } 29 | 30 | renderData.insert(renderData.end(), v.begin(), v.end()); 31 | } 32 | if(shouldDraw(cubeID, (Cube::Type) c->getCube(x, y, z-1))) { // back face 33 | texX = (Cube::TEXTURE_INDEXES[cubeID][1] % (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 34 | texY = (Cube::TEXTURE_INDEXES[cubeID][1] / (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 35 | 36 | v[0] = Cube::Vert(x+1, y , z, 1, texX , texY+Cube::TEXSIZE, c->calcLight(x+1, y , z, 0, 0, -1)); 37 | v[1] = Cube::Vert(x , y+1, z, 1, texX+Cube::TEXSIZE, texY , c->calcLight(x , y+1, z, 0, 0, -1)); 38 | v[2] = Cube::Vert(x+1, y+1, z, 1, texX , texY , c->calcLight(x+1, y+1, z, 0, 0, -1)); 39 | 40 | v[3] = Cube::Vert(x , y , z, 1, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x , y , z, 0, 0, -1)); 41 | v[4] = Cube::Vert(x , y+1, z, 1, texX+Cube::TEXSIZE, texY , c->calcLight(x , y+1, z, 0, 0, -1)); 42 | v[5] = Cube::Vert(x+1, y , z, 1, texX , texY+Cube::TEXSIZE, c->calcLight(x+1, y , z, 0, 0, -1)); 43 | 44 | if((v[0].l + v[1].l) < (v[2].l + v[3].l)) { 45 | v[0] = v[3]; 46 | v[4] = v[2]; 47 | } 48 | 49 | renderData.insert(renderData.end(), v.begin(), v.end()); 50 | } 51 | if(shouldDraw(cubeID, (Cube::Type) c->getCube(x+1, y, z))) { // left face 52 | texX = (Cube::TEXTURE_INDEXES[cubeID][2] % (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 53 | texY = (Cube::TEXTURE_INDEXES[cubeID][2] / (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 54 | 55 | v[0] = Cube::Vert(x+1, y , z+1, 2, texX , texY+Cube::TEXSIZE, c->calcLight(x+1, y , z+1, 1, 0, 0)); 56 | v[1] = Cube::Vert(x+1, y , z , 2, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x+1, y , z , 1, 0, 0)); 57 | v[2] = Cube::Vert(x+1, y+1, z+1, 2, texX , texY , c->calcLight(x+1, y+1, z+1, 1, 0, 0)); 58 | 59 | v[3] = Cube::Vert(x+1, y , z , 2, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x+1, y , z , 1, 0, 0)); 60 | v[4] = Cube::Vert(x+1, y+1, z , 2, texX+Cube::TEXSIZE, texY , c->calcLight(x+1, y+1, z , 1, 0, 0)); 61 | v[5] = Cube::Vert(x+1, y+1, z+1, 2, texX , texY , c->calcLight(x+1, y+1, z+1, 1, 0, 0)); 62 | 63 | if((v[1].l + v[2].l) < (v[0].l + v[4].l)) { 64 | v[1] = v[4]; 65 | v[5] = v[0]; 66 | } 67 | 68 | renderData.insert(renderData.end(), v.begin(), v.end()); 69 | } 70 | if(shouldDraw(cubeID, (Cube::Type) c->getCube(x-1, y, z))) { // right face 71 | texX = (Cube::TEXTURE_INDEXES[cubeID][3] % (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 72 | texY = (Cube::TEXTURE_INDEXES[cubeID][3] / (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 73 | 74 | v[0] = Cube::Vert(x , y , z+1, 3, texX , texY+Cube::TEXSIZE, c->calcLight(x , y , z+1, -1, 0, 0)); 75 | v[1] = Cube::Vert(x , y+1, z+1, 3, texX , texY , c->calcLight(x , y+1, z+1, -1, 0, 0)); 76 | v[2] = Cube::Vert(x , y , z , 3, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x , y , z , -1, 0, 0)); 77 | 78 | v[3] = Cube::Vert(x , y+1, z+1, 3, texX , texY , c->calcLight(x , y+1, z+1, -1, 0, 0)); 79 | v[4] = Cube::Vert(x , y+1, z , 3, texX+Cube::TEXSIZE, texY , c->calcLight(x , y+1, z , -1, 0, 0)); 80 | v[5] = Cube::Vert(x , y , z , 3, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x , y , z , -1, 0, 0)); 81 | 82 | if((v[1].l + v[2].l) < (v[0].l + v[4].l)) { 83 | v[1] = v[4]; 84 | v[5] = v[0]; 85 | } 86 | 87 | renderData.insert(renderData.end(), v.begin(), v.end()); 88 | } 89 | if(shouldDraw(cubeID, (Cube::Type) c->getCube(x, y-1, z))) { // bottom face 90 | texX = (Cube::TEXTURE_INDEXES[cubeID][4] % (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 91 | texY = (Cube::TEXTURE_INDEXES[cubeID][4] / (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 92 | 93 | v[0] = Cube::Vert(x+1, y, z , 4, texX+Cube::TEXSIZE, texY , c->calcLight(x+1, y, z , 0, -1, 0)); 94 | v[1] = Cube::Vert(x , y, z+1, 4, texX , texY+Cube::TEXSIZE, c->calcLight(x , y, z+1, 0, -1, 0)); 95 | v[2] = Cube::Vert(x , y, z , 4, texX , texY , c->calcLight(x , y, z , 0, -1, 0)); 96 | 97 | v[3] = Cube::Vert(x+1, y, z , 4, texX+Cube::TEXSIZE, texY , c->calcLight(x+1, y, z , 0, -1, 0)); 98 | v[4] = Cube::Vert(x+1, y, z+1, 4, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x+1, y, z+1, 0, -1, 0)); 99 | v[5] = Cube::Vert(x , y, z+1, 4, texX , texY+Cube::TEXSIZE, c->calcLight(x , y, z+1, 0, -1, 0)); 100 | 101 | if((v[0].l + v[1].l) < (v[2].l + v[4].l)) { 102 | v[1] = v[4]; 103 | v[3] = v[2]; 104 | } 105 | 106 | renderData.insert(renderData.end(), v.begin(), v.end()); 107 | } 108 | if(shouldDraw(cubeID, (Cube::Type) c->getCube(x, y+1, z))) { // bottom face 109 | texX = (Cube::TEXTURE_INDEXES[cubeID][5] % (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 110 | texY = (Cube::TEXTURE_INDEXES[cubeID][5] / (Cube::ATLAS_SIZE/Cube::TEXSIZE))*Cube::TEXSIZE; 111 | 112 | v[0] = Cube::Vert(x+1, y+1, z , 5, texX+Cube::TEXSIZE, texY , c->calcLight(x+1, y+1, z , 0, 1, 0)); 113 | v[1] = Cube::Vert(x , y+1, z , 5, texX , texY , c->calcLight(x , y+1, z , 0, 1, 0)); 114 | v[2] = Cube::Vert(x , y+1, z+1, 5, texX , texY+Cube::TEXSIZE, c->calcLight(x , y+1, z+1, 0, 1, 0)); 115 | 116 | v[3] = Cube::Vert(x+1, y+1, z , 5, texX+Cube::TEXSIZE, texY , c->calcLight(x+1, y+1, z , 0, 1, 0)); 117 | v[4] = Cube::Vert(x , y+1, z+1, 5, texX , texY+Cube::TEXSIZE, c->calcLight(x , y+1, z+1, 0, 1, 0)); 118 | v[5] = Cube::Vert(x+1, y+1, z+1, 5, texX+Cube::TEXSIZE, texY+Cube::TEXSIZE, c->calcLight(x+1, y+1, z+1, 0, 1, 0)); 119 | 120 | if((v[0].l + v[2].l) < (v[1].l + v[5].l)) { 121 | v[2] = v[5]; 122 | v[3] = v[1]; 123 | } 124 | 125 | renderData.insert(renderData.end(), v.begin(), v.end()); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/DecTrees.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DECTREES_HPP 2 | #define DECTREES_HPP 3 | #include "Dec.hpp" 4 | #include "Noise2D.hpp" 5 | 6 | #define DECTREES_MAX_GRID_SIZE 4 7 | 8 | class DecTrees final : public Dec { 9 | public: 10 | DecTrees(std::mt19937* rng, BiomeIntParam minGrid, BiomeIntParam maxGrid, BiomeIntParam gridCutoff, BiomeIntParam dropChance) : 11 | minGrid(minGrid), 12 | maxGrid(maxGrid), 13 | gridCutoff(gridCutoff), 14 | dropChance(dropChance), 15 | gridNoise(rng), 16 | dispNoiseX(rng), 17 | dispNoiseY(rng), 18 | dropNoise(rng), 19 | genNoise(rng) {} 20 | virtual ~DecTrees() {} 21 | 22 | void decorate(ColumnGenerator::ColumnData* col) { 23 | std::vector treePositions = getTreePositions(col); 24 | for(const vec2i& t : treePositions) 25 | genTree(col, t); 26 | } 27 | 28 | void genTree(ColumnGenerator::ColumnData* col, const vec2i& pos) { 29 | // Create a generator for this specific world pos 30 | vec2i wPos = col->pos*CHUNKSIZE + pos; 31 | unsigned int seed = std::numeric_limits::max()*getNoise(&genNoise, wPos.x, wPos.y, 0.0f, 1.0f, 1.0f); 32 | std::mt19937 gen(seed); 33 | 34 | int top = GENERATIONHEIGHT*CHUNKSIZE; 35 | 36 | // Tree height 37 | int tHeight = 8+gen()%8; 38 | 39 | // Find Base 40 | unsigned int base = 0; 41 | for(int i = top-1; i >= 0; --i) 42 | if(col->raw[pos.x*CHUNKSIZE*CHUNKSIZE*GENERATIONHEIGHT+pos.y*CHUNKSIZE*GENERATIONHEIGHT+i] != 0) { 43 | base = i; 44 | break; 45 | } 46 | 47 | struct Branch { 48 | unsigned int height, length, dir; 49 | }; 50 | 51 | // directions a branch for a given dir [n, s, e, w] 52 | // can branch in 53 | static const vec3i bDirs[4][9] = { 54 | { 55 | { 0, 0, 1},{ 1, 0, 1},{-1, 0, 1}, 56 | { 0, 1, 1},{ 1, 1, 1},{-1, 1, 1}, 57 | { 0,-1, 1},{ 1,-1, 1},{-1,-1, 1}, 58 | }, 59 | { 60 | { 0, 0,-1},{ 1, 0,-1},{-1, 0,-1}, 61 | { 0, 1,-1},{ 1, 1,-1},{-1, 1,-1}, 62 | { 0,-1,-1},{ 1,-1,-1},{-1,-1,-1}, 63 | }, 64 | { 65 | { 1, 0, 0},{ 1, 0, 1},{ 1, 0,-1}, 66 | { 1, 1, 0},{ 1, 1, 1},{ 1, 1,-1}, 67 | { 1,-1, 0},{ 1,-1, 1},{ 1,-1,-1}, 68 | }, 69 | { 70 | {-1, 0, 0},{-1, 0, 1},{-1, 0,-1}, 71 | {-1, 1, 0},{-1, 1, 1},{-1, 1,-1}, 72 | {-1,-1, 0},{-1,-1, 1},{-1,-1,-1}, 73 | } 74 | }; 75 | 76 | std::vector branches(2+gen()%5); 77 | std::vector leafCubes; 78 | 79 | for(Branch& b : branches) { 80 | b.height = tHeight-4+gen()%5; 81 | b.length = 1+gen()%5; 82 | b.dir = gen()%4; 83 | } 84 | 85 | // Draw trunk 86 | for(int i = 0; i < tHeight; ++i) { 87 | vec3i c = {pos.x, base+i+1, pos.y}; 88 | col->setDecorationRC(c, 1, 6); 89 | if(tHeight-i < 5) 90 | leafCubes.push_back(c); 91 | } 92 | 93 | // Draw branches 94 | for(const Branch& b : branches) { 95 | vec3i p = {pos.x, base+b.height, pos.y}; 96 | for(unsigned int i = 1; i <= b.length; ++i) { 97 | p += bDirs[b.dir][gen()%9]; 98 | col->setDecorationRC(p, 1, 6); 99 | leafCubes.push_back(p); 100 | } 101 | } 102 | 103 | // Draw leaves 104 | for(const vec3i& c: leafCubes) { 105 | col->setDecorationRC(c+vec3i( 0, 0, 1), 1, 9); 106 | col->setDecorationRC(c+vec3i( 0, 0, -1), 1, 9); 107 | col->setDecorationRC(c+vec3i( 1, 0, 0), 1, 9); 108 | col->setDecorationRC(c+vec3i(-1, 0, 0), 1, 9); 109 | col->setDecorationRC(c+vec3i( 0, -1, 0), 1, 9); 110 | col->setDecorationRC(c+vec3i( 0, 1, 0), 1, 9); 111 | } 112 | for(const vec3i& c: leafCubes) { 113 | for(int dx = -1; dx <= 1; ++dx) 114 | for(int dy = -1; dy <= 1; ++dy) 115 | for(int dz = -1; dz <= 1; ++dz) { 116 | // Randomly drop some of the outer leaves 117 | // to look more detailed 118 | int manhattanDist = abs(dx)+abs(dy)+abs(dz); 119 | if((gen()%3 == 0 && manhattanDist > 1)) 120 | continue; 121 | col->setDecorationRC(c+vec3i(dx, dy, dz), 1, 9); 122 | } 123 | } 124 | } 125 | 126 | std::vector getTreePositions(const ColumnGenerator::ColumnData* col) { 127 | std::vector trees; 128 | vec2i offset = col->pos*CHUNKSIZE; 129 | int margin = (1 << DECTREES_MAX_GRID_SIZE); 130 | for(int i = -margin; i < CHUNKSIZE+margin; ++i) 131 | for(int j = -margin; j < CHUNKSIZE+margin; ++j) { 132 | // Get the generation params for this cube's biome 133 | Biome b = col->biomes[(i+BIOME_MATRIX_MARGIN)*BIOME_MATRIX_SIZE+(j+BIOME_MATRIX_MARGIN)]; 134 | const std::valarray* params = &BIOME_INT_PARAMS[b]; 135 | 136 | // c = coords for this cube 137 | vec2i c = vec2i(i,j) + offset; 138 | 139 | // Early exit if trees disabled 140 | if((*params)[dropChance] == 100) 141 | continue; 142 | 143 | // DECTREES_MIN_GRID_SIZE <= gridsize <= DECTREES_MAX_GRID_SIZE 144 | float gridSizeNoise = getNoise(&gridNoise, c.x, c.y, 0.0f, 1.0f, 60.0f, 4); 145 | int gridsize = 1 << int(floor((*params)[minGrid]+gridSizeNoise*(((*params)[maxGrid]+1)-(*params)[minGrid]))); 146 | 147 | // Gridsize too big 148 | if(gridsize > (1 << int(glm::floor((*params)[gridCutoff])))) 149 | continue; 150 | 151 | // For this gridsize, this is not the adequate 152 | // position to displace 153 | if(c%gridsize != vec2i(0)) 154 | continue; 155 | 156 | // Random drop 157 | if(getNoise(&dropNoise, c.x, c.y, 0.0f, 1.0f, 100.0f, 4) < (*params)[dropChance]*0.01f) 158 | continue; 159 | 160 | // Random displacement 161 | vec2f disp = vec2f( 162 | getNoise(&dispNoiseX, c.x, c.y, -0.5f, 0.5f, 0.1f)*gridsize-1, 163 | getNoise(&dispNoiseY, c.x, c.y, -0.5f, 0.5f, 0.1f)*gridsize-1 164 | ); 165 | 166 | // The final position of the tree 167 | vec2i p = c - offset + vec2i(glm::round(disp)); 168 | 169 | // Only processed if it falls inside this chunk 170 | if(p.x >= 0 && p.x < CHUNKSIZE && p.y >= 0 && p.y < CHUNKSIZE) 171 | trees.push_back(p); 172 | } 173 | return trees; 174 | } 175 | 176 | private: 177 | float getNoise(Noise2D* noise, float x, float y, float min, float max, float scale, int octaves=1) { 178 | return min+(max-min)*noise->octavedGet(x/scale, y/scale, octaves); 179 | } 180 | 181 | BiomeIntParam minGrid; 182 | BiomeIntParam maxGrid; 183 | BiomeIntParam gridCutoff; 184 | BiomeIntParam dropChance; 185 | Noise2D gridNoise; 186 | Noise2D dispNoiseX; 187 | Noise2D dispNoiseY; 188 | Noise2D dropNoise; 189 | Noise2D genNoise; 190 | }; 191 | 192 | #endif // DECTREES_HPP 193 | -------------------------------------------------------------------------------- /game/SceneMain/world/Chunk.cpp: -------------------------------------------------------------------------------- 1 | #include "Chunk.hpp" 2 | #include "World.hpp" 3 | #include "../DeferredContainer.hpp" 4 | #include 5 | #include "../Manager.hpp" 6 | #include "Sun.hpp" 7 | #include "Cube.hpp" 8 | 9 | #pragma GCC diagnostic ignored "-Wchar-subscripts" 10 | 11 | std::vector Chunk::visibilityNodes; 12 | vec3c Chunk::d[6] = { 13 | vec3c(-1,0,0), 14 | vec3c(1,0,0), 15 | vec3c(0,-1,0), 16 | vec3c(0,1,0), 17 | vec3c(0,0,-1), 18 | vec3c(0,0,1) 19 | }; 20 | 21 | Chunk::Chunk(int x, unsigned int y, int z) : 22 | XPOS(x), YPOS(y), ZPOS(z) { 23 | if(Game::i() != nullptr) { 24 | world = (World*)Game::i()->getObjectByName("world"); 25 | renderer = (DeferredContainer*)Game::i()->getObjectByName("deferred"); 26 | } 27 | memset(cubes,0,sizeof(cubes)); 28 | } 29 | 30 | Chunk::~Chunk() { 31 | if(terrainModel != nullptr) delete terrainModel; 32 | if(transModel != nullptr) delete transModel; 33 | } 34 | 35 | void Chunk::initStructures() { 36 | visibilityNodes.clear(); 37 | for(unsigned int x = 0; x < CHUNKSIZE; x++) 38 | for(unsigned int y = 0; y < CHUNKSIZE; y++) 39 | for(unsigned int z = 0; z < CHUNKSIZE; z++) 40 | if(x == 0 || y == 0 || z == 0 || x == CHUNKSIZE-1 || y == CHUNKSIZE-1 || z == CHUNKSIZE-1) 41 | visibilityNodes.push_back(vec3c(x,y,z)); 42 | } 43 | 44 | Chunk::Face Chunk::getOppositeFace(Chunk::Face f) { 45 | switch(f) { 46 | case MINX: return MAXX; 47 | case MINY: return MAXY; 48 | case MINZ: return MAXZ; 49 | case MAXX: return MINX; 50 | case MAXY: return MINY; 51 | case MAXZ: return MINZ; 52 | case ALL_FACES: return ALL_FACES; 53 | } 54 | return ALL_FACES; 55 | } 56 | 57 | void Chunk::update(float deltaTime) { 58 | (void) deltaTime; 59 | drawedByPlayer = false; 60 | } 61 | 62 | void Chunk::draw() const { 63 | if(renderer->getMode() == DeferredContainer::Deferred) { 64 | terrainModel->drawBatched(Programs.get("deferredChunk")); 65 | drawedByPlayer = true; 66 | } 67 | else if(renderer->getMode() == DeferredContainer::ShadowMap) { 68 | terrainModel->drawBatched(Programs.get("depthShader")); 69 | } 70 | else if(renderer->getMode() == DeferredContainer::TransShadowMap) { 71 | transModel->drawBatched(Programs.get("depthShader")); 72 | } 73 | else if(renderer->getMode() == DeferredContainer::Forward) { 74 | transModel->drawBatched(Programs.get("forwardChunk")); 75 | } 76 | } 77 | 78 | #define LIGHTSUM_SIZE (CHUNKSIZE+AO_MAX_RAD+AO_MAX_RAD) 79 | static int lightSum[LIGHTSUM_SIZE][LIGHTSUM_SIZE][LIGHTSUM_SIZE]; 80 | 81 | void Chunk::calcLightSum() { 82 | for(int x = 0; x < LIGHTSUM_SIZE; x++) 83 | for(int y = 0; y < LIGHTSUM_SIZE; y++) 84 | for(int z = 0; z < LIGHTSUM_SIZE; z++) 85 | lightSum[x][y][z] = (getCube(x-AO_MAX_RAD, y-AO_MAX_RAD, z-AO_MAX_RAD) == 0) ? 1 : 0; 86 | for(int x = 1; x < LIGHTSUM_SIZE; x++) 87 | for(int y = 0; y < LIGHTSUM_SIZE; y++) 88 | for(int z = 0; z < LIGHTSUM_SIZE; z++) 89 | lightSum[x][y][z] += lightSum[x-1][y][z]; 90 | for(int x = 0; x < LIGHTSUM_SIZE; x++) 91 | for(int y = 1; y < LIGHTSUM_SIZE; y++) 92 | for(int z = 0; z < LIGHTSUM_SIZE; z++) 93 | lightSum[x][y][z] += lightSum[x][y-1][z]; 94 | for(int x = 0; x < LIGHTSUM_SIZE; x++) 95 | for(int y = 0; y < LIGHTSUM_SIZE; y++) 96 | for(int z = 1; z < LIGHTSUM_SIZE; z++) 97 | lightSum[x][y][z] += lightSum[x][y][z-1]; 98 | } 99 | 100 | int Chunk::sumRect(int x1, int y1, int z1, int x2, int y2, int z2) const { 101 | using std::swap; 102 | if(x1 > x2) swap(x1, x2); 103 | if(y1 > y2) swap(y1, y2); 104 | if(z1 > z2) swap(z1, z2); 105 | 106 | x1--; 107 | y1--; 108 | z1--; 109 | 110 | x1 += AO_MAX_RAD; 111 | y1 += AO_MAX_RAD; 112 | z1 += AO_MAX_RAD; 113 | x2 += AO_MAX_RAD; 114 | y2 += AO_MAX_RAD; 115 | z2 += AO_MAX_RAD; 116 | 117 | return lightSum[x2][y2][z2] 118 | - lightSum[x2][y2][z1] - lightSum[x2][y1][z2] - lightSum[x1][y2][z2] 119 | + lightSum[x2][y1][z1] + lightSum[x1][y2][z1] + lightSum[x1][y1][z2] 120 | - lightSum[x1][y1][z1]; 121 | } 122 | 123 | float Chunk::calcSubLight(int x, int y, int z, int dx, int dy, int dz, int d) const { 124 | int x1 = dx == 1 ? x : x-d; 125 | int x2 = dx == -1 ? x : x+d; 126 | int y1 = dy == 1 ? y : y-d; 127 | int y2 = dy == -1 ? y : y+d; 128 | int z1 = dz == 1 ? z : z-d; 129 | int z2 = dz == -1 ? z : z+d; 130 | 131 | return sumRect(x1, y1, z1, x2-1, y2-1, z2-1) / float(d*d*d*4); 132 | } 133 | 134 | unsigned char Chunk::calcLight(int x, int y, int z, int dx, int dy, int dz) const { 135 | float light = 0; 136 | light += calcSubLight(x, y, z, dx, dy, dz, 1) * 0.5; 137 | light += calcSubLight(x, y, z, dx, dy, dz, 2) * 0.25; 138 | light += calcSubLight(x, y, z, dx, dy, dz, 4) * 0.15; 139 | light += calcSubLight(x, y, z, dx, dy, dz, 8) * 0.1; 140 | light = pow(light, 2); 141 | return light*255; 142 | } 143 | 144 | bool Chunk::isSurrounded() const { 145 | vec3i p = getAbsolutePos(); 146 | for(int x = -1; x <= 1; ++x) 147 | for(int y = -1; y <= 1; ++y) 148 | if(world->outOfBounds(p + vec3i(CHUNKSIZE*x, 0, CHUNKSIZE*y))) return false; 149 | return true; 150 | } 151 | 152 | void Chunk::rebuildMesh() { 153 | if(!needsMeshRebuild) return; 154 | if(!isSurrounded()) return; 155 | needsMeshRebuild = false; 156 | if(terrainModel == nullptr) initMesh(); 157 | std::vector renderData; 158 | renderData.reserve(terrainModel->getVertexCount()); 159 | std::vector transRenderData; 160 | transRenderData.reserve(transModel->getVertexCount()); 161 | boundingBox = AABB(); 162 | calcLightSum(); 163 | for(int z = 0; z < CHUNKSIZE; ++z) 164 | for(int y = 0; y < CHUNKSIZE; ++y) 165 | for(int x = 0; x < CHUNKSIZE; ++x) { 166 | Cube::Type c = (Cube::Type) cubes[x][y][z]; 167 | if(c != Cube::AIR) { 168 | if(Cube::getFlag(c, Cube::TRANSPARENT)) { 169 | unsigned int oldSize = transRenderData.size(); 170 | pushCubeToArray(x, y, z, c, this, transRenderData); 171 | if(transRenderData.size() > oldSize){ 172 | boundingBox.extend(vec3f(x, y, z)); 173 | boundingBox.extend(vec3f(x+1, y+1, z+1)); 174 | } 175 | } 176 | else { 177 | unsigned int oldSize = renderData.size(); 178 | pushCubeToArray(x, y, z, c, this, renderData); 179 | if(renderData.size() > oldSize){ 180 | boundingBox.extend(vec3f(x, y, z)); 181 | boundingBox.extend(vec3f(x+1, y+1, z+1)); 182 | } 183 | } 184 | } 185 | } 186 | terrainModel->setVertexData(&renderData[0], renderData.size()); 187 | transModel->setVertexData(&transRenderData[0], transRenderData.size()); 188 | hasVertices = (!renderData.empty() || !transRenderData.empty()); 189 | rebuildVisibilityGraph(); 190 | } 191 | 192 | bool Chunk::visibilityTest(Chunk::Face exit) const { 193 | if(visibilityGraph.all()) return true; 194 | for(int i = 0; i < 6; ++i) 195 | if(facesVisited.test(i) && visibilityGraph.test(getVisibilityIndex(i, exit))) 196 | return true; 197 | return false; 198 | } 199 | 200 | void Chunk::initMesh() { 201 | std::vector elements = { 202 | Vertex::Attribute("a_position", Vertex::Attribute::UnsignedByte, 3, Vertex::Attribute::ConvertToFloat), 203 | Vertex::Attribute("a_normal", Vertex::Attribute::UnsignedByte, 1), 204 | Vertex::Attribute("a_texCoord", Vertex::Attribute::UnsignedShort, 2, Vertex::Attribute::ConvertToFloat), 205 | Vertex::Attribute("a_light", Vertex::Attribute::UnsignedByte, 1, Vertex::Attribute::ConvertToFloatNormalized) 206 | }; 207 | terrainModel = new MeshBatched(Vertex::Format(elements)); 208 | transModel = new MeshBatched(Vertex::Format(elements)); 209 | boundingBoxModel = &Meshes.get("1x1Cube"); 210 | } 211 | 212 | void Chunk::rebuildVisibilityGraph() { 213 | visibilityGraph.reset(); 214 | bool visited[CHUNKSIZE][CHUNKSIZE][CHUNKSIZE]; 215 | memset(&visited, 0, sizeof(bool)*CHUNKSIZE*CHUNKSIZE*CHUNKSIZE); 216 | for(unsigned int i = 0; i < visibilityNodes.size(); ++i) { 217 | vec3c& src = visibilityNodes[i]; 218 | if(visited[src.x][src.y][src.z] || !Cube::getFlag((Cube::Type)cubes[src.x][src.y][src.z], Cube::TRANSPARENT)) continue; 219 | std::bitset<6> faces(0); //faces the current bfs has touched 220 | std::queue q; 221 | q.push(src); 222 | visited[src.x][src.y][src.z] = true; //visited by any bfs 223 | while(!q.empty()) { 224 | vec3c c = q.front(); q.pop(); //current 225 | if(c.x == 0) faces.set(MINX); 226 | else if(c.x == CHUNKSIZE-1) faces.set(MAXX); 227 | if(c.y == 0) faces.set(MINY); 228 | else if(c.y == CHUNKSIZE-1) faces.set(MAXY); 229 | if(c.z == 0) faces.set(MINZ); 230 | else if(c.z == CHUNKSIZE-1) faces.set(MAXZ); 231 | for(int j = 0; j < 6; ++j) { 232 | vec3c n = c + d[j]; //neighbor 233 | //cull out-of-chunk nodes 234 | if(n.x < 0 || n.y < 0 || n.z < 0 || n.x == CHUNKSIZE || n.y == CHUNKSIZE || n.z == CHUNKSIZE) continue; 235 | 236 | //don't visit already visited nodes 237 | if(visited[n.x][n.y][n.z]) 238 | continue; 239 | //don't visit non-transparent nodes 240 | if(!Cube::getFlag((Cube::Type)cubes[n.x][n.y][n.z], Cube::TRANSPARENT)) 241 | continue; 242 | 243 | visited[n.x][n.y][n.z] = true; 244 | q.push(n); 245 | } 246 | // Early exit if we have reached all faces 247 | if(faces.all()) { 248 | visibilityGraph.set(); 249 | return; 250 | } 251 | } 252 | for(int i = 0; i < 6; ++ i) 253 | for(int j = i+1; j < 6; ++j) { 254 | if(!(faces.test(i) && faces.test(j))) continue; 255 | visibilityGraph.set(getVisibilityIndex(i,j)); 256 | visibilityGraph.set(getVisibilityIndex(j,i)); 257 | } 258 | if(visibilityGraph.all()) return; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/terrainFunctions.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TERRAINFUNCTIONS_HPP 2 | #define TERRAINFUNCTIONS_HPP 3 | #include "commons.hpp" 4 | #include "Noise2D.hpp" 5 | #include "Noise3D.hpp" 6 | #include "Biome.hpp" 7 | 8 | typedef double floatType; //put double here for more precision in world gen. 9 | 10 | class Function3D { //abstract 11 | public: 12 | Function3D() {} 13 | virtual ~Function3D() {} 14 | //x,z are world coords 15 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) = 0; 16 | }; 17 | 18 | class Function2D : public Function3D{ //abstract 19 | public: 20 | Function2D() : Function3D() {} 21 | virtual ~Function2D() {} 22 | //x,z are world coords 23 | virtual floatType getValue(int x, int z, const std::valarray* params) = 0; 24 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) override final { 25 | floatType val = getValue(x, z, params); 26 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 27 | data[y] = val; 28 | } 29 | }; 30 | 31 | class FunctionTerrain {//abstract 32 | public: 33 | FunctionTerrain() {} 34 | virtual ~FunctionTerrain() {} 35 | //x,z are world coords 36 | virtual void fillData(int x, int z, unsigned int* data, const std::valarray* params, const std::valarray* intParams) = 0; 37 | }; 38 | 39 | class Function3DSimplex : public Function3D { 40 | public: 41 | Function3DSimplex(std::mt19937* generator, BiomeParam min, BiomeParam max, float scale) 42 | : noise(generator), min(min), max(max), scale(scale) { 43 | } 44 | ~Function3DSimplex() { 45 | } 46 | void fillData(int x, int z, floatType* data, const std::valarray* params) override { 47 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) { 48 | data[y] = (*params)[min] + ((*params)[max]-(*params)[min])*noise.octavedGet(x/scale, y/scale, z/scale, 4); 49 | } 50 | } 51 | 52 | private: 53 | Noise3D noise; 54 | 55 | BiomeParam min; 56 | BiomeParam max; 57 | float scale; 58 | }; 59 | 60 | class Function3DAdd : public Function3D { 61 | public: 62 | Function3DAdd(const std::vector& operands) : operands(operands) { 63 | } 64 | ~Function3DAdd() { 65 | for(Function3D* f : operands) 66 | delete f; 67 | } 68 | 69 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) override { 70 | std::vector sources; 71 | for(unsigned int i = 1; i < operands.size(); ++i) { 72 | sources.push_back(new floatType[GENERATIONHEIGHT*CHUNKSIZE]); 73 | operands[i]->fillData(x, z, sources[i-1], params); 74 | } 75 | operands[0]->fillData(x, z, data, params); 76 | for(floatType* s : sources) 77 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 78 | data[y] += s[y]; 79 | for(floatType* s : sources) 80 | delete[] s; 81 | } 82 | 83 | private: 84 | std::vector operands; 85 | }; 86 | 87 | class Function3DDiv : public Function3D { 88 | public: 89 | Function3DDiv(Function3D* A, Function3D* B) : funcA(A), funcB(B) { 90 | } 91 | ~Function3DDiv() { 92 | delete funcA; 93 | delete funcB; 94 | } 95 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) override { 96 | floatType* divisor = new floatType[GENERATIONHEIGHT*CHUNKSIZE]; 97 | funcA->fillData(x, z, data, params); 98 | funcB->fillData(x, z, divisor, params); 99 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 100 | data[y] /= divisor[y]; 101 | delete[] divisor; 102 | } 103 | 104 | private: 105 | Function3D* funcA; 106 | Function3D* funcB; 107 | }; 108 | 109 | class Function3DSub : public Function3D { 110 | public: 111 | Function3DSub(Function3D* A, Function3D* B): funcA(A), funcB(B) { 112 | } 113 | ~Function3DSub(){ 114 | delete funcA; 115 | delete funcB; 116 | } 117 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) override { 118 | floatType* substract = new floatType[GENERATIONHEIGHT*CHUNKSIZE]; 119 | funcA->fillData(x, z, data, params); 120 | funcB->fillData(x, z, substract, params); 121 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 122 | data[y] -= substract[y]; 123 | delete[] substract; 124 | } 125 | 126 | private: 127 | Function3D* funcA; 128 | Function3D* funcB; 129 | }; 130 | 131 | class Function3DYcoord : public Function3D { 132 | public: 133 | Function3DYcoord() { 134 | } 135 | ~Function3DYcoord() { 136 | } 137 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) override { 138 | (void) params; 139 | (void) x; 140 | (void) z; 141 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 142 | data[y] = y; 143 | } 144 | }; 145 | 146 | class Function3DHelix : public Function3D { 147 | public: 148 | Function3DHelix(float period, float width, float range, float offset, float yoffset, float zoffset, float tiling, float sin, float sin2) : 149 | period(period), 150 | width(width), 151 | range(range), 152 | offset(offset), 153 | yoffset(yoffset), 154 | zoffset(zoffset), 155 | tiling(tiling), 156 | sin(sin), 157 | sin2(sin2) 158 | { 159 | } 160 | 161 | ~Function3DHelix() { 162 | } 163 | 164 | virtual void fillData(int x, int z, floatType* data, const std::valarray* params) override { 165 | (void) params; 166 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 167 | data[y] = helix(x, y, z); 168 | } 169 | 170 | private: 171 | float helix(float x, float y, float z) { 172 | float mc = x; //direction of the helix 173 | float ma = glm::mod(y-yoffset+sin*glm::sin(mc/sin2),width*tiling); //perp vector 2 174 | float mb = glm::mod(z-zoffset,width*tiling); //perp vector 2 175 | float cx = ma - width - glm::cos((mc+offset)/period) * width; 176 | float cz = mb - width - glm::sin((mc+offset)/period) * width; 177 | if(cx > 0 && cz > 0 && cx < range && cz < range) return 1000; 178 | if(glm::abs(ma-width+2) < 1 && glm::abs(mb-width-3) < 3) return 1000; 179 | return 0; 180 | } 181 | 182 | float period = 5; 183 | float width = 10; 184 | float range = 5; 185 | float offset = 0; 186 | float yoffset = 0; 187 | float zoffset = 0; 188 | float tiling = 5; 189 | float sin = 0; 190 | float sin2 = 60; 191 | }; 192 | 193 | class Function2DConst : public Function2D { 194 | public: 195 | Function2DConst(BiomeParam val) : val(val) { 196 | } 197 | ~Function2DConst() { 198 | } 199 | floatType getValue(int x, int z, const std::valarray* params) override { 200 | (void) x; 201 | (void) z; 202 | return (*params)[val]; 203 | } 204 | 205 | private: 206 | BiomeParam val; 207 | }; 208 | 209 | class Function2DSimplex : public Function2D { 210 | public: 211 | Function2DSimplex(std::mt19937* generator, BiomeParam min, BiomeParam max, float scale) 212 | : noise(generator), min(min), max(max), scale(scale) { 213 | } 214 | ~Function2DSimplex() { 215 | } 216 | floatType getValue(int x, int z, const std::valarray* params) override { 217 | return (*params)[min] + ((*params)[max]-(*params)[min])*noise.octavedGet(x/scale, z/scale, 4); 218 | } 219 | 220 | private: 221 | Noise2D noise; 222 | 223 | BiomeParam min; 224 | BiomeParam max; 225 | float scale; 226 | }; 227 | 228 | class FunctionTerrainHeightmap : public FunctionTerrain { 229 | public: 230 | FunctionTerrainHeightmap(Function2D *source, BiomeIntParam blockID) : 231 | source(source), blockID(blockID) { 232 | } 233 | ~FunctionTerrainHeightmap() { 234 | delete source; 235 | } 236 | virtual void fillData(int x, int z, unsigned int* data, const std::valarray* params, const std::valarray* intParams) override { 237 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) 238 | data[y] = source->getValue(x, z, params) < y? 0 : (*intParams)[blockID]; 239 | } 240 | 241 | private: 242 | Function2D* source; 243 | BiomeIntParam blockID; 244 | }; 245 | 246 | class FunctionTerrainOverlay : public FunctionTerrain { 247 | public: 248 | FunctionTerrainOverlay(FunctionTerrain *source, BiomeIntParam overlayID, BiomeIntParam surfaceID, BiomeParam depth) : 249 | source(source), overlayID(overlayID), surfaceID(surfaceID), depth(depth) { 250 | } 251 | 252 | ~FunctionTerrainOverlay() { 253 | delete source; 254 | } 255 | 256 | virtual void fillData(int x, int z, unsigned int* data, const std::valarray* params, const std::valarray* intParams) override { 257 | source->fillData(x, z, data, params, intParams); 258 | // Early exit 259 | if((*intParams)[surfaceID] == (*intParams)[overlayID]) 260 | return; 261 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE-1; ++y) 262 | if (data[y] == (unsigned int)(*intParams)[surfaceID] && data[y+1] == 0) 263 | for (int a = 0; a < (*params)[depth]; ++a) { 264 | if (y-a >= 0 && data[y-a] == (unsigned int)(*intParams)[surfaceID]) 265 | data[y-a] = (*intParams)[overlayID]; 266 | else 267 | break; 268 | } 269 | } 270 | 271 | private: 272 | FunctionTerrain* source; 273 | BiomeIntParam overlayID; 274 | BiomeIntParam surfaceID; 275 | BiomeParam depth; 276 | }; 277 | 278 | class FunctionTerrainVolume : public FunctionTerrain { 279 | public: 280 | FunctionTerrainVolume(Function3D *source, unsigned int blockID) : 281 | source(source), blockID(blockID) { 282 | } 283 | ~FunctionTerrainVolume() { 284 | delete source; 285 | } 286 | virtual void fillData(int x, int z, unsigned int* data, const std::valarray* params, const std::valarray* intParams) override { 287 | (void) intParams; 288 | floatType* sourceData = new floatType[GENERATIONHEIGHT*CHUNKSIZE]; 289 | source->fillData(x, z, sourceData, params); 290 | for(int y = 0; y < GENERATIONHEIGHT*CHUNKSIZE; ++y) { 291 | if(sourceData[y] <= 0.0f) 292 | data[y] = 0; 293 | else 294 | data[y] = blockID; 295 | } 296 | delete[] sourceData; 297 | } 298 | 299 | private: 300 | Function3D* source; 301 | unsigned int blockID; 302 | }; 303 | 304 | class FunctionTerrainOcean : public FunctionTerrain { 305 | public: 306 | FunctionTerrainOcean(FunctionTerrain *source, BiomeIntParam seaLevel) : 307 | source(source), seaLevel(seaLevel) { 308 | } 309 | 310 | ~FunctionTerrainOcean() { 311 | delete source; 312 | } 313 | 314 | virtual void fillData(int x, int z, unsigned int* data, const std::valarray* params, const std::valarray* intParams) override { 315 | source->fillData(x, z, data, params, intParams); 316 | // Early exit 317 | if((*intParams)[seaLevel] == 0) 318 | return; 319 | for(int y = 0; y < (*intParams)[seaLevel]; ++y) if (data[y] == 0) data[y] = 10; 320 | } 321 | 322 | private: 323 | FunctionTerrain* source; 324 | BiomeIntParam seaLevel; 325 | }; 326 | 327 | #endif // TERRAINFUNCTION_HPP 328 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/biomeFunctions.hpp: -------------------------------------------------------------------------------- 1 | #ifndef BIOMEFUNCTIONS_HPP 2 | #define BIOMEFUNCTIONS_HPP 3 | #include "commons.hpp" 4 | 5 | class BiomeSet final { 6 | public: 7 | BiomeSet(std::set b, bool reverse=false) : b(b), reverse(reverse) {} 8 | ~BiomeSet() {} 9 | 10 | bool test(int biome) const { 11 | return reverse? !b.count(biome) : b.count(biome); 12 | } 13 | private: 14 | const std::set b; 15 | const bool reverse = false; 16 | }; 17 | 18 | class FunctionBiome { //abstract 19 | public: 20 | FunctionBiome(std::mt19937* generator) { 21 | randomOffset = (*generator)(); 22 | } 23 | virtual ~FunctionBiome() { 24 | } 25 | 26 | virtual std::vector getBiomeData(int px, int pz, int sx, int sz) const = 0; 27 | 28 | protected: 29 | int randomOffset = 0; 30 | const long multiplier = 0x5DEECE66Dl; 31 | const long addend = 0xBl; 32 | const long mask = (1l << 48) - 1; 33 | 34 | int randForPos(int max, int px, int pz, int n) const { 35 | long seed = px*341651197+pz*84719323+n*517375 + randomOffset; 36 | 37 | seed = (seed ^ multiplier) & mask; 38 | seed = (seed * multiplier + addend) & mask; 39 | if ((max & -max) == max) { // i.e., max is a power of 2 40 | seed = ((unsigned long) seed) >> (48-31); 41 | return (int)((max * seed) >> 31); 42 | } 43 | 44 | return (int)(((seed % max)+max)%max); 45 | } 46 | }; 47 | 48 | class BiomeConst final : public FunctionBiome { 49 | public: 50 | BiomeConst(std::mt19937* generator, int biome) : FunctionBiome(generator), biome(biome) { 51 | } 52 | 53 | virtual ~BiomeConst() { 54 | } 55 | 56 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 57 | (void) px; 58 | (void) pz; 59 | return std::vector(sx*sz, biome); 60 | } 61 | private: 62 | const int biome = 0; 63 | }; 64 | 65 | class BiomeCombine final : public FunctionBiome { 66 | public: 67 | BiomeCombine(std::mt19937* generator, FunctionBiome* baseA, FunctionBiome* baseB, BiomeSet setA, BiomeSet setB, int replace=-1) 68 | : FunctionBiome(generator), baseA(baseA), baseB(baseB), setA(setA), setB(setB), replace(replace) { 69 | } 70 | virtual ~BiomeCombine() { 71 | } 72 | 73 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 74 | std::vector dataA = baseA->getBiomeData(px, pz, sx, sz); 75 | std::vector dataB = baseB->getBiomeData(px, pz, sx, sz); 76 | 77 | for(int i = 0; i < sx*sz; i++) { 78 | if(setA.test(dataA[i]) && setB.test(dataB[i])) { 79 | if(replace == -1) dataA[i] = dataB[i]; 80 | else dataA[i] = replace; 81 | } 82 | } 83 | 84 | return dataA; 85 | } 86 | private: 87 | const FunctionBiome* baseA = nullptr; 88 | const FunctionBiome* baseB = nullptr; 89 | const BiomeSet setA; 90 | const BiomeSet setB; 91 | const int replace = -1; 92 | }; 93 | 94 | class BiomeIsland final : public FunctionBiome { 95 | public: 96 | BiomeIsland(std::mt19937* generator, FunctionBiome* base) : FunctionBiome(generator), base(base) { 97 | } 98 | virtual ~BiomeIsland() { 99 | } 100 | 101 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 102 | int px2 = px - 1; 103 | int pz2 = pz - 1; 104 | int sx2 = sx + 2; 105 | int sz2 = sz + 2; 106 | std::vector baseData = base->getBiomeData(px2, pz2, sx2, sz2); 107 | std::vector data(sx * sz); 108 | 109 | for (int z = 0; z < sz; ++z) { 110 | for (int x = 0; x < sx; ++x) { 111 | int topLeft = baseData[x + 0 + (z + 0) * sx2]; 112 | int topRight = baseData[x + 2 + (z + 0) * sx2]; 113 | int bottomLeft = baseData[x + 0 + (z + 2) * sx2]; 114 | int bottomRight = baseData[x + 2 + (z + 2) * sx2]; 115 | int center = baseData[x + 1 + (z + 1) * sx2]; 116 | 117 | //ocean surrounded by some other biome 118 | if (center == 0 && (topLeft != 0 || topRight != 0 || bottomLeft != 0 || bottomRight != 0)) { 119 | int max = 1; 120 | int r = 1; 121 | 122 | //Choose randomly from the 4 values one that's not 0 123 | if ( topLeft != 0 && randForPos(max++, x + px, z + pz, 0) == 0) r = topLeft; 124 | if ( topRight != 0 && randForPos(max++, x + px, z + pz, 1) == 0) r = topRight; 125 | if ( bottomLeft != 0 && randForPos(max++, x + px, z + pz, 2) == 0) r = bottomLeft; 126 | if (bottomRight != 0 && randForPos(max++, x + px, z + pz, 3) == 0) r = bottomRight; 127 | 128 | if (randForPos(3, x + px, z + pz, 4) == 0) 129 | data[x + z * sx] = r; 130 | else 131 | data[x + z * sx] = 0; 132 | } 133 | //some other biome by the ocean 134 | else if (center > 0 && (topLeft == 0 || topRight == 0 || bottomLeft == 0 || bottomRight == 0)) { 135 | //if rand()%5 and id not icePlains 136 | if (randForPos(5, x + px, z + pz, 5) == 0) 137 | data[x + z * sx] = 0; 138 | else 139 | data[x + z * sx] = center; 140 | } 141 | //inland biome or full ocean 142 | else 143 | data[x + z * sx] = center; 144 | } 145 | } 146 | 147 | return data; 148 | } 149 | private: 150 | const FunctionBiome* base = nullptr; 151 | }; 152 | 153 | class BiomeOutline final : public FunctionBiome { 154 | public: 155 | BiomeOutline(std::mt19937* generator, FunctionBiome* base, BiomeSet biomes, BiomeSet nextTo, int outlineBiome, bool outer = false) : 156 | FunctionBiome(generator), base(base), biomes(biomes), nextTo(nextTo), outlineBiome(outlineBiome), outer(outer) { 157 | VBE_ASSERT_SIMPLE(base != nullptr); 158 | } 159 | 160 | virtual ~BiomeOutline() { 161 | } 162 | 163 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 164 | std::vector baseData = base->getBiomeData(px - 1, pz - 1, sx + 2, sz + 2); 165 | std::vector result(sx * sz); 166 | 167 | for (int z = 0; z < sz; ++z) { 168 | for (int x = 0; x < sx; ++x) { 169 | int center = baseData[x + 1 + (z + 1) * (sx + 2)]; 170 | int bottom = baseData[x + 1 + (z + 1 - 1) * (sx + 2)]; 171 | int right = baseData[x + 1 + 1 + (z + 1) * (sx + 2)]; 172 | int left = baseData[x + 1 - 1 + (z + 1) * (sx + 2)]; 173 | int top = baseData[x + 1 + (z + 1 + 1) * (sx + 2)]; 174 | 175 | if(outer) { 176 | if (!biomes.test(center) 177 | && (biomes.test(bottom) || biomes.test(right) || biomes.test(left) || biomes.test(top)) 178 | && nextTo.test(bottom) && nextTo.test(right) && nextTo.test(left) && nextTo.test(top)) 179 | result[x+z*sx] = outlineBiome; 180 | else 181 | result[x+z*sx] = center; 182 | } 183 | else { 184 | if (biomes.test(center) 185 | && (bottom != center || right != center || left != center || top != center) 186 | && nextTo.test(bottom) && nextTo.test(right) && nextTo.test(left) && nextTo.test(top)) 187 | result[x+z*sx] = outlineBiome; 188 | else 189 | result[x+z*sx] = center; 190 | } 191 | } 192 | } 193 | return result; 194 | } 195 | 196 | private: 197 | const FunctionBiome* base = nullptr; 198 | const BiomeSet biomes; 199 | const BiomeSet nextTo; 200 | const int outlineBiome = 0; 201 | const bool outer = false; 202 | }; 203 | 204 | class BiomeReplace final : public FunctionBiome { 205 | public: 206 | BiomeReplace(std::mt19937* generator, FunctionBiome* base, BiomeSet from, std::vector> to, bool noEdges = false) : 207 | FunctionBiome(generator), base(base), from(from), to(to), noEdges(noEdges) { 208 | VBE_ASSERT_SIMPLE(base != nullptr); 209 | VBE_ASSERT_SIMPLE(!to.empty()); 210 | } 211 | BiomeReplace(std::mt19937* generator, FunctionBiome* base, BiomeSet from, int to, int prob, bool noEdges = false) : 212 | FunctionBiome(generator), base(base), from(from), to({{to, 1}, {-1, prob-1}}), noEdges(noEdges) { 213 | VBE_ASSERT_SIMPLE(prob > 1); 214 | VBE_ASSERT_SIMPLE(base != nullptr); 215 | } 216 | 217 | virtual ~BiomeReplace() { 218 | } 219 | 220 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 221 | int s = 0; 222 | for(const std::pair& t : to) 223 | s += t.second; 224 | 225 | if(noEdges) { 226 | std::vector baseData = base->getBiomeData(px - 1, pz - 1, sx + 2, sz + 2); 227 | std::vector result(sx * sz); 228 | 229 | for (int z = 0; z < sz; ++z) { 230 | for (int x = 0; x < sx; ++x) { 231 | int old = baseData[x + 1 + (z + 1) * (sx + 2)]; 232 | int val = old; 233 | if(from.test(val)) { 234 | int r = randForPos(s, px + x, pz + z, 0); 235 | for(const std::pair& p : to) { 236 | r -= p.second; 237 | if(r < 0) { 238 | if(p.first != -1) 239 | val = p.first; 240 | break; 241 | } 242 | } 243 | } 244 | 245 | if (val == old) 246 | result[x + z * sx] = old; 247 | else { 248 | int top = baseData[x + 1 + (z + 1 - 1) * (sx + 2)]; 249 | int right = baseData[x + 1 + 1 + (z + 1) * (sx + 2)]; 250 | int left = baseData[x + 1 - 1 + (z + 1) * (sx + 2)]; 251 | int down = baseData[x + 1 + (z + 1 + 1) * (sx + 2)]; 252 | 253 | if (top == old && right == old && left == old && down == old) 254 | result[x + z * sx] = val; 255 | else 256 | result[x + z * sx] = old; 257 | } 258 | } 259 | } 260 | return result; 261 | } 262 | 263 | std::vector result = base->getBiomeData(px, pz, sx, sz); 264 | for (int z = 0; z < sz; ++z) 265 | for (int x = 0; x < sx; ++x) 266 | if(from.test(result[x + z * sx])) { 267 | int r = randForPos(s, px + x, pz + z, 0); 268 | for(const std::pair& p : to) { 269 | r -= p.second; 270 | if(r < 0) { 271 | if(p.first != -1) 272 | result[x + z * sx] = p.first; 273 | break; 274 | } 275 | } 276 | } 277 | 278 | return result ; 279 | } 280 | 281 | private: 282 | const FunctionBiome* base = nullptr; 283 | const BiomeSet from; 284 | const std::vector> to; 285 | const bool noEdges = false; 286 | }; 287 | 288 | class BiomeSmooth final : public FunctionBiome { 289 | public: 290 | BiomeSmooth(std::mt19937* generator, FunctionBiome* base) : FunctionBiome(generator), base(base) { 291 | } 292 | virtual ~BiomeSmooth() { 293 | } 294 | 295 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 296 | int px2 = px - 1; 297 | int pz2 = pz - 1; 298 | int sx2 = sx + 2; 299 | int sz2 = sz + 2; 300 | std::vector baseData = base->getBiomeData(px2, pz2, sx2, sz2); 301 | std::vector data(sx * sz); 302 | 303 | for (int z = 0; z < sz; ++z) { 304 | for (int x = 0; x < sx; ++x) { 305 | int left = baseData[x + 0 + (z + 1) * sx2]; 306 | int right = baseData[x + 2 + (z + 1) * sx2]; 307 | int top = baseData[x + 1 + (z + 0) * sx2]; 308 | int bottom = baseData[x + 1 + (z + 2) * sx2]; 309 | int center = baseData[x + 1 + (z + 1) * sx2]; 310 | 311 | if (left == right && top == bottom) { 312 | if (randForPos(2, x + px, z + pz, 0) == 0) 313 | center = left; 314 | else 315 | center = top; 316 | } 317 | else { 318 | if (left == right) 319 | center = left; 320 | if (top == bottom) 321 | center = top; 322 | } 323 | data[x + z * sx] = center; 324 | } 325 | } 326 | return data; 327 | } 328 | 329 | private: 330 | const FunctionBiome* base = nullptr; 331 | }; 332 | 333 | class BiomeZoom final : public FunctionBiome { 334 | public: 335 | BiomeZoom(std::mt19937* generator, FunctionBiome* base, bool fuzzy=false) : FunctionBiome(generator), base(base), fuzzy(fuzzy) { 336 | VBE_ASSERT_SIMPLE(base != nullptr); 337 | } 338 | 339 | virtual ~BiomeZoom() { 340 | } 341 | 342 | std::vector getBiomeData(int px, int pz, int sx, int sz) const override { 343 | int baseX = px >> 1; 344 | int baseZ = pz >> 1; 345 | int baseSizeX = (sx >> 1) + 3; 346 | int baseSizeZ = (sz >> 1) + 3; 347 | int zoomedSizeX = baseSizeX * 2; 348 | int zoomedSizeZ = baseSizeZ * 2; 349 | std::vector baseData = base->getBiomeData(baseX, baseZ, baseSizeX, baseSizeZ); 350 | std::vector zoomedData = std::vector(zoomedSizeX * zoomedSizeZ); 351 | 352 | for (int z = 0; z < baseSizeZ - 1; ++z) { 353 | int currZoomedZ = (z << 1) * zoomedSizeX; 354 | int topleft = baseData[0 + (z + 0) * baseSizeX]; //baseData[z][0] 355 | int bottomleft = baseData[0 + (z + 1) * baseSizeX]; //baseData[z+1][0] 356 | 357 | for (int x = 0; x < baseSizeX - 1; ++x) { 358 | int topright = baseData[x + 1 + (z + 0) * baseSizeX]; //baseData[z][x+1] 359 | int bottomright = baseData[x + 1 + (z + 1) * baseSizeX]; //baseData[z+1][x+1] 360 | // Set topleft corner 361 | zoomedData[currZoomedZ] = topleft; 362 | // Set bottomleft corner 363 | zoomedData[currZoomedZ++ + zoomedSizeX] = choose(topleft, bottomleft, x + baseX, z + baseZ, 0); 364 | // Set topright corner 365 | zoomedData[currZoomedZ] = choose(topleft, topright, x + baseX, z + baseZ, 1); 366 | // Set bottomRight corner 367 | if(fuzzy) 368 | zoomedData[currZoomedZ++ + zoomedSizeX] = choose(topleft, topright, bottomleft, bottomright, x + baseX, z + baseZ, 2); 369 | else 370 | zoomedData[currZoomedZ++ + zoomedSizeX] = modeOrRandom(topleft, topright, bottomleft, bottomright, x + baseX, z + baseZ, 2); 371 | topleft = topright; 372 | bottomleft = bottomright; 373 | } 374 | } 375 | 376 | std::vector result = std::vector(sx * sz); 377 | 378 | for (int z = 0; z < sz; ++z) { 379 | std::vector::const_iterator start = zoomedData.begin() + (z + (pz & 1)) * zoomedSizeX + (px & 1); 380 | result.insert( 381 | result.begin() + (z*sx), 382 | start, 383 | start+sx 384 | ); 385 | } 386 | 387 | return result; 388 | } 389 | 390 | private: 391 | int choose(int a, int b, int x, int z, int n) const { 392 | return randForPos(2, x, z, n) == 0 ? a : b; 393 | } 394 | 395 | int choose(int a, int b, int c, int d, int x, int z, int n) const { 396 | int r = randForPos(4, x, z, n); 397 | return r == 0 ? a : (r == 1 ? b : (r == 2 ? c : d)); 398 | } 399 | 400 | int modeOrRandom(int a, int b, int c, int d, int x, int z, int n) const { 401 | if (b == c && c == d) return b; 402 | else if (a == b && a == c) return a; 403 | else if (a == b && a == d) return a; 404 | else if (a == c && a == d) return a; 405 | else if (a == b && c != d) return a; 406 | else if (a == c && b != d) return a; 407 | else if (a == d && b != c) return a; 408 | else if (b == a && c != d) return b; 409 | else if (b == c && a != d) return b; 410 | else if (b == d && a != c) return b; 411 | else if (c == a && b != d) return c; 412 | else if (c == b && a != d) return c; 413 | else if (c == d && a != b) return c; 414 | else if (d == a && b != c) return c; 415 | else if (d == b && a != c) return c; 416 | else if (d == c && a != b) return c; 417 | else { 418 | int r = this->randForPos(4, x, z, n); 419 | return r == 0 ? a : (r == 1 ? b : (r == 2 ? c : d)); 420 | } 421 | } 422 | 423 | const FunctionBiome* base = nullptr; 424 | const bool fuzzy = false; 425 | }; 426 | 427 | #endif // BIOMEFUNCTIONS_HPP 428 | -------------------------------------------------------------------------------- /game/SceneMain/world/generator/ColumnGenerator.cpp: -------------------------------------------------------------------------------- 1 | #include "ColumnGenerator.hpp" 2 | #include "../Column.hpp" 3 | #include "../Chunk.hpp" 4 | #include "TaskPool.hpp" 5 | #include "DecTrees.hpp" 6 | 7 | #define NWORKERS_GENERATING 4 8 | #define NWORKERS_DECORATING 1 9 | #define NWORKERS_BUILDING 1 10 | #define NWORKERS_KILLING 1 11 | #define BIOME_RADIUS 32 12 | 13 | ColumnGenerator::ColumnGenerator(int seed) { 14 | // Create locks 15 | loadedLock = std::unique_lock(loadedMutex, std::defer_lock); 16 | doneLock = std::unique_lock(doneMutex, std::defer_lock); 17 | 18 | // Create pools 19 | generatePool = new TaskPool(NWORKERS_GENERATING); 20 | decoratePool = new TaskPool(NWORKERS_DECORATING); 21 | buildPool = new TaskPool(NWORKERS_BUILDING); 22 | killPool = new TaskPool(NWORKERS_KILLING); 23 | 24 | // Create function tree for creation 25 | generator.seed(seed); 26 | Function2DSimplex* simplex21 = new Function2DSimplex(&generator, SIMPLEX_LOW, SIMPLEX_HIGH, 100.0f); 27 | FunctionTerrainHeightmap* terrain1 = new FunctionTerrainHeightmap(simplex21, MAIN_TERRAIN); 28 | FunctionTerrainOverlay* over1 = new FunctionTerrainOverlay(terrain1, LOW_SURFACE, MAIN_TERRAIN, LOW_SURFACE_DEPTH); 29 | FunctionTerrainOverlay* over2 = new FunctionTerrainOverlay(over1, SURFACE, LOW_SURFACE, SURFACE_DEPTH); 30 | FunctionTerrainOcean* ocean = new FunctionTerrainOcean(over2, SEA_LEVEL); 31 | terrainEntry = ocean; 32 | 33 | // Create function tree for biomes 34 | FunctionBiome* b = new BiomeConst(&generator, OCEAN); 35 | b = new BiomeReplace(&generator, b, BiomeSet({OCEAN}), PLAINS, 2, false); 36 | b = new BiomeZoom(&generator, b, true); 37 | b = new BiomeSmooth(&generator, b); 38 | b = new BiomeZoom(&generator, b, true); 39 | b = new BiomeSmooth(&generator, b); 40 | b = new BiomeZoom(&generator, b, true); 41 | b = new BiomeSmooth(&generator, b); 42 | b = new BiomeZoom(&generator, b, true); 43 | b = new BiomeSmooth(&generator, b); 44 | b = new BiomeZoom(&generator, b, true); 45 | b = new BiomeSmooth(&generator, b); 46 | b = new BiomeZoom(&generator, b, true); 47 | b = new BiomeSmooth(&generator, b); 48 | b = new BiomeZoom(&generator, b, true); 49 | b = new BiomeSmooth(&generator, b); 50 | b = new BiomeSmooth(&generator, b); 51 | biomeEntry = b; 52 | 53 | //// Testing, flat grass at y=70 54 | //if(0) { 55 | // Function3DYcoord* c = new Function3DYcoord(); 56 | // Function2DConst* co = new Function2DConst(70); 57 | // Function3DSub* s = new Function3DSub(co, c); 58 | // FunctionTerrainVolume* v1 = new FunctionTerrainVolume(s, 2); 59 | // FunctionTerrainOverlay* o1 = new FunctionTerrainOverlay(v1,1,2,4); 60 | // FunctionTerrainOverlay* o2 = new FunctionTerrainOverlay(o1,3,1,1); 61 | // terrainEntry = o2; 62 | //} 63 | 64 | // Create decorators 65 | decorators.push_back(new DecTrees(&generator, TREE_MIN_GRID, TREE_MAX_GRID, TREE_CUTOFF, TREE_DROP_CHANCE)); 66 | } 67 | 68 | ColumnGenerator::~ColumnGenerator() { 69 | generatePool->discard(); 70 | delete generatePool; 71 | delete decoratePool; 72 | delete buildPool; 73 | delete killPool; 74 | delete terrainEntry; 75 | delete biomeEntry; 76 | while(!done.empty()) { 77 | delete done.front(); 78 | done.pop(); 79 | } 80 | for(auto d : decorators) 81 | delete d; 82 | } 83 | 84 | void ColumnGenerator::update() { 85 | VBE_ASSERT_SIMPLE(locked()); 86 | killPool->discard(); 87 | buildPool->discard(); 88 | decoratePool->discard(); 89 | std::list toDelete; 90 | for(const std::pair& kv : loaded) { 91 | if(!inPlayerArea(kv.first) && kv.second->canDelete()) { 92 | queueDelete(kv.first); 93 | continue; 94 | } 95 | switch(kv.second->state) { 96 | case ColumnData::Loading: 97 | case ColumnData::Building: 98 | case ColumnData::Deleting: 99 | case ColumnData::Built: 100 | break; 101 | case ColumnData::Raw: 102 | queueDecorate(kv.first); 103 | break; 104 | case ColumnData::Decorated: 105 | queueBuild(kv.first); 106 | break; 107 | case ColumnData::Deleted: 108 | toDelete.push_back(kv.first); 109 | break; 110 | default: 111 | break; 112 | } 113 | } 114 | for(const vec2i& p : toDelete) { 115 | ColumnData* cp = loaded.at(p); 116 | loaded.erase(p); 117 | if(cp->col != nullptr) delete cp->col; 118 | if(cp->raw != nullptr) delete[] cp->raw; 119 | if(cp->biomes != nullptr) delete[] cp->biomes; 120 | delete cp; 121 | } 122 | } 123 | 124 | void ColumnGenerator::queueLoad(vec2i colPos) { 125 | generatePool->enqueue([this, colPos]() { 126 | ColumnData* colData = nullptr; 127 | 128 | // Grab the job. 129 | { 130 | std::lock_guard lock(loadedMutex); 131 | if(loaded.find(colPos) != loaded.end()) return; 132 | colData = new ColumnData(); 133 | colData->pos = colPos; 134 | ++colData->refCount; 135 | loaded.insert(std::pair(colPos, colData)); 136 | } 137 | 138 | // Generate biomes 139 | colData->biomes = new Biome[BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE]; 140 | std::vector biomevec = biomeEntry->getBiomeData(colPos.x*CHUNKSIZE-BIOME_MATRIX_MARGIN, colPos.y*CHUNKSIZE-BIOME_MATRIX_MARGIN, BIOME_MATRIX_SIZE, BIOME_MATRIX_SIZE); 141 | for(int x = 0; x < BIOME_MATRIX_SIZE; ++x) 142 | for(int z = 0; z < BIOME_MATRIX_SIZE; ++z) 143 | colData->biomes[x*BIOME_MATRIX_SIZE+z] = Biome(biomevec[z*BIOME_MATRIX_SIZE+x]); 144 | 145 | static thread_local int* biomeSums = new int[BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE*NUM_BIOMES]; 146 | 147 | // Two-pass 2D biome cumsum 148 | for(int b = 0; b < NUM_BIOMES; ++b) 149 | for(int x = 0; x < BIOME_MATRIX_SIZE; ++x) 150 | for(int z = 0; z < BIOME_MATRIX_SIZE; ++z) { 151 | biomeSums[b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE+x*BIOME_MATRIX_SIZE+z] = colData->biomes[x*BIOME_MATRIX_SIZE+z] == Biome(b)? 1 : 0; 152 | if(x > 0) biomeSums[b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE+x*BIOME_MATRIX_SIZE+z] += biomeSums[b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE+(x-1)*BIOME_MATRIX_SIZE+z]; 153 | } 154 | for(int b = 0; b < NUM_BIOMES; ++b) 155 | for(int x = 0; x < BIOME_MATRIX_SIZE; ++x) 156 | for(int z = 1; z < BIOME_MATRIX_SIZE; ++z) 157 | biomeSums[b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE+x*BIOME_MATRIX_SIZE+z] += biomeSums[b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE+x*BIOME_MATRIX_SIZE+(z-1)]; 158 | 159 | // Generate cube data 160 | unsigned int* raw = new unsigned int[CHUNKSIZE*CHUNKSIZE*CHUNKSIZE*GENERATIONHEIGHT]; 161 | for(int x = 0; x < CHUNKSIZE; ++x) 162 | for(int z = 0; z < CHUNKSIZE; ++z) { 163 | // Calculate average 164 | std::valarray par; 165 | par.resize(NUM_BIOME_PARAMS, 0.0f); 166 | int total = 0; 167 | for(int b = 0; b < NUM_BIOMES; ++b) { 168 | // + Top right 169 | int sum = biomeSums[ 170 | b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE 171 | +(x+BIOME_MATRIX_MARGIN+BIOME_RADIUS)*BIOME_MATRIX_SIZE 172 | +(z+BIOME_MATRIX_MARGIN+BIOME_RADIUS) 173 | ]; 174 | // - Bottom right 175 | sum -= biomeSums[ 176 | b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE 177 | +(x+BIOME_MATRIX_MARGIN+BIOME_RADIUS)*BIOME_MATRIX_SIZE 178 | +(z+BIOME_MATRIX_MARGIN-BIOME_RADIUS) 179 | ]; 180 | // - Top left 181 | sum -= biomeSums[ 182 | b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE 183 | +(x+BIOME_MATRIX_MARGIN-BIOME_RADIUS)*BIOME_MATRIX_SIZE 184 | +(z+BIOME_MATRIX_MARGIN+BIOME_RADIUS) 185 | ]; 186 | // + Bottom left 187 | sum += biomeSums[ 188 | b*BIOME_MATRIX_SIZE*BIOME_MATRIX_SIZE 189 | +(x+BIOME_MATRIX_MARGIN-BIOME_RADIUS)*BIOME_MATRIX_SIZE 190 | +(z+BIOME_MATRIX_MARGIN-BIOME_RADIUS) 191 | ]; 192 | total += sum; 193 | par += BIOME_PARAMS[b]*float(sum); 194 | } 195 | par /= total; 196 | // Generate terrain 197 | terrainEntry->fillData( 198 | colPos.x*CHUNKSIZE+x, 199 | colPos.y*CHUNKSIZE+z, 200 | &raw[x*CHUNKSIZE*CHUNKSIZE*GENERATIONHEIGHT + z*CHUNKSIZE*GENERATIONHEIGHT], 201 | &par, 202 | &BIOME_INT_PARAMS[colData->biomes[(BIOME_MATRIX_MARGIN+x)*BIOME_MATRIX_SIZE+(BIOME_MATRIX_MARGIN+z)]] 203 | ); 204 | } 205 | 206 | // Finished generating. Mark it as Raw. 207 | { 208 | std::lock_guard lock(loadedMutex); 209 | VBE_ASSERT_SIMPLE(loaded.find(colPos) != loaded.end()); 210 | VBE_ASSERT_SIMPLE(loaded.at(colPos) == colData); 211 | VBE_ASSERT_SIMPLE(colData->state == ColumnData::Loading); 212 | colData->state = ColumnData::Raw; 213 | colData->raw = raw; 214 | --colData->refCount; 215 | } 216 | }); 217 | } 218 | 219 | void ColumnGenerator::queueDecorate(vec2i colPos) { 220 | buildPool->enqueue([this, colPos]() { 221 | // Grab the job for Decorating 222 | ColumnData* colData = nullptr; 223 | bool onMyOwn = false; 224 | { 225 | std::lock_guard lock(loadedMutex); 226 | auto it = loaded.find(colPos); 227 | if(it == loaded.end()) return; 228 | colData = it->second; 229 | if(colData->state != ColumnData::Raw) return; 230 | for(int x = -1; x <= 1; ++x) 231 | for(int y = -1; y <= 1; ++y) { 232 | auto it2 = loaded.find(colPos+vec2i(x, y)); 233 | // This chunk is in the edge of the currently loaded world. 234 | // Wait for neighboring chunks to be loaded/created 235 | if(it2 == loaded.end()) 236 | return; 237 | // This chunk has a neighbor who is being created or destroyed 238 | if(it2->second->state < ColumnData::Raw || 239 | it2->second->state > ColumnData::Built) 240 | return; 241 | // Special case (shouldn't happen unless you tamper with weird 242 | // savegames or have manually deleted chunks. What happens here 243 | // is that a chunk is being loaded from scratch with a fully loaded 244 | // (and built) chunk by it's side. Since decorating is a chunk-to-chunk 245 | // process (each chunk needs the neighboring input to proceed), if we just 246 | // go on we'll be losing the potential decorations from the already-built 247 | // chunk. Hence, we must do it differently: Generate this chunk by generating 248 | // all 8 surrounding chunks and decorating them, then apply this chunk's 249 | // decorations to the real neighbors who are too being decorated (skip the 250 | // built ones) 251 | if(it2->second->state > ColumnData::Decorated) 252 | onMyOwn = true; 253 | } 254 | for(int x = -1; x <= 1; ++x) 255 | for(int y = -1; y <= 1; ++y) 256 | ++loaded.at(colPos+vec2i(x, y))->refCount; 257 | colData->state = ColumnData::Decorating; 258 | } 259 | 260 | // Regenerating broken chunks is not supported yet. Try not to mess up your saves ;) 261 | VBE_ASSERT(!onMyOwn, "Unsupported standalone chunk regen"); 262 | (void) onMyOwn; 263 | 264 | // Decorate here. You can add any stuff you whant by calling setDecorationRC on colData. 265 | // Decorations can be placed anywhere on the nearby world space: That is: from 266 | // [-16, 31] on x and z, [0, min(32767, CHUNKSIZE*GENERATIONHEIGHT)] on y. If these 267 | // decorations fall out of this chunk (outside of the [0, 15] range for x or z) they will 268 | // be pushed onto the corresponding chunk at the end of the decoration stage. 269 | 270 | // Testing decorations for spawn chunk 271 | if(colPos == vec2i(0,0)) { 272 | for(short i = -2; i < 2; ++i) 273 | for(short y = 0; y < 128; ++y) { 274 | colData->setDecorationRC(i , y, 8, 0, 5); 275 | colData->setDecorationRC(8 , y, i, 0, 5); 276 | colData->setDecorationRC(16+i, y, 8, 0, 5); 277 | colData->setDecorationRC(8 , y, 16+i, 0, 5); 278 | } 279 | for(short y = 0; y < 256; ++y) 280 | colData->setDecorationRC(8, y, 8, 0, 6); 281 | } 282 | 283 | for(Dec* d : decorators) 284 | d->decorate(colData); 285 | 286 | // Finished decorating. Mark it as decorated. 287 | { 288 | std::lock_guard lock(loadedMutex); 289 | VBE_ASSERT_SIMPLE(loaded.find(colPos) != loaded.end()); 290 | VBE_ASSERT_SIMPLE(loaded.at(colPos) == colData); 291 | VBE_ASSERT_SIMPLE(colData->state == ColumnData::Decorating); 292 | colData->state = ColumnData::Decorated; 293 | for(int x = -1; x <= 1; ++x) 294 | for(int y = -1; y <= 1; ++y) { 295 | ColumnData* nd = loaded.at(colPos+vec2i(x, y)); 296 | --nd->refCount; 297 | // If this is the current chunk or the chunk is already fully decorated, skip 298 | if(nd == colData || nd->state > ColumnData::Decorated) 299 | continue; 300 | // Push all decorations onto neighboring chunks so that they can use them 301 | // on their building step. 302 | nd->decOut.insert(colData->decIn[x+1][y+1].begin(), colData->decIn[x+1][y+1].end()); 303 | } 304 | } 305 | }); 306 | } 307 | 308 | void ColumnGenerator::queueBuild(vec2i colPos) { 309 | buildPool->enqueue([this, colPos]() { 310 | // Grab the job for Building 311 | ColumnData* colData = nullptr; 312 | { 313 | std::lock_guard lock(loadedMutex); 314 | auto it = loaded.find(colPos); 315 | if(it == loaded.end()) return; 316 | colData = it->second; 317 | if(colData->state != ColumnData::Decorated) return; 318 | for(int x = -1; x <= 1; ++x) 319 | for(int y = -1; y <= 1; ++y) { 320 | auto it2 = loaded.find(colPos+vec2i(x, y)); 321 | if(it2 == loaded.end()) 322 | return; 323 | if(it2->second->state < ColumnData::Decorated || 324 | it2->second->state > ColumnData::Built) { 325 | return; 326 | } 327 | } 328 | colData->state = ColumnData::Building; 329 | ++colData->refCount; // for the player 330 | } 331 | 332 | // Generate Column object 333 | Column* col = new Column(colPos.x,colPos.y); 334 | // Gather decorations 335 | colData->decOut.insert(colData->decIn[1][1].begin(), colData->decIn[1][1].end()); 336 | // Resize to hold all the chunks 337 | col->chunks.resize(GENERATIONHEIGHT,nullptr); 338 | // Traverse all and keep an eye on whether it's full or not 339 | for(int i = 0; i < GENERATIONHEIGHT; ++i) { 340 | col->chunks[i] = new Chunk(colPos.x,i,colPos.y); 341 | bool full = false; 342 | for(int x = 0; x < CHUNKSIZE; ++x) 343 | for(int z = 0; z < CHUNKSIZE; ++z) 344 | for(int y = 0; y < CHUNKSIZE; ++y) { 345 | vec3us c = {x, i*CHUNKSIZE+y, z}; 346 | // Start off with generation data 347 | unsigned int dest = colData->raw[c.x*CHUNKSIZE*CHUNKSIZE*GENERATIONHEIGHT + c.z*CHUNKSIZE*GENERATIONHEIGHT + c.y]; 348 | // Search for match 349 | auto it = colData->decOut.lower_bound(std::make_pair(c, 0)); 350 | if(it != colData->decOut.end() && it->first.first == c) { 351 | // Search for highest layer 352 | while(it != colData->decOut.end() && it->first.first == c) 353 | ++it; 354 | --it; 355 | // Only override Generation val if layer > 127 or air. 356 | if(it->first.second >= 127 || dest == 0) 357 | dest = it->second; 358 | } 359 | if(dest != 0) full = true; 360 | col->chunks[i]->cubes[x][y][z] = dest; 361 | } 362 | // If chunk is empty, delete it 363 | if(!full) { 364 | delete col->chunks[i]; 365 | col->chunks[i] = nullptr; 366 | } 367 | } 368 | // Shrink chunk vector 369 | for(int i = GENERATIONHEIGHT-1; i >= 0; --i) { 370 | if(col->chunks[i] == nullptr) col->chunks.resize(i); 371 | else break; 372 | } 373 | 374 | // Finished building. Mark it as built. 375 | { 376 | std::lock_guard lock(loadedMutex); 377 | VBE_ASSERT_SIMPLE(loaded.find(colPos) != loaded.end()); 378 | VBE_ASSERT_SIMPLE(loaded.at(colPos) == colData); 379 | VBE_ASSERT_SIMPLE(colData->state == ColumnData::Building); 380 | colData->state = ColumnData::Built; 381 | colData->col = col; 382 | --colData->refCount; // for the thread 383 | ++colData->refCount; // for the player 384 | } 385 | 386 | // Queue for collection 387 | { 388 | std::lock_guard lock(doneMutex); 389 | done.push(col); 390 | } 391 | }); 392 | } 393 | 394 | void ColumnGenerator::queueDelete(vec2i colPos) { 395 | buildPool->enqueue([this, colPos]() { 396 | // Grab the job for Building 397 | ColumnData* colData = nullptr; 398 | { 399 | std::lock_guard lock(loadedMutex); 400 | auto it = loaded.find(colPos); 401 | if(it == loaded.end()) return; 402 | colData = it->second; 403 | if(!colData->canDelete()) return; 404 | colData->state = ColumnData::Deleting; 405 | } 406 | 407 | // Destroy me like one of your french girls 408 | // (save to disk, etc etc) 409 | 410 | // Finished Deleting. Mark it as deleted 411 | // Main thread will actually call the delete operator. 412 | { 413 | std::lock_guard lock(loadedMutex); 414 | VBE_ASSERT_SIMPLE(loaded.find(colPos) != loaded.end()); 415 | VBE_ASSERT_SIMPLE(loaded.at(colPos) == colData); 416 | VBE_ASSERT_SIMPLE(colData->state == ColumnData::Deleting); 417 | colData->state = ColumnData::Deleted; 418 | } 419 | }); 420 | } 421 | 422 | bool ColumnGenerator::locked() const { 423 | return (loadedLock.owns_lock() && doneLock.owns_lock()); 424 | } 425 | 426 | void ColumnGenerator::lock() { 427 | VBE_ASSERT_SIMPLE(!locked()); 428 | loadedLock.lock(); 429 | doneLock.lock(); 430 | } 431 | 432 | void ColumnGenerator::unlock() { 433 | VBE_ASSERT_SIMPLE(locked()); 434 | loadedLock.unlock(); 435 | doneLock.unlock(); 436 | } 437 | 438 | void ColumnGenerator::discardGenerateTasks() { 439 | generatePool->discard(); 440 | } 441 | 442 | Column* ColumnGenerator::pullDone() { 443 | std::unique_lock l; 444 | if(!locked()) l = std::unique_lock(doneMutex); 445 | 446 | if (done.empty()) return nullptr; 447 | 448 | Column* result = done.front(); 449 | done.pop(); 450 | 451 | return result; 452 | } 453 | 454 | void ColumnGenerator::unloadColumn(Column* col) { 455 | std::unique_lock l; 456 | if(!locked()) l = std::unique_lock(loadedMutex); 457 | 458 | vec2i colPos = {col->getX(), col->getZ()}; 459 | VBE_ASSERT_SIMPLE(loaded.find(colPos) != loaded.end()); 460 | VBE_ASSERT_SIMPLE(loaded.at(colPos)->col == col); 461 | VBE_ASSERT_SIMPLE(loaded.at(colPos)->state == ColumnData::Built); 462 | loaded.at(colPos)->refCount--; 463 | } 464 | 465 | bool ColumnGenerator::inPlayerArea(const vec2i& colPos) const { 466 | return colPos.x >= relevantMin.x && 467 | colPos.x <= relevantMax.x && 468 | colPos.y >= relevantMin.y && 469 | colPos.y <= relevantMax.y; 470 | } 471 | 472 | void ColumnGenerator::setRelevantArea(const vec2i& min, const vec2i&max) { 473 | VBE_ASSERT_SIMPLE(min.x <= max.x && min.y <= max.y); 474 | relevantMin = min; 475 | relevantMax = max; 476 | } 477 | --------------------------------------------------------------------------------