Compare commits

..

20 Commits

Author SHA1 Message Date
c00f05aafd feat(renderer): smooth shadow sun direction transitions using quantized directions and slerp 2026-06-17 22:09:50 +08:00
dc1ef70231 fix(texture): set texture wrap mode to clamp to edge 2026-06-17 21:13:23 +08:00
e224110452 feat(renderer): add runtime shader and shadow mode controls
Introduce user-controllable shader on/off, shadow mode (rotated Poisson disk, 3x3 grid, or off), light cull face, and discard transparent in depth pass. Expose all settings in new dev panel "shader" tab. Move ambient strength slider to the new tab.
2026-06-17 20:16:20 +08:00
74b0aebc9f chore(world): add missing <numbers> include 2026-06-17 19:16:02 +08:00
be425a705c feat(dev-panel): add tick freeze toggle and fix TickType sign
Add checkbox to freeze tick advancement in dev panel.
Change TickType from unsigned long long to signed long long to prevent underflow.
2026-06-17 19:15:16 +08:00
31bf337f6f perf(shadow): increase depth map resolution and refine PCF sampling 2026-06-17 15:23:38 +08:00
662f10047a feat(renderer): add shadow mapping with PCF soft shadows
Introduce shadow mapping using a dedicated depth framebuffer and shader. The block fragment shader now performs percentage-closer filtering (PCF) with Poisson disk sampling and random rotation for soft shadows. The vertex shader outputs light-space coordinates. A new depth shader pair handles rendering from the light's perspective, discarding transparent fragments. The renderer sets up the light projection based on the camera position and sun direction, and applies the shadow factor to diffuse lighting. Day/night cycle can now be toggled off in the world server thread.
2026-06-16 22:27:08 +08:00
943c6f1f46 fix(game_time): use unsigned tick type and enforce positive tick speed 2026-06-16 18:51:47 +08:00
7ede49da72 feat(renderer): make ambient strength adjustable via dev panel 2026-06-16 18:44:59 +08:00
a4f92e3659 feat(world): add day/night cycle with server tick system 2026-06-16 16:17:47 +08:00
f43ef64691 feat(rendering): add basic diffuse and ambient lighting to block rendering 2026-06-16 13:53:47 +08:00
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
zhenyan121
bbf8b4e969 refactor: river (#10)
* fix: correct snowy grass block texture

* refactor: river generation

* fix: water placement error due to interpolation

* perf: improve river naturalness

* feat: add river tab item

* fix: path truncation
2026-05-23 14:29:41 +08:00
zhenyan121
a54e87dbc6 refactor: terrain generation (#9)
* feat: add BlockType

* refactor: use fBM for heightmap generation

* feat: improve mountain realism

* refactor: adjust mountain spawn probability

* feat: add biome boundary blending

* refactor: remove resolve_biome_adjacency_conflict function

* feat: add snowy plain

* perf: speed up world generation

* refactor: lower overall terrain height
2026-05-23 10:33:52 +08:00
106 changed files with 3777 additions and 609 deletions

3
.gitignore vendored
View File

@@ -40,4 +40,5 @@ CMakeError.log
*.swo *.swo
*~ *~
.DS_Store .DS_Store
assets/config.toml assets/config.toml
.venv/

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.14

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}
@@ -119,6 +133,12 @@ add_executable(${PROJECT_NAME}
src/gameplay/builders/forest_builder.cpp src/gameplay/builders/forest_builder.cpp
src/gameplay/cave_carver.cpp src/gameplay/cave_carver.cpp
src/gameplay/cave_path.cpp src/gameplay/cave_path.cpp
src/gameplay/builders/snowy_plain_builder.cpp
src/gameplay/river_worm.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")
@@ -158,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)
@@ -181,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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,10 @@
id = 7
is_blend = true
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

@@ -1,15 +1,125 @@
#version 460 #version 460
in vec2 tc; in vec2 tc;
in vec3 normal;
in vec3 vert_pos;
in vec4 FragPosLightSpace;
flat in int tex_layer; flat in int tex_layer;
out vec4 color; out vec4 color;
layout (binding = 0) uniform sampler2D shadowMap;
layout (binding = 1) uniform sampler2DArray samp;
uniform float ambientStrength;
uniform vec3 sunlightColor;
uniform vec3 sunlightDir;
uniform bool shader_on;
uniform int shadowMode;
const vec2 poissonDisk[8] = vec2[](
vec2( 0.1440, 0.7659), vec2(-0.5761, 0.4479),
vec2(-0.3220, -0.6058), vec2( 0.5693, -0.4048),
vec2(-0.1276, 0.1657), vec2(-0.0649, -0.0165),
vec2( 0.2773, -0.0305), vec2(-0.1134, -0.2122)
);
float random(vec3 seed) {
return fract(sin(dot(seed, vec3(12.9898,78.233,45.5432))) * 43758.5453);
}
float ShadowCalculation(vec4 fragPosLightSpace, vec3 norm, vec3 lightDir)
{
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z < 0.0 || projCoords.z > 1.0) {
return 0.0;
}
float currentDepth = projCoords.z;
vec2 texelSize = 1.0 / vec2(textureSize(shadowMap, 0));
float shadow = 0.0;
float bias =
max(
0.0003,
0.001 * (1.0 - dot(norm, lightDir))
);
if (shadowMode == 0) {
vec3 seed = vert_pos * 37.0 + sin(vert_pos * 91.7) * 13.0;
float angle = random(seed) * 6.2831853;; // 2*PI
float s = sin(angle), c = cos(angle);
mat2 rot = mat2(c, -s, s, c);
float radius = 0.7;
const int samples = 8;
for (int i = 0; i < samples; ++i) {
vec2 offset = rot * poissonDisk[i] * radius * texelSize;
float pcfDepth = texture(shadowMap, projCoords.xy + offset).r;
shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0);
}
shadow /= float(samples);
} else if (shadowMode == 1) {
for (int x = -1; x <= 1; ++x) {
for (int y = -1; y <= 1; ++y) {
vec2 offset = vec2(x, y) * texelSize;
float pcfDepth = texture(shadowMap, projCoords.xy + offset).r;
shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0);
}
}
shadow /= 9.0;
} else if (shadowMode == 2) {
// pcf off
float pcfDepth =
texture(shadowMap, projCoords.xy).r;
shadow =
currentDepth - bias > pcfDepth
? 1.0
: 0.0;
} else {
float pcfDepth =
texture(shadowMap, projCoords.xy).r;
shadow =
currentDepth - bias > pcfDepth
? 1.0
: 0.0;
}
return shadow;
}
layout (binding = 0) uniform sampler2DArray samp;
void main(void) { void main(void) {
color = texture(samp, vec3(tc, tex_layer)); vec4 objectColor = texture(samp, vec3(tc, tex_layer));
if (color.a < 0.5) {
if (objectColor.a < 0.8) {
discard; discard;
} }
if (!shader_on) {
color = objectColor;
return;
}
vec3 lightDir = normalize(-sunlightDir);
vec3 ambient = ambientStrength * sunlightColor;
vec3 norm = normalize(normal);
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * sunlightColor;
float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir);
color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb, objectColor.a);
//color = varyingColor; //color = varyingColor;
} }

View File

@@ -3,8 +3,12 @@
layout (location = 0) in vec3 pos; layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord; layout (location = 1) in vec2 texCoord;
layout (location = 2) in float layer; layout (location = 2) in float layer;
layout (location = 3) in vec3 aNormal;
out vec2 tc; out vec2 tc;
out vec3 normal;
out vec3 vert_pos;
flat out int tex_layer; flat out int tex_layer;
out vec4 FragPosLightSpace;
mat4 buildRotateX(float rad); mat4 buildRotateX(float rad);
mat4 buildRotateY(float rad); mat4 buildRotateY(float rad);
@@ -13,13 +17,21 @@ mat4 buildTranslate(float x, float y, float z);
uniform mat4 mv_matrix; uniform mat4 mv_matrix;
uniform mat4 proj_matrix; uniform mat4 proj_matrix;
uniform mat4 norm_matrix;
uniform mat4 lightSpaceMatrix;
void main(void) { void main(void) {
gl_Position = proj_matrix * mv_matrix * vec4(pos, 1.0); vec4 viewPos = mv_matrix * vec4(pos, 1.0);
vert_pos = pos;
tc = texCoord; tc = texCoord;
tex_layer = int(layer); tex_layer = int(layer);
normal = mat3(norm_matrix) * aNormal;
FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0);
gl_Position = proj_matrix * viewPos;
} }
mat4 buildTranslate(float x, float y, float z) { mat4 buildTranslate(float x, float y, float z) {

View File

@@ -0,0 +1,16 @@
#version 460
in vec2 tc;
flat in int tex_layer;
layout (binding = 1) uniform sampler2DArray samp;
uniform bool is_discard_tranparent;
void main() {
if (is_discard_tranparent) {
vec4 texColor = texture(samp, vec3(tc, tex_layer));
if (texColor.a < 0.8)
discard;
}
//gl_FragDepth = gl_FragCoord.z;
}

View File

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

View File

@@ -2,8 +2,10 @@
out vec4 frag_color; out vec4 frag_color;
uniform vec3 color;
void main(void) { void main(void) {
frag_color = vec4(0.529, 0.808, 0.922, 1.0); frag_color = vec4(color, 1.0);
} }

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.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 399 B

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 482 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 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.

Before

Width:  |  Height:  |  Size: 262 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 914 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,9 @@ 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;
glm::vec3 get_camera_front() 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 = 8;
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,16 +16,16 @@ 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;
constexpr float DEFAULT_ACCELERATION = 10.0f; constexpr float DEFAULT_ACCELERATION = 10.0f;
constexpr float DEFAULT_DECELERATION = 15.0f; constexpr float DEFAULT_DECELERATION = 15.0f;
constexpr float DEFAULT_G = 22.5f; constexpr float DEFAULT_G = 22.5f;
static constexpr int SIZE_X = CHUNK_SIZE; constexpr int SIZE_X = CHUNK_SIZE;
static constexpr int SIZE_Y = WORLD_SIZE_Y; constexpr int SIZE_Y = WORLD_SIZE_Y;
static constexpr int SIZE_Z = CHUNK_SIZE; constexpr int SIZE_Z = CHUNK_SIZE;
constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}, constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
{1, 1}, {-1, 1}, {1, -1}, {-1, -1}}; {1, 1}, {-1, 1}, {1, -1}, {-1, -1}};

View File

@@ -44,13 +44,19 @@ private:
bool m_need_save_config = false; bool m_need_save_config = false;
bool m_gen_thread_running = true; bool m_gen_thread_running = true;
int m_theme = 0; int m_theme = 0;
int m_pre_set_day_tick = 0;
int m_pre_set_tick_speed = 1;
bool m_tick_frezze = false;
void show_about_table_bar(); void show_about_table_bar();
void show_biome_table_bar(); void show_biome_table_bar();
void show_time_table_bar();
void show_cave_table_bar(); void show_cave_table_bar();
void show_river_table_bar();
void show_settings_tab_item(); void show_settings_tab_item();
void show_world_tab_item(); void show_world_tab_item();
void show_player_tab_item(); void show_player_tab_item();
void show_items_tab_item(); void show_items_tab_item();
void show_shader_tab_item();
void update_config_view(); void update_config_view();
void update_player_profile(); void update_player_profile();

