Compare commits

7 Commits

Author SHA1 Message Date
zhenyan121
f4114c2699 refactor: world generation (#17)
* refactor: use TBB for concurrent hash maps and parallelize chunk processing

* fix: tbb link fail

* refactor(chunk): remove biome check for caves in rivers and oceans

* refactor(random): replace std distributions with custom implementations

Avoid overhead and platform-dependent behavior of `<random>` distributions by using direct engine operations and integer arithmetic. This ensures deterministic, cross-platform results and improves performance.

* refactor(generation): use chunk seed for cave and river paths

- Use per-chunk seed instead of global path_id for cave and river generation.
- Remove unused m_sum variables and m_path_id members.
- Clamp river yaw within 10 degrees of initial direction.
- Fix river radius interpolation (use t instead of 1-t).
- Lower sea level from 64 to 63.
2026-06-14 11:36:37 +08:00
zhenyan121
932463663f feat: ocean (#16)
* feat(gameplay): add Ocean biome with water generation and heightmap adjustments

- Introduce Ocean biome enum, builder, and detection logic.
- Add ocean water building to all existing biomes and modify heightmap thresholds for low mountainous areas.
- Skip cave and river generation in Ocean (and River) biomes; avoid carving water blocks.
- Comment out border blending call and update block fill logic.

* fix(gameplay): re-enable border blending and protect water in cave gen

* refactor(generation): move ocean water build to later phase

* feat(block): add is_transitional property and refine border blending

* fix(block): set stone block as transitional

* fix(world): generate temporary chunks for surface blend neighbor data

* fix(gameplay): simplify block fill logic in blend_surface_blocks_borders

* refactor(tree): remove debug logging and unused include
2026-06-12 19:42:59 +08:00
zhenyan121
bac3df801b refactor: chunk render (#15)
* refactor(renderer): centralize VAO setup and rename init_underwater

* refactor(gameplay): replace VBO with VAO for vertex data

* fix(renderer): move depth test enable to world rendering

The `glEnable(GL_DEPTH_TEST)` call was incorrectly placed in the general `render()` function, affecting UI and text rendering. Moved it to the start of the world rendering loop to ensure depth testing is only active during 3D world pass.
2026-06-11 14:58:39 +08:00
zhenyan121
d0bc8d627f refactor: transparent render (#14)
* fix(renderer): defer uniform location retrieval and add view matrix in outline rendering

* refactor(gameplay): encapsulate per-type vertex data into VertexData struct

* feat(rendering): separate transparent blocks into discard and blend modes

* feat(renderer): implement order-independent transparency

* fix(shaders): reduce alpha discard threshold to 0.8
2026-06-11 12:21:19 +08:00
zhenyan121
2906106597 feat: water rendering (#13)
* refactor: update water texture

* feat(gameplay): implement transparent block rendering with depth sorting

* fix(world): use camera pos for distance calculations, make water passable

* refactor(player): use ivec3 for block pass check

* feat(camera): add underwater detection

* feat(renderer): add underwater effect with framebuffer post-processing

* feat(block): add gas property and refactor solid block check

* fix(assets): set leaf block transparency to false

* fix(block): set leaf as transparent
2026-05-30 15:11:40 +08:00
zhenyan121
a0139dd315 fix: msvc build fail (#12) 2026-05-28 21:55:44 +08:00
zhenyan121
5901ab7cd9 feat: grass (#11)
* feat: add grass texture and update grass_block texture

* feat: add block data

* feat: add blocks_tool

* feat: add sync info and change function in blocks_tools

* feat: add check and new function

* refactor: make block texture loading data-driven

* feat: add rendering for grass

* feat: passable grass

* feat: random grass place

* fix: memory leak in TextureManager::load_cross_plane_texture
2026-05-28 21:34:36 +08:00
79 changed files with 2342 additions and 626 deletions

View File

@@ -58,6 +58,21 @@ if (WIN32)
if(TARGET freetype) if(TARGET freetype)
add_library(Freetype::Freetype ALIAS freetype) add_library(Freetype::Freetype ALIAS freetype)
endif() endif()
set(_BUILD_SHARED_LIBS_SAVED ${BUILD_SHARED_LIBS})
set(BUILD_SHARED_LIBS ON)
FetchContent_Declare(
onetbb
GIT_REPOSITORY https://github.com/uxlfoundation/oneTBB.git
GIT_TAG v2023.0.0
)
set(BUILD_TESTING OFF CACHE BOOL "Build tests" FORCE)
set(TBB_TEST OFF CACHE BOOL "Build TBB tests" FORCE)
FetchContent_MakeAvailable(onetbb)
set(BUILD_SHARED_LIBS ${_BUILD_SHARED_LIBS_SAVED})
unset(_BUILD_SHARED_LIBS_SAVED)
endif() endif()
FetchContent_Declare( FetchContent_Declare(
@@ -83,7 +98,6 @@ FetchContent_MakeAvailable(tomlplusplus)
add_subdirectory(third_party/imgui) add_subdirectory(third_party/imgui)
set(INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include) set(INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
add_executable(${PROJECT_NAME} add_executable(${PROJECT_NAME}
@@ -122,6 +136,9 @@ add_executable(${PROJECT_NAME}
src/gameplay/builders/snowy_plain_builder.cpp src/gameplay/builders/snowy_plain_builder.cpp
src/gameplay/river_worm.cpp src/gameplay/river_worm.cpp
src/gameplay/river_path.cpp src/gameplay/river_path.cpp
src/block.cpp
src/gameplay/vertex_data.cpp
src/gameplay/builders/ocean_builder.cpp
) )
if(CMAKE_BUILD_TYPE STREQUAL "Debug") if(CMAKE_BUILD_TYPE STREQUAL "Debug")
@@ -161,10 +178,11 @@ target_link_libraries(${PROJECT_NAME}
Freetype::Freetype Freetype::Freetype
tomlplusplus::tomlplusplus tomlplusplus::tomlplusplus
imgui imgui
tbb
) )
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_link_libraries(${PROJECT_NAME} PRIVATE tbb) # target_link_libraries(${PROJECT_NAME} PRIVATE tbb)
endif() endif()
if (UNIX AND NOT APPLE) if (UNIX AND NOT APPLE)
@@ -184,3 +202,19 @@ if (UNIX AND NOT APPLE)
target_compile_options(${PROJECT_NAME} PRIVATE ${EGL_CFLAGS_OTHER} ${Wayland_CFLAGS_OTHER}) target_compile_options(${PROJECT_NAME} PRIVATE ${EGL_CFLAGS_OTHER} ${Wayland_CFLAGS_OTHER})
endif() endif()
if (WIN32)
foreach(TBB_LIB IN ITEMS tbb tbbmalloc tbbmalloc_proxy)
if(TARGET ${TBB_LIB})
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:${TBB_LIB}>
$<TARGET_FILE_DIR:${PROJECT_NAME}>
COMMENT "Copying ${TBB_LIB}.dll"
)
else()
message(STATUS "Target ${TBB_LIB} not found, skipping copy")
endif()
endforeach()
endif()

View File

@@ -1,5 +1,10 @@
name = "air"
id = 0 id = 0
is_blend = false
is_cross_plane = false
is_discard = true
is_gas = true
is_liquid = false is_liquid = false
is_passable = true is_passable = true
is_cross_plane = false is_transitional = false
is_transparent = true
name = 'air'

View File

@@ -1,5 +1,10 @@
name = "dirt"
id = 2 id = 2
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = true
is_transparent = false
name = 'dirt'

View File

@@ -1,5 +1,10 @@
name = "grass"
id = 9 id = 9
is_blend = false
is_cross_plane = true
is_discard = true
is_gas = false
is_liquid = false is_liquid = false
is_passable = true is_passable = true
is_cross_plane = true is_transitional = false
is_transparent = true
name = 'grass'

View File

@@ -1,5 +1,10 @@
name = "grass_block"
id = 1 id = 1
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = true
is_transparent = false
name = 'grass_block'

View File

@@ -1,5 +1,10 @@
name = "leaf"
id = 6 id = 6
is_blend = false
is_cross_plane = false
is_discard = true
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = false
is_transparent = true
name = 'leaf'

View File

@@ -1,5 +1,10 @@
name = "log"
id = 5 id = 5
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = false
is_transparent = false
name = 'log'

View File

@@ -1,5 +1,10 @@
name = "sand"
id = 4 id = 4
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = true
is_transparent = false
name = 'sand'

View File

@@ -1,5 +1,10 @@
name = "snowy_grass_block"
id = 8 id = 8
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = true
is_transparent = false
name = 'snowy_grass_block'

View File

@@ -1,5 +1,10 @@
name = "stone"
id = 3 id = 3
is_blend = false
is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = false is_liquid = false
is_passable = false is_passable = false
is_cross_plane = false is_transitional = true
is_transparent = false
name = 'stone'

View File

@@ -1,5 +1,10 @@
name = "template" name = "template"
id = 0 id = 0
is_liquid = false is_liquid = false
is_gas = false
is_passable = false is_passable = false
is_cross_plane = false is_cross_plane = false
is_transparent = false
is_discard = false
is_blend = false
is_transitional = false

View File

@@ -1,5 +1,10 @@
name = "water"
id = 7 id = 7
is_liquid = true is_blend = true
is_passable = false
is_cross_plane = false is_cross_plane = false
is_discard = false
is_gas = false
is_liquid = true
is_passable = true
is_transitional = false
is_transparent = true
name = 'water'

View File

@@ -0,0 +1,28 @@
#version 460
layout (location = 0) out vec4 accum;
layout (location = 1) out float reveal;
in vec2 tc;
flat in int tex_layer;
in float v_depth;
layout (binding = 0) uniform sampler2DArray samp;
float weight(float z, float a) {
float intermediate = 0.03 / (1e-5 + pow(z / 200.0, 4.0));
return a * clamp(intermediate, 1e-2, 3e2);
}
void main() {
vec4 color = texture(samp, vec3(tc, tex_layer));
float alpha = color.a;
if (alpha < 1e-4) discard;
float w = weight(v_depth, alpha);
accum = vec4(color.rgb * alpha * w, alpha * w);
reveal = alpha;
}

View File

@@ -0,0 +1,21 @@
#version 460
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in float layer;
out vec2 tc;
flat out int tex_layer;
out float v_depth;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
void main(void) {
vec4 view_pos = mv_matrix * vec4(pos, 1.0);
gl_Position = proj_matrix * view_pos;
tc = texCoord;
tex_layer = int(layer);
v_depth = -view_pos.z;
}

View File

@@ -0,0 +1,19 @@
#version 460
uniform sampler2D u_accumTex;
uniform sampler2D u_revealTex;
in vec2 TexCoord;
out vec4 FragColor;
void main() {
vec4 a = texture(u_accumTex, TexCoord);
float r = texture(u_revealTex, TexCoord).r;
if (a.a < 1e-4) discard;
vec3 color = a.rgb / max(a.a, 1e-5);
float transmittance = r;
float opacity = 1.0 - transmittance;
FragColor = vec4(color * opacity, opacity);
}

View File

@@ -0,0 +1,11 @@
#version 460
layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 texCoord;
out vec2 TexCoord;
void main() {
gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);
TexCoord = texCoord;
}

View File

@@ -8,7 +8,7 @@ layout (binding = 0) uniform sampler2DArray samp;
void main(void) { void main(void) {
color = texture(samp, vec3(tc, tex_layer)); color = texture(samp, vec3(tc, tex_layer));
if (color.a < 0.5) { if (color.a < 0.8) {
discard; discard;
} }
//color = varyingColor; //color = varyingColor;

View File

@@ -0,0 +1,35 @@
#version 460
in vec2 TexCoord;
out vec4 FragColor;
uniform sampler2D u_sceneTexture;
uniform float u_time;
uniform bool u_underwater;
uniform vec3 u_waterColor;
uniform float u_fogDensity;
void main() {
vec4 original = texture(u_sceneTexture, TexCoord);
if (!u_underwater) {
FragColor = original;
return;
}
vec2 distoredUV = TexCoord;
float strength = 0.003;
distoredUV.x += sin(TexCoord.y * 15.0 + u_time * 5.0) * strength;
distoredUV.y += cos(TexCoord.x * 15.0 + u_time * 4.3) * strength;
distoredUV = clamp(distoredUV, 0.001, 0.999);
vec4 distorted = texture(u_sceneTexture, distoredUV);
float caustic = 0.9 + 0.1 * sin(TexCoord.x * 20.0 + u_time) * cos(TexCoord.y * 20.0 + u_time * 1.2);
vec3 causticLight = vec3(caustic, caustic * 0.95, caustic * 0.9);
//vec3 causticLight = vec3(1.0);
float fogFactor = clamp(1.0 - (TexCoord.y * u_fogDensity * 10.0), 0.0, 1.0);
vec3 mixed = mix(u_waterColor, distorted.rgb * causticLight, fogFactor);
FragColor = vec4(mixed, 1.0);
}

View File

@@ -0,0 +1,11 @@
#version 460
layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 texCoord;
out vec2 TexCoord;
void main() {
gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);
TexCoord = texCoord;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

View File

@@ -16,6 +16,7 @@ private:
Player* m_player; Player* m_player;
float m_last_mouse_x, m_last_mouse_y; float m_last_mouse_x, m_last_mouse_y;
glm::vec3 m_camera_pos; glm::vec3 m_camera_pos;
bool m_under_water = false;
public: public:
Camera(); Camera();
@@ -29,6 +30,8 @@ public:
const glm::mat4 get_camera_lookat() const; const glm::mat4 get_camera_lookat() const;
const glm::vec3& get_camera_pos() const; const glm::vec3& get_camera_pos() const;
bool is_under_water() const;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -6,9 +6,8 @@ namespace Cubed {
constexpr int WORLD_SIZE_Y = 256; constexpr int WORLD_SIZE_Y = 256;
constexpr int CHUNK_SIZE = 16; constexpr int CHUNK_SIZE = 16;
constexpr int SEA_LEVEL = 64; constexpr int SEA_LEVEL = 63;
constexpr int MAX_BLOCK_NUM = 9;
constexpr int MAX_UI_NUM = 1; constexpr int MAX_UI_NUM = 1;
constexpr int MAX_BLOCK_STATUS = 1; constexpr int MAX_BLOCK_STATUS = 1;
constexpr int MAX_BIOME_SUM = 4; constexpr int MAX_BIOME_SUM = 4;
@@ -17,7 +16,7 @@ constexpr int MAX_CHARACTER = 128;
constexpr int PRE_LOAD_DISTANCE = 24; constexpr int PRE_LOAD_DISTANCE = 24;
constexpr int MAX_DISTANCE = 128; constexpr int MAX_DISTANCE = 128;
constexpr int CROSS_PLANE_DISTANCE = 8;
constexpr float DEFAULT_FOV = 70.0f; constexpr float DEFAULT_FOV = 70.0f;
constexpr float DEFAULT_MAX_WALK_SPEED = 4.5f; constexpr float DEFAULT_MAX_WALK_SPEED = 4.5f;
constexpr float DEFAULT_MAX_RUN_SPEED = 7.0f; constexpr float DEFAULT_MAX_RUN_SPEED = 7.0f;

View File

@@ -15,6 +15,7 @@ enum class BiomeType {
MOUNTAIN, MOUNTAIN,
RIVER, RIVER,
SNOWY_PLAIN, SNOWY_PLAIN,
OCEAN,
NONE NONE
}; };

View File

@@ -1,8 +1,5 @@
#pragma once #pragma once
#include "Cubed/constants.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include <array>
#include <glad/glad.h> #include <glad/glad.h>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <string> #include <string>
@@ -40,16 +37,55 @@ struct LookBlock {
glm::ivec3 normal; glm::ivec3 normal;
}; };
constexpr std::array<std::string_view, MAX_BLOCK_NUM> BLOCK_REISTER{ struct BlockData {
"air", "grass_block", "dirt", "stone", "sand", "log", "leaf", std::string name;
"water", "snowy_grass_block"}; BlockType id = 0;
const std::array<bool, MAX_BLOCK_NUM> TRANSPARENT_MAP{ bool is_liquid = false;
true, false, false, false, false, false, true, false, false}; bool is_gas = false;
inline bool is_in_transparent_map(unsigned id) { bool is_passable = false;
ASSERT_MSG(id < MAX_BLOCK_NUM, "ID is invaild"); bool is_cross_plane = false;
return TRANSPARENT_MAP[id]; bool is_transparent = false;
bool is_discard = false;
bool is_blend = false;
bool is_transitional = false;
BlockData(BlockType b_id, std::string_view b_name, bool liquid,
bool passable, bool cross_plane, bool transparent, bool gas,
bool discard, bool blend, bool transitional)
: name(b_name), id(b_id), is_liquid(liquid), is_gas(gas),
is_passable(passable), is_cross_plane(cross_plane),
is_transparent(transparent), is_discard(discard), is_blend(blend),
is_transitional(transitional) {}
};
class BlockManager {
public:
static const std::vector<BlockData>& datas();
static void init();
static unsigned sums();
static unsigned cross_plane_sum();
static const std::string& name_form_id(BlockType id);
static bool is_gas(BlockType id);
static bool is_liquid(BlockType id);
static bool is_cross_plane(BlockType id);
static bool is_transparent(BlockType id);
static bool is_passable(BlockType id);
static bool is_discard(BlockType id);
static bool is_blend(BlockType id);
static bool is_transitional(BlockType id);
static BlockType cross_plane_index(BlockType id);
private:
static void set_up_cross_plane_map();
static inline std::vector<BlockData> m_datas;
static inline bool is_init = false;
static inline std::unordered_map<BlockType, BlockType> m_cross_plane_map;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -9,8 +9,10 @@ public:
virtual ChunkGenerator& get_chunk_generator() = 0; virtual ChunkGenerator& get_chunk_generator() = 0;
virtual void build_biome() = 0; virtual void build_biome() = 0;
virtual void build_vegetation() = 0; virtual void build_vegetation() = 0;
void ocean_water_build();
protected: protected:
void build_bottom(); void build_bottom();
void place_grass();
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -0,0 +1,23 @@
#pragma once
#pragma once
#include "Cubed/gameplay/builders/biome_builder.hpp"
namespace Cubed {
class ChunkGenerator;
class OceanBuilder : public BiomeBuilder {
public:
OceanBuilder(ChunkGenerator& chunk_generator);
void build_biome() override;
ChunkGenerator& get_chunk_generator() override;
void build_vegetation() override;
private:
ChunkGenerator& m_chunk_generator;
void build_blocks();
};
} // namespace Cubed

View File

@@ -1,10 +1,14 @@
#pragma once #pragma once
#include "Cubed/gameplay/cave_path.hpp" #include "Cubed/gameplay/cave_path.hpp"
#include <tbb/concurrent_hash_map.h>
namespace Cubed { namespace Cubed {
class CaveCarver { class CaveCarver {
using CaveHashMap = tbb::concurrent_hash_map<unsigned, CavePath>;
public: public:
CaveCarver(); CaveCarver();
std::unordered_map<unsigned, CavePath>& paths(); CaveHashMap& paths();
void init(unsigned world_seed); void init(unsigned world_seed);
void reload(unsigned world_seed); void reload(unsigned world_seed);
void add_path(const glm::vec3& pos, unsigned chunk_seed); void add_path(const glm::vec3& pos, unsigned chunk_seed);
@@ -15,9 +19,8 @@ public:
float& cave_probability(); float& cave_probability();
private: private:
std::unordered_map<unsigned, CavePath> m_paths; CaveHashMap m_paths;
unsigned m_seed = 0; unsigned m_seed = 0;
int m_sum = 0;
Random m_random; Random m_random;
float m_cave_probability = 0.035f; float m_cave_probability = 0.035f;
}; };

View File

@@ -5,12 +5,16 @@
#include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/cubed_random.hpp"
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <unordered_set> #include <tbb/concurrent_hash_map.h>
namespace Cubed { namespace Cubed {
class CavePath { class CavePath {
using ChunkPosSet =
tbb::concurrent_hash_map<ChunkPos, bool, ChunkPos::TBBHash>;
public: public:
CavePath(unsigned int world_seed, int path_id, const glm::vec3& start_pos); CavePath(unsigned int chunk_seed, unsigned world_seed,
const glm::vec3& start_pos);
const std::vector<PathPoint>& points() const; const std::vector<PathPoint>& points() const;
void clear_chunk(const ChunkPos& pos); void clear_chunk(const ChunkPos& pos);
bool is_finished() const; bool is_finished() const;
@@ -34,7 +38,6 @@ private:
static inline int m_step_min = 10; static inline int m_step_min = 10;
static inline int m_step_max = 400; static inline int m_step_max = 400;
int m_path_id = 0;
unsigned int m_seed = 0; unsigned int m_seed = 0;
float m_yaw = 0.0f; float m_yaw = 0.0f;
float m_pitch = 0.0f; float m_pitch = 0.0f;
@@ -44,7 +47,7 @@ private:
Random m_random; Random m_random;
std::vector<PathPoint> m_points; std::vector<PathPoint> m_points;
std::unordered_set<ChunkPos, ChunkPos::Hash> m_pending_chunks; ChunkPosSet m_pending_chunks;
void collect_path_points(); void collect_path_points();
void precompute_chunk_coverage(); void precompute_chunk_coverage();
}; };

View File

@@ -4,24 +4,25 @@
#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/chunk_generator.hpp"
#include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/primitive_data.hpp" #include "Cubed/gameplay/vertex_data.hpp"
#include <atomic> #include <atomic>
#include <mutex>
namespace Cubed { namespace Cubed {
class World; class World;
// if want to use, do init_chunk(), gen_vertex_data() and // if want to use, do init_chunk(), gen_vertex_data() and
class Chunk { class Chunk {
private: private:
using OptionalBlockVectorArray =
std::array<std::optional<std::vector<BlockType>>, 4>;
static constexpr int SIZE_X = CHUNK_SIZE; static constexpr int SIZE_X = CHUNK_SIZE;
static constexpr int SIZE_Y = WORLD_SIZE_Y; static constexpr int SIZE_Y = WORLD_SIZE_Y;
static constexpr int SIZE_Z = CHUNK_SIZE; static constexpr int SIZE_Z = CHUNK_SIZE;
static constexpr int VERTEX_DATA_SUM = 4;
std::atomic<bool> m_dirty{false}; std::atomic<bool> m_dirty{false};
std::atomic<bool> m_need_upload{true}; std::atomic<bool> m_need_upload{true};
std::atomic<bool> m_is_on_gen_vertex_data{false}; std::atomic<bool> m_is_on_gen_vertex_data{false};
std::atomic<size_t> m_vertex_sum = 0;
std::atomic<BiomeType> m_biome = BiomeType::PLAIN; std::atomic<BiomeType> m_biome = BiomeType::PLAIN;
std::mutex m_vertexs_data_mutex; std::mutex m_vertexs_data_mutex;
@@ -32,9 +33,14 @@ private:
HeightMapArray m_heightmap; HeightMapArray m_heightmap;
// the index is a array of block id // the index is a array of block id
std::vector<BlockType> m_blocks; std::vector<BlockType> m_blocks;
GLuint m_vbo = 0;
std::vector<Vertex> m_vertexs_data;
/*
0 - normal
1 - cross_plane
2 - normal_discard
3 - transparent and blend
*/
std::vector<VertexData> m_vertex_data;
float frequency = 0.01f; float frequency = 0.01f;
float height = 80; float height = 80;
unsigned m_seed = 0; unsigned m_seed = 0;
@@ -42,6 +48,9 @@ private:
BiomeConditions m_conditions; BiomeConditions m_conditions;
void clear_dirty(); void clear_dirty();
void gen_vertices(const OptionalBlockVectorArray& neighbor_block);
void gen_cross_plane_vertices(int world_x, int world_y, int world_z,
BlockType id);
public: public:
Chunk(World& world, ChunkPos chunk_pos); Chunk(World& world, ChunkPos chunk_pos);
@@ -89,12 +98,20 @@ public:
// 1 : (-1, 0) // 1 : (-1, 0)
// 2 : (0, 1) // 2 : (0, 1)
// 3 : (0, -1) // 3 : (0, -1)
void gen_vertex_data( void gen_vertex_data(const OptionalBlockVectorArray& neighbor_block);
const std::array<const std::vector<BlockType>*, 4>& neighbor_block);
void upload_to_gpu(); void upload_to_gpu();
GLuint get_vbo() const; GLuint get_normal_vao() const;
size_t get_vertex_sum() const; size_t get_normal_vertices_sum() const;
GLuint get_cross_vao() const;
size_t get_cross_vertices_sum() const;
GLuint get_normal_discard_vao() const;
size_t get_normal_discard_vertices_sum() const;
GLuint get_normal_blend_vao() const;
size_t get_normal_blend_vertices_sum() const;
bool is_dirty() const; bool is_dirty() const;
void mark_dirty(); void mark_dirty();

View File

@@ -4,6 +4,7 @@
#include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/biome.hpp"
#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/builders/biome_builder.hpp" #include "Cubed/gameplay/builders/biome_builder.hpp"
#include "Cubed/gameplay/path_point.hpp"
#include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/cubed_random.hpp"
#include <atomic> #include <atomic>
@@ -45,6 +46,7 @@ public:
Chunk& chunk(); Chunk& chunk();
Random& random(); Random& random();
const std::array<BiomeType, 8>& neighbor_biome() const; const std::array<BiomeType, 8>& neighbor_biome() const;
void ocean_build();
void generate_cave(); void generate_cave();
void generate_river(); void generate_river();
@@ -60,6 +62,9 @@ private:
unsigned m_chunk_seed = 0; unsigned m_chunk_seed = 0;
void make_biome_builder(); void make_biome_builder();
void
carve_worm(const std::vector<PathPoint>& points, const ChunkPos& chunk_pos,
std::function<void(int /*x*/, int /*y*/, int /*z*/)> on_hit);
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -16,7 +16,14 @@ struct ChunkPos {
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2)); return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
} }
}; };
struct TBBHash {
std::size_t hash(const ChunkPos& p) const {
return ChunkPos::Hash{}(p);
}
bool equal(const ChunkPos& a, const ChunkPos& b) const {
return a == b;
}
};
ChunkPos operator+(const ChunkPos& pos) const { ChunkPos operator+(const ChunkPos& pos) const {
return ChunkPos{x + pos.x, z + pos.z}; return ChunkPos{x + pos.x, z + pos.z};
} }

View File

@@ -104,6 +104,7 @@ public:
Gait& gait(); Gait& gait();
GameMode& game_mode(); GameMode& game_mode();
const World& get_world() const;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -5,13 +5,16 @@
#include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/cubed_random.hpp"
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <unordered_set> #include <tbb/concurrent_hash_map.h>
namespace Cubed { namespace Cubed {
class RiverPath { class RiverPath {
using ChunkPosSet =
tbb::concurrent_hash_map<ChunkPos, bool, ChunkPos::TBBHash>;
public: public:
RiverPath(unsigned int world_seed, int path_id, const glm::vec3& start_pos); RiverPath(unsigned int chunk_seed, unsigned world_seed,
const glm::vec3& start_pos);
const std::vector<PathPoint>& points() const; const std::vector<PathPoint>& points() const;
void clear_chunk(const ChunkPos& pos); void clear_chunk(const ChunkPos& pos);
bool is_finished() const; bool is_finished() const;
@@ -32,12 +35,12 @@ private:
static inline float m_radius_y_max = 8.0f; static inline float m_radius_y_max = 8.0f;
static inline float m_delta_angle_min = -3.0f; static inline float m_delta_angle_min = -3.0f;
static inline float m_delta_angle_max = 3.0f; static inline float m_delta_angle_max = 3.0f;
static inline int m_step_min = 150; static inline int m_step_min = 200;
static inline int m_step_max = 400; static inline int m_step_max = 400;
int m_path_id = 0;
unsigned int m_seed = 0; unsigned int m_seed = 0;
float m_yaw = 0.0f; float m_yaw = 0.0f;
float m_initial_yaw = 0.0f;
float m_pitch = 0.0f; float m_pitch = 0.0f;
int m_step = 0; int m_step = 0;
float m_step_len = 1.0f; float m_step_len = 1.0f;
@@ -45,7 +48,7 @@ private:
Random m_random; Random m_random;
std::vector<PathPoint> m_points; std::vector<PathPoint> m_points;
std::unordered_set<ChunkPos, ChunkPos::Hash> m_pending_chunks; ChunkPosSet m_pending_chunks;
void collect_path_points(); void collect_path_points();
void precompute_chunk_coverage(); void precompute_chunk_coverage();
}; };

View File

@@ -4,13 +4,15 @@
#include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/cubed_random.hpp"
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <unordered_map> #include <tbb/concurrent_hash_map.h>
namespace Cubed { namespace Cubed {
class RiverWorm { class RiverWorm {
using RiverHashMap = tbb::concurrent_hash_map<unsigned, RiverPath>;
public: public:
RiverWorm(); RiverWorm();
std::unordered_map<unsigned, RiverPath>& paths(); RiverHashMap& paths();
void init(unsigned world_seed); void init(unsigned world_seed);
void reload(unsigned world_seed); void reload(unsigned world_seed);
void add_path(const glm::vec3& pos, unsigned chunk_seed); void add_path(const glm::vec3& pos, unsigned chunk_seed);
@@ -21,9 +23,8 @@ public:
float& river_probability(); float& river_probability();
private: private:
std::unordered_map<unsigned, RiverPath> m_paths; RiverHashMap m_paths;
unsigned m_seed = 0; unsigned m_seed = 0;
int m_sum = 0;
Random m_random; Random m_random;
float m_probability = 0.01f; float m_probability = 0.01f;
}; };

View File

@@ -0,0 +1,25 @@
#pragma once
#include "Cubed/primitive_data.hpp"
#include <atomic>
#include <glad/glad.h>
#include <vector>
namespace Cubed {
class World;
struct VertexData {
std::vector<Vertex> m_vertices;
GLuint m_vbo = 0;
GLuint m_vao = 0;
std::atomic<std::size_t> m_sum{0};
World& m_world;
VertexData(World& world);
~VertexData();
VertexData(const VertexData&) = delete;
VertexData(VertexData&&) noexcept;
VertexData& operator=(const VertexData&) = delete;
VertexData& operator=(VertexData&&) noexcept;
void upload();
void update_sum();
};
} // namespace Cubed

View File

@@ -15,18 +15,26 @@
namespace Cubed { namespace Cubed {
struct ChunkRenderSnapshot { struct ChunkRenderSnapshot {
GLuint vbo; GLuint normal_vao;
size_t vertex_count; size_t normal_vertices_count;
GLuint cross_vao;
size_t cross_vertices_count;
GLuint normal_discard_vao;
size_t normal_discard_vertices_count;
GLuint normal_blend_vao;
size_t normal_blend_vertices_count;
glm::vec3 center; glm::vec3 center;
glm::vec3 half_extents; glm::vec3 half_extents;
}; };
class Player; class Player;
class TextureManager;
class World { class World {
private: private:
using OptionalBlockVectorArray =
std::array<std::optional<std::vector<BlockType>>, 4>;
using ChunkPtrUpdateList = std::vector<std::pair<ChunkPos, Chunk*>>; using ChunkPtrUpdateList = std::vector<std::pair<ChunkPos, Chunk*>>;
using ChunkUpdateList = std::vector<std::pair<ChunkPos, Chunk>>; using ChunkPairVector = std::vector<std::pair<ChunkPos, Chunk>>;
using ConstChunkMap = using ConstChunkMap =
std::unordered_map<ChunkPos, const Chunk*, ChunkPos::Hash>; std::unordered_map<ChunkPos, const Chunk*, ChunkPos::Hash>;
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>; using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
@@ -41,8 +49,10 @@ private:
std::mutex m_gen_signal_mutex; std::mutex m_gen_signal_mutex;
std::mutex m_new_chunk_queue_mutex; std::mutex m_new_chunk_queue_mutex;
std::mutex m_delete_vbo_mutex; std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
std::mutex m_gen_player_pos_mutex; std::mutex m_gen_player_pos_mutex;
std::vector<GLuint> m_pending_delete_vbo; std::vector<GLuint> m_pending_delete_vbo;
std::vector<GLuint> m_pending_delete_vao;
std::condition_variable m_gen_cv; std::condition_variable m_gen_cv;
std::atomic<bool> m_gen_running{false}; std::atomic<bool> m_gen_running{false};
std::atomic<bool> m_need_gen_chunk{false}; std::atomic<bool> m_need_gen_chunk{false};
@@ -62,14 +72,16 @@ private:
void gen_chunks_internal(); void gen_chunks_internal();
void sync_player_pos(glm::vec3& player_pos); void sync_player_pos(glm::vec3& player_pos);
void compute_required_chunks(ChunkPosSet& required_chunks, void
ChunkHashMap& temp_neighbor); compute_required_chunks(ChunkPosSet& required_chunks,
ChunkPairVector& temp_neighbor,
std::vector<ChunkPos>& need_gen_temp_chunks_pos);
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&, void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
const ChunkPosSet&); const ChunkPosSet&);
void void
build_neighbor_context_for_new_chunks(ConstChunkMap& new_chunks_neighbor, build_neighbor_context_for_new_chunks(ConstChunkMap& new_chunks_neighbor,
ChunkPtrUpdateList& affected_neighbor, ChunkPtrUpdateList& affected_neighbor,
const ChunkUpdateList& new_chunks); const ChunkPairVector& new_chunks);
void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&, void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&,
ConstChunkMap&); ConstChunkMap&);
@@ -86,22 +98,19 @@ public:
Player& get_player(const std::string& name); Player& get_player(const std::string& name);
void init_world(); void init_world();
bool is_aabb_in_frustum(const glm::vec3& center,
const glm::vec3& half_extents);
int get_block(const glm::ivec3& block_pos) const; int get_block(const glm::ivec3& block_pos) const;
bool is_block(const glm::ivec3& block_pos) const; bool is_solid(const glm::ivec3& block_pos) const;
bool can_pass_block(const glm::ivec3& block_pos) const;
BlockType get_block_tpye(const glm::ivec3& block_pos) const;
static ChunkPos chunk_pos(int world_x, int world_z); static ChunkPos chunk_pos(int world_x, int world_z);
void need_gen(); void need_gen();
void render(const glm::mat4& mvp_matrix);
void set_block(const glm::ivec3& pos, unsigned id); void set_block(const glm::ivec3& pos, unsigned id);
void update(float delta_time); void update(float delta_time);
void push_delete_vbo(GLuint vbo); void push_delete_vbo(GLuint vbo);
void push_delete_vao(GLuint vao);
void hot_reload(); void hot_reload();
void rebuild_world(); void rebuild_world();
@@ -114,6 +123,8 @@ public:
CaveCarver& cave_carcer(); CaveCarver& cave_carcer();
RiverWorm& river_worm(); RiverWorm& river_worm();
std::vector<glm::vec4>& planes();
std::vector<ChunkRenderSnapshot>& render_snapshots();
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -1,21 +1,25 @@
#pragma once #pragma once
#include <string> // #include <string>
#include <unordered_map> // #include <unordered_map>
#include <vector> // #include <vector>
namespace Cubed { namespace Cubed {
class MapTable { class MapTable {
private: private:
static inline std::unordered_map<unsigned, std::string> id_to_name_map; /*
static inline std::unordered_map<size_t, unsigned> name_to_id_map; static inline std::unordered_map<unsigned, std::string> id_to_name_map;
static inline std::vector<std::string> item_id_to_name; static inline std::unordered_map<size_t, unsigned> name_to_id_map;
static inline std::vector<std::string> item_id_to_name;
*/
public: public:
// please using reference // please using reference
/*
static std::string_view get_name_from_id(unsigned id); static std::string_view get_name_from_id(unsigned id);
static unsigned get_id_from_name(const std::string& name); static unsigned get_id_from_name(const std::string& name);
static std::string_view item_name(unsigned id); static std::string_view item_name(unsigned id);
static const std::vector<std::string>& item_map(); static const std::vector<std::string>& item_map();
*/
static void init_map(); static void init_map();
}; };

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
namespace Cubed { namespace Cubed {
#pragma region NORMAL_BLOCK
constexpr float VERTICES_POS[6][6][3] = { constexpr float VERTICES_POS[6][6][3] = {
// ===== front (z = +1) ===== // ===== front (z = +1) =====
{{0.0f, 0.0f, 1.0f}, // bottom left {{0.0f, 0.0f, 1.0f}, // bottom left
@@ -91,7 +91,7 @@ constexpr float TEX_COORDS[6][6][2] = {
{0.0f, 1.0f}, // back left {0.0f, 1.0f}, // back left
{0.0f, 0.0f}} // front left {0.0f, 0.0f}} // front left
}; };
#pragma endregion
constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, constexpr float CUBE_VER[24] = {0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0,
0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0, 1.0,
0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0}; 0.0, 1.0, 1.0, 1.0, 1.0, 0.0, 1.0, 1.0};
@@ -112,6 +112,49 @@ constexpr float SQUARE_TEXTURE_POS[6][2] = {
{0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f}, {0.0f, 0.0f}, {0.0f, 1.0f}, {1.0f, 1.0f},
{1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f},
}; };
#pragma region CROSS_PLANE
constexpr float CROSS_VERTICES_POS[2][6][3] = {
// ===== Plane 1: bottom-front-left to top-back-right =====
{{0.0f, 0.0f, 0.0f}, // bottom front left
{0.0f, 1.0f, 0.0f}, // top front left
{1.0f, 1.0f, 1.0f}, // top back right
{1.0f, 1.0f, 1.0f}, // top back right
{1.0f, 0.0f, 1.0f}, // bottom back right
{0.0f, 0.0f, 0.0f}}, // bottom front left
// ===== Plane 2: bottom-front-right to top-back-left =====
{{1.0f, 0.0f, 0.0f}, // bottom front right
{1.0f, 1.0f, 0.0f}, // top front right
{0.0f, 1.0f, 1.0f}, // top back left
{0.0f, 1.0f, 1.0f}, // top back left
{0.0f, 0.0f, 1.0f}, // bottom back left
{1.0f, 0.0f, 0.0f}}, // bottom front right
};
constexpr float CROSS_TEX_COORDS[2][6][2] = {
// ===== Plane 1: bottom-front-left to top-back-right =====
{{0.0f, 1.0f}, // bottom left
{0.0f, 0.0f}, // top left
{1.0f, 0.0f}, // top right
{1.0f, 0.0f}, // top right
{1.0f, 1.0f}, // bottom right
{0.0f, 1.0f}}, // bottom left
// ===== Plane 2: bottom-front-right to top-back-left =====
{{0.0f, 1.0f}, // bottom left
{0.0f, 0.0f}, // top left
{1.0f, 0.0f}, // top right
{1.0f, 0.0f}, // top right
{1.0f, 1.0f}, // bottom right
{0.0f, 1.0f}}, // bottom left
};
#pragma endregion
constexpr float QUAD_VERTICES[] = {
// postion // texcoorlds
-1.0f, 1.0f, 0.0f, 1.0f, -1.0f, -1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f};
struct Vertex { struct Vertex {
float x = 0.0f, y = 0.0f, z = 0.0f; float x = 0.0f, y = 0.0f, z = 0.0f;

View File

@@ -15,7 +15,7 @@ class World;
class DevPanel; class DevPanel;
class Renderer { class Renderer {
public: public:
constexpr static int NUM_VAO = 5; constexpr static int NUM_VAO = 7;
Renderer(const Camera& camera, World& world, Renderer(const Camera& camera, World& world,
const TextureManager& texture_manager, DevPanel& dev_panel); const TextureManager& texture_manager, DevPanel& dev_panel);
@@ -24,8 +24,10 @@ public:
void init(); void init();
const Shader& get_shader(const std::string& name) const; const Shader& get_shader(const std::string& name) const;
void render(); void render();
void update(float delta_time);
void update_fov(float fov); void update_fov(float fov);
void update_proj_matrix(float aspect, float width, float height); void update_proj_matrix(float aspect, float width, float height);
void updata_framebuffer(int width, int height);
private: private:
const Camera& m_camera; const Camera& m_camera;
@@ -35,23 +37,48 @@ private:
float m_aspect = 0.0f; float m_aspect = 0.0f;
float m_fov = DEFAULT_FOV; float m_fov = DEFAULT_FOV;
float m_delta_time = 0.0f;
float m_width = 0.0f;
float m_height = 0.0f;
glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat; glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat;
GLuint m_mv_loc; GLuint m_mv_loc = 0;
GLuint m_proj_loc; GLuint m_proj_loc = 0;
GLuint m_sky_vbo; GLuint m_sky_vbo = 0;
GLuint m_text_vbo; GLuint m_text_vbo = 0;
GLuint m_outline_indices_vbo; GLuint m_outline_indices_vbo = 0;
GLuint m_outline_vbo; GLuint m_outline_vbo = 0;
GLuint m_ui_vbo; GLuint m_ui_vbo = 0;
GLuint m_fbo = 0;
GLuint m_screen_texture = 0;
GLuint m_depth_render_buffer = 0;
GLuint m_oit_fbo = 0;
GLuint m_accum_texture = 0;
GLuint m_reveal_texture = 0;
GLuint m_oit_depth_render_buffer = 0;
GLuint m_quad_vbo = 0;
glm::mat4 m_ui_proj; glm::mat4 m_ui_proj;
glm::mat4 m_ui_m_matrix; glm::mat4 m_ui_m_matrix;
std::unordered_map<std::size_t, Shader> m_shaders; std::unordered_map<std::size_t, Shader> m_shaders;
/*
0 - quad vao
1 - sky vao
2 - outline vao
3 - ui vao
4 - text vao
*/
std::vector<GLuint> m_vao; std::vector<GLuint> m_vao;
std::vector<Vertex2D> m_ui; std::vector<Vertex2D> m_ui;
void init_quad();
void init_text(); void init_text();
void render_outline(); void render_outline();
@@ -59,6 +86,7 @@ private:
void render_text(); void render_text();
void render_ui(); void render_ui();
void render_world(); void render_world();
void render_underwater();
void render_dev_panel(); void render_dev_panel();
}; };

View File

@@ -1,7 +1,7 @@
#pragma once #pragma once
#include <glad/glad.h> #include <glad/glad.h>
#include <string> #include <string>
#include <unordered_map>
namespace Cubed { namespace Cubed {
class Shader { class Shader {
@@ -26,6 +26,7 @@ private:
GLuint m_program = 0; GLuint m_program = 0;
std::size_t m_hash = 0; std::size_t m_hash = 0;
std::string m_name = "-1"; std::string m_name = "-1";
mutable std::unordered_map<std::string, GLint> m_uniform_cache;
}; };
} // namespace Cubed } // namespace Cubed

View File

@@ -8,16 +8,19 @@ namespace Cubed {
class TextureManager { class TextureManager {
private: private:
bool m_need_reload = false; bool m_need_reload = false;
GLuint m_block_status_array; GLuint m_block_status_array = 0;
GLuint m_texture_array; GLuint m_texture_array = 0;
GLuint m_ui_array; GLuint m_cross_plane_array = 0;
GLuint m_ui_array = 0;
GLfloat m_max_aniso = 0.0f; GLfloat m_max_aniso = 0.0f;
int m_aniso = 1; int m_aniso = 1;
std::vector<GLuint> m_item_textures; std::vector<GLuint> m_item_textures;
void load_block_status(unsigned status_id); void load_block_status(unsigned status_id);
void load_block_texture(unsigned block_id); void load_block_texture(unsigned block_id);
void load_item_texture(const std::string& name); void load_block_item_texture(unsigned id);
void load_cross_plane_texture(unsigned id);
void load_ui_texture(unsigned id); void load_ui_texture(unsigned id);
void init_item(); void init_item();
void init_block(); void init_block();
@@ -31,6 +34,7 @@ public:
void delet_texture(); void delet_texture();
GLuint get_block_status_array() const; GLuint get_block_status_array() const;
GLuint get_texture_array() const; GLuint get_texture_array() const;
GLuint get_cross_plane_array() const;
GLuint get_ui_array() const; GLuint get_ui_array() const;
const std::vector<GLuint>& item_textures() const; const std::vector<GLuint>& item_textures() const;
// Must call after MapTable::init_map() and glfwMakeContextCurrent(window); // Must call after MapTable::init_map() and glfwMakeContextCurrent(window);

View File

@@ -7,7 +7,17 @@ namespace HASH {
inline std::size_t str(std::string_view value) { inline std::size_t str(std::string_view value) {
return std::hash<std::string_view>{}(value); return std::hash<std::string_view>{}(value);
} }
inline uint32_t mix_hash(int32_t a, int32_t b, uint32_t fixed_seed) { inline uint32_t combine_32(uint32_t seed, uint32_t v) {
seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
inline uint32_t chunk_seed_hash(int32_t a, int32_t b, uint32_t fixed_seed) {
uint32_t seed =
combine_32(combine_32(fixed_seed, (uint32_t)a), (uint32_t)b);
return seed;
}
/*
inline uint32_t chunk_seed_hash(int32_t a, int32_t b, uint32_t fixed_seed) {
uint32_t h = fixed_seed; uint32_t h = fixed_seed;
h ^= (uint32_t)a * 0xcc9e2d51u; h ^= (uint32_t)a * 0xcc9e2d51u;
@@ -27,10 +37,8 @@ inline uint32_t mix_hash(int32_t a, int32_t b, uint32_t fixed_seed) {
return h; return h;
} }
inline uint32_t combine_32(uint32_t seed, uint32_t v) { */
seed ^= v + 0x9e3779b9 + (seed << 6) + (seed >> 2);
return seed;
}
} // namespace HASH } // namespace HASH
} // namespace Cubed } // namespace Cubed

View File

@@ -9,6 +9,9 @@ void extract_frustum_planes(const glm::mat4& mvp_matrix,
std::vector<glm::vec4>& planes); std::vector<glm::vec4>& planes);
float smootherstep(float edge0, float edge1, float x); float smootherstep(float edge0, float edge1, float x);
bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
const std::vector<glm::vec4>& planes);
float deterministic_random(int x, int z, uint64_t seed);
} // namespace Math } // namespace Math
} // namespace Cubed } // namespace Cubed

View File

@@ -6,6 +6,7 @@ readme = "README.md"
requires-python = ">=3.14" requires-python = ">=3.14"
dependencies = [ dependencies = [
"loguru>=0.7.3", "loguru>=0.7.3",
"pytomlpp>=1.1.0",
] ]
[dependency-groups] [dependency-groups]

View File

@@ -1,51 +1,325 @@
import argparse import argparse
import copy
import sys import sys
import tomllib from functools import singledispatch
from pathlib import Path from pathlib import Path
from pprint import pprint
from typing import Any from typing import Any
import pytomlpp
from loguru import logger from loguru import logger
VERSION = "0.0.1" VERSION = "0.0.1"
DATA_PATH = "assets/data/block" DATA_PATH = "assets/data/block"
TEXTURE_PATH = "assets/texture/block" TEXTURE_PATH = "assets/texture/block"
work_path = Path(__file__).parent.parent
data_path = work_path / DATA_PATH
texture_path = work_path / TEXTURE_PATH
def show_data_list():
work_path = Path().parent def collect_blocks() -> list[dict[str, Any]]:
data_path = work_path / DATA_PATH
blocks: list[dict[str, Any]] = [] blocks: list[dict[str, Any]] = []
for block in data_path.rglob("*.toml"): for block in data_path.rglob("*.toml"):
if not block.is_file(): if not block.is_file():
continue continue
if block.name == "template.toml": if block.name == "template.toml":
continue continue
with open(block, "rb") as f:
blocks.append(tomllib.load(f))
blocks.sort(key=lambda x: x["id"])
blocks.append(pytomlpp.loads(block.read_text(encoding="utf-8")))
blocks.sort(key=lambda x: x["id"])
return blocks
def save_data(blocks: list[dict[str, Any]]):
for block in blocks:
block_path: Path = data_path / (block["name"] + ".toml")
if not block_path.is_file():
logger.warning(
f"Block: {block_path} is not Exists and Will Create A New One"
)
block_path.write_text(pytomlpp.dumps(block))
def sync_template_value():
blocks = collect_blocks()
template_path = data_path / "template.toml"
if not template_path.is_file():
logger.error("Template.toml is not Exists!")
return
template_block = pytomlpp.loads(template_path.read_text(encoding="utf-8"))
add_count = 0
for key, value in template_block.items():
for block in blocks:
if key not in block:
block[key] = value
add_count += 1
save_data(blocks)
logger.info(f"Synced {add_count} template fields to blocks")
@singledispatch
def show_data_info(arg: Any):
logger.error("No Match show_data_info")
@show_data_info.register(type(None))
def _(arg: None):
blocks = collect_blocks()
print("Please Input Block Name or Id, Input exit or e to Exit")
while True:
input_str = input("Name or Id: ")
try:
id = int(input_str)
if id >= len(blocks) or id < 0:
print(f"Id: {id} Not Find, Input e or exit to Exit")
continue
pprint(blocks[id])
except ValueError:
if input_str.lower() == "exit" or input_str.lower() == "e":
break
find = False
for block in blocks:
if block["name"] == input_str:
pprint(block)
find = True
break
if not find:
print(f"Name: {input_str} Not Find, Input e or exit to Exit")
@show_data_info.register(int)
def _(id: int):
blocks = collect_blocks()
if id >= len(blocks) or id < 0:
logger.error(f"ID: {id} is Not Invaild!")
return
pprint(blocks[id])
@show_data_info.register(str)
def _(name: str):
blocks = collect_blocks()
find = False
for block in blocks:
if block["name"] == name:
pprint(block)
find = True
break
if not find:
logger.error(f"Block Name: {name} Not Find")
def change_key(block: dict[str, Any], key: str, value: str):
if type(block[key]) is str:
block[key] = value
elif type(block[key]) is int:
try:
v = int(value)
block[key] = v
except ValueError:
logger.error("The Value Is Not A Int")
return False
elif type(block[key]) is bool:
if value.lower() == "true" or value.lower() == "t":
block[key] = True
elif value.lower() == "false" or value.lower() == "f":
block[key] = False
else:
logger.error("The Value Is Not A Bool")
return False
elif type(block[key]) is float:
try:
v = float(value)
block[key] = v
except ValueError:
logger.error("The Value Is Not A Float")
return False
else:
logger.error("Unkown Key Type")
return False
return True
def handle_change(block: dict[str, Any]) -> dict[str, Any]:
print("Please Input Block Key, Input exit or e to Exit")
while True:
key = input("Key: ")
if key.lower() == "exit" or key.lower() == "e":
break
if key not in block:
logger.error("The Key Is Not Exists!")
continue
value = input("Value: ")
old_name = block[key]
if change_key(block, key, value):
print("Change Success")
if key == "name":
old_path: Path = data_path / (old_name + ".toml")
try:
old_path.unlink()
except FileNotFoundError:
logger.warning(
f"Name Change But Old File {old_name}.toml is Not Exists!"
)
else:
print("Change Fail")
pprint(block)
return block
@singledispatch
def change_data(arg: Any):
logger.error("Not Match change")
@change_data.register(int)
def _(id: int):
blocks = collect_blocks()
if id >= len(blocks) or id < 0:
logger.error(f"ID: {id} is Invaild!")
return
pprint(blocks[id])
blocks[id] = handle_change(blocks[id])
save_data(blocks)
@change_data.register(str)
def _(name: str):
blocks = collect_blocks()
find = False
for i, block in enumerate(blocks):
if block["name"] == name:
pprint(block)
blocks[i] = handle_change(block)
save_data(blocks)
find = True
break
if not find:
logger.error(f"Block Name: {name} Not Find")
@change_data.register(type(None))
def _(arg: None):
blocks = collect_blocks()
print("Please Input Block Name or Id, Input exit or e to Exit")
while True:
input_str = input("Name or Id: ")
try:
id = int(input_str)
if id >= len(blocks) or id < 0:
print(f"Id: {id} Not Find, Input e or exit to Exit")
continue
pprint(blocks[id])
blocks[id] = handle_change(blocks[id])
save_data(blocks)
except ValueError:
if input_str.lower() == "exit" or input_str.lower() == "e":
break
find = False
for i, block in enumerate(blocks):
if block["name"] == input_str:
pprint(block)
blocks[i] = handle_change(block)
save_data(blocks)
find = True
break
if not find:
print(f"Name: {input_str} Not Find, Input e or exit to Exit")
def show_data_list():
blocks = collect_blocks()
for block in blocks: for block in blocks:
print(f"id: {block['id']} name: {block['name']}") print(f"id: {block['id']} name: {block['name']}")
def check_path(): def check_path():
work_path = Path().parent
logger.info(f"Work Path {work_path.resolve()}") logger.info(f"Work Path {work_path.resolve()}")
logger.info(f"Script Dir {sys.path[0]}") logger.info(f"Script Dir {sys.path[0]}")
data_exists = True
data_path = work_path / DATA_PATH
if not data_path.exists(): if not data_path.exists():
logger.error(f"Blcoks Data Path {data_path} not Exists!") logger.error(f"Blocks Data Path {data_path} not Exists!")
data_exists = False
else: else:
logger.info(f"Blocks Data Path {data_path}") logger.info(f"Blocks Data Path {data_path}")
texture_exists = True
texture_path = work_path / TEXTURE_PATH
if not texture_path.exists(): if not texture_path.exists():
logger.error(f"Blocks Texture Path {texture_path} not Exists!") logger.error(f"Blocks Texture Path {texture_path} not Exists!")
texture_exists = False
else: else:
logger.info(f"Blocks Texture Path {texture_path}") logger.info(f"Blocks Texture Path {texture_path}")
return data_exists and texture_exists
def check_integrity():
find_error = False
errors = 0
if check_path():
blocks = collect_blocks()
template_path = data_path / "template.toml"
if not template_path.is_file():
logger.error("Template.toml is not Exists!")
find_error = True
errors += 1
return
template_block = pytomlpp.loads(template_path.read_text(encoding="utf-8"))
n = len(blocks)
for i in range(n):
if "id" not in blocks[i]:
logger.error(f"Id: {i} not Exists!")
find_error = True
errors += 1
continue
if blocks[i]["id"] != i:
logger.error(
f"Id Error, Block {blocks[i].get('name', 'Unknow')} Id Should Be {i} Instead of {blocks[i]['id']}"
)
find_error = True
errors += 1
for key, value in template_block.items():
if key not in blocks[i]:
logger.error(
f"Key Error, Block {blocks[i].get('name', 'Unknow')} Key {key} not Exists!"
)
find_error = True
errors += 1
continue
if type(blocks[i][key]) is not type(value):
logger.error(
f"Value Type Error, Block {blocks[i].get(key, 'Unknow')} The Type Should Be {type(value)}, Instead of {type(blocks[i][key])}"
)
find_error = True
errors += 1
if find_error:
logger.error(f"Find {errors} Errors")
else:
logger.info("No Error")
def add_new_block():
blocks = collect_blocks()
template_path = data_path / "template.toml"
if not template_path.is_file():
logger.error("Template.toml is not Exists, Can't Create A New Block!")
return
template_block = pytomlpp.loads(template_path.read_text(encoding="utf-8"))
new_block = copy.deepcopy(template_block)
num = len(blocks)
logger.info(f"New Block Id is {num}")
new_block["id"] = num
for key in template_block:
if key == "id":
continue
nvalue = input(f"Input {key}: ")
if not change_key(new_block, key, nvalue):
logger.error(f"Add Key {key} Value {nvalue} Fail")
return
new_block_path: Path = data_path / (new_block["name"] + ".toml")
new_block_path.write_text(pytomlpp.dumps(new_block))
logger.info("Successfully Add New Block!")
pprint(new_block)
def handle_args(args: argparse.Namespace): def handle_args(args: argparse.Namespace):
@@ -56,6 +330,30 @@ def handle_args(args: argparse.Namespace):
check_path() check_path()
if args.list: if args.list:
show_data_list() show_data_list()
if args.sync:
sync_template_value()
if args.info:
if args.info == "EMPTY":
show_data_info(None)
else:
try:
id = int(args.info)
show_data_info(id)
except ValueError:
show_data_info(args.info)
if args.change:
if args.change == "EMPTY":
change_data(None)
else:
try:
id = int(args.change)
change_data(id)
except ValueError:
change_data(args.change)
if args.check:
check_integrity()
if args.new:
add_new_block()
def init_parser(parser: argparse.ArgumentParser): def init_parser(parser: argparse.ArgumentParser):
@@ -66,6 +364,26 @@ def init_parser(parser: argparse.ArgumentParser):
"--path", action="store_true", help="Check Blcoks Data and Texture Path" "--path", action="store_true", help="Check Blcoks Data and Texture Path"
) )
parser.add_argument("-l", "--list", action="store_true", help="Show Blocks List") parser.add_argument("-l", "--list", action="store_true", help="Show Blocks List")
parser.add_argument(
"-s",
"--sync",
action="store_true",
help="Sync Template.toml Value to Other Toml, Only New Value Will Add",
)
parser.add_argument(
"-i",
"--info",
nargs="?",
const="EMPTY",
help="Show Block Data, If Provide Id Will Print the Corresponding Blcok Data, You Can Input Id or Name",
)
parser.add_argument(
"-c", "--change", nargs="?", const="EMPTY", help="Change Block Data"
)
parser.add_argument(
"-C", "--check", action="store_true", help="Check The Block Data Integrity"
)
parser.add_argument("-n", "--new", action="store_true", help="Add A New Block")
def main(): def main():

View File

@@ -51,7 +51,7 @@ void App::init() {
cursor_enter_callback); cursor_enter_callback);
glfwSetCharCallback(m_window.get_glfw_window(), char_callback); glfwSetCharCallback(m_window.get_glfw_window(), char_callback);
ChunkGenerator::init(); ChunkGenerator::init();
BlockManager::init();
m_renderer.init(); m_renderer.init();
Logger::info("Renderer Init Success"); Logger::info("Renderer Init Success");
m_window.update_viewport(); m_window.update_viewport();
@@ -257,6 +257,7 @@ void App::update() {
m_renderer.update_fov(fov + 5.0f); m_renderer.update_fov(fov + 5.0f);
} }
} }
m_renderer.update(delta_time);
} }
int App::start_cubed_application(int argc, char** argv) { int App::start_cubed_application(int argc, char** argv) {

185
src/block.cpp Normal file
View File

@@ -0,0 +1,185 @@
#include "Cubed/gameplay/block.hpp"
#include "Cubed/config.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#include <filesystem>
#include <toml++/toml.hpp>
namespace fs = std::filesystem;
using namespace std::string_literals;
namespace {
std::string block_data_dir = ASSETS_PATH + "data/block"s;
template <Cubed::TomlValueType T>
std::optional<T> safe_get_value(const toml::table& table, std::string_view key,
const T& default_value) {
auto value = table[key].value<T>();
if (value == std::nullopt) {
Cubed::Logger::warn("Key {} Is Not Find, Wiil Set the Default Value {}",
key, default_value);
value = default_value;
}
return value;
}
} // namespace
namespace Cubed {
const std::vector<BlockData>& BlockManager::datas() {
ASSERT(is_init);
return m_datas;
}
unsigned BlockManager::sums() {
ASSERT(is_init);
return m_datas.size();
}
unsigned BlockManager::cross_plane_sum() {
ASSERT(is_init);
return m_cross_plane_map.size();
}
const std::string& BlockManager::name_form_id(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].name;
}
return m_datas[id].name;
}
bool BlockManager::is_gas(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_gas;
}
return m_datas[id].is_gas;
}
bool BlockManager::is_liquid(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_liquid;
}
return m_datas[id].is_liquid;
}
bool BlockManager::is_cross_plane(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_cross_plane;
}
return m_datas[id].is_cross_plane;
}
bool BlockManager::is_transparent(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_transparent;
}
return m_datas[id].is_transparent;
}
bool BlockManager::is_passable(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_passable;
}
return m_datas[id].is_passable;
}
bool BlockManager::is_discard(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_discard;
}
return m_datas[id].is_discard;
}
bool BlockManager::is_blend(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_blend;
}
return m_datas[id].is_blend;
}
bool BlockManager::is_transitional(BlockType id) {
if (id >= sums()) {
Logger::error("Id {}, is Over The Max Id", id, sums() - 1);
return m_datas[0].is_transitional;
}
return m_datas[id].is_transitional;
}
void BlockManager::init() {
fs::path data_path{block_data_dir};
for (auto entry : fs::recursive_directory_iterator(data_path)) {
if (!entry.is_regular_file()) {
continue;
}
if (entry.path().filename() == "template.toml") {
continue;
}
toml::table block;
try {
block = toml::parse_file(entry.path().string());
} catch (const toml::parse_error& err) {
Logger::error("Load Block Data {} Fail, Parser Error {}",
entry.path().string(), err.what());
ASSERT(false);
}
auto id = block["id"].value<int>();
if (id == std::nullopt) {
Logger::error("Very Serious Error, Block Id Not Find !!!, Please "
"Check The Block Data Integrity");
std::abort();
}
auto name = block["name"].value<std::string>();
if (name == std::nullopt) {
Logger::error("Very Serious Error, Block Name Not Find !!!, Please "
"Check The Block Data Integrity");
std::abort();
}
auto is_liquid = safe_get_value(block, "is_liquid", false);
auto is_passable = safe_get_value(block, "is_passable", false);
auto is_cross_plane = safe_get_value(block, "is_cross_plane", false);
auto is_transparent = safe_get_value(block, "is_transparent", false);
auto is_gas = safe_get_value(block, "is_gas", false);
auto is_discard = safe_get_value(block, "is_discard", false);
auto is_blend = safe_get_value(block, "is_blend", false);
auto is_transitional = safe_get_value(block, "is_transitional", false);
m_datas.emplace_back(*id, *name, *is_liquid, *is_passable,
*is_cross_plane, *is_transparent, *is_gas,
*is_discard, *is_blend, *is_transitional);
}
std::sort(
m_datas.begin(), m_datas.end(),
[](const BlockData& a, const BlockData& b) { return a.id < b.id; });
set_up_cross_plane_map();
is_init = true;
}
BlockType BlockManager::cross_plane_index(BlockType id) {
auto it = m_cross_plane_map.find(id);
if (it == m_cross_plane_map.end()) {
Logger::error("Can't Find Cross Plane Id {}", id);
ASSERT(false);
throw std::out_of_range{"Can't Find Cross Plane Id" +
std::to_string(id)};
}
return it->second;
}
void BlockManager::set_up_cross_plane_map() {
unsigned cur_id = 0;
for (const auto& data : m_datas) {
if (data.is_cross_plane) {
m_cross_plane_map[data.id] = cur_id;
cur_id++;
}
}
}
} // namespace Cubed

View File

@@ -1,6 +1,7 @@
#include "Cubed/camera.hpp" #include "Cubed/camera.hpp"
#include "Cubed/gameplay/player.hpp" #include "Cubed/gameplay/player.hpp"
#include "Cubed/gameplay/world.hpp"
#include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_assert.hpp"
namespace Cubed { namespace Cubed {
@@ -12,6 +13,13 @@ void Camera::update_move_camera() {
auto pos = m_player->get_player_pos(); auto pos = m_player->get_player_pos();
// pos.y need to add 1.6f to center // pos.y need to add 1.6f to center
m_camera_pos = glm::vec3(pos.x, pos.y + 1.6f, pos.z); m_camera_pos = glm::vec3(pos.x, pos.y + 1.6f, pos.z);
glm::ivec3 block_pos = glm::floor(m_camera_pos);
auto& world = m_player->get_world();
if (world.get_block_tpye(block_pos) == 7) {
m_under_water = true;
} else {
m_under_water = false;
}
} }
void Camera::camera_init(Player* player) { void Camera::camera_init(Player* player) {
@@ -50,4 +58,6 @@ const glm::mat4 Camera::get_camera_lookat() const {
const glm::vec3& Camera::get_camera_pos() const { return m_camera_pos; } const glm::vec3& Camera::get_camera_pos() const { return m_camera_pos; }
bool Camera::is_under_water() const { return m_under_water; }
} // namespace Cubed } // namespace Cubed

View File

@@ -3,7 +3,6 @@
#include "Cubed/app.hpp" #include "Cubed/app.hpp"
#include "Cubed/config.hpp" #include "Cubed/config.hpp"
#include "Cubed/gameplay/player.hpp" #include "Cubed/gameplay/player.hpp"
#include "Cubed/map_table.hpp"
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include <imgui.h> #include <imgui.h>
@@ -551,7 +550,7 @@ void DevPanel::show_player_tab_item() {
void DevPanel::show_items_tab_item() { void DevPanel::show_items_tab_item() {
auto& textures = m_app.texture_manager().item_textures(); auto& textures = m_app.texture_manager().item_textures();
auto& names = MapTable::item_map(); // auto& names = MapTable::item_map();
if (ImGui::BeginTabItem("item")) { if (ImGui::BeginTabItem("item")) {
ImGui::Text("Place Block "); ImGui::Text("Place Block ");
ImGui::SameLine(); ImGui::SameLine();
@@ -567,7 +566,7 @@ void DevPanel::show_items_tab_item() {
} }
if (ImGui::IsItemHovered()) { if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip(); ImGui::BeginTooltip();
ImGui::Text("%s", names[i].c_str()); ImGui::Text("%s", BlockManager::name_form_id(i).c_str());
ImGui::EndTooltip(); ImGui::EndTooltip();
} }
if (i % 10 != 0) { if (i % 10 != 0) {

View File

@@ -62,6 +62,10 @@ std::string get_biome_str(BiomeType biome) {
break; break;
case SNOWY_PLAIN: case SNOWY_PLAIN:
str = "Snowy Plain"; str = "Snowy Plain";
break;
case OCEAN:
str = "Ocean";
break;
case NONE: case NONE:
str = "Unknown"; str = "Unknown";
break; break;
@@ -190,6 +194,9 @@ BiomeType determine_biome(const BiomeConditions& conditions) {
if (conditions.mountainous > 0.75) { if (conditions.mountainous > 0.75) {
return MOUNTAIN; return MOUNTAIN;
} }
if (conditions.mountainous < 0.25) {
return OCEAN;
}
auto temp = conditions.temp; auto temp = conditions.temp;
auto humid = conditions.humid; auto humid = conditions.humid;
if (temp < 0.5) { if (temp < 0.5) {

View File

@@ -15,5 +15,47 @@ void BiomeBuilder::build_bottom() {
} }
} }
} }
void BiomeBuilder::place_grass() {
ChunkGenerator& chunk_generator = get_chunk_generator();
Chunk& chunk = chunk_generator.chunk();
auto& blocks = chunk.blocks();
const auto& heightmap = chunk.get_heightmap();
auto& random = chunk_generator.random();
for (int x = 0; x < SIZE_X; ++x) {
for (int z = 0; z < SIZE_Z; ++z) {
int y = heightmap[x][z];
BlockType top_id = blocks[Chunk::index(x, y, z)];
if (top_id != 1) {
continue;
}
if (blocks[Chunk::index(x, y + 1, z)] != 0) {
continue;
}
if (random.random_bool(0.2)) {
if (y + 1 < SIZE_Y) {
blocks[Chunk::index(x, y + 1, z)] = 9;
}
}
}
}
}
void BiomeBuilder::ocean_water_build() {
ChunkGenerator& chunk_generator = get_chunk_generator();
Chunk& chunk = chunk_generator.chunk();
auto& blocks = chunk.blocks();
const auto& heightmap = chunk.get_heightmap();
for (int x = 0; x < SIZE_X; ++x) {
for (int z = 0; z < SIZE_Z; ++z) {
int height = heightmap[x][z];
if (height <= SEA_LEVEL) {
for (int y = height; y <= SEA_LEVEL; y++) {
blocks[Chunk::index(x, y, z)] = 7;
}
}
}
}
}
} // namespace Cubed } // namespace Cubed

View File

@@ -4,6 +4,7 @@
#include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/chunk_generator.hpp"
#include "Cubed/gameplay/tree.hpp" #include "Cubed/gameplay/tree.hpp"
#include <algorithm>
#include <numeric> #include <numeric>
namespace Cubed { namespace Cubed {
@@ -52,6 +53,7 @@ void ForestBuilder::build_vegetation() {
} }
} }
} }
place_grass();
} }
ChunkGenerator& ForestBuilder::get_chunk_generator() { ChunkGenerator& ForestBuilder::get_chunk_generator() {

View File

@@ -0,0 +1,34 @@
#include "Cubed/gameplay/builders/ocean_builder.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_generator.hpp"
namespace Cubed {
OceanBuilder::OceanBuilder(ChunkGenerator& chunk_generator)
: m_chunk_generator(chunk_generator) {}
void OceanBuilder::build_biome() {
BiomeBuilder::build_bottom();
build_blocks();
};
void OceanBuilder::build_blocks() {
auto& m_chunk = m_chunk_generator.chunk();
auto& m_blocks = m_chunk.blocks();
auto& m_heightmap = m_chunk.heightmap();
for (int x = 0; x < CHUNK_SIZE; x++) {
for (int z = 0; z < CHUNK_SIZE; z++) {
int height = static_cast<int>(m_heightmap[x][z]);
for (int y = 5; y <= height; y++) {
m_blocks[Chunk::index(x, y, z)] = 3;
}
}
}
}
void OceanBuilder::build_vegetation() {}
ChunkGenerator& OceanBuilder::get_chunk_generator() {
return m_chunk_generator;
};
} // namespace Cubed

View File

@@ -29,7 +29,7 @@ void PlainBuilder::build_blocks() {
} }
} }
void PlainBuilder::build_vegetation() {} void PlainBuilder::build_vegetation() { place_grass(); }
ChunkGenerator& PlainBuilder::get_chunk_generator() { ChunkGenerator& PlainBuilder::get_chunk_generator() {
return m_chunk_generator; return m_chunk_generator;

View File

@@ -5,11 +5,10 @@
namespace Cubed { namespace Cubed {
CaveCarver::CaveCarver() {} CaveCarver::CaveCarver() {}
std::unordered_map<unsigned, CavePath>& CaveCarver::paths() { return m_paths; } CaveCarver::CaveHashMap& CaveCarver::paths() { return m_paths; }
void CaveCarver::init(unsigned world_seed) { void CaveCarver::init(unsigned world_seed) {
m_seed = world_seed; m_seed = world_seed;
m_sum = 0;
m_random.init(m_seed); m_random.init(m_seed);
} }
@@ -20,16 +19,18 @@ void CaveCarver::reload(unsigned world_seed) {
} }
void CaveCarver::add_path(const glm::vec3& pos, unsigned chunk_seed) { void CaveCarver::add_path(const glm::vec3& pos, unsigned chunk_seed) {
m_paths.emplace(chunk_seed, CavePath{m_seed, m_sum, pos}); m_paths.emplace(chunk_seed, CavePath{chunk_seed, m_seed, pos});
m_sum++;
} }
void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos, void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos,
unsigned chunk_seed) { unsigned chunk_seed) {
auto it = m_paths.find(chunk_seed); {
if (it != m_paths.end()) { CaveHashMap::const_accessor acc;
return; if (m_paths.find(acc, chunk_seed)) {
return;
}
} }
Random random{chunk_seed}; Random random{chunk_seed};
if (random.random_bool(static_cast<double>(m_cave_probability))) { if (random.random_bool(static_cast<double>(m_cave_probability))) {
const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE; const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE;
@@ -47,10 +48,17 @@ void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos,
} }
void CaveCarver::cleanup_finished_caves() { void CaveCarver::cleanup_finished_caves() {
std::erase_if(m_paths, std::vector<unsigned int> finished_keys;
[](const auto& kv) { return kv.second.is_finished(); }); for (const auto& pair : m_paths) {
if (pair.second.is_finished()) {
finished_keys.push_back(pair.first);
}
}
for (const auto& key : finished_keys) {
m_paths.erase(key);
}
} }
int CaveCarver::cave_sum() const { return m_sum; } int CaveCarver::cave_sum() const { return m_paths.size(); }
float& CaveCarver::cave_probability() { return m_cave_probability; } float& CaveCarver::cave_probability() { return m_cave_probability; }
} // namespace Cubed } // namespace Cubed

View File

@@ -6,10 +6,9 @@
#include <algorithm> #include <algorithm>
namespace Cubed { namespace Cubed {
CavePath::CavePath(unsigned int world_seed, int path_id, CavePath::CavePath(unsigned int chunk_seed, unsigned world_seed,
const glm::vec3& start_pos) { const glm::vec3& start_pos) {
m_path_id = path_id; m_seed = HASH::combine_32(chunk_seed, world_seed);
m_seed = HASH::combine_32(world_seed, path_id);
m_random.init(m_seed); m_random.init(m_seed);
m_yaw = m_random.random_float(0.0f, 360.0f); m_yaw = m_random.random_float(0.0f, 360.0f);
m_pitch = m_random.random_float(-10.0f, 10.0f); m_pitch = m_random.random_float(-10.0f, 10.0f);
@@ -75,7 +74,8 @@ void CavePath::precompute_chunk_coverage() {
for (int cx = min_cx; cx <= max_cx; ++cx) for (int cx = min_cx; cx <= max_cx; ++cx)
for (int cz = min_cz; cz <= max_cz; ++cz) for (int cz = min_cz; cz <= max_cz; ++cz)
m_pending_chunks.insert({cx, cz}); m_pending_chunks.insert(
std::make_pair(ChunkPos{cx, cz}, false));
} }
} }

View File

@@ -9,41 +9,36 @@
namespace Cubed { namespace Cubed {
Chunk::Chunk(World& world, ChunkPos chunk_pos) Chunk::Chunk(World& world, ChunkPos chunk_pos)
: m_chunk_pos(chunk_pos), m_world(world) {} : m_chunk_pos(chunk_pos), m_world(world) {
for (int i = 0; i < VERTEX_DATA_SUM; i++) {
Chunk::~Chunk() { m_vertex_data.emplace_back(m_world);
if (m_vbo != 0) {
m_world.push_delete_vbo(m_vbo);
} }
} }
Chunk::~Chunk() {}
Chunk::Chunk(Chunk&& other) noexcept Chunk::Chunk(Chunk&& other) noexcept
: m_dirty(other.is_dirty()), m_need_upload(other.m_need_upload.load()), : m_dirty(other.is_dirty()), m_need_upload(other.m_need_upload.load()),
m_is_on_gen_vertex_data(other.m_is_on_gen_vertex_data.load()), m_is_on_gen_vertex_data(other.m_is_on_gen_vertex_data.load()),
m_vertex_sum(other.m_vertex_sum.load()), m_biome(other.m_biome.load()), m_biome(other.m_biome.load()), m_chunk_pos(std::move(other.m_chunk_pos)),
m_chunk_pos(std::move(other.m_chunk_pos)), m_world(other.m_world), m_world(other.m_world), m_heightmap(std::move(other.m_heightmap)),
m_heightmap(std::move(other.m_heightmap)), m_blocks(std::move(other.m_blocks)),
m_blocks(std::move(other.m_blocks)), m_vbo(other.m_vbo), m_vertex_data(std::move(other.m_vertex_data)), m_seed(other.m_seed),
m_vertexs_data(std::move(other.m_vertexs_data)), m_seed(other.m_seed), m_conditions(other.m_conditions) {}
m_conditions(other.m_conditions) {
other.m_vbo = 0;
}
Chunk& Chunk::operator=(Chunk&& other) noexcept { Chunk& Chunk::operator=(Chunk&& other) noexcept {
// Logger::info("other Chunk pos {} {} in Chunk& Chunk::operator=(Chunk&& // Logger::info("other Chunk pos {} {} in Chunk& Chunk::operator=(Chunk&&
// other) this {}", other.m_chunk_pos.x, other.m_chunk_pos.z, // other) this {}", other.m_chunk_pos.x, other.m_chunk_pos.z,
// static_cast<const void*>(&other)); // static_cast<const void*>(&other));
m_vbo = other.m_vbo;
other.m_vbo = 0;
m_chunk_pos = std::move(other.m_chunk_pos); m_chunk_pos = std::move(other.m_chunk_pos);
m_heightmap = std::move(other.m_heightmap); m_heightmap = std::move(other.m_heightmap);
m_blocks = std::move(other.m_blocks); m_blocks = std::move(other.m_blocks);
m_dirty = other.is_dirty(); m_dirty = other.is_dirty();
m_vertexs_data = std::move(other.m_vertexs_data); m_vertex_data = std::move(other.m_vertex_data);
m_biome = other.m_biome.load(); m_biome = other.m_biome.load();
m_is_on_gen_vertex_data = other.m_is_on_gen_vertex_data.load(); m_is_on_gen_vertex_data = other.m_is_on_gen_vertex_data.load();
m_need_upload = other.m_need_upload.load(); m_need_upload = other.m_need_upload.load();
m_vertex_sum = other.m_vertex_sum.load();
m_seed = other.m_seed; m_seed = other.m_seed;
m_conditions = other.m_conditions; m_conditions = other.m_conditions;
return *this; return *this;
@@ -108,136 +103,47 @@ int Chunk::index(const glm::vec3& pos) {
return Chunk::index(pos.x, pos.y, pos.z); return Chunk::index(pos.x, pos.y, pos.z);
} }
void Chunk::gen_vertex_data( void Chunk::gen_vertex_data(const OptionalBlockVectorArray& neighbor_block) {
const std::array<const std::vector<BlockType>*, 4>& neighbor_block) {
if (m_is_on_gen_vertex_data) { if (m_is_on_gen_vertex_data) {
return; return;
} }
m_is_on_gen_vertex_data = true; m_is_on_gen_vertex_data = true;
std::lock_guard lk(m_vertexs_data_mutex); std::lock_guard lk(m_vertexs_data_mutex);
m_vertexs_data.clear();
static const glm::ivec3 DIR[6] = {{0, 0, 1}, {1, 0, 0}, {0, 0, -1}, for (auto& data : m_vertex_data) {
{-1, 0, 0}, {0, 1, 0}, {0, -1, 0}}; data.m_vertices.clear();
}
for (int x = 0; x < SIZE_X; x++) {
for (int y = 0; y < SIZE_Y; y++) { gen_vertices(neighbor_block);
for (int z = 0; z < SIZE_Z; z++) { for (auto& data : m_vertex_data) {
int world_x = x + m_chunk_pos.x * CHUNK_SIZE; data.update_sum();
int world_z = z + m_chunk_pos.z * CHUNK_SIZE;
int world_y = y;
int cur_id = m_blocks[index(x, y, z)];
// air
if (cur_id == 0) {
continue;
}
for (int face = 0; face < 6; face++) {
int nx = x + DIR[face].x;
int ny = y + DIR[face].y;
int nz = z + DIR[face].z;
bool neighbor_cull = false;
if (nx < 0 || nx >= SIZE_X || ny < 0 || ny >= SIZE_Y ||
nz < 0 || nz >= SIZE_Z) {
int world_nx = world_x + DIR[face].x;
int world_ny = world_y + DIR[face].y;
int world_nz = world_z + DIR[face].z;
auto [neighbor_x, neighbor_z] =
World::chunk_pos(world_nx, world_nz);
auto is_cull =
[&](const std::vector<BlockType>* chunk_blocks) {
if (chunk_blocks == nullptr) {
return true;
}
int x, y, z;
y = world_ny;
x = world_nx - neighbor_x * CHUNK_SIZE;
z = world_nz - neighbor_z * CHUNK_SIZE;
if (x < 0 || y < 0 || z < 0 ||
x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
return false;
}
int idx = Chunk::index(x, y, z);
// not init
if (static_cast<size_t>(idx) >=
chunk_blocks->size()) {
// Logger::warn("not init");
return true;
}
auto id = (*chunk_blocks)[idx];
if (is_in_transparent_map(id)) {
if (id == cur_id) {
return true;
} else {
return false;
}
} else {
return true;
}
};
if (m_chunk_pos.x + 1 == neighbor_x) {
neighbor_cull = is_cull(neighbor_block[0]);
} else if (m_chunk_pos.x - 1 == neighbor_x) {
neighbor_cull = is_cull(neighbor_block[1]);
} else if (m_chunk_pos.z + 1 == neighbor_z) {
neighbor_cull = is_cull(neighbor_block[2]);
} else if (m_chunk_pos.z - 1 == neighbor_z) {
neighbor_cull = is_cull(neighbor_block[3]);
}
// neighbor_cull = m_world.is_block(glm::ivec3(world_x,
// world_y, world_z) + DIR[face]);
} else {
auto id = m_blocks[index(nx, ny, nz)];
if (!is_in_transparent_map(id)) {
neighbor_cull = true;
} else {
if (id == cur_id) {
neighbor_cull = true;
} else {
neighbor_cull = false;
}
}
}
if (neighbor_cull) {
continue;
}
for (int i = 0; i < 6; i++) {
Vertex vex = {
VERTICES_POS[face][i][0] + (float)world_x * 1.0f,
VERTICES_POS[face][i][1] + (float)world_y * 1.0f,
VERTICES_POS[face][i][2] + (float)world_z * 1.0f,
TEX_COORDS[face][i][0],
TEX_COORDS[face][i][1],
static_cast<float>(cur_id * 6 + face)
};
m_vertexs_data.emplace_back(vex);
}
}
}
}
} }
m_vertex_sum = m_vertexs_data.size();
m_need_upload = true; m_need_upload = true;
m_is_on_gen_vertex_data = false; m_is_on_gen_vertex_data = false;
} }
GLuint Chunk::get_vbo() const { return m_vbo; } GLuint Chunk::get_normal_vao() const { return m_vertex_data[0].m_vao; }
size_t Chunk::get_vertex_sum() const { size_t Chunk::get_normal_vertices_sum() const {
if (m_vertex_sum == 0) { if (m_vertex_data[0].m_sum == 0) {
Logger::warn("m_vertex_sum is 0"); Logger::warn("m_normal_vertices_sum is 0");
} }
return m_vertex_sum.load(); return m_vertex_data[0].m_sum.load();
}
GLuint Chunk::get_cross_vao() const { return m_vertex_data[1].m_vao; }
size_t Chunk::get_cross_vertices_sum() const {
return m_vertex_data[1].m_sum.load();
}
GLuint Chunk::get_normal_discard_vao() const { return m_vertex_data[2].m_vao; }
size_t Chunk::get_normal_discard_vertices_sum() const {
return m_vertex_data[2].m_sum.load();
}
GLuint Chunk::get_normal_blend_vao() const { return m_vertex_data[3].m_vao; }
size_t Chunk::get_normal_blend_vertices_sum() const {
return m_vertex_data[3].m_sum.load();
} }
void Chunk::gen_phase_one() { void Chunk::gen_phase_one() {
@@ -292,9 +198,8 @@ void Chunk::gen_phase_six(
Logger::error("ChunkGenerator is Nullptr"); Logger::error("ChunkGenerator is Nullptr");
return; return;
} }
// This must be fully completed before any other operations can proceed!
m_generator->blend_surface_blocks_borders(neighbor_block); m_generator->blend_surface_blocks_borders(neighbor_block);
m_generator->generate_cave();
m_generator->generate_river();
} }
void Chunk::gen_phase_seven() { void Chunk::gen_phase_seven() {
@@ -302,6 +207,10 @@ void Chunk::gen_phase_seven() {
Logger::error("ChunkGenerator is Nullptr"); Logger::error("ChunkGenerator is Nullptr");
return; return;
} }
m_generator->ocean_build();
m_generator->generate_river();
m_generator->generate_cave();
m_generator->generate_vegetation(); m_generator->generate_vegetation();
mark_dirty(); mark_dirty();
m_generator = nullptr; m_generator = nullptr;
@@ -310,14 +219,13 @@ void Chunk::gen_phase_seven() {
void Chunk::upload_to_gpu() { void Chunk::upload_to_gpu() {
ASSERT(is_need_upload()); ASSERT(is_need_upload());
if (m_vbo == 0) {
glGenBuffers(1, &m_vbo);
}
std::lock_guard lk(m_vertexs_data_mutex); std::lock_guard lk(m_vertexs_data_mutex);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, m_vertexs_data.size() * sizeof(Vertex), for (auto& data : m_vertex_data) {
m_vertexs_data.data(), GL_DYNAMIC_DRAW); data.upload();
glBindBuffer(GL_ARRAY_BUFFER, 0); }
// after fininshed it, can use // after fininshed it, can use
clear_dirty(); clear_dirty();
m_need_upload = false; m_need_upload = false;
@@ -356,4 +264,169 @@ unsigned Chunk::seed() const {
BiomeConditions& Chunk::conditions() { return m_conditions; } BiomeConditions& Chunk::conditions() { return m_conditions; }
void Chunk::gen_vertices(const OptionalBlockVectorArray& neighbor_block) {
static const glm::ivec3 DIR[6] = {{0, 0, 1}, {1, 0, 0}, {0, 0, -1},
{-1, 0, 0}, {0, 1, 0}, {0, -1, 0}};
for (int x = 0; x < SIZE_X; x++) {
for (int y = 0; y < SIZE_Y; y++) {
for (int z = 0; z < SIZE_Z; z++) {
int world_x = x + m_chunk_pos.x * CHUNK_SIZE;
int world_z = z + m_chunk_pos.z * CHUNK_SIZE;
int world_y = y;
int cur_id = m_blocks[index(x, y, z)];
// air
if (cur_id == 0) {
continue;
}
for (int face = 0; face < 6; face++) {
int nx = x + DIR[face].x;
int ny = y + DIR[face].y;
int nz = z + DIR[face].z;
bool neighbor_culled = false;
if (nx < 0 || nx >= SIZE_X || ny < 0 || ny >= SIZE_Y ||
nz < 0 || nz >= SIZE_Z) {
int world_nx = world_x + DIR[face].x;
int world_ny = world_y + DIR[face].y;
int world_nz = world_z + DIR[face].z;
auto [neighbor_x, neighbor_z] =
World::chunk_pos(world_nx, world_nz);
auto is_culled =
[&](const std::optional<std::vector<BlockType>>&
chunk_blocks) {
if (chunk_blocks == std::nullopt) {
return true;
}
int x, y, z;
y = world_ny;
x = world_nx - neighbor_x * CHUNK_SIZE;
z = world_nz - neighbor_z * CHUNK_SIZE;
if (x < 0 || y < 0 || z < 0 ||
x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
return false;
}
int idx = Chunk::index(x, y, z);
// not init
if (static_cast<size_t>(idx) >=
chunk_blocks->size()) {
// Logger::warn("not init");
return true;
}
auto id = (*chunk_blocks)[idx];
// transparent
if (BlockManager::is_transparent(id)) {
if (id == cur_id) {
return true;
} else {
return false;
}
} else {
return true;
}
};
if (m_chunk_pos.x + 1 == neighbor_x) {
neighbor_culled = is_culled(neighbor_block[0]);
} else if (m_chunk_pos.x - 1 == neighbor_x) {
neighbor_culled = is_culled(neighbor_block[1]);
} else if (m_chunk_pos.z + 1 == neighbor_z) {
neighbor_culled = is_culled(neighbor_block[2]);
} else if (m_chunk_pos.z - 1 == neighbor_z) {
neighbor_culled = is_culled(neighbor_block[3]);
}
// neighbor_cull = m_world.is_block(glm::ivec3(world_x,
// world_y, world_z) + DIR[face]);
} else {
auto neighbor_id = m_blocks[index(nx, ny, nz)];
// transparent block
if (!BlockManager::is_transparent(neighbor_id)) {
neighbor_culled = true;
} else {
if (neighbor_id == cur_id) {
neighbor_culled = true;
} else {
neighbor_culled = false;
}
}
}
if (neighbor_culled) {
continue;
}
if (BlockManager::is_cross_plane(cur_id)) {
gen_cross_plane_vertices(world_x, world_y, world_z,
cur_id);
}
for (int i = 0; i < 6; i++) {
Vertex vex = {
VERTICES_POS[face][i][0] + (float)world_x * 1.0f,
VERTICES_POS[face][i][1] + (float)world_y * 1.0f,
VERTICES_POS[face][i][2] + (float)world_z * 1.0f,
TEX_COORDS[face][i][0],
TEX_COORDS[face][i][1],
static_cast<float>(cur_id * 6 + face)
};
if (BlockManager::is_transparent(cur_id)) {
if (BlockManager::is_discard(cur_id) &&
BlockManager::is_blend(cur_id)) {
Logger::warn(
"Block id {} is both discard and blend is "
"must only one can true !!!",
cur_id);
}
if (BlockManager::is_discard(cur_id)) {
m_vertex_data[2].m_vertices.emplace_back(vex);
} else if (BlockManager::is_blend(cur_id)) {
m_vertex_data[3].m_vertices.emplace_back(vex);
} else {
Logger::warn("Id {} is transparent but not "
"discard or blend",
cur_id);
m_vertex_data[3].m_vertices.emplace_back(vex);
}
} else {
m_vertex_data[0].m_vertices.emplace_back(vex);
}
}
}
}
}
}
}
void Chunk::gen_cross_plane_vertices(int world_x, int world_y, int world_z,
BlockType id) {
if (!BlockManager::is_cross_plane(id)) {
Logger::warn("Block {} {} {} id {} is not cross plane", world_x,
world_y, world_z, id);
return;
}
for (int face = 0; face < 2; face++) {
for (int i = 0; i < 6; i++) {
Vertex vex = {
CROSS_VERTICES_POS[face][i][0] + (float)world_x * 1.0f,
CROSS_VERTICES_POS[face][i][1] + (float)world_y * 1.0f,
CROSS_VERTICES_POS[face][i][2] + (float)world_z * 1.0f,
CROSS_TEX_COORDS[face][i][0],
CROSS_TEX_COORDS[face][i][1],
static_cast<float>(BlockManager::cross_plane_index(id))
};
m_vertex_data[1].m_vertices.emplace_back(vex);
}
}
}
// Logger::info("Cross Sum {}", m_cross_vertices_sum.load());
} // namespace Cubed } // namespace Cubed

View File

@@ -3,12 +3,14 @@
#include "Cubed/gameplay/builders/desert_builder.hpp" #include "Cubed/gameplay/builders/desert_builder.hpp"
#include "Cubed/gameplay/builders/forest_builder.hpp" #include "Cubed/gameplay/builders/forest_builder.hpp"
#include "Cubed/gameplay/builders/mountain_builder.hpp" #include "Cubed/gameplay/builders/mountain_builder.hpp"
#include "Cubed/gameplay/builders/ocean_builder.hpp"
#include "Cubed/gameplay/builders/plain_builder.hpp" #include "Cubed/gameplay/builders/plain_builder.hpp"
#include "Cubed/gameplay/builders/river_builder.hpp" #include "Cubed/gameplay/builders/river_builder.hpp"
#include "Cubed/gameplay/builders/snowy_plain_builder.hpp" #include "Cubed/gameplay/builders/snowy_plain_builder.hpp"
#include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/tree.hpp" #include "Cubed/gameplay/tree.hpp"
#include "Cubed/gameplay/world.hpp" #include "Cubed/gameplay/world.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/math_tools.hpp" #include "Cubed/tools/math_tools.hpp"
#include "Cubed/tools/perlin_noise.hpp" #include "Cubed/tools/perlin_noise.hpp"
@@ -16,11 +18,11 @@ namespace Cubed {
using enum BiomeType; using enum BiomeType;
constexpr int BLEND_RADIUS = 12; constexpr int BLEND_RADIUS = 8;
ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) { ChunkGenerator::ChunkGenerator(Chunk& chunk) : m_chunk(chunk) {
ASSERT_MSG(is_init, "ChunksGenerator is not init"); ASSERT_MSG(is_init, "ChunksGenerator is not init");
ChunkPos pos = m_chunk.get_chunk_pos(); ChunkPos pos = m_chunk.get_chunk_pos();
unsigned seed = HASH::mix_hash(pos.x, pos.z, m_generator_seed); unsigned seed = HASH::chunk_seed_hash(pos.x, pos.z, m_generator_seed);
m_random.init(seed); m_random.init(seed);
m_chunk_seed = seed; m_chunk_seed = seed;
} }
@@ -165,18 +167,43 @@ void ChunkGenerator::generate_heightmap() {
amplitude = std::lerp(10, 40, t); amplitude = std::lerp(10, 40, t);
*/ */
float t; float t;
if (mountainous >= 0.7f) { if (mountainous >= 0.95f) {
t = Math::smootherstep(0.7f, 0.75, mountainous); t = Math::smootherstep(0.95f, 1.0f, mountainous);
base_y = std::lerp(70, 88, t); base_y = std::lerp(130, 140, t);
amplitude = std::lerp(28, 48, t); amplitude = std::lerp(38, 48, t);
} else if (mountainous >= 0.65f) { } else if (mountainous >= 0.85f) {
t = Math::smootherstep(0.65f, 0.7f, mountainous); t = Math::smootherstep(0.85f, 0.95f, mountainous);
base_y = std::lerp(66, 70, t); base_y = std::lerp(100, 130, t);
amplitude = std::lerp(28, 38, t);
} else if (mountainous >= 0.8) {
t = Math::smootherstep(0.8f, 0.85f, mountainous);
base_y = std::lerp(85, 100, t);
amplitude = std::lerp(18, 28, t); amplitude = std::lerp(18, 28, t);
} else if (mountainous >= 0.75f) {
t = Math::smootherstep(0.75f, 0.8f, mountainous);
base_y = std::lerp(70, 85, t);
amplitude = std::lerp(6, 18, t);
} else if (mountainous >= 0.7) {
t = Math::smootherstep(0.7f, 0.75f, mountainous);
base_y = std::lerp(66, 70, t);
amplitude = std::lerp(6, 6, t);
} else if (mountainous >= 0.45f) {
t = Math::smootherstep(0.45f, 0.7f, mountainous);
base_y = std::lerp(64, 66, t);
amplitude = std::lerp(6, 6, t);
} else if (mountainous >= 0.3f) {
t = Math::smootherstep(0.3f, 0.45f, mountainous);
base_y = std::lerp(60, 64, t);
amplitude = std::lerp(6, 6, t);
} else if (mountainous >= 0.25f) {
t = Math::smootherstep(0.25f, 0.3f, mountainous);
base_y = std::lerp(44, 60, t);
amplitude = std::lerp(6, 6, t);
} else { } else {
t = Math::smootherstep(0.55, 0.65, mountainous); t = Math::smootherstep(0.0f, 0.25f, mountainous);
base_y = std::lerp(58, 66, t); base_y = std::lerp(35, 44, t);
amplitude = std::lerp(8, 18, t); amplitude = std::lerp(3, 6, t);
} }
heightmap[x][z] = heightmap[x][z] =
base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain, base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain,
@@ -450,9 +477,11 @@ void ChunkGenerator::blend_surface_blocks_borders(
for (int y = WORLD_HEIGHT - 1; y >= 0; --y) { for (int y = WORLD_HEIGHT - 1; y >= 0; --y) {
int idx = Chunk::index(nx, y, int idx = Chunk::index(nx, y,
nz); // linear index: y * area + z * size + x nz); // linear index: y * area + z * size + x
if (idx >= 0 && idx < static_cast<int>(blocks.size()) && if (idx >= 0 && idx < static_cast<int>(blocks.size())) {
blocks[idx] != 0) { BlockType neighbor_type = blocks[idx];
return blocks[idx]; if (BlockManager::is_transitional(neighbor_type)) {
return neighbor_type;
}
} }
} }
return 0; // fallback, should not happen for valid chunks return 0; // fallback, should not happen for valid chunks
@@ -472,8 +501,8 @@ void ChunkGenerator::blend_surface_blocks_borders(
// Weight map: type -> total weight // Weight map: type -> total weight
std::unordered_map<BlockType, float> weights; std::unordered_map<BlockType, float> weights;
weights[type_self] = 1.0f; // self weight float self_weight = 1.0f;
weights[type_self] = self_weight;
// --- Right neighbor (index 0) --- // --- Right neighbor (index 0) ---
if (neighbor_block[0] && x >= CHUNK_SIZE - BLEND_RADIUS) { if (neighbor_block[0] && x >= CHUNK_SIZE - BLEND_RADIUS) {
int dist = (CHUNK_SIZE - 1) - x; int dist = (CHUNK_SIZE - 1) - x;
@@ -522,47 +551,50 @@ void ChunkGenerator::blend_surface_blocks_borders(
} }
} }
if (weights.empty()) {
continue;
}
// Find type with maximum total weight // Find type with maximum total weight
BlockType final_type = type_self; BlockType final_type = type_self;
float max_weight = weights[type_self]; /*float max_weight = weights[type_self];
for (const auto& [type, w] : weights) { for (const auto& [type, w] : weights) {
if (w > max_weight) { if (w > max_weight) {
max_weight = w; max_weight = w;
final_type = type; final_type = type;
} }
}*/
float sum = 0.0f;
for (auto& kv : weights) {
sum += kv.second;
}
float rnd = m_random.random_float(0.0f, 1.0f);
float accum = 0.0f;
for (auto [t, w] : weights) {
accum += w / sum;
if (rnd < accum) {
final_type = t;
break;
}
} }
if (final_type == 0) { if (!BlockManager::is_transitional(final_type)) {
return; continue;
} }
// Update the top block if the type changed // Update the top block if the type changed
if (final_type != type_self) { if (final_type != type_self) {
// top block // top block
if (final_type == 7 && top_y > SEA_LEVEL) { BlockType new_surface = final_type;
if (type_self == 7) { m_blocks[Chunk::index(x, top_y, z)] = new_surface;
m_blocks[Chunk::index(x, top_y, z)] = 0;
} else {
m_blocks[Chunk::index(x, top_y, z)] = type_self;
}
} else {
m_blocks[Chunk::index(x, top_y, z)] = final_type;
}
// bottom block // bottom block
unsigned fill_type = 2; unsigned fill_type = 2;
if (final_type == 1) { if (final_type == 1 || final_type == 8) {
fill_type = 2; fill_type = 2;
} else if (final_type == 4) { } else {
fill_type = 4; fill_type = final_type;
} }
for (int y = top_y - 5; y < top_y; y++) { for (int y = std::max(0, top_y - 5); y < top_y; y++) {
if (fill_type == 7 && y > SEA_LEVEL) { m_blocks[Chunk::index(x, y, z)] = fill_type;
m_blocks[Chunk::index(x, y, z)] = 0;
} else {
m_blocks[Chunk::index(x, y, z)] = fill_type;
}
} }
} }
} }
@@ -599,63 +631,109 @@ void ChunkGenerator::make_biome_builder() {
case SNOWY_PLAIN: case SNOWY_PLAIN:
m_biome_builder = std::make_unique<SnowyPlainBuilder>(*this); m_biome_builder = std::make_unique<SnowyPlainBuilder>(*this);
break; break;
case OCEAN:
m_biome_builder = std::make_unique<OceanBuilder>(*this);
break;
case NONE: case NONE:
m_biome_builder = nullptr; m_biome_builder = nullptr;
break; break;
} }
} }
void ChunkGenerator::ocean_build() { m_biome_builder->ocean_water_build(); }
void ChunkGenerator::carve_worm(
const std::vector<PathPoint>& points, const ChunkPos& chunk_pos,
std::function<void(int /*x*/, int /*y*/, int /*z*/)> on_hit) {
const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE;
const int CHUNK_MIN_Z = chunk_pos.z * CHUNK_SIZE;
const int CHUNK_MAX_X = CHUNK_MIN_X + SIZE_X - 1;
const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1;
const int CHUNK_MIN_Y = 0;
const int CHUNK_MAX_Y = SIZE_Y - 1;
for (const auto& point : points) {
const glm::vec3& center = point.pos;
float rad_xz = point.rad_xz;
float rad_y = point.rad_y;
if (center.x + rad_xz < CHUNK_MIN_X ||
center.x - rad_xz > CHUNK_MAX_X ||
center.z + rad_xz < CHUNK_MIN_Z ||
center.z - rad_xz > CHUNK_MAX_Z || center.y + rad_y < CHUNK_MIN_Y ||
center.y - rad_y > CHUNK_MAX_Y) {
continue;
}
int min_x = static_cast<int>(std::floor(center.x - rad_xz));
int max_x = static_cast<int>(std::floor(center.x + rad_xz));
int min_z = static_cast<int>(std::floor(center.z - rad_xz));
int max_z = static_cast<int>(std::floor(center.z + rad_xz));
int min_y = static_cast<int>(std::floor(center.y - rad_y));
int max_y = static_cast<int>(std::floor(center.y + rad_y));
min_x = std::max(min_x, CHUNK_MIN_X);
max_x = std::min(max_x, CHUNK_MAX_X);
min_z = std::max(min_z, CHUNK_MIN_Z);
max_z = std::min(max_z, CHUNK_MAX_Z);
min_y = std::max(min_y, CHUNK_MIN_Y);
max_y = std::min(max_y, CHUNK_MAX_Y);
glm::vec3 right_raw =
glm::cross(point.tangent, glm::vec3(0.0f, 1.0f, 0.0f));
if (glm::dot(right_raw, right_raw) < 1e-6f)
right_raw = glm::cross(point.tangent, glm::vec3(1.0f, 0.0f, 0.0f));
glm::vec3 right = glm::normalize(right_raw);
glm::vec3 up = glm::normalize(glm::cross(point.tangent, right));
float inv_a2 = 1.0f / (point.rad_xz * point.rad_xz);
float inv_b2 = 1.0f / (point.rad_y * point.rad_y);
for (int wy = min_y; wy <= max_y; ++wy) {
if (wy == 0)
continue;
float dy = static_cast<float>(wy) - point.pos.y;
float vy_contrib = dy * up.y;
float vy2 = vy_contrib * vy_contrib * inv_b2;
if (vy2 >= 1.0f)
continue;
for (int wx = min_x; wx <= max_x; ++wx) {
float dx = static_cast<float>(wx) - point.pos.x;
for (int wz = min_z; wz <= max_z; ++wz) {
float dz = static_cast<float>(wz) - point.pos.z;
glm::vec3 to_point(dx, dy, dz);
float h = glm::dot(to_point, right);
float v = glm::dot(to_point, up);
if (h * h * inv_a2 + v * v * inv_b2 > 1.0f)
continue;
int x = wx - CHUNK_MIN_X;
on_hit(x, wy, wz - CHUNK_MIN_Z);
}
}
}
}
}
void ChunkGenerator::generate_cave() { void ChunkGenerator::generate_cave() {
auto& cave_carver = m_chunk.world().cave_carcer(); auto& cave_carver = m_chunk.world().cave_carcer();
auto& paths = cave_carver.paths(); auto& paths = cave_carver.paths();
const auto& chunk_pos = m_chunk.chunk_pos(); const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks(); auto& blocks = m_chunk.blocks();
const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE;
const int CHUNK_MIN_Z = chunk_pos.z * CHUNK_SIZE;
const int CHUNK_MAX_X = CHUNK_MIN_X + SIZE_X - 1;
const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1;
const int CHUNK_MIN_Y = 0;
const int CHUNK_MAX_Y = SIZE_Y - 1;
for (auto& [id, path] : paths) { for (auto& [id, path] : paths) {
for (const auto& point : path.points()) {
const glm::vec3& center = point.pos; carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void {
float rad_xz = point.rad_xz; int idx = Chunk::index(x, y, z);
float rad_y = point.rad_y; if (blocks[idx] == 7)
return;
int min_x = static_cast<int>(std::floor(center.x - rad_xz)); if (y < WORLD_SIZE_Y - 1 && blocks[Chunk::index(x, y + 1, z)] == 7)
int max_x = static_cast<int>(std::floor(center.x + rad_xz)); return;
int min_z = static_cast<int>(std::floor(center.z - rad_xz)); blocks[idx] = 0;
int max_z = static_cast<int>(std::floor(center.z + rad_xz)); });
int min_y = static_cast<int>(std::floor(center.y - rad_y));
int max_y = static_cast<int>(std::floor(center.y + rad_y));
min_x = std::max(min_x, CHUNK_MIN_X);
max_x = std::min(max_x, CHUNK_MAX_X);
min_z = std::max(min_z, CHUNK_MIN_Z);
max_z = std::min(max_z, CHUNK_MAX_Z);
min_y = std::max(min_y, CHUNK_MIN_Y);
max_y = std::min(max_y, CHUNK_MAX_Y);
for (int wx = min_x; wx <= max_x; ++wx) {
int x = wx - CHUNK_MIN_X;
for (int wz = min_z; wz <= max_z; ++wz) {
int z = wz - CHUNK_MIN_Z;
for (int wy = min_y; wy <= max_y; ++wy) {
int y = wy;
glm::vec3 pos(static_cast<float>(wx),
static_cast<float>(wy),
static_cast<float>(wz));
if (point.contains(pos)) {
if (y == 0) {
continue;
}
blocks[Chunk::index(x, y, z)] = 0;
}
}
}
}
}
path.clear_chunk(chunk_pos); path.clear_chunk(chunk_pos);
} }
} }
@@ -666,63 +744,27 @@ void ChunkGenerator::generate_river() {
auto& paths = river_worm.paths(); auto& paths = river_worm.paths();
const auto& chunk_pos = m_chunk.chunk_pos(); const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks(); auto& blocks = m_chunk.blocks();
const int CHUNK_MIN_X = chunk_pos.x * CHUNK_SIZE;
const int CHUNK_MIN_Z = chunk_pos.z * CHUNK_SIZE;
const int CHUNK_MAX_X = CHUNK_MIN_X + SIZE_X - 1;
const int CHUNK_MAX_Z = CHUNK_MIN_Z + SIZE_Z - 1;
const int CHUNK_MIN_Y = 0;
const int CHUNK_MAX_Y = SIZE_Y - 1;
bool is_river = false; bool is_river = false;
for (auto& [id, path] : paths) { for (auto& [id, path] : paths) {
for (const auto& point : path.points()) { if ((m_chunk.biome() == BiomeType::DESERT) ||
if (m_chunk.biome() == BiomeType::DESERT) { (m_chunk.biome() == BiomeType::OCEAN)) {
path.clear_chunk(chunk_pos); path.clear_chunk(chunk_pos);
continue; continue;
}
const glm::vec3& center = point.pos;
float rad_xz = point.rad_xz;
float rad_y = point.rad_y;
int min_x = static_cast<int>(std::floor(center.x - rad_xz));
int max_x = static_cast<int>(std::floor(center.x + rad_xz));
int min_z = static_cast<int>(std::floor(center.z - rad_xz));
int max_z = static_cast<int>(std::floor(center.z + rad_xz));
int min_y = static_cast<int>(std::floor(center.y - rad_y));
int max_y = static_cast<int>(std::floor(center.y + rad_y));
min_x = std::max(min_x, CHUNK_MIN_X);
max_x = std::min(max_x, CHUNK_MAX_X);
min_z = std::max(min_z, CHUNK_MIN_Z);
max_z = std::min(max_z, CHUNK_MAX_Z);
min_y = std::max(min_y, CHUNK_MIN_Y);
max_y = std::min(max_y, CHUNK_MAX_Y);
for (int wx = min_x; wx <= max_x; ++wx) {
int x = wx - CHUNK_MIN_X;
for (int wz = min_z; wz <= max_z; ++wz) {
int z = wz - CHUNK_MIN_Z;
for (int wy = min_y; wy <= max_y; ++wy) {
int y = wy;
glm::vec3 pos(static_cast<float>(wx),
static_cast<float>(wy),
static_cast<float>(wz));
if (point.contains(pos)) {
if (y > SEA_LEVEL) {
blocks[Chunk::index(x, y, z)] = 0;
continue;
}
is_river = true;
if (blocks[Chunk::index(x, y, z)] == 0) {
continue;
}
blocks[Chunk::index(x, y, z)] = 7;
}
}
}
}
} }
carve_worm(path.points(), chunk_pos, [&](int x, int y, int z) -> void {
int idx = Chunk::index(x, y, z);
if (y > SEA_LEVEL) {
blocks[idx] = 0;
return;
}
is_river = true;
if (blocks[idx] == 0) {
return;
}
blocks[idx] = 7;
});
path.clear_chunk(chunk_pos); path.clear_chunk(chunk_pos);
} }
if (is_river) { if (is_river) {

View File

@@ -88,7 +88,7 @@ bool Player::ray_cast(const glm::vec3& start, const glm::vec3& front,
float t = 0.0f; float t = 0.0f;
normal = glm::vec3(0.0f, 0.0f, 0.0f); normal = glm::vec3(0.0f, 0.0f, 0.0f);
while (t <= distance) { while (t <= distance) {
if (m_world.is_block(glm::ivec3(ix, iy, iz))) { if (m_world.is_solid(glm::ivec3(ix, iy, iz))) {
block_pos = glm::ivec3(ix, iy, iz); block_pos = glm::ivec3(ix, iy, iz);
return true; return true;
} }
@@ -300,14 +300,14 @@ void Player::update_lookup_block() {
if (m_look_block != std::nullopt) { if (m_look_block != std::nullopt) {
if (Input::get_input_state().mouse_state.left) { if (Input::get_input_state().mouse_state.left) {
if (m_world.is_block(m_look_block->pos)) { if (m_world.is_solid(m_look_block->pos)) {
m_world.set_block(m_look_block->pos, 0); m_world.set_block(m_look_block->pos, 0);
} }
Input::get_input_state().mouse_state.left = false; Input::get_input_state().mouse_state.left = false;
} }
if (Input::get_input_state().mouse_state.right) { if (Input::get_input_state().mouse_state.right) {
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal; glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
if (!m_world.is_block(near_pos)) { if (!m_world.is_solid(near_pos)) {
auto x = near_pos.x; auto x = near_pos.x;
auto y = near_pos.y; auto y = near_pos.y;
auto z = near_pos.z; auto z = near_pos.z;
@@ -421,7 +421,7 @@ void Player::update_x_move() {
for (int x = minx; x <= maxx; ++x) { for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) { for (int y = miny; y <= maxy; ++y) {
for (int z = minz; z <= maxz; ++z) { for (int z = minz; z <= maxz; ++z) {
if (m_world.is_block(glm::vec3{x, y, z})) { if (!m_world.can_pass_block(glm::ivec3{x, y, z})) {
AABB block_box = {glm::vec3{static_cast<float>(x), AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y), static_cast<float>(y),
static_cast<float>(z)}, static_cast<float>(z)},
@@ -455,7 +455,7 @@ void Player::update_y_move() {
for (int x = minx; x <= maxx; ++x) { for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) { for (int y = miny; y <= maxy; ++y) {
for (int z = minz; z <= maxz; ++z) { for (int z = minz; z <= maxz; ++z) {
if (m_world.is_block(glm::vec3{x, y, z})) { if (!m_world.can_pass_block(glm::ivec3{x, y, z})) {
AABB block_box = {glm::vec3{static_cast<float>(x), AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y), static_cast<float>(y),
static_cast<float>(z)}, static_cast<float>(z)},
@@ -493,7 +493,7 @@ void Player::update_z_move() {
for (int x = minx; x <= maxx; ++x) { for (int x = minx; x <= maxx; ++x) {
for (int y = miny; y <= maxy; ++y) { for (int y = miny; y <= maxy; ++y) {
for (int z = minz; z <= maxz; ++z) { for (int z = minz; z <= maxz; ++z) {
if (m_world.is_block(glm::vec3{x, y, z})) { if (!m_world.can_pass_block(glm::ivec3{x, y, z})) {
AABB block_box = {glm::vec3{static_cast<float>(x), AABB block_box = {glm::vec3{static_cast<float>(x),
static_cast<float>(y), static_cast<float>(y),
static_cast<float>(z)}, static_cast<float>(z)},
@@ -526,13 +526,13 @@ void Player::update_scroll(double yoffset) {
if (m_game_mode == CREATIVE) { if (m_game_mode == CREATIVE) {
if (yoffset < 0) { if (yoffset < 0) {
m_place_block += 1; m_place_block += 1;
if (m_place_block >= MAX_BLOCK_NUM) { if (m_place_block >= BlockManager::sums()) {
m_place_block = 1; m_place_block = 1;
} }
} else { } else {
m_place_block -= 1; m_place_block -= 1;
if (m_place_block <= 0) { if (m_place_block <= 0) {
m_place_block = MAX_BLOCK_NUM - 1; m_place_block = BlockManager::sums() - 1;
} }
} }
} }
@@ -547,5 +547,5 @@ float& Player::g() { return m_g; }
unsigned Player::place_block() const { return m_place_block; }; unsigned Player::place_block() const { return m_place_block; };
Gait& Player::gait() { return m_gait; } Gait& Player::gait() { return m_gait; }
GameMode& Player::game_mode() { return m_game_mode; } GameMode& Player::game_mode() { return m_game_mode; }
const World& Player::get_world() const { return m_world; }
} // namespace Cubed } // namespace Cubed

View File

@@ -5,12 +5,15 @@
#include <algorithm> #include <algorithm>
namespace Cubed { namespace Cubed {
RiverPath::RiverPath(unsigned int world_seed, int path_id, RiverPath::RiverPath(unsigned int chunk_seed, unsigned world_seed,
const glm::vec3& start_pos) { const glm::vec3& start_pos) {
m_path_id = path_id;
m_seed = HASH::combine_32(world_seed, path_id); m_seed = HASH::combine_32(chunk_seed, world_seed);
m_random.init(m_seed); m_random.init(m_seed);
m_yaw = m_random.random_float(0.0f, 360.0f); m_yaw = m_random.random_float(0.0f, 360.0f);
m_initial_yaw = m_yaw;
m_pitch = 0.0f; m_pitch = 0.0f;
m_start_path_point.pos = start_pos; m_start_path_point.pos = start_pos;
m_start_path_point.rad_xz = m_start_path_point.rad_xz =
@@ -41,14 +44,15 @@ void RiverPath::collect_path_points() {
float t = Math::smootherstep(0, m_step - 1, i); float t = Math::smootherstep(0, m_step - 1, i);
float drad_xz = m_start_path_point.rad_xz * (1.0f - t); float drad_xz = m_start_path_point.rad_xz * t;
float drad_y = m_start_path_point.rad_y * (1.0f - t); float drad_y = m_start_path_point.rad_y * t;
drad_xz = std::max(drad_xz, 4.0f); drad_xz = std::max(drad_xz, 4.0f);
drad_y = std::max(drad_y, 4.0f); drad_y = std::max(drad_y, 4.0f);
m_points.emplace_back(m_points[i].pos + glm::vec3{dx, dy, dz}, drad_xz, m_points.emplace_back(m_points[i].pos + glm::vec3{dx, dy, dz}, drad_xz,
drad_y); drad_y);
m_yaw += m_random.random_float(m_delta_angle_min, m_delta_angle_max); m_yaw += m_random.random_float(m_delta_angle_min, m_delta_angle_max);
m_yaw = std::clamp(m_yaw, m_initial_yaw - 10.0f, m_initial_yaw + 10.0f);
} }
auto n = m_points.size(); auto n = m_points.size();
if (n >= 2) { if (n >= 2) {
@@ -72,7 +76,8 @@ void RiverPath::precompute_chunk_coverage() {
for (int cx = min_cx; cx <= max_cx; ++cx) for (int cx = min_cx; cx <= max_cx; ++cx)
for (int cz = min_cz; cz <= max_cz; ++cz) for (int cz = min_cz; cz <= max_cz; ++cz)
m_pending_chunks.insert({cx, cz}); m_pending_chunks.insert(
std::make_pair(ChunkPos{cx, cz}, false));
} }
} }

View File

@@ -5,11 +5,11 @@
namespace Cubed { namespace Cubed {
RiverWorm::RiverWorm() {} RiverWorm::RiverWorm() {}
std::unordered_map<unsigned, RiverPath>& RiverWorm::paths() { return m_paths; } RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; }
void RiverWorm::init(unsigned world_seed) { void RiverWorm::init(unsigned world_seed) {
m_seed = world_seed; m_seed = world_seed;
m_sum = 0;
m_random.init(m_seed); m_random.init(m_seed);
} }
@@ -20,15 +20,16 @@ void RiverWorm::reload(unsigned world_seed) {
} }
void RiverWorm::add_path(const glm::vec3& pos, unsigned chunk_seed) { void RiverWorm::add_path(const glm::vec3& pos, unsigned chunk_seed) {
m_paths.emplace(chunk_seed, RiverPath{m_seed, m_sum, pos}); m_paths.emplace(chunk_seed, RiverPath{chunk_seed, m_seed, pos});
m_sum++;
} }
void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos, void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos,
unsigned chunk_seed) { unsigned chunk_seed) {
auto it = m_paths.find(chunk_seed); {
if (it != m_paths.end()) { RiverHashMap::const_accessor acc;
return; if (m_paths.find(acc, chunk_seed)) {
return;
}
} }
Random random{chunk_seed}; Random random{chunk_seed};
if (random.random_bool(static_cast<double>(m_probability))) { if (random.random_bool(static_cast<double>(m_probability))) {
@@ -44,10 +45,17 @@ void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos,
} }
void RiverWorm::cleanup_finished_rivers() { void RiverWorm::cleanup_finished_rivers() {
std::erase_if(m_paths, std::vector<unsigned> finished_keys;
[](const auto& kv) { return kv.second.is_finished(); }); for (const auto& pair : m_paths) {
if (pair.second.is_finished()) {
finished_keys.push_back(pair.first);
}
}
for (const auto& key : finished_keys) {
m_paths.erase(key);
}
} }
int RiverWorm::river_sum() const { return m_sum; } int RiverWorm::river_sum() const { return m_paths.size(); }
float& RiverWorm::river_probability() { return m_probability; } float& RiverWorm::river_probability() { return m_probability; }
} // namespace Cubed } // namespace Cubed

View File

@@ -31,7 +31,6 @@ bool build_tree(Chunk& chunk, const glm::ivec3& pos) {
auto& block = chunk.get_chunk_blocks(); auto& block = chunk.get_chunk_blocks();
if (block[Chunk::index(pos)] != 1) { if (block[Chunk::index(pos)] != 1) {
Logger::info("Root is not Grass Block");
return false; return false;
} }
for (const auto& d : TREE) { for (const auto& d : TREE) {

View File

@@ -0,0 +1,61 @@
#include "Cubed/gameplay/vertex_data.hpp"
#include "Cubed/gameplay/world.hpp"
namespace Cubed {
VertexData::VertexData(World& world) : m_world(world) {}
VertexData::~VertexData() {
if (m_vbo != 0) {
m_world.push_delete_vbo(m_vbo);
}
if (m_vao != 0) {
m_world.push_delete_vao(m_vao);
}
}
VertexData::VertexData(VertexData&& o) noexcept
: m_vertices(std::move(o.m_vertices)), m_vbo(o.m_vbo), m_vao(o.m_vao),
m_sum(o.m_sum.load()), m_world(o.m_world) {
o.m_vbo = 0;
o.m_sum = 0;
o.m_vao = 0;
}
VertexData& VertexData::operator=(VertexData&& o) noexcept {
m_vbo = o.m_vbo;
o.m_vbo = 0;
m_sum = o.m_sum.load();
o.m_sum = 0;
m_vertices = std::move(o.m_vertices);
m_vao = o.m_vao;
o.m_vao = 0;
return *this;
}
void VertexData::upload() {
if (m_vertices.size() == 0) {
return;
}
if (m_vao == 0) {
glGenVertexArrays(1, &m_vao);
}
if (m_vbo == 0) {
glGenBuffers(1, &m_vbo);
}
glBindVertexArray(m_vao);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, m_vertices.size() * sizeof(Vertex),
m_vertices.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, s));
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, layer));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void VertexData::update_sum() { m_sum = m_vertices.size(); }
} // namespace Cubed

View File

@@ -1,11 +1,11 @@
#include "Cubed/gameplay/world.hpp" #include "Cubed/gameplay/world.hpp"
#include "Cubed/config.hpp" #include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp"
#include "Cubed/gameplay/player.hpp" #include "Cubed/gameplay/player.hpp"
#include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/math_tools.hpp"
#include <execution>
namespace Cubed { namespace Cubed {
@@ -26,6 +26,13 @@ World::~World() {
} }
m_pending_delete_vbo.clear(); m_pending_delete_vbo.clear();
} }
{
std::lock_guard lk(m_delete_vao_mutex);
for (auto x : m_pending_delete_vao) {
glDeleteVertexArrays(1, &x);
}
m_pending_delete_vao.clear();
}
} }
bool World::can_move(const AABB& player_box) const { return true; } bool World::can_move(const AABB& player_box) const { return true; }
@@ -84,6 +91,7 @@ void World::init_world() {
void World::init_chunks() { void World::init_chunks() {
hot_reload(); hot_reload();
while (!m_chunk_gen_finished) { while (!m_chunk_gen_finished) {
// Logger::info("World Spawn: {:.2f}%", m_chunk_gen_fraction.load());
std::this_thread::sleep_for(std::chrono::microseconds(200)); std::this_thread::sleep_for(std::chrono::microseconds(200));
} }
} }
@@ -290,32 +298,6 @@ void World::init_chunks() {
} }
} }
*/ */
void World::render(const glm::mat4& mvp_matrix) {
Math::extract_frustum_planes(mvp_matrix, m_planes);
int rendered_sum = 0;
for (const auto& snapshot : m_render_snapshots) {
if (is_aabb_in_frustum(snapshot.center, snapshot.half_extents)) {
glBindBuffer(GL_ARRAY_BUFFER, snapshot.vbo);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, s));
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex),
(void*)offsetof(Vertex, layer));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glDrawArrays(GL_TRIANGLES, 0, snapshot.vertex_count);
glBindBuffer(GL_ARRAY_BUFFER, 0);
rendered_sum++;
}
}
DebugCollector::get().report(
"rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum));
}
ChunkPos World::chunk_pos(int world_x, int world_z) { ChunkPos World::chunk_pos(int world_x, int world_z) {
int chunk_x, chunk_z; int chunk_x, chunk_z;
@@ -340,8 +322,10 @@ void World::gen_chunks_internal() {
m_chunk_gen_fraction = 0.0f; m_chunk_gen_fraction = 0.0f;
m_chunk_gen_finished = false; m_chunk_gen_finished = false;
ChunkPosSet required_chunks; ChunkPosSet required_chunks;
ChunkHashMap temp_neighbor; ChunkPairVector temp_neighbor;
compute_required_chunks(required_chunks, temp_neighbor); std::vector<ChunkPos> need_gen_temp_chunks_pos;
compute_required_chunks(required_chunks, temp_neighbor,
need_gen_temp_chunks_pos);
ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!"); ASSERT_MSG(!required_chunks.empty(), "required chunks is empty!!");
@@ -359,11 +343,14 @@ void World::gen_chunks_internal() {
m_chunk_gen_fraction = 0.1f; m_chunk_gen_fraction = 0.1f;
ChunkUpdateList new_chunks; ChunkPairVector new_chunks;
ChunkHashMap new_temp_chunks;
for (auto& pos : need_gen_chunks_pos) { for (auto& pos : need_gen_chunks_pos) {
new_chunks.push_back({pos, Chunk(*this, pos)}); new_chunks.push_back({pos, Chunk(*this, pos)});
} }
for (auto& pos : need_gen_temp_chunks_pos) {
new_temp_chunks.emplace(pos, Chunk(*this, pos));
}
ConstChunkMap new_chunks_neighbor; ConstChunkMap new_chunks_neighbor;
// affected neighbor // affected neighbor
ChunkPtrUpdateList affected_neighbor; ChunkPtrUpdateList affected_neighbor;
@@ -373,17 +360,28 @@ void World::gen_chunks_internal() {
// build new chunk, but the neighbor in m_chunks also need to re-build // build new chunk, but the neighbor in m_chunks also need to re-build
for (auto& [pos, chunk] : new_chunks) { std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
chunk.gen_phase_one(); [this](std::pair<ChunkPos, Chunk>& new_chunk) {
m_cave_carcer.try_to_add_path(pos, chunk.seed()); auto& [pos, chunk] = new_chunk;
m_river_worm.try_to_add_path(pos, chunk.seed()); chunk.gen_phase_one();
} m_cave_carcer.try_to_add_path(pos, chunk.seed());
m_river_worm.try_to_add_path(pos, chunk.seed());
});
std::for_each(new_temp_chunks.begin(), new_temp_chunks.end(),
[](std::pair<const ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_one();
});
// precompute path to ensure the continuity of the path // precompute path to ensure the continuity of the path
for (auto& [pos, chunk] : temp_neighbor) { std::for_each(std::execution::par, temp_neighbor.begin(),
chunk.gen_phase_one(); temp_neighbor.end(),
m_cave_carcer.try_to_add_path(pos, chunk.seed()); [this](std::pair<ChunkPos, Chunk>& new_chunk) {
m_river_worm.try_to_add_path(pos, chunk.seed()); auto& [pos, chunk] = new_chunk;
} chunk.gen_phase_one();
m_cave_carcer.try_to_add_path(pos, chunk.seed());
m_river_worm.try_to_add_path(pos, chunk.seed());
});
m_chunk_gen_fraction = 0.2f; m_chunk_gen_fraction = 0.2f;
@@ -421,15 +419,19 @@ void World::gen_chunks_internal() {
m_chunk_gen_fraction = 0.3f; m_chunk_gen_fraction = 0.3f;
for (auto& [pos, chunks] : new_chunks) { std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
chunks.gen_phase_three(); [](std::pair<ChunkPos, Chunk>& pair) {
auto& [pos, chunks] = pair;
chunks.gen_phase_three();
});
for (auto& [pos, chunk] : new_temp_chunks) {
chunk.gen_phase_three();
} }
// for (auto& [pos, chunks] : temp_neighbor) { // for (auto& [pos, chunks] : temp_neighbor) {
// chunks.gen_phase_three(); // chunks.gen_phase_three();
// } // }
m_chunk_gen_fraction = 0.4f;
/* /*
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
for (auto& [pos, chunks] : temp_neighbor) { for (auto& [pos, chunks] : temp_neighbor) {
@@ -473,68 +475,106 @@ void World::gen_chunks_internal() {
} }
} }
*/ */
m_chunk_gen_fraction = 0.4f;
m_chunk_gen_fraction = 0.5f;
for (auto& [pos, chunks] : new_chunks) { for (auto& [pos, chunks] : new_chunks) {
chunks.gen_phase_five(); chunks.gen_phase_five();
} }
m_chunk_gen_fraction = 0.45f;
for (auto& [pos, chunk] : new_temp_chunks) {
chunk.gen_phase_five();
}
m_chunk_gen_fraction = 0.5f;
/* /*
for (auto& [pos, chunks] : temp_neighbor) { for (auto& [pos, chunks] : temp_neighbor) {
chunks.gen_phase_five(); chunks.gen_phase_five();
} }
*/ */
std::array<std::optional<std::vector<BlockType>>, 4> neighbor_blocks_data; std::vector<std::pair<Chunk*, OptionalBlockVectorArray>>
for (auto& [pos, chunks] : new_chunks) { new_chunks_surface_blend_data(new_chunks.size());
for (size_t idx = 0; idx < new_chunks.size(); idx++) {
auto& [pos, chunk] = new_chunks[idx];
new_chunks_surface_blend_data[idx].first = &chunk;
{ {
// std::lock_guard lk(m_chunks_mutex); // std::lock_guard lk(m_chunks_mutex);
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i]; auto neighbor_pos = pos + CHUNK_DIR[i];
auto it = new_chunks_neighbor.find(neighbor_pos); auto it = new_chunks_neighbor.find(neighbor_pos);
if (it == new_chunks_neighbor.end()) { if (it == new_chunks_neighbor.end()) {
neighbor_blocks_data[i] = std::nullopt; auto it = new_temp_chunks.find(neighbor_pos);
if (it == new_temp_chunks.end()) {
new_chunks_surface_blend_data[idx].second[i] =
std::nullopt;
Logger::warn(
"Can't find neighbor for chunk surface blend");
continue;
}
new_chunks_surface_blend_data[idx].second[i] =
it->second.get_chunk_blocks();
continue; continue;
} }
neighbor_blocks_data[i] = it->second->get_chunk_blocks(); new_chunks_surface_blend_data[idx].second[i] =
it->second->get_chunk_blocks();
} }
} }
chunks.gen_phase_six(neighbor_blocks_data);
} }
for (auto& [pos, chunks] : new_chunks) { std::for_each(
chunks.gen_phase_seven(); std::execution::par, new_chunks_surface_blend_data.begin(),
} new_chunks_surface_blend_data.end(),
[](std::pair<Chunk*, OptionalBlockVectorArray>& new_chunk_data) {
auto& [chunk, neighbor_data] = new_chunk_data;
chunk->gen_phase_six(neighbor_data);
});
m_chunk_gen_fraction = 0.55f;
std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
[](std::pair<ChunkPos, Chunk>& new_chunk) {
auto& [pos, chunk] = new_chunk;
chunk.gen_phase_seven();
});
m_chunk_gen_fraction = 0.6f; m_chunk_gen_fraction = 0.6f;
std::array<const std::vector<BlockType>*, 4> neighbor_block; std::vector<std::pair<Chunk*, OptionalBlockVectorArray>>
for (auto& [pos, chunk] : new_chunks) { new_chunk_vertices_data(new_chunks.size());
for (size_t idx = 0; idx < new_chunks.size(); idx++) {
auto& [pos, chunk] = new_chunks[idx];
new_chunk_vertices_data[idx].first = &chunk;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]);
if (it != new_chunks_neighbor.end()) { if (it != new_chunks_neighbor.end()) {
neighbor_block[i] = &(it->second->get_chunk_blocks()); new_chunk_vertices_data[idx].second[i] =
(it->second->get_chunk_blocks());
} else { } else {
neighbor_block[i] = nullptr; new_chunk_vertices_data[idx].second[i] = std::nullopt;
} }
} }
chunk.gen_vertex_data(neighbor_block);
} }
std::for_each(
std::execution::par, new_chunk_vertices_data.begin(),
new_chunk_vertices_data.end(),
[](std::pair<Chunk*, OptionalBlockVectorArray>& new_chunk_data) {
auto& [chunk, neighbor_data] = new_chunk_data;
chunk->gen_vertex_data(neighbor_data);
});
m_chunk_gen_fraction = 0.7f; m_chunk_gen_fraction = 0.7f;
build_neighbor_context_for_affected_neighbors(affected_neighbor, build_neighbor_context_for_affected_neighbors(affected_neighbor,
new_chunks_neighbor); new_chunks_neighbor);
m_chunk_gen_fraction = 0.8f; m_chunk_gen_fraction = 0.8f;
OptionalBlockVectorArray neighbor_block;
for (auto& [pos, chunk] : affected_neighbor) { for (auto& [pos, chunk] : affected_neighbor) {
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]); auto it = new_chunks_neighbor.find(pos + CHUNK_DIR[i]);
if (it != new_chunks_neighbor.end()) { if (it != new_chunks_neighbor.end()) {
neighbor_block[i] = &(it->second->get_chunk_blocks()); neighbor_block[i] = (it->second->get_chunk_blocks());
} else { } else {
neighbor_block[i] = nullptr; neighbor_block[i] = std::nullopt;
} }
} }
chunk->gen_vertex_data(neighbor_block); chunk->gen_vertex_data(neighbor_block);
@@ -560,32 +600,53 @@ void World::sync_player_pos(glm::vec3& player_pos) {
player_pos = m_gen_player_pos; player_pos = m_gen_player_pos;
} }
void World::compute_required_chunks(ChunkPosSet& required_chunks, void World::compute_required_chunks(
ChunkHashMap& temp_neighbor) { ChunkPosSet& required_chunks, ChunkPairVector& temp_neighbor,
std::vector<ChunkPos>& need_gen_temp_chunks_pos) {
glm::vec3 player_pos; glm::vec3 player_pos;
sync_player_pos(player_pos); sync_player_pos(player_pos);
int x = std::floor(player_pos.x); int x = std::floor(player_pos.x);
int z = std::floor(player_pos.z); int z = std::floor(player_pos.z);
auto [chunk_x, chunk_z] = chunk_pos(x, z); auto [chunk_x, chunk_z] = chunk_pos(x, z);
int radius = m_rendering_distance;
int r2 = radius * radius;
required_chunks.reserve(radius * radius);
required_chunks.reserve(m_rendering_distance * m_rendering_distance); for (int dx = -radius; dx <= radius; ++dx) {
int half = m_rendering_distance / 2; for (int dz = -radius; dz <= radius; ++dz) {
for (int u = chunk_x - half; u <= chunk_x + half; ++u) { if (dx * dx + dz * dz <= r2) {
for (int v = chunk_z - half; v <= chunk_z + half; ++v) { required_chunks.emplace(chunk_x + dx, chunk_z + dz);
required_chunks.emplace(u, v); }
}
}
int new_radius = radius + 1;
int new_r2 = new_radius * new_radius;
for (int dx = -new_radius; dx <= new_radius; ++dx) {
for (int dz = -new_radius; dz <= new_radius; ++dz) {
if (dx * dx + dz * dz <= new_r2) {
int nx = chunk_x + dx;
int nz = chunk_z + dz;
auto it = required_chunks.find({nx, nz});
if (it == required_chunks.end()) {
need_gen_temp_chunks_pos.push_back({nx, nz});
}
}
} }
} }
int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max()); int max_path_len = std::max(CavePath::step_max(), RiverPath::step_max());
half = std::ceil(static_cast<float>(max_path_len) / CHUNK_SIZE) * 2; radius = max_path_len / 2;
for (int u = chunk_x - half; u <= chunk_x + half; ++u) { r2 = radius * radius;
for (int v = chunk_z - half; v <= chunk_z + half; ++v) { for (int dx = -radius; dx <= radius; ++dx) {
ChunkPos pos{u, v}; for (int dz = -radius; dz <= radius; ++dz) {
auto it = required_chunks.find(pos); if (dx * dx + dz * dz <= r2) {
if (it != required_chunks.end()) { ChunkPos pos{chunk_x + dx, chunk_z + dz};
continue; auto it = required_chunks.find(pos);
if (it != required_chunks.end()) {
continue;
}
temp_neighbor.emplace_back(pos, Chunk(*this, pos));
} }
temp_neighbor.emplace(pos, Chunk(*this, pos));
} }
} }
} }
@@ -612,7 +673,7 @@ void World::sync_and_collect_missing_chunks(
void World::build_neighbor_context_for_new_chunks( void World::build_neighbor_context_for_new_chunks(
ConstChunkMap& new_chunks_neighbor, ChunkPtrUpdateList& affected_neighbor, ConstChunkMap& new_chunks_neighbor, ChunkPtrUpdateList& affected_neighbor,
const ChunkUpdateList& new_chunks) { const ChunkPairVector& new_chunks) {
{ {
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
for (auto& [pos, chunk] : new_chunks) { for (auto& [pos, chunk] : new_chunks) {
@@ -690,21 +751,6 @@ void World::need_gen() {
m_gen_cv.notify_one(); m_gen_cv.notify_one();
} }
bool World::is_aabb_in_frustum(const glm::vec3& center,
const glm::vec3& half_extents) {
for (const auto& plane : m_planes) {
// distance
float d = glm::dot(glm::vec3(plane), center) + plane.w;
float r = half_extents.x * std::abs(plane.x) +
half_extents.y * std::abs(plane.y) +
half_extents.z * std::abs(plane.z);
if (d + r < 0) {
return false;
}
}
return true;
}
int World::get_block(const glm::ivec3& block_pos) const { int World::get_block(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
@@ -723,7 +769,7 @@ int World::get_block(const glm::ivec3& block_pos) const {
return chunk_blocks[Chunk::index(x, y, z)]; return chunk_blocks[Chunk::index(x, y, z)];
} }
bool World::is_block(const glm::ivec3& block_pos) const { bool World::is_solid(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z); auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex); std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z}); auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
@@ -738,13 +784,52 @@ bool World::is_block(const glm::ivec3& block_pos) const {
return false; return false;
} }
auto id = chunk_blocks[Chunk::index(x, y, z)]; auto id = chunk_blocks[Chunk::index(x, y, z)];
if (id == 0) { if (BlockManager::is_gas(id) || BlockManager::is_liquid(id)) {
return false; return false;
} else { } else {
return true; return true;
} }
} }
bool World::can_pass_block(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
if (it == m_chunks.end()) {
return true;
}
const auto& chunk_blocks = it->second.get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
return true;
}
auto id = chunk_blocks[Chunk::index(x, y, z)];
return BlockManager::is_passable(id);
}
BlockType World::get_block_tpye(const glm::ivec3& block_pos) const {
auto [chunk_x, chunk_z] = chunk_pos(block_pos.x, block_pos.z);
std::lock_guard lk(m_chunks_mutex);
auto it = m_chunks.find(ChunkPos{chunk_x, chunk_z});
if (it == m_chunks.end()) {
Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
block_pos.z);
return 0;
}
const auto& chunk_blocks = it->second.get_chunk_blocks();
auto [x, y, z] = Chunk::world_to_block(block_pos, {chunk_x, chunk_z});
if (x < 0 || y < 0 || z < 0 || x >= CHUNK_SIZE || y >= WORLD_SIZE_Y ||
z >= CHUNK_SIZE) {
Logger::error("Can't Find Block {} {} {}", block_pos.x, block_pos.y,
block_pos.z);
return 0;
}
return chunk_blocks[Chunk::index(x, y, z)];
}
void World::set_block(const glm::ivec3& block_pos, unsigned id) { void World::set_block(const glm::ivec3& block_pos, unsigned id) {
int world_x, world_y, world_z; int world_x, world_y, world_z;
@@ -794,6 +879,15 @@ void World::update(float delta_time) {
} }
m_pending_delete_vbo.clear(); m_pending_delete_vbo.clear();
} }
{
std::lock_guard lk(m_delete_vao_mutex);
for (auto x : m_pending_delete_vao) {
glDeleteVertexArrays(1, &x);
}
m_pending_delete_vao.clear();
}
{ {
std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex); std::scoped_lock lk(m_chunks_mutex, m_new_chunk_queue_mutex);
m_new_chunk.clear(); m_new_chunk.clear();
@@ -824,13 +918,13 @@ void World::update(float delta_time) {
for (auto& [pos, chunk] : m_chunks) { for (auto& [pos, chunk] : m_chunks) {
if (chunk.is_dirty()) { if (chunk.is_dirty()) {
// the curial fator influence // the curial fator influence
std::array<const std::vector<BlockType>*, 4> neighbor_block; OptionalBlockVectorArray neighbor_block;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
auto it = m_chunks.find(pos + CHUNK_DIR[i]); auto it = m_chunks.find(pos + CHUNK_DIR[i]);
if (it != m_chunks.end()) { if (it != m_chunks.end()) {
neighbor_block[i] = &(it->second.get_chunk_blocks()); neighbor_block[i] = (it->second.get_chunk_blocks());
} else { } else {
neighbor_block[i] = nullptr; neighbor_block[i] = std::nullopt;
} }
} }
chunk.gen_vertex_data(neighbor_block); chunk.gen_vertex_data(neighbor_block);
@@ -841,7 +935,12 @@ void World::update(float delta_time) {
chunk.upload_to_gpu(); chunk.upload_to_gpu();
} }
m_render_snapshots.push_back( m_render_snapshots.push_back(
{chunk.get_vbo(), chunk.get_vertex_sum(), {chunk.get_normal_vao(), chunk.get_normal_vertices_sum(),
chunk.get_cross_vao(), chunk.get_cross_vertices_sum(),
chunk.get_normal_discard_vao(),
chunk.get_normal_discard_vertices_sum(),
chunk.get_normal_blend_vao(),
chunk.get_normal_blend_vertices_sum(),
glm::vec3(static_cast<float>(pos.x * CHUNK_SIZE) + glm::vec3(static_cast<float>(pos.x * CHUNK_SIZE) +
static_cast<float>(CHUNK_SIZE / 2), static_cast<float>(CHUNK_SIZE / 2),
static_cast<float>(WORLD_SIZE_Y / 2), static_cast<float>(WORLD_SIZE_Y / 2),
@@ -860,6 +959,11 @@ void World::push_delete_vbo(GLuint vbo) {
m_pending_delete_vbo.push_back(vbo); m_pending_delete_vbo.push_back(vbo);
} }
void World::push_delete_vao(GLuint vao) {
std::lock_guard lk(m_delete_vao_mutex);
m_pending_delete_vao.push_back(vao);
}
void World::hot_reload() { void World::hot_reload() {
auto& config = Config::get(); auto& config = Config::get();
int dist = config.get<int>("world.rendering_distance"); int dist = config.get<int>("world.rendering_distance");
@@ -898,4 +1002,8 @@ void World::rendering_distance(int rendering_distance) {
CaveCarver& World::cave_carcer() { return m_cave_carcer; } CaveCarver& World::cave_carcer() { return m_cave_carcer; }
RiverWorm& World::river_worm() { return m_river_worm; } RiverWorm& World::river_worm() { return m_river_worm; }
std::vector<glm::vec4>& World::planes() { return m_planes; }
std::vector<ChunkRenderSnapshot>& World::render_snapshots() {
return m_render_snapshots;
};
} // namespace Cubed } // namespace Cubed

View File

@@ -1,11 +1,7 @@
#include "Cubed/map_table.hpp" #include "Cubed/map_table.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/cubed_hash.hpp"
namespace Cubed { namespace Cubed {
/*
std::string_view MapTable::get_name_from_id(unsigned id) { std::string_view MapTable::get_name_from_id(unsigned id) {
auto it = id_to_name_map.find(id); auto it = id_to_name_map.find(id);
ASSERT_MSG(it != id_to_name_map.end(), ASSERT_MSG(it != id_to_name_map.end(),
@@ -25,8 +21,9 @@ std::string_view MapTable::item_name(unsigned id) {
} }
const std::vector<std::string>& MapTable::item_map() { return item_id_to_name; } const std::vector<std::string>& MapTable::item_map() { return item_id_to_name; }
*/
void MapTable::init_map() { void MapTable::init_map() {
/*
id_to_name_map.reserve(MAX_BLOCK_NUM); id_to_name_map.reserve(MAX_BLOCK_NUM);
name_to_id_map.reserve(MAX_BLOCK_NUM); name_to_id_map.reserve(MAX_BLOCK_NUM);
@@ -37,6 +34,7 @@ void MapTable::init_map() {
for (auto s : BLOCK_REISTER) { for (auto s : BLOCK_REISTER) {
item_id_to_name.emplace_back(s); item_id_to_name.emplace_back(s);
} }
*/
} }
} // namespace Cubed } // namespace Cubed

View File

@@ -12,6 +12,7 @@
#include "Cubed/tools/cubed_hash.hpp" #include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/font.hpp" #include "Cubed/tools/font.hpp"
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include "Cubed/tools/math_tools.hpp"
#include "Cubed/tools/shader_tools.hpp" #include "Cubed/tools/shader_tools.hpp"
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
@@ -33,6 +34,14 @@ Renderer::~Renderer() {
glDeleteBuffers(1, &m_text_vbo); glDeleteBuffers(1, &m_text_vbo);
glBindVertexArray(0); glBindVertexArray(0);
glDeleteVertexArrays(NUM_VAO, m_vao.data()); glDeleteVertexArrays(NUM_VAO, m_vao.data());
glDeleteFramebuffers(1, &m_fbo);
glDeleteTextures(1, &m_screen_texture);
glDeleteRenderbuffers(1, &m_depth_render_buffer);
glDeleteFramebuffers(1, &m_oit_fbo);
glDeleteTextures(1, &m_accum_texture);
glDeleteTextures(1, &m_reveal_texture);
glDeleteRenderbuffers(1, &m_oit_depth_render_buffer);
} }
void Renderer::hot_reload() { void Renderer::hot_reload() {
@@ -49,7 +58,7 @@ void Renderer::init() {
Logger::info("Renderer: {}", Logger::info("Renderer: {}",
reinterpret_cast<const char*>(glGetString(GL_RENDERER))); reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
Shader world_shader{"world", "shaders/block_v_shader.glsl", Shader world_shader{"normal_block", "shaders/block_v_shader.glsl",
"shaders/block_f_shader.glsl"}; "shaders/block_f_shader.glsl"};
Shader outline_shader{"outline", "shaders/outline_v_shader.glsl", Shader outline_shader{"outline", "shaders/outline_v_shader.glsl",
"shaders/outline_f_shader.glsl"}; "shaders/outline_f_shader.glsl"};
@@ -59,13 +68,24 @@ void Renderer::init() {
"shaders/ui_f_shader.glsl"}; "shaders/ui_f_shader.glsl"};
Shader text_shdaer{"text", "shaders/text_v_shader.glsl", Shader text_shdaer{"text", "shaders/text_v_shader.glsl",
"shaders/text_f_shader.glsl"}; "shaders/text_f_shader.glsl"};
Shader under_water_shader{"under_water",
"shaders/under_water_v_shader.glsl",
"shaders/under_water_f_shader.glsl"};
Shader accum_shader{"accum", "shaders/block_accumulation_v_shader.glsl",
"shaders/block_accumulation_f_shader.glsl"};
Shader composite_block_shader{"composite",
"shaders/block_composite_v_shader.glsl",
"shaders/block_composite_f_shader.glsl"};
m_shaders.insert({world_shader.hash(), std::move(world_shader)}); m_shaders.insert({world_shader.hash(), std::move(world_shader)});
m_shaders.insert({outline_shader.hash(), std::move(outline_shader)}); m_shaders.insert({outline_shader.hash(), std::move(outline_shader)});
m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)}); m_shaders.insert({sky_shdaer.hash(), std::move(sky_shdaer)});
m_shaders.insert({ui_shdaer.hash(), std::move(ui_shdaer)}); m_shaders.insert({ui_shdaer.hash(), std::move(ui_shdaer)});
m_shaders.insert({text_shdaer.hash(), std::move(text_shdaer)}); m_shaders.insert({text_shdaer.hash(), std::move(text_shdaer)});
m_shaders.insert(
{under_water_shader.hash(), std::move(under_water_shader)});
m_shaders.insert({accum_shader.hash(), std::move(accum_shader)});
m_shaders.insert(
{composite_block_shader.hash(), std::move(composite_block_shader)});
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
@@ -86,20 +106,28 @@ void Renderer::init() {
m_vao.resize(NUM_VAO); m_vao.resize(NUM_VAO);
glGenVertexArrays(NUM_VAO, m_vao.data()); glGenVertexArrays(NUM_VAO, m_vao.data());
glBindVertexArray(0); glBindVertexArray(0);
glGenBuffers(1, &m_outline_vbo);
glBindVertexArray(m_vao[2]);
glGenBuffers(1, &m_outline_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_outline_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_outline_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(CUBE_VER), CUBE_VER, GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, sizeof(CUBE_VER), CUBE_VER, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glGenBuffers(1, &m_outline_indices_vbo); glGenBuffers(1, &m_outline_indices_vbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_outline_indices_vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_outline_indices_vbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(OUTLINE_CUBE_INDICES), glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(OUTLINE_CUBE_INDICES),
OUTLINE_CUBE_INDICES, GL_STATIC_DRAW); OUTLINE_CUBE_INDICES, GL_STATIC_DRAW);
glBindVertexArray(m_vao[1]);
glGenBuffers(1, &m_sky_vbo); glGenBuffers(1, &m_sky_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_sky_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_sky_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_POS), VERTICES_POS, glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES_POS), VERTICES_POS,
GL_STATIC_DRAW); GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindVertexArray(m_vao[3]);
glGenBuffers(1, &m_ui_vbo); glGenBuffers(1, &m_ui_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_ui_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_ui_vbo);
@@ -112,13 +140,23 @@ void Renderer::init() {
glBufferData(GL_ARRAY_BUFFER, m_ui.size() * sizeof(Vertex2D), m_ui.data(), glBufferData(GL_ARRAY_BUFFER, m_ui.size() * sizeof(Vertex2D), m_ui.data(),
GL_STATIC_DRAW); GL_STATIC_DRAW);
glGenBuffers(1, &m_text_vbo); glBindBuffer(GL_ARRAY_BUFFER, m_ui_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_text_vbo); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*)0);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D),
glBindBuffer(GL_ARRAY_BUFFER, 0); (void*)offsetof(Vertex2D, s));
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, layer));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
init_quad();
init_text(); init_text();
hot_reload(); hot_reload();
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
} }
const Shader& Renderer::get_shader(const std::string& name) const { const Shader& Renderer::get_shader(const std::string& name) const {
@@ -127,28 +165,51 @@ const Shader& Renderer::get_shader(const std::string& name) const {
return it->second; return it->second;
} }
void Renderer::init_quad() {
glBindVertexArray(m_vao[0]);
glGenBuffers(1, &m_quad_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_quad_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(QUAD_VERTICES), QUAD_VERTICES,
GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
(void*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
(void*)(2 * sizeof(float)));
}
void Renderer::init_text() { void Renderer::init_text() {
glBindVertexArray(m_vao[4]);
glGenBuffers(1, &m_text_vbo);
glBindBuffer(GL_ARRAY_BUFFER, m_text_vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
const auto& shader = get_shader("text"); const auto& shader = get_shader("text");
Text::set_loc(shader); Text::set_loc(shader);
DebugCollector::get().init_text(); DebugCollector::get().init_text();
} }
void Renderer::render() { void Renderer::render() {
glClearColor(0.0, 0.0, 0.0, 1.0); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glClear(GL_COLOR_BUFFER_BIT);
glClear(GL_DEPTH_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(m_vao[0]);
render_sky(); render_sky();
glBindVertexArray(m_vao[1]);
render_world(); render_world();
glBindVertexArray(m_vao[2]);
render_outline(); render_outline();
glBindVertexArray(m_vao[3]);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
render_underwater();
render_ui(); render_ui();
glBindVertexArray(m_vao[4]);
render_text(); render_text();
glBindVertexArray(0);
render_dev_panel(); render_dev_panel();
} }
@@ -156,23 +217,24 @@ void Renderer::render_outline() {
const auto& shader = get_shader("outline"); const auto& shader = get_shader("outline");
shader.use(); shader.use();
m_mv_loc = shader.loc("mv_matrix");
m_proj_loc = shader.loc("proj_matrix");
const auto& block_pos = m_world.get_look_block_pos("TestPlayer"); const auto& block_pos = m_world.get_look_block_pos("TestPlayer");
if (block_pos != std::nullopt) { if (block_pos != std::nullopt) {
m_mv_loc = shader.loc("mv_matrix");
m_proj_loc = shader.loc("proj_matrix");
m_m_mat = m_m_mat =
glm::translate(glm::mat4(1.0f), glm::vec3(block_pos.value().pos)); glm::translate(glm::mat4(1.0f), glm::vec3(block_pos.value().pos));
m_v_mat = m_camera.get_camera_lookat();
m_mv_mat = m_v_mat * m_m_mat; m_mv_mat = m_v_mat * m_m_mat;
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
glBindBuffer(GL_ARRAY_BUFFER, m_outline_vbo); glBindVertexArray(m_vao[2]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_outline_indices_vbo);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
glLineWidth(4.0f); glLineWidth(4.0f);
@@ -196,9 +258,7 @@ void Renderer::render_sky() {
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
glBindBuffer(GL_ARRAY_BUFFER, m_sky_vbo); glBindVertexArray(m_vao[1]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glEnableVertexAttribArray(0);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
@@ -207,10 +267,18 @@ void Renderer::render_sky() {
} }
void Renderer::render_text() { void Renderer::render_text() {
glBindVertexArray(m_vao[4]);
const auto& shader = get_shader("text"); const auto& shader = get_shader("text");
shader.use(); shader.use();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
m_proj_loc = shader.loc("projection"); m_proj_loc = shader.loc("projection");
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj));
@@ -228,34 +296,46 @@ void Renderer::render_ui() {
shader.use(); shader.use();
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
m_mv_loc = shader.loc("m_matrix"); m_mv_loc = shader.loc("m_matrix");
m_proj_loc = shader.loc("proj_matrix"); m_proj_loc = shader.loc("proj_matrix");
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_ui_m_matrix)); glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_ui_m_matrix));
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_ui_proj));
glBindBuffer(GL_ARRAY_BUFFER, m_ui_vbo); glBindVertexArray(m_vao[3]);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex2D), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, s));
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex2D),
(void*)offsetof(Vertex2D, layer));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_ui_array()); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_ui_array());
glDrawArrays(GL_TRIANGLES, 0, 6); glDrawArrays(GL_TRIANGLES, 0, 6);
Tools::check_opengl_error(); Tools::check_opengl_error();
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }
void Renderer::render_underwater() {
const auto& shader = get_shader("under_water");
shader.use();
glBindVertexArray(m_vao[0]);
glUniform1i(shader.loc("u_sceneTexture"), 0);
glUniform1f(shader.loc("u_time"), glfwGetTime());
glUniform1i(shader.loc("u_underwater"), m_camera.is_under_water());
glUniform3f(shader.loc("u_waterColor"), 0.1f, 0.25f, 0.35f);
glUniform1f(shader.loc("u_fogDensity"), 0.08f);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_screen_texture);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
void Renderer::update(float delta_time) { m_delta_time = delta_time; }
void Renderer::update_fov(float fov) { void Renderer::update_fov(float fov) {
m_fov = fov; m_fov = fov;
m_p_mat = glm::perspective(glm::radians(fov), m_aspect, 0.1f, 1000.0f); m_p_mat = glm::perspective(glm::radians(fov), m_aspect, 0.1f, 1000.0f);
@@ -272,22 +352,217 @@ void Renderer::update_proj_matrix(float aspect, float width, float height) {
glm::scale(glm::mat4(1.0f), glm::vec3(50.0f, 50.0f, 1.0f)); glm::scale(glm::mat4(1.0f), glm::vec3(50.0f, 50.0f, 1.0f));
} }
void Renderer::render_world() { void Renderer::updata_framebuffer(int width, int height) {
const auto& shader = get_shader("world"); if (width <= 0 || height <= 0)
shader.use(); return;
if (m_fbo == 0) {
glGenFramebuffers(1, &m_fbo);
}
if (m_oit_fbo == 0) {
glGenFramebuffers(1, &m_oit_fbo);
}
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glDeleteTextures(1, &m_screen_texture);
glDeleteRenderbuffers(1, &m_depth_render_buffer);
glGenTextures(1, &m_screen_texture);
glBindTexture(GL_TEXTURE_2D, m_screen_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB,
GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
m_screen_texture, 0);
glGenRenderbuffers(1, &m_depth_render_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_depth_render_buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_depth_render_buffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
Logger::error("FBO incomplete after resize!");
} else {
Logger::info("Frame Buffer Complete!");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo);
glDeleteTextures(1, &m_accum_texture);
glDeleteTextures(1, &m_reveal_texture);
glDeleteRenderbuffers(1, &m_oit_depth_render_buffer);
glGenTextures(1, &m_accum_texture);
glBindTexture(GL_TEXTURE_2D, m_accum_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0, GL_RGBA,
GL_HALF_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
m_accum_texture, 0);
glGenTextures(1, &m_reveal_texture);
glBindTexture(GL_TEXTURE_2D, m_reveal_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16F, width, height, 0, GL_RED,
GL_HALF_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D,
m_reveal_texture, 0);
glGenRenderbuffers(1, &m_oit_depth_render_buffer);
glBindRenderbuffer(GL_RENDERBUFFER, m_oit_depth_render_buffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, m_oit_depth_render_buffer);
GLenum draw_buffer[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1};
glDrawBuffers(2, draw_buffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
Logger::error("FBO incomplete after resize!");
} else {
Logger::info("Frame Buffer Complete!");
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
m_width = width;
m_height = height;
}
void Renderer::render_world() {
const auto& normal_block_shader = get_shader("normal_block");
normal_block_shader.use();
m_mv_loc = normal_block_shader.loc("mv_matrix");
m_proj_loc = normal_block_shader.loc("proj_matrix");
m_mv_loc = shader.loc("mv_matrix");
m_proj_loc = shader.loc("proj_matrix");
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_manager.get_texture_array());
m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); m_m_mat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f));
m_v_mat = m_camera.get_camera_lookat(); m_v_mat = m_camera.get_camera_lookat();
m_mv_mat = m_v_mat * m_m_mat; m_mv_mat = m_v_mat * m_m_mat;
glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat)); glUniformMatrix4fv(m_mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat)); glUniformMatrix4fv(m_proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
m_mvp_mat = m_p_mat * m_mv_mat; m_mvp_mat = m_p_mat * m_mv_mat;
m_world.render(m_mvp_mat);
auto& camera_pos = m_camera.get_camera_pos();
auto& m_planes = m_world.planes();
auto& m_render_snapshots = m_world.render_snapshots();
Math::extract_frustum_planes(m_mvp_mat, m_planes);
int rendered_sum = 0;
glEnable(GL_DEPTH_TEST);
for (const auto& snapshot : m_render_snapshots) {
if (Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_vertices_count);
rendered_sum++;
}
}
// cross_plane and discard
for (const auto& snapshot : m_render_snapshots) {
if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) {
continue;
}
glm::vec2 camera_pos_xz{camera_pos.x, camera_pos.z};
if (snapshot.cross_vertices_count != 0) {
glm::vec2 center_xz{snapshot.center.x, snapshot.center.z};
float dist2d = glm::distance(camera_pos_xz, center_xz);
if (dist2d <= CROSS_PLANE_DISTANCE * 16) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_cross_plane_array());
glBindVertexArray(snapshot.cross_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.cross_vertices_count);
}
}
if (snapshot.normal_discard_vertices_count != 0) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_discard_vao);
glDrawArrays(GL_TRIANGLES, 0,
snapshot.normal_discard_vertices_count);
}
}
// copy depth buffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_oit_fbo);
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height,
GL_DEPTH_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_oit_fbo);
// pass one accumulate
auto& accum_shader = get_shader("accum");
accum_shader.use();
GLint mv_loc = accum_shader.loc("mv_matrix");
GLint proj_loc = accum_shader.loc("proj_matrix");
glUniformMatrix4fv(mv_loc, 1, GL_FALSE, glm::value_ptr(m_mv_mat));
glUniformMatrix4fv(proj_loc, 1, GL_FALSE, glm::value_ptr(m_p_mat));
glBindFramebuffer(GL_FRAMEBUFFER, m_oit_fbo);
glClearBufferfv(GL_COLOR, 0, glm::value_ptr(glm::vec4(0.0f)));
float one = 1.0f;
glClearBufferfv(GL_COLOR, 1, &one);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunci(0, GL_ONE, GL_ONE);
glBlendFunci(1, GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
for (const auto& snapshot : m_render_snapshots) {
if (!Math::is_aabb_in_frustum(snapshot.center, snapshot.half_extents,
m_planes)) {
continue;
}
if (snapshot.normal_blend_vertices_count != 0) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_blend_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_blend_vertices_count);
}
}
auto& composite_shader = get_shader("composite");
glDisable(GL_BLEND);
composite_shader.use();
glUniform1i(composite_shader.loc("u_accumTex"), 0);
glUniform1i(composite_shader.loc("u_revealTex"), 1);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBindVertexArray(m_vao[0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_accum_texture);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_reveal_texture);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
DebugCollector::get().report(
"rendered_chunk", "Rendered Chunk: " + std::to_string(rendered_sum));
} }
void Renderer::render_dev_panel() { void Renderer::render_dev_panel() {

View File

@@ -18,7 +18,8 @@ Shader::Shader(const std::string& name, const std::string& v_shader_path,
Shader::Shader(Shader&& shader) noexcept Shader::Shader(Shader&& shader) noexcept
: m_program(shader.m_program), m_hash(shader.m_hash), : m_program(shader.m_program), m_hash(shader.m_hash),
m_name(std::move(shader.m_name)) { m_name(std::move(shader.m_name)),
m_uniform_cache(std::move(shader.m_uniform_cache)) {
shader.m_hash = 0; shader.m_hash = 0;
shader.m_program = 0; shader.m_program = 0;
} }
@@ -33,6 +34,7 @@ Shader& Shader::operator=(Shader&& shader) noexcept {
m_hash = shader.m_hash; m_hash = shader.m_hash;
m_name = std::move(shader.m_name); m_name = std::move(shader.m_name);
m_program = shader.m_program; m_program = shader.m_program;
m_uniform_cache = std::move(shader.m_uniform_cache);
shader.m_hash = 0; shader.m_hash = 0;
shader.m_program = 0; shader.m_program = 0;
return *this; return *this;
@@ -59,11 +61,18 @@ std::size_t Shader::hash() const {
GLuint Shader::loc(const std::string& loc) const { GLuint Shader::loc(const std::string& loc) const {
ASSERT_MSG(m_program != 0, "Shader program not created"); ASSERT_MSG(m_program != 0, "Shader program not created");
auto it = m_uniform_cache.find(loc);
if (it != m_uniform_cache.end()) {
return it->second;
}
GLint pos = glGetUniformLocation(m_program, loc.c_str()); GLint pos = glGetUniformLocation(m_program, loc.c_str());
if (pos == -1) { if (pos == -1) {
Logger::info("Shader name {}, loc name {}, pos {}", m_name, loc, pos); Logger::error("Shader name {}, loc name {}, pos {}", m_name, loc, pos);
ASSERT_MSG(pos == -1, "Can't find UniformLocation"); ASSERT_MSG(pos == -1, "Can't find UniformLocation");
} }
m_uniform_cache[loc] = pos;
return static_cast<GLuint>(pos); return static_cast<GLuint>(pos);
} }

View File

@@ -16,6 +16,7 @@ TextureManager::~TextureManager() { delet_texture(); }
void TextureManager::delet_texture() { void TextureManager::delet_texture() {
glDeleteTextures(1, &m_texture_array); glDeleteTextures(1, &m_texture_array);
glDeleteTextures(1, &m_block_status_array); glDeleteTextures(1, &m_block_status_array);
glDeleteTextures(1, &m_cross_plane_array);
for (auto& id : m_item_textures) { for (auto& id : m_item_textures) {
glDeleteTextures(1, &id); glDeleteTextures(1, &id);
} }
@@ -28,6 +29,9 @@ GLuint TextureManager::get_block_status_array() const {
GLuint TextureManager::get_texture_array() const { return m_texture_array; } GLuint TextureManager::get_texture_array() const { return m_texture_array; }
GLuint TextureManager::get_cross_plane_array() const {
return m_cross_plane_array;
}
GLuint TextureManager::get_ui_array() const { return m_ui_array; } GLuint TextureManager::get_ui_array() const { return m_ui_array; }
const std::vector<GLuint>& TextureManager::item_textures() const { const std::vector<GLuint>& TextureManager::item_textures() const {
@@ -47,12 +51,18 @@ void TextureManager::load_block_status(unsigned id) {
} }
void TextureManager::load_block_texture(unsigned id) { void TextureManager::load_block_texture(unsigned id) {
ASSERT_MSG(id < MAX_BLOCK_NUM, "Exceed the max block sum limit"); ASSERT_MSG(id < BlockManager::sums(), "Exceed the max block sum limit");
std::string name{MapTable::get_name_from_id(id)}; std::string name{BlockManager::name_form_id(id)};
// air don`t need texture // air don`t need texture
if (id == 0) { if (id == 0) {
return; return;
} }
if (BlockManager::is_cross_plane(id)) {
load_cross_plane_texture(id);
return;
}
unsigned char* image_data[6]; unsigned char* image_data[6];
std::string block_texture_path = "texture/block/" + name; std::string block_texture_path = "texture/block/" + name;
@@ -73,7 +83,11 @@ void TextureManager::load_block_texture(unsigned id) {
} }
} }
void TextureManager::load_item_texture(const std::string& name) { void TextureManager::load_block_item_texture(unsigned id) {
ASSERT_MSG(id < BlockManager::sums(), "Exceed the max block sum limit");
std::string name{BlockManager::name_form_id(id)};
std::string path = "texture/item/block/" + name + ".png"; std::string path = "texture/item/block/" + name + ".png";
unsigned char* data = nullptr; unsigned char* data = nullptr;
data = Tools::load_image_data(path); data = Tools::load_image_data(path);
@@ -95,6 +109,17 @@ void TextureManager::load_item_texture(const std::string& name) {
Tools::delete_image_data(data); Tools::delete_image_data(data);
} }
void TextureManager::load_cross_plane_texture(unsigned id) {
std::string path =
"texture/block/" + BlockManager::name_form_id(id) + "/cross.png";
unsigned char* image_data = Tools::load_image_data(path);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array);
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0,
BlockManager::cross_plane_index(id), 16, 16, 1, GL_RGBA,
GL_UNSIGNED_BYTE, image_data);
Tools::delete_image_data(image_data);
}
void TextureManager::load_ui_texture(unsigned id) { void TextureManager::load_ui_texture(unsigned id) {
ASSERT_MSG(id < MAX_UI_NUM, "Exceed the max ui sum limit"); ASSERT_MSG(id < MAX_UI_NUM, "Exceed the max ui sum limit");
@@ -107,19 +132,22 @@ void TextureManager::load_ui_texture(unsigned id) {
Tools::delete_image_data(image_data); Tools::delete_image_data(image_data);
} }
void TextureManager::init_item() {
auto& map = MapTable::item_map();
for (const auto& name : map) {
load_item_texture(name);
}
}
void TextureManager::init_block() { void TextureManager::init_block() {
glGenTextures(1, &m_texture_array); glGenTextures(1, &m_texture_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16, MAX_BLOCK_NUM * 6, 0, glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16,
GL_RGBA, GL_UNSIGNED_BYTE, nullptr); BlockManager::sums() * 6, 0, GL_RGBA, GL_UNSIGNED_BYTE,
for (int i = 0; i < MAX_BLOCK_NUM; i++) { nullptr);
glGenTextures(1, &m_cross_plane_array);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, 16, 16,
BlockManager::cross_plane_sum(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
nullptr);
for (unsigned i = 0; i < BlockManager::sums(); i++) {
load_block_texture(i); load_block_texture(i);
load_block_item_texture(i);
} }
glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array); glBindTexture(GL_TEXTURE_2D_ARRAY, m_texture_array);
@@ -131,6 +159,17 @@ void TextureManager::init_block() {
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY, glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY,
static_cast<GLfloat>(m_aniso)); static_cast<GLfloat>(m_aniso));
} }
glBindTexture(GL_TEXTURE_2D_ARRAY, m_cross_plane_array);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
if (m_aniso >= 1) {
glTexParameterf(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_ANISOTROPY,
static_cast<GLfloat>(m_aniso));
}
Logger::info("Block Texture Load Success"); Logger::info("Block Texture Load Success");
} }
void TextureManager::init_ui() { void TextureManager::init_ui() {
@@ -181,7 +220,6 @@ void TextureManager::init_texture() {
init_block(); init_block();
init_block_status(); init_block_status();
init_ui(); init_ui();
init_item();
} }
void TextureManager::update() { void TextureManager::update() {

View File

@@ -5,8 +5,15 @@ namespace Cubed {
Random::Random() {} Random::Random() {}
Random::Random(unsigned seed) { init(seed); } Random::Random(unsigned seed) { init(seed); }
bool Random::random_bool(double probability) { bool Random::random_bool(double probability) {
std::bernoulli_distribution dist(probability); if (probability <= 0.0)
return dist(m_engine); return false;
if (probability >= 1.0)
return true;
const double MAX_VAL = 4294967295.0;
unsigned threshold = static_cast<unsigned>(probability * MAX_VAL);
return m_engine() <= threshold;
} }
std::mt19937& Random::engine() { return m_engine; } std::mt19937& Random::engine() { return m_engine; }
@@ -18,12 +25,23 @@ void Random::init(unsigned seed) {
m_engine.seed(seed); m_engine.seed(seed);
} }
int Random::random_int(int min, int max) { int Random::random_int(int min, int max) {
std::uniform_int_distribution<int> dist(min, max); unsigned range = static_cast<unsigned>(max - min) + 1;
return dist(m_engine);
const unsigned LIMIT =
(std::numeric_limits<unsigned>::max() / range) * range;
unsigned r;
do {
r = m_engine();
} while (r >= LIMIT);
return min + static_cast<int>(r % range);
} }
float Random::random_float(float min, float max) { float Random::random_float(float min, float max) {
std::uniform_real_distribution<float> dist(min, max);
return dist(m_engine);
}
unsigned r = m_engine() >> 8;
float t = static_cast<float>(r) * (1.0f / 16777216.0f);
float result = min + t * (max - min);
return result;
}
} // namespace Cubed } // namespace Cubed

View File

@@ -45,6 +45,26 @@ float smootherstep(float edge0, float edge1, float x) {
return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f); return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f);
} }
bool is_aabb_in_frustum(const glm::vec3& center, const glm::vec3& half_extents,
const std::vector<glm::vec4>& planes) {
for (const auto& plane : planes) {
// distance
float d = glm::dot(glm::vec3(plane), center) + plane.w;
float r = half_extents.x * std::abs(plane.x) +
half_extents.y * std::abs(plane.y) +
half_extents.z * std::abs(plane.z);
if (d + r < 0) {
return false;
}
}
return true;
}
float deterministic_random(int x, int z, uint64_t seed) {
uint64_t h = seed;
h = h * 6364136223846793005ULL + (uint64_t)x;
h = h * 6364136223846793005ULL + (uint64_t)z;
return (float)(h >> 40) / (float)(1 << 24);
}
} // namespace Math } // namespace Math
} // namespace Cubed } // namespace Cubed

View File

@@ -40,7 +40,7 @@ GLuint create_shader_program(const std::string& v_shader_path,
Tools::check_opengl_error(); Tools::check_opengl_error();
glGetShaderiv(f_shader, GL_COMPILE_STATUS, &fc); glGetShaderiv(f_shader, GL_COMPILE_STATUS, &fc);
if (fc != 1) { if (fc != 1) {
Logger::error("vertex compilation failed"); Logger::error("fragment compilation failed");
Tools::print_shader_log(f_shader); Tools::print_shader_log(f_shader);
ASSERT(0); ASSERT(0);
} }

View File

@@ -42,8 +42,8 @@ void Window::update_viewport() {
m_aspect = (float)m_width / (float)m_height; m_aspect = (float)m_width / (float)m_height;
glViewport(0, 0, m_width, m_height); glViewport(0, 0, m_width, m_height);
m_renderer.update_proj_matrix(m_aspect, m_width, m_height); m_renderer.update_proj_matrix(m_aspect, m_width, m_height);
m_renderer.updata_framebuffer(m_width, m_height);
auto& config = Config::get(); auto& config = Config::get();
config.set("window.width", windowed_width); config.set("window.width", windowed_width);
config.set("window.height", windowed_height); config.set("window.height", windowed_height);
} }

12
uv.lock generated
View File

@@ -17,6 +17,7 @@ version = "0.1.0"
source = { virtual = "." } source = { virtual = "." }
dependencies = [ dependencies = [
{ name = "loguru" }, { name = "loguru" },
{ name = "pytomlpp" },
] ]
[package.dev-dependencies] [package.dev-dependencies]
@@ -25,7 +26,10 @@ dev = [
] ]
[package.metadata] [package.metadata]
requires-dist = [{ name = "loguru", specifier = ">=0.7.3" }] requires-dist = [
{ name = "loguru", specifier = ">=0.7.3" },
{ name = "pytomlpp", specifier = ">=1.1.0" },
]
[package.metadata.requires-dev] [package.metadata.requires-dev]
dev = [{ name = "ruff", specifier = ">=0.15.14" }] dev = [{ name = "ruff", specifier = ">=0.15.14" }]
@@ -43,6 +47,12 @@ wheels = [
{ url = "https://mirrors.ustc.edu.cn/pypi/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, { url = "https://mirrors.ustc.edu.cn/pypi/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" },
] ]
[[package]]
name = "pytomlpp"
version = "1.1.0"
source = { registry = "https://mirrors.ustc.edu.cn/pypi/simple" }
sdist = { url = "https://mirrors.ustc.edu.cn/pypi/packages/10/fe/50ca1ac0c1a932d3e71311baff531c0395e546ec0cd42bc8a8a88c36e00b/pytomlpp-1.1.0.tar.gz", hash = "sha256:61a0f73e7ba2fe8bba4e99ce6d2701491885850b53aad8f3e46d03c4b31e594d", size = 1323755, upload-time = "2025-11-29T20:52:09.582Z" }
[[package]] [[package]]
name = "ruff" name = "ruff"
version = "0.15.14" version = "0.15.14"