View File

@@ -5,9 +5,25 @@
namespace Cubed { namespace Cubed {
constexpr float BIOME_NOISE_FREQUENCY = 0.03f; constexpr float BIOME_NOISE_FREQUENCY = 0.06f;
constexpr float HEIGHTMAP_NOISE_FREQUENCY = 0.001f; constexpr float HEIGHTMAP_NOISE_FREQUENCY = 0.001f;
enum class BiomeType { PLAIN = 0, FOREST, DESERT, MOUNTAIN, RIVER, NONE }; constexpr float MOUNTAINOUS_NOISE_FREQUENCY = 0.003f;
enum class BiomeType {
PLAIN = 0,
FOREST,
DESERT,
MOUNTAIN,
RIVER,
SNOWY_PLAIN,
OCEAN,
NONE
};
struct BiomeConditions {
float temp = 0.0f;
float humid = 0.0f;
float mountainous = 0.0f;
};
struct BiomeHeightRange { struct BiomeHeightRange {
int base_y; int base_y;
@@ -47,13 +63,14 @@ struct MountainParams : public BaseBiomeParams {};
struct RiverParams : public BaseBiomeParams {}; struct RiverParams : public BaseBiomeParams {};
std::string get_biome_str(BiomeType biome); std::string get_biome_str(BiomeType biome);
BiomeType get_biome_from_noise(float temp, float humid); // std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome);
std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome); // BiomeHeightRange get_biome_height_range(BiomeType biome);
BiomeHeightRange get_biome_height_range(BiomeType biome);
BiomeType safe_int_to_biome(int x); BiomeType safe_int_to_biome(int x);
int get_interpolated_height(float world_x, float world_z, float temp, int get_interpolated_height(float world_x, float world_z, float temp,
float humid); float humid);
BiomeType determine_biome(const BiomeConditions& conditions);
PlainParams& plain_params(); PlainParams& plain_params();
ForestParams& forest_params(); ForestParams& forest_params();
DesertParams& desert_params(); DesertParams& desert_params();

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,15 +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", "water"}; std::string name;
BlockType id = 0;
const std::array<bool, MAX_BLOCK_NUM> TRANSPARENT_MAP{ bool is_liquid = false;
true, false, false, false, false, false, true}; 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,9 +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 fill_water(); 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

@@ -0,0 +1,21 @@
#pragma once
#include "Cubed/gameplay/builders/biome_builder.hpp"
namespace Cubed {
class ChunkGenerator;
class SnowyPlainBuilder : public BiomeBuilder {
public:
SnowyPlainBuilder(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,13 +1,17 @@
#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<int, 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); void add_path(const glm::vec3& pos, unsigned chunk_seed);
void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed); void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed);
void cleanup_finished_caves(); void cleanup_finished_caves();
@@ -15,9 +19,8 @@ public:
float& cave_probability(); float& cave_probability();
private: private:
std::unordered_map<int, 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

@@ -1,50 +1,20 @@
#pragma once #pragma once
#include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/path_point.hpp"
#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 {
struct PathPoint {
glm::vec3 pos;
glm::vec3 tangent{0.0f, 0.0f, 1.0f};
float rad_xz;
float rad_y;
PathPoint(const glm::vec3& p, float rx, float ry)
: pos(p), rad_xz(rx), rad_y(ry) {}
bool contains(const glm::vec3& other_pos) const {
glm::vec3 to_point = other_pos - pos;
glm::vec3 world_up(0.0f, 1.0f, 0.0f);
glm::vec3 right = glm::normalize(glm::cross(tangent, world_up));
if (glm::length(right) < 0.001f) {
glm::vec3 alt_up(1.0f, 0.0f, 0.0f);
right = glm::normalize(glm::cross(tangent, alt_up));
}
glm::vec3 up = glm::normalize(glm::cross(right, tangent));
float horizontal_dist = glm::dot(to_point, right);
float vertical_dist = glm::dot(to_point, up);
float a = rad_xz;
float b = rad_y;
if (a <= 0.0f || b <= 0.0f)
return false;
float check = (horizontal_dist * horizontal_dist) / (a * a) +
(vertical_dist * vertical_dist) / (b * b);
return check <= 1.0f;
}
};
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;
@@ -68,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;
@@ -78,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,13 +33,24 @@ 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;
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);
@@ -86,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();
@@ -108,6 +128,7 @@ public:
std::vector<BlockType>& blocks(); std::vector<BlockType>& blocks();
World& world(); World& world();
unsigned seed() const; unsigned seed() const;
BiomeConditions& conditions();
}; };
} // namespace Cubed } // namespace Cubed

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,9 @@ 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_river();
private: private:
static inline std::atomic<bool> is_init{false}; static inline std::atomic<bool> is_init{false};
@@ -58,7 +62,9 @@ private:
unsigned m_chunk_seed = 0; unsigned m_chunk_seed = 0;
void make_biome_builder(); void make_biome_builder();
void generate_cave(); 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

@@ -0,0 +1,10 @@
#pragma once
// Prevent unsigned underflow issues in subtraction
using TickType = long long;
constexpr int DEFAULT_PER_TICK_TIME = 50;
constexpr TickType DAY_TIME = 24000;
constexpr TickType PER_HOUR = 1000;

View File

@@ -0,0 +1,36 @@
#pragma once
#include <glm/glm.hpp>
struct PathPoint {
glm::vec3 pos;
glm::vec3 tangent{0.0f, 0.0f, 1.0f};
float rad_xz;
float rad_y;
PathPoint(const glm::vec3& p, float rx, float ry)
: pos(p), rad_xz(rx), rad_y(ry) {}
bool contains(const glm::vec3& other_pos) const {
glm::vec3 to_point = other_pos - pos;
glm::vec3 world_up(0.0f, 1.0f, 0.0f);
glm::vec3 right = glm::normalize(glm::cross(tangent, world_up));
if (glm::length(right) < 0.001f) {
glm::vec3 alt_up(1.0f, 0.0f, 0.0f);
right = glm::normalize(glm::cross(tangent, alt_up));
}
glm::vec3 up = glm::normalize(glm::cross(right, tangent));
float horizontal_dist = glm::dot(to_point, right);
float vertical_dist = glm::dot(to_point, up);
float a = rad_xz;
float b = rad_y;
if (a <= 0.0f || b <= 0.0f)
return false;
float check = (horizontal_dist * horizontal_dist) / (a * a) +
(vertical_dist * vertical_dist) / (b * b);
return check <= 1.0f;
}
};

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

@@ -0,0 +1,55 @@
#pragma once
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/path_point.hpp"
#include "Cubed/tools/cubed_random.hpp"
#include <glm/glm.hpp>
#include <tbb/concurrent_hash_map.h>
namespace Cubed {
class RiverPath {
using ChunkPosSet =
tbb::concurrent_hash_map<ChunkPos, bool, ChunkPos::TBBHash>;
public:
RiverPath(unsigned int chunk_seed, unsigned world_seed,
const glm::vec3& start_pos);
const std::vector<PathPoint>& points() const;
void clear_chunk(const ChunkPos& pos);
bool is_finished() const;
static float& radius_xz_min();
static float& radius_xz_max();
static float& radius_y_min();
static float& radius_y_max();
static float& delta_angle_min();
static float& delta_angle_max();
static int& step_min();
static int& step_max();
private:
static inline float m_radius_xz_min = 5.0f;
static inline float m_radius_xz_max = 10.0f;
static inline float m_radius_y_min = 4.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_max = 3.0f;
static inline int m_step_min = 200;
static inline int m_step_max = 400;
unsigned int m_seed = 0;
float m_yaw = 0.0f;
float m_initial_yaw = 0.0f;
float m_pitch = 0.0f;
int m_step = 0;
float m_step_len = 1.0f;
PathPoint m_start_path_point{{0.0f, 0.0f, 0.0f}, 0.0f, 0.0f};
Random m_random;
std::vector<PathPoint> m_points;
ChunkPosSet m_pending_chunks;
void collect_path_points();
void precompute_chunk_coverage();
};
} // namespace Cubed

View File

@@ -0,0 +1,32 @@
#pragma once
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/river.path.hpp"
#include "Cubed/tools/cubed_random.hpp"
#include <glm/glm.hpp>
#include <tbb/concurrent_hash_map.h>
namespace Cubed {
class RiverWorm {
using RiverHashMap = tbb::concurrent_hash_map<unsigned, RiverPath>;
public:
RiverWorm();
RiverHashMap& paths();
void init(unsigned world_seed);
void reload(unsigned world_seed);
void add_path(const glm::vec3& pos, unsigned chunk_seed);
void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed);
void cleanup_finished_rivers();
int river_sum() const;
float& river_probability();
private:
RiverHashMap m_paths;
unsigned m_seed = 0;
Random m_random;
float m_probability = 0.01f;
};
}; // namespace Cubed

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<Vertex3D> 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

@@ -2,6 +2,8 @@
#include "Cubed/AABB.hpp" #include "Cubed/AABB.hpp"
#include "Cubed/gameplay/cave_carver.hpp" #include "Cubed/gameplay/cave_carver.hpp"
#include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/river_worm.hpp"
#include <atomic> #include <atomic>
#include <condition_variable> #include <condition_variable>
@@ -14,61 +16,86 @@
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>;
using ChunkHashMap = std::unordered_map<ChunkPos, Chunk, ChunkPos::Hash>; using ChunkHashMap = std::unordered_map<ChunkPos, Chunk, ChunkPos::Hash>;
glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f}; glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f};
ChunkHashMap m_chunks; ChunkHashMap m_chunks;
std::unordered_map<std::size_t, Player> m_players; std::unordered_map<std::size_t, Player> m_players;
std::vector<glm::vec4> m_planes; std::vector<glm::vec4> m_planes;
std::thread m_gen_thread; std::thread m_gen_thread;
std::thread m_server_thread;
std::stop_source m_server_stop_source;
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
std::atomic<TickType> m_day_tick = 6000;
mutable std::mutex m_chunks_mutex; mutable std::mutex m_chunks_mutex;
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};
std::atomic<bool> m_is_rebuilding{false}; std::atomic<bool> m_is_rebuilding{false};
std::atomic<bool> m_chunk_gen_finished{false}; std::atomic<bool> m_chunk_gen_finished{false};
std::atomic<bool> m_could_gen{true}; std::atomic<bool> m_could_gen{true};
std::atomic<bool> m_tick_running{true};
std::atomic<int> m_rendering_distance{24}; std::atomic<int> m_rendering_distance{24};
std::atomic<float> m_chunk_gen_fraction{0.0f}; std::atomic<float> m_chunk_gen_fraction{0.0f};
std::atomic<TickType> m_game_ticks{0};
std::vector<ChunkPos> m_dirty_queue; std::vector<ChunkPos> m_dirty_queue;
std::vector<ChunkRenderSnapshot> m_render_snapshots; std::vector<ChunkRenderSnapshot> m_render_snapshots;
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk; std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk;
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk_queue; std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk_queue;
CaveCarver m_cave_carcer; CaveCarver m_cave_carcer;
RiverWorm m_river_worm;
void init_chunks(); void init_chunks();
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
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);
ChunkHashMap& temp_neighbor);
void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&, void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&,
ConstChunkMap&); ConstChunkMap&);
@@ -85,22 +112,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();
@@ -109,9 +133,25 @@ public:
int rendering_distance() const; int rendering_distance() const;
void rendering_distance(int rendering_distance); void rendering_distance(int rendering_distance);
void start_gen_thread(); void start_gen_thread();
void start_server_thread();
void stop_gen_thread(); void stop_gen_thread();
void stop_server_thread();
void serever_run(std::stop_token stoken);
CaveCarver& cave_carcer(); CaveCarver& cave_carcer();
RiverWorm& river_worm();
std::vector<glm::vec4>& planes();
std::vector<ChunkRenderSnapshot>& render_snapshots();
glm::vec3 sunlight_dir() const;
TickType game_tick() const;
TickType day_tick() const;
void day_tick(TickType tick);
int per_tick_time() const;
void per_tick_time(int ms);
bool is_tick_running() const;
void tick_running(bool run);
}; };
} // 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
@@ -92,6 +92,51 @@ constexpr float TEX_COORDS[6][6][2] = {
{0.0f, 0.0f}} // front left {0.0f, 0.0f}} // front left
}; };
constexpr float NORMALS[6][6][3] = {
// ===== front (z = +1) =====
{{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f},
{0.0f, 0.0f, 1.0f}},
// ===== right (x = +1) =====
{{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f},
{1.0f, 0.0f, 0.0f}},
// ===== back (z = -1) =====
{{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f},
{0.0f, 0.0f, -1.0f}},
// ===== left (x = -1) =====
{{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f},
{-1.0f, 0.0f, 0.0f}},
// ===== top (y = +1) =====
{{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f}},
// ===== bottom (y = -1) =====
{{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, -1.0f, 0.0f}}};
#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,11 +157,73 @@ 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
struct Vertex { // ===== 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
};
constexpr float CROSS_NORMALS[2][6][3] = {
// ===== Plane 1: upward =====
{{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f}},
// ===== Plane 2: upward =====
{{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f},
{0.0f, 1.0f, 0.0f}}};
#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 Vertex3D {
float x = 0.0f, y = 0.0f, z = 0.0f; float x = 0.0f, y = 0.0f, z = 0.0f;
float s = 0.0f, t = 0.0f; float s = 0.0f, t = 0.0f;
float layer = 0.0f; float layer = 0.0f;
float nx = 0.0f, ny = 0.0f, nz = 0.0f;
}; };
struct Vertex2D { struct Vertex2D {

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,34 +24,94 @@ 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);
float& ambient_strength();
bool& discard_transparent();
bool& shader_on();
int& shadow_mode();
int& light_cull_face();
private: private:
static constexpr glm::vec3 SUNLIGHT_COLOR{1.0f, 1.0f, 1.0f};
static constexpr glm::vec3 SUN_COLOR{1.00f, 0.95f, 0.80f};
static constexpr glm::vec3 MOON_COLOR{0.75f, 0.80f, 1.00f};
static constexpr glm::vec3 SKY_COLOR{0.529, 0.808, 0.922};
static constexpr float FAR_PLANE = 1000.0f;
static constexpr float NEAR_PLANE = 0.1f;
static constexpr float SUN_SIZE = 50.0f;
static constexpr float MOON_SIZE = 50.0f;
static constexpr float DEPTH_MAP_SIZE = 4096.0f;
static constexpr float ANGLE_STEP_DEG = 0.5f;
float m_ambient_strength = 0.1f;
const Camera& m_camera; const Camera& m_camera;
DevPanel& m_dev_panel; DevPanel& m_dev_panel;
const TextureManager& m_texture_manager; const TextureManager& m_texture_manager;
World& m_world; World& m_world;
bool m_discard_tranparent = true;
bool m_shader_on = true;
int m_shadow_mode = 0;
int m_light_cull_face = 0;
float m_aspect = 0.0f; float m_aspect = 0.0f;
float m_fov = DEFAULT_FOV; float m_fov = DEFAULT_FOV;
glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat;
GLuint m_mv_loc; float m_delta_time = 0.0f;
GLuint m_proj_loc;
GLuint m_sky_vbo; float m_width = 0.0f;
GLuint m_text_vbo; float m_height = 0.0f;
GLuint m_outline_indices_vbo;
GLuint m_outline_vbo; glm::mat4 m_p_mat, m_v_mat, m_m_mat, m_mv_mat, m_mvp_mat, m_norm_mat;
GLuint m_ui_vbo;
GLuint m_mv_loc = 0;
GLuint m_proj_loc = 0;
GLuint m_sky_vbo = 0;
GLuint m_text_vbo = 0;
GLuint m_outline_indices_vbo = 0;
GLuint m_outline_vbo = 0;
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_depth_map_fbo = 0;
GLuint m_depth_map_texture = 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;
glm::vec3 m_blend_from_sundir;
glm::vec3 m_blend_to_sundir;
float m_blend_t = 1.0f;
bool m_blend_initialized = false;
static constexpr float BLEND_DURATION = 0.15f;
/*
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,7 +119,13 @@ 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();
glm::vec3 quantize_sun_direction(const glm::vec3& sundir,
float angle_step_deg) const;
glm::vec3 get_smoothed_shadow_sundir(const glm::vec3& raw_shadow_sundir,
float dt);
}; };
} // namespace Cubed } // namespace Cubed

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,11 @@ 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);
glm::vec3 slerp(const glm::vec3& from, const glm::vec3& to, float t);
} // namespace Math } // namespace Math
} // namespace Cubed } // namespace Cubed

15
pyproject.toml Normal file
View File

@@ -0,0 +1,15 @@
[project]
name = "cubed"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"loguru>=0.7.3",
"pytomlpp>=1.1.0",
]
[dependency-groups]
dev = [
"ruff>=0.15.14",
]

403
scripts/blocks_tool.py Normal file
View File

@@ -0,0 +1,403 @@
import argparse
import copy
import sys
from functools import singledispatch
from pathlib import Path
from pprint import pprint
from typing import Any
import pytomlpp
from loguru import logger
VERSION = "0.0.1"
DATA_PATH = "assets/data/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 collect_blocks() -> list[dict[str, Any]]:
blocks: list[dict[str, Any]] = []
for block in data_path.rglob("*.toml"):
if not block.is_file():
continue
if block.name == "template.toml":
continue
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:
print(f"id: {block['id']} name: {block['name']}")
def check_path():
logger.info(f"Work Path {work_path.resolve()}")
logger.info(f"Script Dir {sys.path[0]}")
data_exists = True
if not data_path.exists():
logger.error(f"Blocks Data Path {data_path} not Exists!")
data_exists = False
else:
logger.info(f"Blocks Data Path {data_path}")
texture_exists = True
if not texture_path.exists():
logger.error(f"Blocks Texture Path {texture_path} not Exists!")
texture_exists = False
else:
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):
if args.version:
print(f"Blocks Tools: {VERSION}")
print(f"Python: {sys.version}")
if args.path:
check_path()
if args.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):
parser.add_argument(
"-v", "--version", action="store_true", help="Show Blocks Tools Version"
)
parser.add_argument(
"--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(
"-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():
parser = argparse.ArgumentParser(description="Block Manage Tool")
init_parser(parser)
if len(sys.argv) == 1:
parser.print_help()
exit(0)
args = parser.parse_args()
handle_args(args)
if __name__ == "__main__":
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,8 @@ 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; }
glm::vec3 Camera::get_camera_front() const { return m_player->get_front(); }
} // 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>
@@ -34,16 +33,16 @@ constexpr int AMPLITUDE_MAX = 80;
constexpr float TREE_FREQ_MIM = 0.001f; constexpr float TREE_FREQ_MIM = 0.001f;
constexpr float TREE_FREQ_MAX = 0.3f; constexpr float TREE_FREQ_MAX = 0.3f;
constexpr float CAVE_PROBABILITY_MIN = 0.005f; constexpr float PATH_PROBABILITY_MIN = 0.005f;
constexpr float CAVE_PROBABILITY_MAX = 0.1f; constexpr float PATH_PROBABILITY_MAX = 0.1f;
constexpr float RADIUS_XZ_MIN = 1.0f; constexpr float RADIUS_XZ_MIN = 1.0f;
constexpr float RADIUS_XZ_MAX = 50.0f; constexpr float RADIUS_XZ_MAX = 50.0f;
constexpr float RADIUS_Y_MIN = 1.0f; constexpr float RADIUS_Y_MIN = 1.0f;
constexpr float RADIUS_Y_MAX = 50.0f; constexpr float RADIUS_Y_MAX = 50.0f;
constexpr float DELTA_ANGLE_MIN = -30.0f; constexpr float DELTA_ANGLE_MIN = -30.0f;
constexpr float DELTA_ANGLE_MAX = 30.0f; constexpr float DELTA_ANGLE_MAX = 30.0f;
constexpr int CAVE_STEP_MIN = 1; constexpr int PATH_STEP_MIN = 1;
constexpr int CAVE_STEP_MAX = 1000; constexpr int PATH_STEP_MAX = 1000;
static int filter_unsigned(ImGuiInputTextCallbackData* data) { static int filter_unsigned(ImGuiInputTextCallbackData* data) {
if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) { if (data->EventFlag == ImGuiInputTextFlags_CallbackCharFilter) {
@@ -85,6 +84,7 @@ void DevPanel::render() {
show_world_tab_item(); show_world_tab_item();
show_player_tab_item(); show_player_tab_item();
show_items_tab_item(); show_items_tab_item();
show_shader_tab_item();
show_about_table_bar(); show_about_table_bar();
ImGui::EndTabBar(); ImGui::EndTabBar();
@@ -108,6 +108,7 @@ void DevPanel::show_about_table_bar() {
ImGui::Text("FreeType"); ImGui::Text("FreeType");
ImGui::Text("toml++"); ImGui::Text("toml++");
ImGui::Text("Dear ImGui"); ImGui::Text("Dear ImGui");
ImGui::Text("Tbb");
ImGui::Separator(); ImGui::Separator();
ImGui::Text("Special Thanks"); ImGui::Text("Special Thanks");
ImGui::Text("TANGERIME"); ImGui::Text("TANGERIME");
@@ -264,12 +265,37 @@ void DevPanel::show_biome_table_bar() {
} }
} }
void DevPanel::show_time_table_bar() {
World& world = m_app.world();
ImGui::Text("Game Tick %llu", world.game_tick());
ImGui::SameLine();
ImGui::Text("Day Tick %llu", world.day_tick());
m_tick_frezze = !world.is_tick_running();
ImGui::SameLine();
if (ImGui::Checkbox("Tick Frezze", &m_tick_frezze)) {
world.tick_running(!m_tick_frezze);
}
if (ImGui::SliderInt("SetDayTick", &m_pre_set_day_tick, 0, DAY_TIME)) {
}
ImGui::SameLine();
if (ImGui::Button("Set##DayTick")) {
world.day_tick(static_cast<TickType>(m_pre_set_day_tick));
}
ImGui::Text("MSPT %d", world.per_tick_time());
if (ImGui::SliderInt("SetMSPT", &m_pre_set_tick_speed, 1, 200)) {
}
ImGui::SameLine();
if (ImGui::Button("Set##MSPT")) {
world.per_tick_time(m_pre_set_tick_speed);
}
}
void DevPanel::show_cave_table_bar() { void DevPanel::show_cave_table_bar() {
auto& cave_carcer = m_app.world().cave_carcer(); auto& cave_carcer = m_app.world().cave_carcer();
ImGui::Text("Total Cave Sum %d", cave_carcer.cave_sum()); ImGui::Text("Total Cave Sum %d", cave_carcer.cave_sum());
ImGui::SliderFloat("Cave Probability", &cave_carcer.cave_probability(), ImGui::SliderFloat("Cave Probability", &cave_carcer.cave_probability(),
CAVE_PROBABILITY_MIN, CAVE_PROBABILITY_MAX); PATH_PROBABILITY_MIN, PATH_PROBABILITY_MAX);
ImGui::SliderFloat("Radius XZ Min", &CavePath::radius_xz_min(), ImGui::SliderFloat("Radius XZ Min", &CavePath::radius_xz_min(),
RADIUS_XZ_MIN, RADIUS_XZ_MAX); RADIUS_XZ_MIN, RADIUS_XZ_MAX);
ImGui::SliderFloat("Radius XZ Max", &CavePath::radius_xz_max(), ImGui::SliderFloat("Radius XZ Max", &CavePath::radius_xz_max(),
@@ -282,10 +308,34 @@ void DevPanel::show_cave_table_bar() {
DELTA_ANGLE_MIN, 0.0f); DELTA_ANGLE_MIN, 0.0f);
ImGui::SliderFloat("Delta Angle Max", &CavePath::delta_angle_max(), 0.0f, ImGui::SliderFloat("Delta Angle Max", &CavePath::delta_angle_max(), 0.0f,
DELTA_ANGLE_MAX); DELTA_ANGLE_MAX);
ImGui::SliderInt("Step Min", &CavePath::step_min(), CAVE_STEP_MIN, ImGui::SliderInt("Step Min", &CavePath::step_min(), PATH_STEP_MIN,
CAVE_STEP_MAX); PATH_STEP_MAX);
ImGui::SliderInt("Step Max", &CavePath::step_max(), CAVE_STEP_MIN, ImGui::SliderInt("Step Max", &CavePath::step_max(), PATH_STEP_MIN,
CAVE_STEP_MAX); PATH_STEP_MAX);
}
void DevPanel::show_river_table_bar() {
auto& river_wrom = m_app.world().river_worm();
ImGui::Text("Total River Sum %d", river_wrom.river_sum());
ImGui::SliderFloat("River Probability", &river_wrom.river_probability(),
PATH_PROBABILITY_MIN, PATH_PROBABILITY_MAX);
ImGui::SliderFloat("Radius XZ Min##river", &RiverPath::radius_xz_min(),
RADIUS_XZ_MIN, RADIUS_XZ_MAX);
ImGui::SliderFloat("Radius XZ Max##river", &RiverPath::radius_xz_max(),
RADIUS_XZ_MIN, RADIUS_XZ_MAX);
ImGui::SliderFloat("Radius Y Min##river", &RiverPath::radius_y_min(),
RADIUS_Y_MIN, RADIUS_Y_MAX);
ImGui::SliderFloat("Radius Y Max##river", &RiverPath::radius_y_max(),
RADIUS_Y_MIN, RADIUS_Y_MAX);
ImGui::SliderFloat("Delta Angle Min##river", &RiverPath::delta_angle_min(),
DELTA_ANGLE_MIN, 0.0f);
ImGui::SliderFloat("Delta Angle Max##river", &RiverPath::delta_angle_max(),
0.0f, DELTA_ANGLE_MAX);
ImGui::SliderInt("Step Min##river", &RiverPath::step_min(), PATH_STEP_MIN,
PATH_STEP_MAX);
ImGui::SliderInt("Step Max##river", &RiverPath::step_max(), PATH_STEP_MIN,
PATH_STEP_MAX);
} }
void DevPanel::show_settings_tab_item() { void DevPanel::show_settings_tab_item() {
@@ -313,6 +363,7 @@ void DevPanel::show_settings_tab_item() {
static_cast<double>(m_config.mouse_sensitivity)); static_cast<double>(m_config.mouse_sensitivity));
m_player->hot_reload(); m_player->hot_reload();
} }
if (ImGui::SliderInt("Distance", &m_config.rendering_distance, 2, if (ImGui::SliderInt("Distance", &m_config.rendering_distance, 2,
128)) { 128)) {
Config::get().set("world.rendering_distance", Config::get().set("world.rendering_distance",
@@ -434,10 +485,18 @@ void DevPanel::show_world_tab_item() {
ImGui::Text("Chunk Build Progress\n"); ImGui::Text("Chunk Build Progress\n");
ImGui::ProgressBar(m_app.world().chunk_gen_fraction()); ImGui::ProgressBar(m_app.world().chunk_gen_fraction());
if (ImGui::BeginTabBar("World Settings")) { if (ImGui::BeginTabBar("World Settings")) {
if (ImGui::BeginTabItem("Time")) {
show_time_table_bar();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Cave")) { if (ImGui::BeginTabItem("Cave")) {
show_cave_table_bar(); show_cave_table_bar();
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
if (ImGui::BeginTabItem("River")) {
show_river_table_bar();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Biome")) { if (ImGui::BeginTabItem("Biome")) {
show_biome_table_bar(); show_biome_table_bar();
ImGui::EndTabItem(); ImGui::EndTabItem();
@@ -523,7 +582,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();
@@ -539,7 +598,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) {
@@ -550,6 +609,27 @@ void DevPanel::show_items_tab_item() {
} }
} }
void DevPanel::show_shader_tab_item() {
static const char* shader_mode[] = {"Rotated Poisson Disk PCF",
"3x3 Square Grid PCF", "PCF off"};
static const char* cull_face_mode[] = {"Front", "Back"};
if (ImGui::BeginTabItem("shader")) {
ImGui::Checkbox("Shader", &m_app.renderer().shader_on());
if (ImGui::SliderFloat("AmbientStrength",
&m_app.renderer().ambient_strength(), 0.0f,
0.35f))
;
ImGui::Checkbox("Discard Transparent",
&m_app.renderer().discard_transparent());
ImGui::Combo("ShaderMode", &m_app.renderer().shadow_mode(), shader_mode,
IM_ARRAYSIZE(shader_mode));
ImGui::Combo("LightCullFaceMode", &m_app.renderer().light_cull_face(),
cull_face_mode, IM_ARRAYSIZE(cull_face_mode));
ImGui::EndTabItem();
}
}
void DevPanel::update_config_view() { void DevPanel::update_config_view() {
auto config = Config::get(); auto config = Config::get();
m_config.fov = m_config.fov =

View File

@@ -1,12 +1,13 @@
#include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/biome.hpp"
#include "Cubed/tools/cubed_assert.hpp" #include "Cubed/tools/cubed_assert.hpp"
#include "Cubed/tools/log.hpp"
#include <unordered_map> #include <unordered_map>
namespace Cubed { namespace Cubed {
using enum BiomeType;
static PlainParams plain{{BiomeType::PLAIN, static PlainParams plain{{BiomeType::PLAIN,
{0.0f, 0.5f}, {0.0f, 0.5f},
{0.0f, 0.5f}, {0.0f, 0.5f},
@@ -59,6 +60,12 @@ std::string get_biome_str(BiomeType biome) {
case RIVER: case RIVER:
str = "River"; str = "River";
break; break;
case SNOWY_PLAIN:
str = "Snowy Plain";
break;
case OCEAN:
str = "Ocean";
break;
case NONE: case NONE:
str = "Unknown"; str = "Unknown";
break; break;
@@ -84,27 +91,7 @@ Biome get_biome_from_noise(float temp, float humid) {
return Biome::FOREST; return Biome::FOREST;
} }
*/ */
BiomeType get_biome_from_noise(float temp, float humid) { /*
using enum BiomeType;
if (plain.temp.first <= temp && temp < plain.temp.second &&
plain.humid.first <= humid && humid < plain.humid.second) {
return PLAIN;
}
if (forest.temp.first <= temp && temp < forest.temp.second &&
forest.humid.first <= humid && humid < forest.humid.second) {
return FOREST;
}
if (desert.temp.first <= temp && temp < desert.temp.second &&
desert.humid.first <= humid && humid < desert.humid.second) {
return DESERT;
}
if (mountain.temp.first <= temp && temp <= mountain.temp.second &&
mountain.humid.first <= humid && humid <= mountain.humid.second) {
return MOUNTAIN;
}
Logger::warn("Invail Temp {} or Humid {}", temp, humid);
return PLAIN;
}
std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome) { std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome) {
using enum BiomeType; using enum BiomeType;
switch (biome) { switch (biome) {
@@ -125,7 +112,8 @@ std::array<float, 3> get_noise_frequencies_for_biome(BiomeType biome) {
Logger::warn("Unknown Biome"); Logger::warn("Unknown Biome");
return {0.003f, 0.015f, 0.06f}; return {0.003f, 0.015f, 0.06f};
} }
*/
/*
BiomeHeightRange get_biome_height_range(BiomeType biome) { BiomeHeightRange get_biome_height_range(BiomeType biome) {
using enum BiomeType; using enum BiomeType;
switch (biome) { switch (biome) {
@@ -146,7 +134,7 @@ BiomeHeightRange get_biome_height_range(BiomeType biome) {
Logger::warn("Unknown Biome"); Logger::warn("Unknown Biome");
return {62, 4}; return {62, 4};
} }
*/
BiomeType safe_int_to_biome(int x) { BiomeType safe_int_to_biome(int x) {
using enum BiomeType; using enum BiomeType;
static const std::unordered_map<int, BiomeType> INT_TO_BIOME_MAP{ static const std::unordered_map<int, BiomeType> INT_TO_BIOME_MAP{
@@ -201,6 +189,33 @@ int get_interpolated_height(float world_x, float world_z, float temp,
return static_cast<int>(h); return static_cast<int>(h);
} }
*/ */
BiomeType determine_biome(const BiomeConditions& conditions) {
if (conditions.mountainous > 0.75) {
return MOUNTAIN;
}
if (conditions.mountainous < 0.25) {
return OCEAN;
}
auto temp = conditions.temp;
auto humid = conditions.humid;
if (temp < 0.5) {
if (humid < 0.5) {
return SNOWY_PLAIN;
} else {
return PLAIN;
}
} else {
if (humid < 0.5) {
return DESERT;
} else {
return FOREST;
}
}
return PLAIN;
}
PlainParams& plain_params() { return plain; } PlainParams& plain_params() { return plain; }
ForestParams& forest_params() { return forest; } ForestParams& forest_params() { return forest; }
DesertParams& desert_params() { return desert; } DesertParams& desert_params() { return desert; }

View File

@@ -15,27 +15,47 @@ void BiomeBuilder::build_bottom() {
} }
} }
} }
void BiomeBuilder::fill_water() { void BiomeBuilder::place_grass() {
ChunkGenerator& chunk_generator = get_chunk_generator(); ChunkGenerator& chunk_generator = get_chunk_generator();
Chunk& chunk = chunk_generator.chunk(); Chunk& chunk = chunk_generator.chunk();
auto& m_blocks = chunk.blocks(); auto& blocks = chunk.blocks();
auto& neighbor = chunk_generator.neighbor_biome(); const auto& heightmap = chunk.get_heightmap();
auto& heightmap = chunk.heightmap(); auto& random = chunk_generator.random();
for (int i = 0; i < 8; i++) { for (int x = 0; x < SIZE_X; ++x) {
if (neighbor[i] == BiomeType::RIVER) { for (int z = 0; z < SIZE_Z; ++z) {
for (int x = 0; x < SIZE_X; x++) { int y = heightmap[x][z];
for (int z = 0; z < SIZE_Z; z++) { BlockType top_id = blocks[Chunk::index(x, y, z)];
if (heightmap[x][z] >= SEA_LEVEL) { if (top_id != 1) {
continue; continue;
} }
int height = heightmap[x][z]; if (blocks[Chunk::index(x, y + 1, z)] != 0) {
for (int y = height; y < SEA_LEVEL; y++) { continue;
m_blocks[Chunk::index(x, y, z)] = 7; }
} if (random.random_bool(0.2)) {
if (y + 1 < SIZE_Y) {
blocks[Chunk::index(x, y + 1, z)] = 9;
} }
} }
return;
} }
} }
} }
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

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

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,7 +53,7 @@ void ForestBuilder::build_vegetation() {
} }
} }
} }
fill_water(); place_grass();
} }
ChunkGenerator& ForestBuilder::get_chunk_generator() { ChunkGenerator& ForestBuilder::get_chunk_generator() {

View File

@@ -18,26 +18,14 @@ void MountainBuilder::build_blocks() {
for (int x = 0; x < CHUNK_SIZE; x++) { for (int x = 0; x < CHUNK_SIZE; x++) {
for (int z = 0; z < CHUNK_SIZE; z++) { for (int z = 0; z < CHUNK_SIZE; z++) {
int height = static_cast<int>(m_heightmap[x][z]); int height = static_cast<int>(m_heightmap[x][z]);
for (int y = 5; y < height - 5; y++) { for (int y = 5; y <= height; y++) {
m_blocks[Chunk::index(x, y, z)] = 3; m_blocks[Chunk::index(x, y, z)] = 3;
} }
for (int y = height - 5; y <= height - 1; y++) {
if (y > 110) {
m_blocks[Chunk::index(x, y, z)] = 3;
} else {
m_blocks[Chunk::index(x, y, z)] = 2;
}
}
if (height > 110) {
m_blocks[Chunk::index(x, height, z)] = 3;
} else {
m_blocks[Chunk::index(x, height, z)] = 1;
}
} }
} }
} }
void MountainBuilder::build_vegetation() { fill_water(); } void MountainBuilder::build_vegetation() {}
ChunkGenerator& MountainBuilder::get_chunk_generator() { ChunkGenerator& MountainBuilder::get_chunk_generator() {
return m_chunk_generator; return m_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() { fill_water(); } 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

@@ -1,6 +1,5 @@
#include "Cubed/gameplay/builders/river_builder.hpp" #include "Cubed/gameplay/builders/river_builder.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/chunk_generator.hpp"
namespace Cubed { namespace Cubed {
RiverBuilder::RiverBuilder(ChunkGenerator& chunk_generator) RiverBuilder::RiverBuilder(ChunkGenerator& chunk_generator)
@@ -12,6 +11,7 @@ void RiverBuilder::build_biome() {
}; };
void RiverBuilder::build_blocks() { void RiverBuilder::build_blocks() {
/*
auto& m_chunk = m_chunk_generator.chunk(); auto& m_chunk = m_chunk_generator.chunk();
auto& m_blocks = m_chunk.blocks(); auto& m_blocks = m_chunk.blocks();
auto& m_heightmap = m_chunk.heightmap(); auto& m_heightmap = m_chunk.heightmap();
@@ -33,9 +33,11 @@ void RiverBuilder::build_blocks() {
} }
} }
} }
*/
} }
void RiverBuilder::build_vegetation() { void RiverBuilder::build_vegetation() {
/*
auto& m_chunk = m_chunk_generator.chunk(); auto& m_chunk = m_chunk_generator.chunk();
auto& m_blocks = m_chunk.blocks(); auto& m_blocks = m_chunk.blocks();
auto& m_heightmap = m_chunk.heightmap(); auto& m_heightmap = m_chunk.heightmap();
@@ -50,6 +52,7 @@ void RiverBuilder::build_vegetation() {
} }
} }
} }
*/
} }
ChunkGenerator& RiverBuilder::get_chunk_generator() { ChunkGenerator& RiverBuilder::get_chunk_generator() {

View File

@@ -0,0 +1,38 @@
#include "Cubed/gameplay/builders/snowy_plain_builder.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/chunk_generator.hpp"
namespace Cubed {
SnowyPlainBuilder::SnowyPlainBuilder(ChunkGenerator& chunk_generator)
: m_chunk_generator(chunk_generator) {}
void SnowyPlainBuilder::build_biome() {
BiomeBuilder::build_bottom();
build_blocks();
};
void SnowyPlainBuilder::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 - 5; y++) {
m_blocks[Chunk::index(x, y, z)] = 3;
}
for (int y = height - 5; y < height; y++) {
m_blocks[Chunk::index(x, y, z)] = 2;
}
m_blocks[Chunk::index(x, height, z)] = 8;
}
}
}
void SnowyPlainBuilder::build_vegetation() {}
ChunkGenerator& SnowyPlainBuilder::get_chunk_generator() {
return m_chunk_generator;
};
} // namespace Cubed

View File

@@ -5,11 +5,10 @@
namespace Cubed { namespace Cubed {
CaveCarver::CaveCarver() {} CaveCarver::CaveCarver() {}
std::unordered_map<int, 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);
} }
@@ -19,13 +18,19 @@ void CaveCarver::reload(unsigned world_seed) {
init(world_seed); init(world_seed);
} }
void CaveCarver::add_path(const glm::vec3& pos) { void CaveCarver::add_path(const glm::vec3& pos, unsigned chunk_seed) {
m_paths.emplace(m_sum, 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) {
{
CaveHashMap::const_accessor acc;
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;
@@ -38,15 +43,22 @@ void CaveCarver::try_to_add_path(const ChunkPos& chunk_pos,
int x = random.random_int(CHUNK_MIN_X, CHUNK_MAX_X); int x = random.random_int(CHUNK_MIN_X, CHUNK_MAX_X);
int y = random.random_int(CHUNK_MIN_Y + 1, max_y); int y = random.random_int(CHUNK_MIN_Y + 1, max_y);
int z = random.random_int(CHUNK_MIN_Z, CHUNK_MAX_Z); int z = random.random_int(CHUNK_MIN_Z, CHUNK_MAX_Z);
add_path(glm::vec3{x, y, z}); add_path(glm::vec3{x, y, z}, chunk_seed);
} }
} }
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,38 @@
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) {}
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;
return *this; return *this;
} }
@@ -106,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 false;
}
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 false;
}
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() {
@@ -253,7 +161,7 @@ void Chunk::gen_phase_two(const std::array<const Chunk*, 8>& adj_chunks) {
Logger::error("ChunkGenerator is Nullptr"); Logger::error("ChunkGenerator is Nullptr");
return; return;
} }
m_generator->resolve_biome_adjacency_conflict(adj_chunks); // m_generator->resolve_biome_adjacency_conflict(adj_chunks);
} }
void Chunk::gen_phase_three() { void Chunk::gen_phase_three() {
@@ -290,7 +198,8 @@ void Chunk::gen_phase_six(
Logger::error("ChunkGenerator is Nullptr"); Logger::error("ChunkGenerator is Nullptr");
return; return;
} }
// m_generator->blend_surface_blocks_borders(neighbor_block); // This must be fully completed before any other operations can proceed!
m_generator->blend_surface_blocks_borders(neighbor_block);
} }
void Chunk::gen_phase_seven() { void Chunk::gen_phase_seven() {
@@ -298,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;
@@ -306,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;
@@ -349,4 +261,180 @@ unsigned Chunk::seed() const {
} }
return m_seed; return m_seed;
} }
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++) {
Vertex3D 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),
NORMALS[face][i][0],
NORMALS[face][i][1],
NORMALS[face][i][2]
};
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++) {
Vertex3D 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)),
CROSS_NORMALS[face][i][0],
CROSS_NORMALS[face][i][1],
CROSS_NORMALS[face][i][2]
};
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,22 +3,26 @@
#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/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/perlin_noise.hpp" #include "Cubed/tools/perlin_noise.hpp"
namespace Cubed { 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;
} }
@@ -59,7 +63,16 @@ void ChunkGenerator::assign_chunk_biome() {
z * BIOME_NOISE_FREQUENCY); z * BIOME_NOISE_FREQUENCY);
float humid = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 1.0f, float humid = PerlinNoise3D::noise(x * BIOME_NOISE_FREQUENCY, 1.0f,
z * BIOME_NOISE_FREQUENCY); z * BIOME_NOISE_FREQUENCY);
auto biome = get_biome_from_noise(temp, humid); float center_x = static_cast<float>(SIZE_X / 2) + x * CHUNK_SIZE + 0.5f;
float center_z = static_cast<float>(SIZE_Z / 2) + z * CHUNK_SIZE + 0.5f;
float mountainous =
PerlinNoise2D::noise(center_x * MOUNTAINOUS_NOISE_FREQUENCY,
center_z * MOUNTAINOUS_NOISE_FREQUENCY);
auto& conditions = m_chunk.conditions();
conditions.mountainous = mountainous;
conditions.humid = humid;
conditions.temp = temp;
auto biome = determine_biome(conditions);
m_chunk.biome(biome); m_chunk.biome(biome);
} }
@@ -139,11 +152,62 @@ void ChunkGenerator::generate_heightmap() {
} }
return value; return value;
}; };
int octaves = 4; int octaves = 4;
float lacunarity = 2.0f; float lacunarity = 2.0f;
float gain = 0.5f; float gain = 0.5f;
heightmap[x][z] = 64 + fbm_height(world_x, world_z, octaves, float base_y = 64;
lacunarity, gain, 40, 0.005f); float amplitude = 40.0f;
float mountainous =
PerlinNoise2D::noise(world_x * MOUNTAINOUS_NOISE_FREQUENCY,
world_z * MOUNTAINOUS_NOISE_FREQUENCY);
/*
float t = Math::smootherstep(0.6, 0.7, mountainous);
base_y = std::lerp(64, 85, t);
amplitude = std::lerp(10, 40, t);
*/
float t;
if (mountainous >= 0.95f) {
t = Math::smootherstep(0.95f, 1.0f, mountainous);
base_y = std::lerp(130, 140, t);
amplitude = std::lerp(38, 48, t);
} else if (mountainous >= 0.85f) {
t = Math::smootherstep(0.85f, 0.95f, mountainous);
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);
} 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 {
t = Math::smootherstep(0.0f, 0.25f, mountainous);
base_y = std::lerp(35, 44, t);
amplitude = std::lerp(3, 6, t);
}
heightmap[x][z] =
base_y + fbm_height(world_x, world_z, octaves, lacunarity, gain,
amplitude, 0.005f);
} }
} }
} }
@@ -395,7 +459,6 @@ void ChunkGenerator::generate_terrain_blocks() {
} }
m_chunk.blocks().assign(CHUNK_SIZE * CHUNK_SIZE * WORLD_SIZE_Y, 0); m_chunk.blocks().assign(CHUNK_SIZE * CHUNK_SIZE * WORLD_SIZE_Y, 0);
m_biome_builder->build_biome(); m_biome_builder->build_biome();
generate_cave();
} }
void ChunkGenerator::blend_surface_blocks_borders( void ChunkGenerator::blend_surface_blocks_borders(
@@ -414,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
@@ -436,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;
@@ -486,32 +551,49 @@ 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 (!BlockManager::is_transitional(final_type)) {
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 (m_chunk.biome() == BiomeType::RIVER && final_type == 1) { BlockType new_surface = final_type;
final_type = 2; m_blocks[Chunk::index(x, top_y, z)] = new_surface;
}
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++) {
m_blocks[Chunk::index(x, y, z)] = fill_type; m_blocks[Chunk::index(x, y, z)] = fill_type;
} }
} }
@@ -546,67 +628,150 @@ void ChunkGenerator::make_biome_builder() {
case RIVER: case RIVER:
m_biome_builder = std::make_unique<RiverBuilder>(*this); m_biome_builder = std::make_unique<RiverBuilder>(*this);
break; break;
case SNOWY_PLAIN:
m_biome_builder = std::make_unique<SnowyPlainBuilder>(*this);
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);
} }
} }
void ChunkGenerator::generate_river() {
auto& river_worm = m_chunk.world().river_worm();
auto& paths = river_worm.paths();
const auto& chunk_pos = m_chunk.chunk_pos();
auto& blocks = m_chunk.blocks();
bool is_river = false;
for (auto& [id, path] : paths) {
if ((m_chunk.biome() == BiomeType::DESERT) ||
(m_chunk.biome() == BiomeType::OCEAN)) {
path.clear_chunk(chunk_pos);
continue;
}
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);
}
if (is_river) {
m_chunk.biome(RIVER);
}
}
Chunk& ChunkGenerator::chunk() { return m_chunk; } Chunk& ChunkGenerator::chunk() { return m_chunk; }
Random& ChunkGenerator::random() { return m_random; } Random& ChunkGenerator::random() { return m_random; }

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

@@ -0,0 +1,98 @@
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/river.path.hpp"
#include "Cubed/tools/cubed_hash.hpp"
#include "Cubed/tools/math_tools.hpp"
#include <algorithm>
namespace Cubed {
RiverPath::RiverPath(unsigned int chunk_seed, unsigned world_seed,
const glm::vec3& start_pos) {
m_seed = HASH::combine_32(chunk_seed, world_seed);
m_random.init(m_seed);
m_yaw = m_random.random_float(0.0f, 360.0f);
m_initial_yaw = m_yaw;
m_pitch = 0.0f;
m_start_path_point.pos = start_pos;
m_start_path_point.rad_xz =
m_random.random_float(m_radius_xz_min, m_radius_xz_max);
m_start_path_point.rad_y =
m_random.random_float(m_radius_y_min, m_radius_y_max);
m_step = m_random.random_int(m_step_min, m_step_max);
m_points.reserve(m_step + 1);
m_points.push_back(m_start_path_point);
collect_path_points();
precompute_chunk_coverage();
}
void RiverPath::collect_path_points() {
for (int i = 0; i < m_step; i++) {
m_yaw = std::fmod(m_yaw, 360.0f);
if (m_yaw < 0.0f)
m_yaw += 360.0f;
float dx = std::cos(glm::radians(m_pitch)) *
std::sin(glm::radians(m_yaw)) * m_step_len;
float dy = std::sin(glm::radians(m_pitch)) * m_step_len;
float dz = std::cos(glm::radians(m_pitch)) *
std::cos(glm::radians(m_yaw)) * m_step_len;
m_points[i].tangent = glm::normalize(glm::vec3{dx, dy, dz});
float t = Math::smootherstep(0, m_step - 1, i);
float drad_xz = m_start_path_point.rad_xz * t;
float drad_y = m_start_path_point.rad_y * t;
drad_xz = std::max(drad_xz, 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,
drad_y);
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();
if (n >= 2) {
m_points[n - 1].tangent = m_points[n - 2].tangent;
}
}
void RiverPath::precompute_chunk_coverage() {
for (const auto& point : m_points) {
float rad = point.rad_xz;
const glm::vec3& center = point.pos;
int min_cx =
static_cast<int>(std::floor((center.x - rad) / CHUNK_SIZE));
int max_cx =
static_cast<int>(std::floor((center.x + rad) / CHUNK_SIZE));
int min_cz =
static_cast<int>(std::floor((center.z - rad) / CHUNK_SIZE));
int max_cz =
static_cast<int>(std::floor((center.z + rad) / CHUNK_SIZE));
for (int cx = min_cx; cx <= max_cx; ++cx)
for (int cz = min_cz; cz <= max_cz; ++cz)
m_pending_chunks.insert(
std::make_pair(ChunkPos{cx, cz}, false));
}
}
void RiverPath::clear_chunk(const ChunkPos& pos) {
m_pending_chunks.erase(pos);
}
const std::vector<PathPoint>& RiverPath::points() const { return m_points; }
bool RiverPath::is_finished() const { return m_pending_chunks.empty(); }
float& RiverPath::radius_xz_min() { return m_radius_xz_min; }
float& RiverPath::radius_xz_max() { return m_radius_xz_max; }
float& RiverPath::radius_y_min() { return m_radius_y_min; }
float& RiverPath::radius_y_max() { return m_radius_y_max; }
float& RiverPath::delta_angle_min() { return m_delta_angle_min; }
float& RiverPath::delta_angle_max() { return m_delta_angle_max; }
int& RiverPath::step_min() { return m_step_min; }
int& RiverPath::step_max() { return m_step_max; }
} // namespace Cubed

View File

@@ -0,0 +1,61 @@
#include "Cubed/gameplay/river_worm.hpp"
#include "Cubed/constants.hpp"
namespace Cubed {
RiverWorm::RiverWorm() {}
RiverWorm::RiverHashMap& RiverWorm::paths() { return m_paths; }
void RiverWorm::init(unsigned world_seed) {
m_seed = world_seed;
m_random.init(m_seed);
}
void RiverWorm::reload(unsigned world_seed) {
m_seed = world_seed;
m_paths.clear();
init(world_seed);
}
void RiverWorm::add_path(const glm::vec3& pos, unsigned chunk_seed) {
m_paths.emplace(chunk_seed, RiverPath{chunk_seed, m_seed, pos});
}
void RiverWorm::try_to_add_path(const ChunkPos& chunk_pos,
unsigned chunk_seed) {
{
RiverHashMap::const_accessor acc;
if (m_paths.find(acc, chunk_seed)) {
return;
}
}
Random random{chunk_seed};
if (random.random_bool(static_cast<double>(m_probability))) {
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;
int x = random.random_int(CHUNK_MIN_X, CHUNK_MAX_X);
int y = SEA_LEVEL + 2;
int z = random.random_int(CHUNK_MIN_Z, CHUNK_MAX_Z);
add_path(glm::vec3{x, y, z}, chunk_seed);
}
}
void RiverWorm::cleanup_finished_rivers() {
std::vector<unsigned> finished_keys;
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_paths.size(); }
float& RiverWorm::river_probability() { return m_probability; }
} // 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,63 @@
#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(Vertex3D),
m_vertices.data(), GL_DYNAMIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D), (void*)0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, s));
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, layer));
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex3D),
(void*)offsetof(Vertex3D, nx));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void VertexData::update_sum() { m_sum = m_vertices.size(); }
} // namespace Cubed

View File

@@ -1,11 +1,14 @@
#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>
#include <glm/gtc/constants.hpp>
#include <numbers>
using namespace std::chrono;
namespace Cubed { namespace Cubed {
@@ -18,6 +21,7 @@ World::World() {}
World::~World() { World::~World() {
stop_gen_thread(); stop_gen_thread();
stop_server_thread();
m_chunks.clear(); m_chunks.clear();
{ {
std::lock_guard lk(m_delete_vbo_mutex); std::lock_guard lk(m_delete_vbo_mutex);
@@ -26,6 +30,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; }
@@ -64,6 +75,7 @@ Player& World::get_player(const std::string& name) {
void World::init_world() { void World::init_world() {
m_cave_carcer.init(ChunkGenerator::seed()); m_cave_carcer.init(ChunkGenerator::seed());
m_river_worm.init(ChunkGenerator::seed());
m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4); m_chunks.reserve(MAX_DISTANCE * MAX_DISTANCE * 4);
auto t1 = std::chrono::system_clock::now(); auto t1 = std::chrono::system_clock::now();
@@ -78,11 +90,14 @@ void World::init_world() {
auto d = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1); auto d = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1);
Logger::info("Chunk Block Init Finish, Time Consuming: {}", d); Logger::info("Chunk Block Init Finish, Time Consuming: {}", d);
start_server_thread();
Logger::info("TestPlayer Create Finish"); Logger::info("TestPlayer Create Finish");
} }
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));
} }
} }
@@ -289,32 +304,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;
@@ -339,43 +328,70 @@ 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;
compute_required_chunks(required_chunks); ChunkPairVector 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!!");
std::vector<ChunkPos> need_gen_chunks_pos; std::vector<ChunkPos> need_gen_chunks_pos;
sync_and_collect_missing_chunks(need_gen_chunks_pos, required_chunks); sync_and_collect_missing_chunks(need_gen_chunks_pos, required_chunks);
Logger::info("New Gen Chunks Sum: {}", need_gen_chunks_pos.size()); Logger::info("New Gen Chunks Sum: {}", need_gen_chunks_pos.size());
Logger::info("Temp Chunks sum {}", temp_neighbor.size());
if (need_gen_chunks_pos.empty()) { if (need_gen_chunks_pos.empty()) {
m_could_gen = true; m_could_gen = true;
m_chunk_gen_fraction = 1.0f; m_chunk_gen_fraction = 1.0f;
return; return;
} }
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;
ChunkHashMap temp_neighbor;
build_neighbor_context_for_new_chunks(
new_chunks_neighbor, affected_neighbor, new_chunks, temp_neighbor);
Logger::info("Temp neighbor sum {}", temp_neighbor.size());
// build new chunk, but the neighbor in m_chunks also need to re-build
for (auto& [pos, chunk] : new_chunks) { build_neighbor_context_for_new_chunks(new_chunks_neighbor,
chunk.gen_phase_one(); affected_neighbor, new_chunks);
m_cave_carcer.try_to_add_path(pos, chunk.seed());
} // build new chunk, but the neighbor in m_chunks also need to re-build
for (auto& [pos, chunk] : temp_neighbor) {
chunk.gen_phase_one(); std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
m_cave_carcer.try_to_add_path(pos, chunk.seed()); [this](std::pair<ChunkPos, Chunk>& new_chunk) {
} 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());
});
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
std::for_each(std::execution::par, temp_neighbor.begin(),
temp_neighbor.end(),
[this](std::pair<ChunkPos, Chunk>& new_chunk) {
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;
/*
std::array<const Chunk*, 8> neighbor_chunks; std::array<const Chunk*, 8> neighbor_chunks;
for (auto& [pos, chunks] : new_chunks) { for (auto& [pos, chunks] : new_chunks) {
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
@@ -383,13 +399,16 @@ void World::gen_chunks_internal() {
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_chunks[i] = nullptr; neighbor_chunks[i] = nullptr;
ASSERT_MSG(false, "Cant Find Neighbot"); // ASSERT_MSG(false, "Cant Find Neighbot");
continue; continue;
} }
neighbor_chunks[i] = it->second; neighbor_chunks[i] = it->second;
} }
chunks.gen_phase_two(neighbor_chunks); chunks.gen_phase_two(neighbor_chunks);
} }
*/
/*
for (auto& [pos, chunks] : temp_neighbor) { for (auto& [pos, chunks] : temp_neighbor) {
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
auto neighbor_pos = pos + CHUNK_DIR[i]; auto neighbor_pos = pos + CHUNK_DIR[i];
@@ -402,14 +421,24 @@ void World::gen_chunks_internal() {
} }
chunks.gen_phase_two(neighbor_chunks); chunks.gen_phase_two(neighbor_chunks);
} }
*/
m_chunk_gen_fraction = 0.3f; m_chunk_gen_fraction = 0.3f;
for (auto& [pos, chunks] : new_chunks) {
chunks.gen_phase_three(); std::for_each(std::execution::par, new_chunks.begin(), new_chunks.end(),
[](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) {
std::array<std::optional<HeightMapArray>, 8> std::array<std::optional<HeightMapArray>, 8>
@@ -451,64 +480,115 @@ void World::gen_chunks_internal() {
chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome); chunks.gen_phase_four(neighbor_chunk_heightmap, neighbor_biome);
} }
} }
*/
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; */
for (auto& [pos, chunks] : new_chunks) {
std::vector<std::pair<Chunk*, OptionalBlockVectorArray>>
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) {
chunks.gen_phase_seven();
} }
std::for_each(
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;
for (auto& [pos, chunk] : new_chunks) { std::vector<std::pair<Chunk*, OptionalBlockVectorArray>>
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);
chunk->need_upload(); chunk->need_upload();
} }
m_chunk_gen_fraction = 0.9f; m_chunk_gen_fraction = 0.9f;
{ {
std::lock_guard lk(m_new_chunk_queue_mutex); std::lock_guard lk(m_new_chunk_queue_mutex);
for (auto& x : new_chunks) { for (auto& x : new_chunks) {
@@ -516,6 +596,7 @@ void World::gen_chunks_internal() {
} }
} }
m_cave_carcer.cleanup_finished_caves(); m_cave_carcer.cleanup_finished_caves();
m_river_worm.cleanup_finished_rivers();
m_chunk_gen_fraction = 1.0f; m_chunk_gen_fraction = 1.0f;
m_chunk_gen_finished = true; m_chunk_gen_finished = true;
} }
@@ -525,19 +606,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(
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());
radius = max_path_len / 2;
r2 = radius * radius;
for (int dx = -radius; dx <= radius; ++dx) {
for (int dz = -radius; dz <= radius; ++dz) {
if (dx * dx + dz * dz <= r2) {
ChunkPos pos{chunk_x + dx, chunk_z + dz};
auto it = required_chunks.find(pos);
if (it != required_chunks.end()) {
continue;
}
temp_neighbor.emplace_back(pos, Chunk(*this, pos));
}
} }
} }
} }
@@ -564,7 +679,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, ChunkHashMap& temp_neighbor) { 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) {
@@ -573,8 +688,6 @@ void World::build_neighbor_context_for_new_chunks(
if (it != m_chunks.end()) { if (it != m_chunks.end()) {
new_chunks_neighbor.insert({it->first, &(it->second)}); new_chunks_neighbor.insert({it->first, &(it->second)});
affected_neighbor.push_back({it->first, &(it->second)}); affected_neighbor.push_back({it->first, &(it->second)});
} else {
temp_neighbor.emplace(pos + dir, Chunk(*this, pos + dir));
} }
} }
} }
@@ -582,9 +695,6 @@ void World::build_neighbor_context_for_new_chunks(
for (auto& [pos, chunk] : new_chunks) { for (auto& [pos, chunk] : new_chunks) {
new_chunks_neighbor.insert({pos, &chunk}); new_chunks_neighbor.insert({pos, &chunk});
} }
for (auto& [pos, chunk] : temp_neighbor) {
new_chunks_neighbor.insert({pos, &chunk});
}
} }
void World::build_neighbor_context_for_affected_neighbors( void World::build_neighbor_context_for_affected_neighbors(
@@ -623,6 +733,11 @@ void World::start_gen_thread() {
}); });
} }
void World::start_server_thread() {
m_server_thread = std::thread(
[this]() { serever_run(m_server_stop_source.get_token()); });
}
void World::stop_gen_thread() { void World::stop_gen_thread() {
m_gen_running = false; m_gen_running = false;
m_gen_cv.notify_all(); m_gen_cv.notify_all();
@@ -632,6 +747,25 @@ void World::stop_gen_thread() {
Logger::info("Gen Thread Stopped"); Logger::info("Gen Thread Stopped");
} }
void World::stop_server_thread() {
m_server_stop_source.request_stop();
if (m_server_thread.joinable()) {
m_server_thread.join();
}
}
void World::serever_run(std::stop_token stoken) {
Logger::info("Server Thread Started!");
while (!stoken.stop_requested()) {
std::this_thread::sleep_for(milliseconds(m_per_tick_time));
if (m_tick_running) {
++m_game_ticks;
m_day_tick = (m_day_tick + 1) % DAY_TIME;
}
}
Logger::info("Server Thread Stopped!");
}
void World::need_gen() { void World::need_gen() {
if (!m_could_gen) { if (!m_could_gen) {
Logger::warn("It is generating or consuming new chunks"); Logger::warn("It is generating or consuming new chunks");
@@ -647,21 +781,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);
@@ -680,7 +799,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});
@@ -695,13 +814,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;
@@ -751,6 +909,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();
@@ -781,13 +948,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);
@@ -798,7 +965,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),
@@ -817,6 +989,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");
@@ -831,6 +1008,7 @@ void World::rebuild_world() {
m_is_rebuilding = true; m_is_rebuilding = true;
stop_gen_thread(); stop_gen_thread();
m_cave_carcer.reload(ChunkGenerator::seed()); m_cave_carcer.reload(ChunkGenerator::seed());
m_river_worm.reload(ChunkGenerator::seed());
{ {
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_chunks.clear(); m_chunks.clear();
@@ -853,5 +1031,54 @@ 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; }
std::vector<glm::vec4>& World::planes() { return m_planes; }
std::vector<ChunkRenderSnapshot>& World::render_snapshots() {
return m_render_snapshots;
};
/*
glm::vec3 World::sunlight_dir() const {
float t = static_cast<float>(m_day_tick) / DAY_TIME;
float azimuth = glm::radians(90.0f - t * 360.0f);
float altitude =
glm::half_pi<float>() * sin((t - 0.25f) * glm::two_pi<float>());
glm::vec3 dir{cos(altitude) * cos(azimuth), sin(altitude),
cos(altitude) * sin(azimuth)};
return glm::normalize(-dir);
}
*/
glm::vec3 World::sunlight_dir() const {
float altitude = sin((m_day_tick - 6 * PER_HOUR) /
static_cast<float>(DAY_TIME / 2) * std::numbers::pi) *
90.0f;
float t = static_cast<float>(m_day_tick) / DAY_TIME;
float azimuth = 90.0f - 360.0f * (t - 0.25f);
float alt = glm::radians(altitude);
float az = glm::radians(azimuth);
glm::vec3 dir;
dir.x = cos(alt) * sin(az);
dir.y = sin(alt);
dir.z = cos(alt) * cos(az);
return glm::normalize(-dir);
}
TickType World::game_tick() const { return m_game_ticks.load(); }
TickType World::day_tick() const { return m_day_tick.load(); }
void World::day_tick(TickType tick) {
tick %= DAY_TIME;
m_day_tick = tick;
}
int World::per_tick_time() const { return m_per_tick_time.load(); }
void World::per_tick_time(int ms) { m_per_tick_time = ms; }
bool World::is_tick_running() const { return m_tick_running.load(); }
void World::tick_running(bool run) { m_tick_running = run; }
} // 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,11 +12,13 @@
#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>
#include <format> #include <format>
#include <glm/gtc/type_ptr.hpp> #include <glm/gtc/type_ptr.hpp>
namespace Cubed { namespace Cubed {
Renderer::Renderer(const Camera& camera, World& world, Renderer::Renderer(const Camera& camera, World& world,
@@ -33,6 +35,17 @@ 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);
glDeleteFramebuffers(1, &m_depth_map_fbo);
glDeleteTextures(1, &m_depth_map_texture);
} }
void Renderer::hot_reload() { void Renderer::hot_reload() {
@@ -49,7 +62,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 +72,27 @@ 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"};
Shader depth_shader{"depth_shader", "shaders/depth_shader.glsl",
"shaders/depth_fragment_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)});
m_shaders.insert({depth_shader.hash(), std::move(depth_shader)});
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL); glDepthFunc(GL_LEQUAL);
@@ -86,20 +113,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 +147,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 +172,52 @@ 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() {
glDisable(GL_FRAMEBUFFER_SRGB);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glClearColor(0.0, 0.0, 0.0, 1.0); glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glClear(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);
glEnable(GL_FRAMEBUFFER_SRGB);
glDisable(GL_DEPTH_TEST);
glClearColor(0.0f, 0.0f, 0.0f, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
render_underwater();
glDisable(GL_FRAMEBUFFER_SRGB);
render_ui(); render_ui();
glBindVertexArray(m_vao[4]);
render_text(); render_text();
glBindVertexArray(0);
render_dev_panel(); render_dev_panel();
} }
@@ -156,23 +225,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);
@@ -195,22 +265,59 @@ 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));
glUniform3fv(shader.loc("color"), 1, glm::value_ptr(SKY_COLOR));
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);
glDrawArrays(GL_TRIANGLES, 0, 36); glDrawArrays(GL_TRIANGLES, 0, 36);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
// draw sun and moon
glDepthMask(GL_FALSE);
glBindVertexArray(m_vao[0]);
// draw sum
glm::vec3 sun_pos = m_camera.get_camera_pos() +
normalize(-m_world.sunlight_dir()) * (FAR_PLANE * 0.9f);
glm::vec3 sun_view_pos = glm::vec3(m_v_mat * glm::vec4(sun_pos, 1.0f));
m_mv_mat = glm::translate(glm::mat4(1.0f), sun_view_pos) *
glm::scale(glm::mat4(1.0f), glm::vec3(SUN_SIZE)) *
glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f));
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));
glUniform3fv(shader.loc("color"), 1, glm::value_ptr(SUN_COLOR));
glDrawArrays(GL_TRIANGLES, 0, 6);
glm::vec3 moon_pos = m_camera.get_camera_pos() +
normalize(m_world.sunlight_dir()) * (FAR_PLANE * 0.9f);
glm::vec3 moon_view_pos = glm::vec3(m_v_mat * glm::vec4(moon_pos, 1.0f));
m_mv_mat = glm::translate(glm::mat4(1.0f), moon_view_pos) *
glm::scale(glm::mat4(1.0f), glm::vec3(MOON_SIZE)) *
glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0.0f));
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));
glUniform3fv(shader.loc("color"), 1, glm::value_ptr(MOON_COLOR));
glDrawArrays(GL_TRIANGLES, 0, 6);
glDepthMask(GL_TRUE);
} }
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 +335,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);
@@ -263,7 +382,8 @@ void Renderer::update_fov(float fov) {
void Renderer::update_proj_matrix(float aspect, float width, float height) { void Renderer::update_proj_matrix(float aspect, float width, float height) {
m_aspect = aspect; m_aspect = aspect;
m_p_mat = glm::perspective(glm::radians(m_fov), aspect, 0.1f, 1000.0f); m_p_mat =
glm::perspective(glm::radians(m_fov), aspect, NEAR_PLANE, FAR_PLANE);
m_ui_proj = glm::ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f); m_ui_proj = glm::ortho(0.0f, width, height, 0.0f, -1.0f, 1.0f);
// scale and then translate // scale and then translate
m_ui_m_matrix = m_ui_m_matrix =
@@ -272,22 +392,368 @@ 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::updata_framebuffer(int width, int height) {
if (width <= 0 || height <= 0)
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);
// depth map fbo
if (m_depth_map_fbo == 0) {
glGenFramebuffers(1, &m_depth_map_fbo);
}
glDeleteTextures(1, &m_depth_map_texture);
glGenTextures(1, &m_depth_map_texture);
glBindTexture(GL_TEXTURE_2D, m_depth_map_texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, DEPTH_MAP_SIZE,
DEPTH_MAP_SIZE, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
float border_color[] = {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border_color);
// Manually compare shadows
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE,
// GL_COMPARE_REF_TO_TEXTURE);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
m_depth_map_texture, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
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() { void Renderer::render_world() {
const auto& shader = get_shader("world"); // shader map
shader.use(); glm::mat4 light_space_matrix;
auto& m_render_snapshots = m_world.render_snapshots();
auto& camera_pos = m_camera.get_camera_pos();
if (m_shader_on) {
const auto& depth_shader = get_shader("depth_shader");
depth_shader.use();
glm::vec3 cam_pos = m_camera.get_camera_pos();
glm::vec3 cam_fwd = m_camera.get_camera_front();
float half_extent = 128.0f;
glm::vec3 center = cam_pos + cam_fwd * (half_extent * 0.5f);
glm::vec3 sundir = glm::normalize(m_world.sunlight_dir());
glm::vec3 raw_shadow_sundir =
quantize_sun_direction(sundir, ANGLE_STEP_DEG);
glm::vec3 shadow_sundir =
get_smoothed_shadow_sundir(raw_shadow_sundir, m_delta_time);
glm::vec3 up = fabs(shadow_sundir.y) > 0.99f ? glm::vec3(0, 0, 1)
: glm::vec3(0, 1, 0);
glm::mat4 light_basis = glm::lookAt(glm::vec3(0.0f), shadow_sundir, up);
float texels_per_unit = DEPTH_MAP_SIZE / (half_extent * 2.0f);
glm::vec3 ls_center = glm::vec3(light_basis * glm::vec4(center, 1.0f));
ls_center.x =
std::round(ls_center.x * texels_per_unit) / texels_per_unit;
ls_center.y =
std::round(ls_center.y * texels_per_unit) / texels_per_unit;
glm::vec3 snapped_center =
glm::vec3(glm::inverse(light_basis) * glm::vec4(ls_center, 1.0f));
float distance = half_extent * 1.5f;
float near_plane = 1.0f;
float far_plane = distance + half_extent * 2.0f;
glm::vec3 light_pos = snapped_center - shadow_sundir * distance;
glm::mat4 light_view = glm::lookAt(light_pos, snapped_center, up);
glm::mat4 light_projection =
glm::ortho(-half_extent, half_extent, -half_extent, half_extent,
near_plane, far_plane);
light_space_matrix = light_projection * light_view;
glUniformMatrix4fv(depth_shader.loc("lightSpaceMatrix"), 1, GL_FALSE,
glm::value_ptr(light_space_matrix));
glUniform1i(depth_shader.loc("is_discard_tranparent"),
m_discard_tranparent);
glViewport(0, 0, DEPTH_MAP_SIZE, DEPTH_MAP_SIZE);
if (m_light_cull_face == 0) {
glCullFace(GL_FRONT);
} else if (m_light_cull_face == 1) {
glCullFace(GL_BACK);
} else {
Logger::warn("Light Cull Face {} Over The Max Selection",
m_light_cull_face);
glCullFace(GL_BACK);
}
glBindFramebuffer(GL_FRAMEBUFFER, m_depth_map_fbo);
glClear(GL_DEPTH_BUFFER_BIT);
glActiveTexture(GL_TEXTURE1);
glEnable(GL_DEPTH_TEST);
for (const auto& snapshot : m_render_snapshots) {
glBindTexture(GL_TEXTURE_2D_ARRAY,
m_texture_manager.get_texture_array());
glBindVertexArray(snapshot.normal_vao);
glDrawArrays(GL_TRIANGLES, 0, snapshot.normal_vertices_count);
}
// cross_plane and discard
for (const auto& snapshot : m_render_snapshots) {
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_texture_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);
}
}
}
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glCullFace(GL_BACK);
glViewport(0, 0, m_width, m_height);
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, m_depth_map_texture);
glActiveTexture(GL_TEXTURE1);
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;
m_norm_mat = glm::transpose(glm::inverse(m_mv_mat));
glm::vec3 light_dir_view =
glm::normalize(glm::mat3(m_v_mat) * m_world.sunlight_dir());
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));
glUniformMatrix4fv(normal_block_shader.loc("norm_matrix"), 1, GL_FALSE,
glm::value_ptr(m_norm_mat));
glUniformMatrix4fv(normal_block_shader.loc("lightSpaceMatrix"), 1, GL_FALSE,
glm::value_ptr(light_space_matrix));
glUniform1f(normal_block_shader.loc("ambientStrength"), m_ambient_strength);
glUniform3fv(normal_block_shader.loc("sunlightColor"), 1,
glm::value_ptr(SUNLIGHT_COLOR));
glUniform3fv(normal_block_shader.loc("sunlightDir"), 1,
glm::value_ptr(light_dir_view));
glUniform1i(normal_block_shader.loc("shadowMode"), m_shadow_mode);
glUniform1i(normal_block_shader.loc("shader_on"), m_shader_on);
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& m_planes = m_world.planes();
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);
glActiveTexture(GL_TEXTURE0);
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() {
@@ -296,4 +762,56 @@ void Renderer::render_dev_panel() {
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
} }
glm::vec3 Renderer::quantize_sun_direction(const glm::vec3& sundir,
float angle_step_deg) const {
float elevation = std::asin(glm::clamp(sundir.y, -1.0f, 1.0f));
float azimuth = std::atan2(sundir.z, sundir.x);
float step = glm::radians(angle_step_deg);
float quantized_elevation = std::round(elevation / step) * step;
float quantized_azimuth = std::round(azimuth / step) * step;
glm::vec3 quantized_dir;
quantized_dir.x =
std::cos(quantized_elevation) * std::cos(quantized_azimuth);
quantized_dir.z =
std::cos(quantized_elevation) * std::sin(quantized_azimuth);
quantized_dir.y = std::sin(quantized_elevation);
return glm::normalize(quantized_dir);
}
glm::vec3
Renderer::get_smoothed_shadow_sundir(const glm::vec3& raw_shadow_sundir,
float dt) {
if (!m_blend_initialized) {
m_blend_from_sundir = raw_shadow_sundir;
m_blend_to_sundir = raw_shadow_sundir;
m_blend_t = 1.0f;
m_blend_initialized = true;
return raw_shadow_sundir;
}
if (raw_shadow_sundir != m_blend_to_sundir) {
glm::vec3 current_displayed = glm::normalize(
Math::slerp(m_blend_from_sundir, m_blend_to_sundir, m_blend_t));
m_blend_from_sundir = current_displayed;
m_blend_to_sundir = raw_shadow_sundir;
m_blend_t = 0.0f;
}
m_blend_t = glm::min(m_blend_t + dt / BLEND_DURATION, 1.0f);
return glm::normalize(
Math::slerp(m_blend_from_sundir, m_blend_to_sundir, m_blend_t));
}
float& Renderer::ambient_strength() { return m_ambient_strength; }
bool& Renderer::discard_transparent() { return m_discard_tranparent; }
bool& Renderer::shader_on() { return m_shader_on; }
int& Renderer::shadow_mode() { return m_shadow_mode; }
int& Renderer::light_cull_face() { return m_light_cull_face; }
} // namespace Cubed } // namespace Cubed

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);
} }

Some files were not shown because too many files have changed in this diff Show More