Compare commits
1 Commits
v0.0.1
...
623f991fcf
| Author | SHA1 | Date | |
|---|---|---|---|
| 623f991fcf |
18
.github/workflows/format-check.yml
vendored
@@ -1,3 +1,4 @@
|
||||
# .github/workflows/format-check.yml
|
||||
name: Code Format Check
|
||||
|
||||
on: [push, pull_request]
|
||||
@@ -8,22 +9,9 @@ permissions:
|
||||
jobs:
|
||||
formatting:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
container: silkeh/clang:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install LLVM 22
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 22
|
||||
|
||||
sudo apt-get install -y clang-format-22
|
||||
|
||||
clang-format-22 --version
|
||||
|
||||
- name: Run clang-format
|
||||
run: |
|
||||
find src include \
|
||||
\( -name '*.cpp' -o -name '*.hpp' \) \
|
||||
-print0 | xargs -0 clang-format-22 --dry-run --Werror
|
||||
find src include -name '*.cpp' -o -name '*.h' -print0 | xargs -0 clang-format --dry-run --Werror
|
||||
154
.github/workflows/release-build.yml
vendored
@@ -1,154 +0,0 @@
|
||||
name: Release-Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: Version
|
||||
required: false
|
||||
default: dev
|
||||
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: windows-2022
|
||||
platform: windows
|
||||
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
|
||||
- name: Get Version
|
||||
id: version
|
||||
shell: bash
|
||||
run: |
|
||||
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
|
||||
VERSION="${GITHUB_REF_NAME#v}"
|
||||
else
|
||||
VERSION="${{ github.event.inputs.version }}"
|
||||
fi
|
||||
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Setup MSVC
|
||||
if: matrix.platform == 'windows'
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
|
||||
- name: Bootstrap vcpkg
|
||||
run: |
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
./vcpkg/bootstrap-vcpkg.bat
|
||||
|
||||
- name: Configure
|
||||
if: matrix.platform == 'windows'
|
||||
run: >
|
||||
cmake
|
||||
-B build
|
||||
-G Ninja
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
-DCMAKE_TOOLCHAIN_FILE=${{ github.workspace }}/vcpkg/scripts/buildsystems/vcpkg.cmake
|
||||
-DVCPKG_TARGET_TRIPLET=x64-windows-release
|
||||
-DVCPKG_BUILD_TYPE=release
|
||||
-DBUILD_TESTING=OFF
|
||||
"-DCUBED_VERSION=${{ steps.version.outputs.version }}"
|
||||
|
||||
- name: Build
|
||||
run: cmake --build build --config Release --target Cubed
|
||||
|
||||
- name: Copy assets
|
||||
if: matrix.platform == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
if (Test-Path assets) {
|
||||
Copy-Item -Path assets -Destination build/Cubed -Recurse
|
||||
} else {
|
||||
Write-Warning "assets folder not found, skipping copy"
|
||||
}
|
||||
|
||||
- name: Create Zip
|
||||
if: matrix.platform == 'windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
Compress-Archive `
|
||||
-Path build/Cubed/* `
|
||||
-DestinationPath Cubed-${{ steps.version.outputs.version }}-windows-x64.zip
|
||||
|
||||
####################################################################
|
||||
# Upload
|
||||
####################################################################
|
||||
|
||||
- name: Upload Windows Artifacts
|
||||
if: matrix.platform == 'windows'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows
|
||||
path: |
|
||||
*.zip
|
||||
|
||||
##########################################################################
|
||||
# Release
|
||||
##########################################################################
|
||||
|
||||
release:
|
||||
|
||||
needs: build
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Download Artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Generate SHA256
|
||||
run: |
|
||||
cd artifacts
|
||||
|
||||
find . -type f | while read file
|
||||
do
|
||||
sha256sum "$file"
|
||||
done > SHA256SUMS
|
||||
|
||||
- name: Determine prerelease
|
||||
id: prerelease
|
||||
run: |
|
||||
MAIN_BRANCH="${{ github.event.repository.default_branch }}"
|
||||
git fetch origin "$MAIN_BRANCH" --depth=1 2>/dev/null || true
|
||||
|
||||
if git merge-base --is-ancestor "${{ github.sha }}" "origin/$MAIN_BRANCH"; then
|
||||
echo "is_prerelease=false" >> $GITHUB_OUTPUT
|
||||
echo "is_prerelease=false"
|
||||
else
|
||||
echo "is_prerelease=true" >> $GITHUB_OUTPUT
|
||||
echo "is_prerelease=true"
|
||||
fi
|
||||
|
||||
- name: Release
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: |
|
||||
artifacts/**/*
|
||||
artifacts/SHA256SUMS
|
||||
generate_release_notes: true
|
||||
draft: false
|
||||
prerelease: ${{ steps.prerelease.outputs.is_prerelease }}
|
||||
4
.gitignore
vendored
@@ -41,6 +41,4 @@ CMakeError.log
|
||||
*~
|
||||
.DS_Store
|
||||
assets/config.toml
|
||||
.venv/
|
||||
pyout/
|
||||
vcpkg_installed/
|
||||
.venv/
|
||||
262
CMakeLists.txt
@@ -1,4 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.21)
|
||||
cmake_minimum_required(VERSION 3.14...3.24)
|
||||
project(Cubed LANGUAGES C CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
@@ -6,118 +6,153 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
if(NOT CMAKE_CONFIGURATION_TYPES
|
||||
AND NOT CMAKE_BUILD_TYPE)
|
||||
|
||||
set(CMAKE_BUILD_TYPE Debug CACHE STRING "" FORCE)
|
||||
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED CUBED_VERSION)
|
||||
set(CUBED_VERSION "dev")
|
||||
if(NOT CMAKE_BUILD_TYPE)
|
||||
set(CMAKE_BUILD_TYPE Debug)
|
||||
endif()
|
||||
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PROJECT_NAME})
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${PROJECT_NAME})
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules"
|
||||
)
|
||||
if(MSVC)
|
||||
add_compile_options(/utf-8)
|
||||
endif()
|
||||
|
||||
include(Dependencies)
|
||||
find_package(OpenGL REQUIRED)
|
||||
|
||||
add_subdirectory(third_party/glad)
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(EGL REQUIRED egl)
|
||||
pkg_check_modules(Wayland REQUIRED wayland-client wayland-egl)
|
||||
find_package(glfw3 REQUIRED)
|
||||
endif()
|
||||
|
||||
add_subdirectory(third_party/imgui)
|
||||
add_library(glad STATIC third_party/glad/src/glad.c)
|
||||
target_include_directories(glad PUBLIC third_party/glad/include)
|
||||
|
||||
add_executable(${PROJECT_NAME})
|
||||
include(FetchContent)
|
||||
|
||||
add_subdirectory(src)
|
||||
|
||||
file(GLOB_RECURSE PROTO_FILES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/src/proto/*.proto
|
||||
)
|
||||
|
||||
protobuf_generate(
|
||||
TARGET ${PROJECT_NAME}
|
||||
LANGUAGE cpp
|
||||
PROTOS ${PROTO_FILES}
|
||||
IMPORT_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/src/proto
|
||||
)
|
||||
|
||||
configure_file(
|
||||
src/version.hpp.in
|
||||
${CMAKE_BINARY_DIR}/generated/version.hpp
|
||||
@ONLY
|
||||
)
|
||||
|
||||
target_compile_options(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
$<$<CONFIG:Debug>:-fno-omit-frame-pointer>
|
||||
$<$<CONFIG:Debug>:-g>
|
||||
#$<$<CONFIG:Debug>:-fsanitize=address>
|
||||
#$<$<CONFIG:Debug>:-fsanitize=thread>
|
||||
|
||||
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wall>
|
||||
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wextra>
|
||||
$<$<CXX_COMPILER_ID:GNU,Clang>:-Wpedantic>
|
||||
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/utf-8>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/W4>
|
||||
)
|
||||
|
||||
target_link_options(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
#$<$<CONFIG:Debug>:-fsanitize=address>
|
||||
#$<$<CONFIG:Debug>:-fsanitize=thread>
|
||||
)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
ASIO_STANDALONE
|
||||
ASIO_NO_DEPRECATED
|
||||
$<$<CONFIG:Debug>:DEBUG_MODE>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:
|
||||
WIN32_LEAN_AND_MEAN
|
||||
NOMINMAX
|
||||
_CRT_SECURE_NO_WARNINGS
|
||||
>
|
||||
)
|
||||
|
||||
if(CMAKE_CONFIGURATION_TYPES)
|
||||
# Visual Studio / Xcode multi-configuration generator
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
ASSETS_PATH="$<$<CONFIG:Debug>:${PROJECT_SOURCE_DIR}/assets/>$<$<NOT:$<CONFIG:Debug>>:./assets/>"
|
||||
if (WIN32)
|
||||
FetchContent_Declare(
|
||||
glfw
|
||||
GIT_REPOSITORY https://github.com/glfw/glfw.git
|
||||
GIT_TAG 3.4
|
||||
)
|
||||
else()
|
||||
# Ninja / Makefiles single-configuration generator
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
ASSETS_PATH="${PROJECT_SOURCE_DIR}/assets/"
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
ASSETS_PATH="./assets/"
|
||||
)
|
||||
|
||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_VULKAN_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(GLFW_STATIC ON CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(glfw)
|
||||
|
||||
FetchContent_Declare(
|
||||
freetype
|
||||
GIT_REPOSITORY https://gitlab.freedesktop.org/freetype/freetype.git
|
||||
GIT_TAG VER-2-14-3
|
||||
)
|
||||
FetchContent_MakeAvailable(freetype)
|
||||
if(TARGET freetype)
|
||||
add_library(Freetype::Freetype ALIAS freetype)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
FetchContent_Declare(
|
||||
glm
|
||||
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||
GIT_TAG 1.0.3
|
||||
|
||||
)
|
||||
FetchContent_MakeAvailable(glm)
|
||||
FetchContent_Declare(
|
||||
soil2
|
||||
GIT_REPOSITORY https://github.com/SpartanJ/SOIL2.git
|
||||
GIT_TAG 1.31
|
||||
|
||||
)
|
||||
FetchContent_MakeAvailable(soil2)
|
||||
FetchContent_Declare(
|
||||
tomlplusplus
|
||||
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
|
||||
GIT_TAG v3.4.0
|
||||
)
|
||||
FetchContent_MakeAvailable(tomlplusplus)
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/include
|
||||
${PROJECT_SOURCE_DIR}/third_party/asio/include
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
${PROJECT_BINARY_DIR}
|
||||
${PROJECT_BINARY_DIR}/src
|
||||
add_subdirectory(third_party/imgui)
|
||||
|
||||
|
||||
set(INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
src/main.cpp
|
||||
src/app.cpp
|
||||
src/debug_collector.cpp
|
||||
src/camera.cpp
|
||||
src/config.cpp
|
||||
src/dev_panel.cpp
|
||||
src/gameplay/biome.cpp
|
||||
src/gameplay/chunk.cpp
|
||||
src/gameplay/chunk_generator.cpp
|
||||
src/gameplay/player.cpp
|
||||
src/gameplay/tree.cpp
|
||||
src/gameplay/world.cpp
|
||||
src/input.cpp
|
||||
src/map_table.cpp
|
||||
src/renderer.cpp
|
||||
src/shader.cpp
|
||||
src/texture_manager.cpp
|
||||
src/tools/cubed_random.cpp
|
||||
src/tools/math_tools.cpp
|
||||
src/tools/shader_tools.cpp
|
||||
src/tools/font.cpp
|
||||
src/tools/perlin_noise.cpp
|
||||
src/ui/text.cpp
|
||||
src/window.cpp
|
||||
src/gameplay/builders/biome_builder.cpp
|
||||
src/gameplay/builders/plain_builder.cpp
|
||||
src/gameplay/builders/mountain_builder.cpp
|
||||
src/gameplay/builders/river_builder.cpp
|
||||
src/gameplay/builders/desert_builder.cpp
|
||||
src/gameplay/builders/forest_builder.cpp
|
||||
src/gameplay/cave_carver.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")
|
||||
message(STATUS "Building with AddressSanitizer enabled for target: ${PROJECT_NAME}")
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE
|
||||
#-fsanitize=address
|
||||
#-fsanitize=thread
|
||||
-fno-omit-frame-pointer
|
||||
-g
|
||||
)
|
||||
|
||||
target_link_options(${PROJECT_NAME} PRIVATE
|
||||
#-fsanitize=address
|
||||
#-fsanitize=thread
|
||||
)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG_MODE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
ASSETS_PATH="${CMAKE_SOURCE_DIR}/assets/"
|
||||
)
|
||||
else()
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
ASSETS_PATH="./assets/"
|
||||
)
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC ${INCLUDE_DIR})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
@@ -129,25 +164,26 @@ target_link_libraries(${PROJECT_NAME}
|
||||
Freetype::Freetype
|
||||
tomlplusplus::tomlplusplus
|
||||
imgui
|
||||
tbb
|
||||
protobuf::libprotobuf
|
||||
absl::log
|
||||
absl::check
|
||||
absl::base
|
||||
absl::strings
|
||||
absl::flat_hash_map
|
||||
zstd::zstd
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
add_custom_command(
|
||||
TARGET ${PROJECT_NAME}
|
||||
POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
$<TARGET_RUNTIME_DLLS:${PROJECT_NAME}>
|
||||
$<TARGET_FILE_DIR:${PROJECT_NAME}>
|
||||
COMMAND_EXPAND_LISTS
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE tbb)
|
||||
endif()
|
||||
|
||||
if (UNIX AND NOT APPLE)
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${EGL_LIBRARIES}
|
||||
${Wayland_LIBRARIES}
|
||||
)
|
||||
endif()
|
||||
|
||||
|
||||
target_include_directories(${PROJECT_NAME}
|
||||
PRIVATE
|
||||
${EGL_INCLUDE_DIRS}
|
||||
${Wayland_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE ${EGL_CFLAGS_OTHER} ${Wayland_CFLAGS_OTHER})
|
||||
endif()
|
||||
|
||||
@@ -5,7 +5,5 @@ is_discard = true
|
||||
is_gas = true
|
||||
is_liquid = false
|
||||
is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'air'
|
||||
roughness = 1.0
|
||||
name = 'air'
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'dirt'
|
||||
roughness = 1.0
|
||||
name = 'dirt'
|
||||
@@ -5,7 +5,5 @@ is_discard = true
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'grass'
|
||||
roughness = 0.9
|
||||
name = 'grass'
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'grass_block'
|
||||
roughness = 0.9
|
||||
name = 'grass_block'
|
||||
@@ -5,7 +5,5 @@ is_discard = true
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'leaf'
|
||||
roughness = 0.7
|
||||
name = 'leaf'
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = false
|
||||
is_transparent = false
|
||||
name = 'log'
|
||||
roughness = 0.7
|
||||
name = 'log'
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'sand'
|
||||
roughness = 0.8
|
||||
name = 'sand'
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'snowy_grass_block'
|
||||
roughness = 0.9
|
||||
name = 'snowy_grass_block'
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = false
|
||||
is_passable = false
|
||||
is_transitional = true
|
||||
is_transparent = false
|
||||
name = 'stone'
|
||||
roughness = 0.75
|
||||
name = 'stone'
|
||||
@@ -7,5 +7,3 @@ is_cross_plane = false
|
||||
is_transparent = false
|
||||
is_discard = false
|
||||
is_blend = false
|
||||
is_transitional = false
|
||||
roughness = 1.0
|
||||
@@ -5,7 +5,5 @@ is_discard = false
|
||||
is_gas = false
|
||||
is_liquid = true
|
||||
is_passable = true
|
||||
is_transitional = false
|
||||
is_transparent = true
|
||||
name = 'water'
|
||||
roughness = 0.02
|
||||
name = 'water'
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 955 B |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 955 B |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
@@ -1,11 +0,0 @@
|
||||
#version 460
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
uniform vec3 color;
|
||||
|
||||
void main(void) {
|
||||
|
||||
frag_color = vec4(color, 1.0);
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 vertices_pos;
|
||||
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
|
||||
void main(void) {
|
||||
gl_Position = proj_matrix * mv_matrix * vec4(vertices_pos, 1.0);
|
||||
}
|
||||
@@ -4,21 +4,10 @@ layout (location = 0) out vec4 accum;
|
||||
layout (location = 1) out float reveal;
|
||||
|
||||
in vec2 tc;
|
||||
in vec3 normal;
|
||||
in vec3 vert_pos;
|
||||
in float roughness;
|
||||
flat in int tex_layer;
|
||||
in float v_depth;
|
||||
layout (binding = 0) uniform sampler2DArray samp;
|
||||
|
||||
uniform float ambientStrength;
|
||||
uniform vec3 sunlightColor;
|
||||
uniform vec3 ambientColor;
|
||||
uniform vec3 sunlightDir;
|
||||
uniform vec3 cameraPos;
|
||||
uniform bool shader_on;
|
||||
uniform float specularStrength;
|
||||
|
||||
float weight(float z, float a) {
|
||||
float intermediate = 0.03 / (1e-5 + pow(z / 200.0, 4.0));
|
||||
|
||||
@@ -26,71 +15,7 @@ float weight(float z, float a) {
|
||||
}
|
||||
|
||||
void main() {
|
||||
|
||||
vec4 objectColor = texture(samp, vec3(tc, tex_layer));
|
||||
|
||||
if (!shader_on) {
|
||||
vec4 color = objectColor;
|
||||
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;
|
||||
return;
|
||||
}
|
||||
|
||||
vec3 lightDir = normalize(-sunlightDir);
|
||||
|
||||
vec3 norm = normalize(normal);
|
||||
|
||||
vec3 V =
|
||||
normalize(cameraPos - vert_pos);
|
||||
|
||||
vec3 H =
|
||||
normalize(lightDir + V);
|
||||
|
||||
vec3 ambient = ambientStrength * ambientColor;
|
||||
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
|
||||
vec3 diffuse = diff * sunlightColor;
|
||||
|
||||
float r =
|
||||
clamp(roughness, 0.0, 1.0);
|
||||
|
||||
float shininess =
|
||||
mix(
|
||||
512.0,
|
||||
4.0,
|
||||
r
|
||||
);
|
||||
float ks =
|
||||
mix(
|
||||
0.8,
|
||||
0.02,
|
||||
r
|
||||
);
|
||||
|
||||
float spec = 0.0;
|
||||
|
||||
if(diff > 0.0)
|
||||
{
|
||||
spec =
|
||||
ks *
|
||||
pow(
|
||||
max(dot(norm,H),0.0),
|
||||
shininess
|
||||
);
|
||||
}
|
||||
|
||||
vec3 specular = spec * sunlightColor * specularStrength;
|
||||
|
||||
vec4 color = vec4((ambient + diffuse) * objectColor.rgb + specular, objectColor.a);
|
||||
|
||||
vec4 color = texture(samp, vec3(tc, tex_layer));
|
||||
float alpha = color.a;
|
||||
if (alpha < 1e-4) discard;
|
||||
|
||||
|
||||
@@ -3,28 +3,19 @@
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 2) in float layer;
|
||||
layout (location = 3) in vec3 aNormal;
|
||||
layout (location = 4) in float Roughness;
|
||||
|
||||
out vec2 tc;
|
||||
out vec3 normal;
|
||||
out vec3 vert_pos;
|
||||
flat out int tex_layer;
|
||||
out float roughness;
|
||||
out float v_depth;
|
||||
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
uniform mat4 norm_matrix;
|
||||
|
||||
|
||||
|
||||
void main(void) {
|
||||
vec4 view_pos = mv_matrix * vec4(pos, 1.0);
|
||||
vert_pos = pos;
|
||||
roughness = Roughness;
|
||||
gl_Position = proj_matrix * view_pos;
|
||||
tc = texCoord;
|
||||
tex_layer = int(layer);
|
||||
v_depth = -view_pos.z;
|
||||
normal = mat3(norm_matrix) * aNormal;
|
||||
gl_Position = proj_matrix * view_pos;
|
||||
}
|
||||
|
||||
@@ -1,363 +1,15 @@
|
||||
#version 460
|
||||
|
||||
in vec2 tc;
|
||||
in vec3 normal;
|
||||
in vec3 vert_pos;
|
||||
in vec3 tangent;
|
||||
in vec3 bitangent;
|
||||
in vec4 FragPosLightSpace;
|
||||
in float roughness;
|
||||
flat in int tex_layer;
|
||||
out vec4 color;
|
||||
layout (binding = 0) uniform sampler2D shadowMap;
|
||||
layout (binding = 1) uniform sampler2DArray samp;
|
||||
layout (binding = 2) uniform sampler2DArray normMap;
|
||||
uniform float ambientStrength;
|
||||
uniform vec3 sunlightColor;
|
||||
uniform vec3 ambientColor;
|
||||
uniform vec3 sunlightDir;
|
||||
uniform vec3 cameraPos;
|
||||
uniform bool shader_on;
|
||||
uniform int shadowMode;
|
||||
uniform float specularStrength;
|
||||
uniform float lightSizeUV;
|
||||
uniform float minRadius;
|
||||
uniform float maxRadius;
|
||||
uniform bool enablePBR;
|
||||
uniform bool flipY;
|
||||
|
||||
uniform int renderDistance;
|
||||
uniform vec3 skyColor;
|
||||
|
||||
const vec2 poissonDisk32[32] = vec2[](
|
||||
vec2(-0.975402, -0.071138),
|
||||
vec2(-0.920347, -0.411420),
|
||||
vec2(-0.883908, 0.217872),
|
||||
vec2(-0.815442, -0.879125),
|
||||
vec2(-0.775043, 0.543896),
|
||||
vec2(-0.698126, -0.227570),
|
||||
vec2(-0.682433, 0.801894),
|
||||
vec2(-0.563905, 0.021517),
|
||||
vec2(-0.443233, -0.975116),
|
||||
vec2(-0.412231, 0.361307),
|
||||
vec2(-0.264969, -0.418930),
|
||||
vec2(-0.241888, 0.997065),
|
||||
vec2(-0.094184, -0.929389),
|
||||
vec2(-0.019101, 0.680997),
|
||||
vec2( 0.143832, -0.141008),
|
||||
vec2( 0.199841, 0.786414),
|
||||
|
||||
vec2( 0.344959, 0.293878),
|
||||
vec2( 0.443233, -0.475115),
|
||||
vec2( 0.537430, -0.473734),
|
||||
vec2( 0.589349, 0.569135),
|
||||
vec2( 0.674281, -0.178897),
|
||||
vec2( 0.791975, 0.190902),
|
||||
vec2( 0.815442, 0.879125),
|
||||
vec2( 0.896420, -0.613392),
|
||||
vec2( 0.945586, -0.768907),
|
||||
vec2( 0.974844, 0.756484),
|
||||
vec2(-0.814100, 0.914376),
|
||||
vec2(-0.382775, 0.276768),
|
||||
vec2(-0.915886, 0.457714),
|
||||
vec2( 0.537800, 0.912200),
|
||||
vec2(-0.620000, -0.650000),
|
||||
vec2( 0.120000, -0.780000)
|
||||
);
|
||||
|
||||
const vec2 poissonDisk16[16] = vec2[](
|
||||
vec2(-0.94201624, -0.39906216), vec2(0.94558609, -0.76890725),
|
||||
vec2(-0.09418410, -0.92938870), vec2(0.34495938, 0.29387760),
|
||||
vec2(-0.91588581, 0.45771432), vec2(-0.81544232, -0.87912464),
|
||||
vec2(-0.38277543, 0.27676845), vec2(0.97484398, 0.75648379),
|
||||
vec2(0.44323325, -0.97511554), vec2(0.53742981, -0.47373420),
|
||||
vec2(-0.26496911, -0.41893023), vec2(0.79197514, 0.19090188),
|
||||
vec2(-0.24188840, 0.99706507), vec2(-0.81409955, 0.91437590),
|
||||
vec2(0.19984126, 0.78641367), vec2(0.14383161, -0.14100790)
|
||||
);
|
||||
const vec2 poissonDisk8[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)
|
||||
);
|
||||
uniform int samples;
|
||||
float random(vec3 seed) {
|
||||
return fract(sin(dot(seed, vec3(12.9898,78.233,45.5432))) * 43758.5453);
|
||||
}
|
||||
|
||||
float FindBlocker(vec2 uv,
|
||||
float zReceiver,
|
||||
vec2 texelSize,
|
||||
float bias,
|
||||
float lightSizeUV)
|
||||
{
|
||||
float avgDepth = 0.0;
|
||||
int blockers = 0;
|
||||
|
||||
float searchRadius = lightSizeUV * 0.5;
|
||||
|
||||
for(int i = 0; i < samples; i++)
|
||||
{
|
||||
vec2 offset;
|
||||
if (samples == 32) {
|
||||
offset =
|
||||
poissonDisk32[i]
|
||||
* searchRadius
|
||||
* texelSize;
|
||||
} else if (samples == 16) {
|
||||
offset =
|
||||
poissonDisk16[i]
|
||||
* searchRadius
|
||||
* texelSize;
|
||||
} else if (samples == 8) {
|
||||
offset =
|
||||
poissonDisk8[i]
|
||||
* searchRadius
|
||||
* texelSize;
|
||||
} else {
|
||||
offset =
|
||||
poissonDisk32[i]
|
||||
* searchRadius
|
||||
* texelSize;
|
||||
}
|
||||
float depth =
|
||||
texture(shadowMap, uv + offset).r;
|
||||
|
||||
if(depth < zReceiver - bias)
|
||||
{
|
||||
avgDepth += depth;
|
||||
blockers++;
|
||||
}
|
||||
}
|
||||
|
||||
if(blockers == 0)
|
||||
return -1.0;
|
||||
|
||||
return avgDepth / blockers;
|
||||
}
|
||||
|
||||
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 =
|
||||
clamp(
|
||||
0.001 * (1.0 - dot(norm, lightDir)),
|
||||
0.0003,
|
||||
0.003
|
||||
);
|
||||
|
||||
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;
|
||||
float radius = mix(1.0, 4.0, currentDepth);
|
||||
|
||||
for (int i = 0; i < samples; ++i) {
|
||||
vec2 offset;
|
||||
if (samples == 32) {
|
||||
offset = rot * poissonDisk32[i] * radius * texelSize;
|
||||
} else if (samples == 16) {
|
||||
offset = rot * poissonDisk16[i] * radius * texelSize;
|
||||
} else if (samples == 8) {
|
||||
offset = rot * poissonDisk8[i] * radius * texelSize;
|
||||
} else {
|
||||
offset = rot * poissonDisk32[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 if (shadowMode == 3) {
|
||||
float avgBlockerDepth =
|
||||
FindBlocker(
|
||||
projCoords.xy,
|
||||
currentDepth,
|
||||
texelSize,
|
||||
bias,
|
||||
lightSizeUV
|
||||
);
|
||||
|
||||
if(avgBlockerDepth < 0.0)
|
||||
{
|
||||
return 0.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 penumbraRatio = (currentDepth - avgBlockerDepth);
|
||||
float radius = clamp(
|
||||
penumbraRatio * lightSizeUV,
|
||||
minRadius,
|
||||
maxRadius
|
||||
);
|
||||
*/
|
||||
float radius =
|
||||
mix(
|
||||
minRadius,
|
||||
maxRadius,
|
||||
smoothstep(
|
||||
0.0,
|
||||
0.05,
|
||||
currentDepth - avgBlockerDepth
|
||||
)
|
||||
);
|
||||
for (int i = 0; i < samples; ++i) {
|
||||
vec2 offset;
|
||||
if (samples == 32) {
|
||||
offset = rot * poissonDisk32[i] * radius * texelSize;
|
||||
} else if (samples == 16) {
|
||||
offset = rot * poissonDisk16[i] * radius * texelSize;
|
||||
} else if (samples == 8) {
|
||||
offset = rot * poissonDisk8[i] * radius * texelSize;
|
||||
} else {
|
||||
offset = rot * poissonDisk32[i] * radius * texelSize;
|
||||
}
|
||||
float pcfDepth = texture(shadowMap, projCoords.xy + offset).r;
|
||||
shadow += (currentDepth - bias > pcfDepth ? 1.0 : 0.0);
|
||||
}
|
||||
shadow /= float(samples);
|
||||
|
||||
} else {
|
||||
float pcfDepth =
|
||||
texture(shadowMap, projCoords.xy).r;
|
||||
|
||||
shadow =
|
||||
currentDepth - bias > pcfDepth
|
||||
? 1.0
|
||||
: 0.0;
|
||||
}
|
||||
|
||||
|
||||
return shadow;
|
||||
}
|
||||
|
||||
vec3 calcNewNormal() {
|
||||
mat3 TBN = mat3(normalize(tangent), normalize(bitangent), normalize(normal));
|
||||
vec3 retrievedNormal = texture(normMap, vec3(tc, tex_layer)).xyz;
|
||||
retrievedNormal = retrievedNormal * 2.0 - 1.0;
|
||||
if (flipY) {
|
||||
retrievedNormal.y = -retrievedNormal.y;
|
||||
}
|
||||
vec3 newNormal = TBN * retrievedNormal;
|
||||
return normalize(newNormal);
|
||||
}
|
||||
layout (binding = 0) uniform sampler2DArray samp;
|
||||
|
||||
void main(void) {
|
||||
vec4 objectColor = texture(samp, vec3(tc, tex_layer));
|
||||
|
||||
if (objectColor.a < 0.8) {
|
||||
color = texture(samp, vec3(tc, tex_layer));
|
||||
if (color.a < 0.8) {
|
||||
discard;
|
||||
}
|
||||
if (!shader_on) {
|
||||
color = objectColor;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
vec3 lightDir = normalize(-sunlightDir);
|
||||
vec3 norm;
|
||||
if (enablePBR) {
|
||||
norm = calcNewNormal();
|
||||
} else {
|
||||
norm = normalize(normal);
|
||||
}
|
||||
|
||||
|
||||
vec3 V =
|
||||
normalize(cameraPos - vert_pos);
|
||||
|
||||
vec3 H =
|
||||
normalize(lightDir + V);
|
||||
|
||||
vec3 ambient = ambientStrength * ambientColor;
|
||||
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
|
||||
vec3 diffuse = diff * sunlightColor;
|
||||
|
||||
float r =
|
||||
clamp(roughness, 0.0, 1.0);
|
||||
|
||||
float shininess =
|
||||
mix(
|
||||
512.0,
|
||||
4.0,
|
||||
r
|
||||
);
|
||||
float ks =
|
||||
mix(
|
||||
0.8,
|
||||
0.02,
|
||||
r
|
||||
);
|
||||
|
||||
float spec = 0.0;
|
||||
|
||||
if(diff > 0.0)
|
||||
{
|
||||
spec =
|
||||
ks *
|
||||
pow(
|
||||
max(dot(norm,H),0.0),
|
||||
shininess
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
vec3 specular = spec * sunlightColor * specularStrength;
|
||||
|
||||
float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir);
|
||||
|
||||
// fog
|
||||
float dist = length(cameraPos - vert_pos);
|
||||
vec4 fogColor = vec4(skyColor, 1.0);
|
||||
float fogStart = renderDistance * 16 * 0.9;
|
||||
float fogEnd = renderDistance * 16;
|
||||
|
||||
float fogFactor = smoothstep(fogEnd, fogStart, dist);
|
||||
color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb + (1.0-shadow) * specular * objectColor.rgb, objectColor.a);
|
||||
|
||||
color = mix(fogColor, color, fogFactor);
|
||||
//color = vec4(normal * 0.5 + 0.5, 1.0);
|
||||
//color = vec4(tangent * 0.5 + 0.5, 1.0);;
|
||||
//color = vec4(norm * 0.5 + 0.5, 1.0);
|
||||
//color = vec4(calcNewNormal(), 1.0);
|
||||
//color = varyingColor;
|
||||
}
|
||||
|
||||
@@ -3,17 +3,8 @@
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 2) in float layer;
|
||||
layout (location = 3) in vec3 aNormal;
|
||||
layout (location = 4) in float Roughness;
|
||||
layout (location = 5) in vec3 aTangent;
|
||||
out vec2 tc;
|
||||
out vec3 normal;
|
||||
out vec3 tangent;
|
||||
out vec3 bitangent;
|
||||
out vec3 vert_pos;
|
||||
flat out int tex_layer;
|
||||
out vec4 FragPosLightSpace;
|
||||
out float roughness;
|
||||
|
||||
mat4 buildRotateX(float rad);
|
||||
mat4 buildRotateY(float rad);
|
||||
@@ -22,24 +13,13 @@ mat4 buildTranslate(float x, float y, float z);
|
||||
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
uniform mat4 model_matrix;
|
||||
uniform mat4 norm_matrix;
|
||||
uniform mat4 lightSpaceMatrix;
|
||||
|
||||
|
||||
|
||||
void main(void) {
|
||||
vec4 viewPos = mv_matrix * vec4(pos, 1.0);
|
||||
|
||||
vert_pos = pos;
|
||||
|
||||
gl_Position = proj_matrix * mv_matrix * vec4(pos, 1.0);
|
||||
tc = texCoord;
|
||||
|
||||
tex_layer = int(layer);
|
||||
roughness = Roughness;
|
||||
normal = normalize(mat3(norm_matrix) * aNormal);
|
||||
tangent = normalize(mat3(model_matrix) * aTangent);
|
||||
bitangent = cross(normal, tangent);
|
||||
FragPosLightSpace = lightSpaceMatrix * vec4(pos, 1.0);
|
||||
gl_Position = proj_matrix * viewPos;
|
||||
}
|
||||
|
||||
mat4 buildTranslate(float x, float y, float z) {
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
#include "noise.glsl"
|
||||
|
||||
vec3 computeSkyColor(vec3 dir) {
|
||||
vec3 sund = normalize(sunDir);
|
||||
|
||||
float t =
|
||||
clamp(
|
||||
dir.y * 0.5 + 0.5,
|
||||
0.0,
|
||||
1.0
|
||||
);
|
||||
|
||||
|
||||
vec3 sky =
|
||||
mix(
|
||||
skyBottom,
|
||||
skyTop,
|
||||
pow(t, horizonSharpness)
|
||||
);
|
||||
|
||||
// cloud
|
||||
if (dir.y > 0.0) {
|
||||
vec2 cloud_uv = dir.xz / (dir.y + 0.15) * 0.5 + vec2(time * 0.005, time * 0.002);
|
||||
float cloud_density = fbm(cloud_uv * 2.0);
|
||||
float safeLow = cloudThresholdLow;
|
||||
float safeHigh = max(cloudThresholdHigh, cloudThresholdLow + 0.001);
|
||||
cloud_density = smoothstep(safeLow,safeHigh, cloud_density);
|
||||
|
||||
|
||||
float fade = smoothstep(0.0, 0.3, dir.y) * (1.0 - smoothstep(0.85, 1.0, dir.y));
|
||||
cloud_density *= fade;
|
||||
|
||||
vec3 cloud_color = mix(skyBottom, vec3(1.0), cloudWhiteMix);
|
||||
sky = mix(sky, cloud_color, cloud_density * 0.6);
|
||||
}
|
||||
|
||||
float sunAmount = max(dot(dir, sund), 0.0);
|
||||
|
||||
//float glow = pow(sunAmount, 8.0) * 0.15;
|
||||
|
||||
float glow = pow(sunAmount, 8.0) * 0.15 + pow(sunAmount, 32.0) * 0.3;
|
||||
|
||||
sky += glow * sunColor;
|
||||
|
||||
return sky;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
float hash(vec2 p) {
|
||||
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
||||
}
|
||||
|
||||
float noise(vec2 p) {
|
||||
vec2 i = floor(p);
|
||||
vec2 f = fract(p);
|
||||
f = f * f * (3.0 - 2.0 * f);
|
||||
float a = hash(i);
|
||||
float b = hash(i + vec2(1.0, 0.0));
|
||||
float c = hash(i + vec2(0.0, 1.0));
|
||||
float d = hash(i + vec2(1.0, 1.0));
|
||||
return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);
|
||||
}
|
||||
|
||||
float fbm(vec2 p) {
|
||||
float v = 0.0;
|
||||
float amp = 0.5;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
v += amp * noise(p);
|
||||
p *= 2.0;
|
||||
amp *= 0.5;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#version 460
|
||||
|
||||
out vec4 color;
|
||||
in vec2 tc;
|
||||
|
||||
void main() {
|
||||
color = vec4(0.0, 0.0, 1.0, 1.0);
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
|
||||
out vec2 tc;
|
||||
|
||||
void main() {
|
||||
vec4 viewPos = mv_matrix * vec4(pos, 1.0);
|
||||
tc = texCoord;
|
||||
gl_Position = proj_matrix * viewPos;
|
||||
}
|
||||
@@ -1,30 +1,9 @@
|
||||
#version 460
|
||||
|
||||
in vec3 dir;
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
|
||||
uniform vec3 skyTop;
|
||||
uniform vec3 skyBottom;
|
||||
|
||||
uniform vec3 sunDir;
|
||||
uniform vec3 sunColor;
|
||||
|
||||
uniform float horizonSharpness;
|
||||
uniform float cloudWhiteMix;
|
||||
|
||||
uniform float cloudThresholdLow;
|
||||
uniform float cloudThresholdHigh;
|
||||
|
||||
uniform float time;
|
||||
|
||||
#include "compute_sky_color.glsl"
|
||||
|
||||
void main(void) {
|
||||
|
||||
vec3 sky = computeSkyColor(dir);
|
||||
frag_color = vec4(0.529, 0.808, 0.922, 1.0);
|
||||
|
||||
frag_color = vec4(sky, 1.0);
|
||||
|
||||
}
|
||||
@@ -5,12 +5,6 @@ layout (location = 0) in vec3 vertices_pos;
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
|
||||
out vec3 dir;
|
||||
|
||||
void main(void) {
|
||||
// Our skybox mesh uses [0,1] coordinates instead of the usual [-1,1].
|
||||
// Shift to the cube center before normalizing to obtain the correct
|
||||
// view direction for atmospheric calculations.
|
||||
dir = normalize(vertices_pos - vec3(0.5));
|
||||
gl_Position = proj_matrix * mv_matrix * vec4(vertices_pos, 1.0);
|
||||
}
|
||||
@@ -1,111 +1,35 @@
|
||||
#version 460
|
||||
|
||||
in vec2 TexCoord;
|
||||
in vec3 rayDir;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
layout (binding = 0) uniform sampler2D u_sceneTexture;
|
||||
layout (binding = 1) uniform sampler2D u_depthTexture;
|
||||
layout (binding = 2) uniform sampler2D u_shadowMap;
|
||||
uniform mat4 u_lightSpaceMatrix;
|
||||
uniform sampler2D u_sceneTexture;
|
||||
uniform float u_time;
|
||||
uniform bool u_underwater;
|
||||
uniform vec3 u_waterColor;
|
||||
uniform float u_fogDensity;
|
||||
uniform mat4 InverseViewProjection;
|
||||
uniform vec3 cameraPos;
|
||||
uniform vec3 sunDir;
|
||||
uniform vec3 sunColor;
|
||||
uniform float waterDensity;
|
||||
|
||||
#include "noise.glsl"
|
||||
|
||||
float getCausticValue(float x, float y, float z) {
|
||||
float w = 8.0;
|
||||
float strength = 4.0;
|
||||
vec2 coord = vec2(x, y) * w + z * 0.5;
|
||||
|
||||
float n = fbm(coord);
|
||||
|
||||
float caustic = pow(n, 3.0) * strength;
|
||||
return caustic;
|
||||
}
|
||||
|
||||
float getShadow(vec3 worldPos) {
|
||||
vec4 posLightSpace = u_lightSpaceMatrix * vec4(worldPos, 1.0);
|
||||
vec3 projCoords = posLightSpace.xyz / posLightSpace.w;
|
||||
projCoords = projCoords * 0.5 + 0.5;
|
||||
|
||||
if (projCoords.z > 1.0) return 1.0;
|
||||
|
||||
float closestDepth = texture(u_shadowMap, projCoords.xy).r;
|
||||
float currentDepth = projCoords.z;
|
||||
|
||||
return currentDepth - 0.005 > closestDepth ? 0.0 : 1.0;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 original = texture(u_sceneTexture, TexCoord);
|
||||
|
||||
|
||||
if (!u_underwater) {
|
||||
FragColor = original;
|
||||
return;
|
||||
}
|
||||
|
||||
float rawDepth = texture(u_depthTexture, TexCoord).r;
|
||||
vec3 ndc = vec3(TexCoord * 2.0 - 1.0, rawDepth * 2.0 - 1.0);
|
||||
vec4 worldPosH = InverseViewProjection * vec4(ndc, 1.0);
|
||||
vec3 worldPos = worldPosH.xyz / worldPosH.w;
|
||||
float sceneDistance = distance(cameraPos, worldPos);
|
||||
|
||||
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 rawCaustic = getCausticValue(TexCoord.x, TexCoord.y, u_time * 0.3);
|
||||
float caustic = 0.5 + rawCaustic * 0.5;
|
||||
vec3 causticLight = vec3(caustic * 0.9, caustic, caustic * 0.7);
|
||||
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);
|
||||
// Volume Scattering
|
||||
vec3 rayOrigin = cameraPos;
|
||||
vec3 rayDirection = normalize(rayDir);
|
||||
|
||||
float maxDist = 10.0; // Maximum visible distance
|
||||
float stepSize = 0.4;
|
||||
float phasePower = 8.0; // Beam Sharpness
|
||||
|
||||
vec3 scattering = vec3(0.0);
|
||||
float transmittance = 1.0;
|
||||
|
||||
float cosTheta = dot(rayDirection, sunDir);
|
||||
float phase = pow(max(cosTheta, 0.0), phasePower);
|
||||
|
||||
for (float t = 0.0; t < maxDist; t += stepSize) {
|
||||
|
||||
if (t > sceneDistance) break;
|
||||
vec3 pos = rayOrigin + rayDirection * t;
|
||||
|
||||
float shadow = getShadow(pos);
|
||||
|
||||
float density = waterDensity;
|
||||
|
||||
|
||||
vec3 absorption = vec3(0.3, 0.08, 0.02); // Absorption coefficient per meter
|
||||
vec3 sunLightAtPos = sunColor * exp(-absorption * t);
|
||||
|
||||
scattering += density * phase * sunLightAtPos * transmittance * stepSize * shadow;
|
||||
|
||||
float extinction = waterDensity * 1.5; // Extinction coefficient, tunable
|
||||
transmittance *= exp(-extinction * stepSize);
|
||||
if (transmittance < 0.01) break;
|
||||
}
|
||||
|
||||
FragColor.rgb = mixed + scattering;
|
||||
FragColor.a = 1.0;
|
||||
FragColor = vec4(mixed, 1.0);
|
||||
}
|
||||
@@ -3,18 +3,9 @@
|
||||
layout (location = 0) in vec2 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
|
||||
uniform mat4 InverseViewProjection;
|
||||
uniform vec3 cameraPos;
|
||||
|
||||
out vec2 TexCoord;
|
||||
out vec3 rayDir;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos.x, pos.y, 0.0, 1.0);
|
||||
TexCoord = texCoord;
|
||||
vec4 clipPos = vec4(pos, 1.0, 1.0);
|
||||
vec4 worldPosH = InverseViewProjection * clipPos;
|
||||
vec3 worldPos = worldPosH.xyz / worldPosH.w;
|
||||
vec3 RayDir = worldPos - cameraPos;
|
||||
rayDir = normalize(RayDir);
|
||||
gl_Position = clipPos;
|
||||
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) out vec4 accum;
|
||||
layout (location = 1) out float reveal;
|
||||
|
||||
in vec2 tc;
|
||||
in vec3 normal;
|
||||
in vec3 vert_pos;
|
||||
in vec3 world_pos;
|
||||
in float roughness;
|
||||
flat in int tex_layer;
|
||||
in float v_depth;
|
||||
layout (binding = 0) uniform sampler2DArray samp;
|
||||
uniform sampler2D sceneColorTex; // binding 1
|
||||
uniform sampler2D sceneDepthTex; // binding 2
|
||||
|
||||
uniform mat4 proj_matrix;
|
||||
uniform mat4 inv_proj_matrix;
|
||||
uniform mat4 inv_view_matrix;
|
||||
|
||||
uniform float ambientStrength;
|
||||
uniform vec3 sunlightColor;
|
||||
uniform vec3 ambientColor;
|
||||
uniform vec3 sunlightDir;
|
||||
uniform bool shader_on;
|
||||
uniform float specularStrength;
|
||||
|
||||
uniform vec3 skyTop;
|
||||
uniform vec3 skyBottom;
|
||||
uniform vec3 sunColor;
|
||||
uniform float horizonSharpness;
|
||||
uniform float cloudWhiteMix;
|
||||
uniform float cloudThresholdLow;
|
||||
uniform float cloudThresholdHigh;
|
||||
uniform float time;
|
||||
|
||||
uniform vec3 sunDir; // world!
|
||||
|
||||
uniform bool underwater;
|
||||
|
||||
uniform float refractStrength;
|
||||
|
||||
uniform bool enablePerturb;
|
||||
uniform bool enableDepthFade;
|
||||
|
||||
#include "compute_sky_color.glsl"
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// Reconstruct eye-space coordinates from screen UV and depth buffer value
|
||||
vec3 reconstructViewPos(vec2 uv, float depth) {
|
||||
vec4 clip = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
|
||||
vec4 view = inv_proj_matrix * clip;
|
||||
return view.xyz / view.w;
|
||||
}
|
||||
// Screen-space ray marching, origin/dir are in eye space
|
||||
bool traceSSR(vec3 origin, vec3 dir, out vec2 hitUV) {
|
||||
const int COARSE_STEPS = 32;
|
||||
const float COARSE_STEP = 0.6;
|
||||
const int REFINE_STEPS = 8;
|
||||
const float THICKNESS = 0.3;
|
||||
|
||||
vec3 pos = origin;
|
||||
vec3 prevPos = origin;
|
||||
bool foundCoarse = false;
|
||||
|
||||
|
||||
for (int i = 0; i < COARSE_STEPS; ++i) {
|
||||
prevPos = pos;
|
||||
pos += dir * COARSE_STEP;
|
||||
|
||||
vec4 clip = proj_matrix * vec4(pos, 1.0);
|
||||
if (clip.w <= 0.0) return false;
|
||||
vec3 ndc = clip.xyz / clip.w;
|
||||
if (abs(ndc.x) > 1.0 || abs(ndc.y) > 1.0) return false;
|
||||
|
||||
vec2 uv = ndc.xy * 0.5 + 0.5;
|
||||
float sceneDepth = texture(sceneDepthTex, uv).r;
|
||||
vec3 scenePos = reconstructViewPos(uv, sceneDepth);
|
||||
|
||||
if (pos.z - scenePos.z < 0.0) {
|
||||
foundCoarse = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundCoarse) return false;
|
||||
|
||||
|
||||
vec3 lo = prevPos;
|
||||
vec3 hi = pos;
|
||||
|
||||
for (int i = 0; i < REFINE_STEPS; ++i) {
|
||||
vec3 mid = (lo + hi) * 0.5;
|
||||
|
||||
vec4 clip = proj_matrix * vec4(mid, 1.0);
|
||||
vec3 ndc = clip.xyz / clip.w;
|
||||
vec2 uv = ndc.xy * 0.5 + 0.5;
|
||||
float sceneDepth = texture(sceneDepthTex, uv).r;
|
||||
vec3 scenePos = reconstructViewPos(uv, sceneDepth);
|
||||
|
||||
if (mid.z - scenePos.z < 0.0) {
|
||||
hi = mid;
|
||||
} else {
|
||||
lo = mid;
|
||||
}
|
||||
}
|
||||
|
||||
vec4 finalClip = proj_matrix * vec4(hi, 1.0);
|
||||
vec3 finalNdc = finalClip.xyz / finalClip.w;
|
||||
vec2 finalUV = finalNdc.xy * 0.5 + 0.5;
|
||||
|
||||
float finalDepth = texture(sceneDepthTex, finalUV).r;
|
||||
vec3 finalScenePos = reconstructViewPos(finalUV, finalDepth);
|
||||
|
||||
if (abs(hi.z - finalScenePos.z) > THICKNESS) return false;
|
||||
|
||||
hitUV = finalUV;
|
||||
return true;
|
||||
}
|
||||
void main() {
|
||||
|
||||
vec4 objectColor = texture(samp, vec3(tc, tex_layer));
|
||||
|
||||
if (!shader_on) {
|
||||
vec4 color = objectColor;
|
||||
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;
|
||||
return;
|
||||
}
|
||||
// Normal perturbation
|
||||
vec3 N;
|
||||
if (enablePerturb) {
|
||||
const float wave_speed_scale = 40.0;
|
||||
vec2 waveUV1 = world_pos.xz * 8.0 + vec2(time * 0.05 * wave_speed_scale, time * 0.03 * wave_speed_scale);
|
||||
vec2 waveUV2 = world_pos.xz * 13.0 + vec2(-time * 0.04 * wave_speed_scale, time * 0.06 * wave_speed_scale);
|
||||
|
||||
float wave1 = noise(waveUV1);
|
||||
float wave2 = noise(waveUV2);
|
||||
|
||||
vec2 normalPerturb = vec2(wave1 - 0.5, wave2 - 0.5) * 0.1;
|
||||
N = normalize(normal + vec3(normalPerturb.x, 0.0, normalPerturb.y));
|
||||
} else {
|
||||
N = normalize(normal);
|
||||
}
|
||||
|
||||
vec3 V = normalize(-vert_pos);
|
||||
|
||||
vec3 reflectColor;
|
||||
vec3 refractColor;
|
||||
float fresnel;
|
||||
|
||||
vec2 screenUV = gl_FragCoord.xy / vec2(textureSize(sceneDepthTex, 0));
|
||||
|
||||
// water depth
|
||||
float sceneDepthRaw = texture(sceneDepthTex, screenUV).r;
|
||||
vec3 sceneViewPos = reconstructViewPos(screenUV, sceneDepthRaw);
|
||||
float waterDepth = vert_pos.z - sceneViewPos.z;
|
||||
waterDepth = max(waterDepth, 0.0);
|
||||
|
||||
const float DEPTH_FADE_DISTANCE = 10;
|
||||
float depthFactor = clamp(waterDepth / DEPTH_FADE_DISTANCE, 0.0, 1.0);
|
||||
|
||||
if (underwater) {
|
||||
reflectColor = vec3(0.0);
|
||||
refractColor = objectColor.rgb;
|
||||
fresnel = 0.0;
|
||||
} else {
|
||||
vec3 R = reflect(-V, N);
|
||||
vec3 origin = vert_pos + N * 0.05;
|
||||
vec2 hitUV;
|
||||
|
||||
if (traceSSR(origin, R, hitUV)) {
|
||||
reflectColor = texture(sceneColorTex, hitUV).rgb;
|
||||
} else {
|
||||
vec3 worldR = mat3(inv_view_matrix) * R;
|
||||
reflectColor = computeSkyColor(worldR);
|
||||
}
|
||||
|
||||
float effectiveRefractStrength = refractStrength * (1.0 - depthFactor * 0.5);
|
||||
vec2 refractOffset = N.xz * effectiveRefractStrength;
|
||||
vec2 refractUV = clamp(screenUV + refractOffset, vec2(0.001), vec2(0.999));
|
||||
|
||||
refractColor = texture(sceneColorTex, refractUV).rgb;
|
||||
|
||||
fresnel = pow(1.0 - max(dot(N, V), 0.0), 5.0);
|
||||
fresnel = mix(0.02, 1.0, fresnel);
|
||||
}
|
||||
|
||||
vec3 lightDir = normalize(-sunlightDir);
|
||||
vec3 H = normalize(lightDir + V);
|
||||
vec3 ambient = ambientStrength * ambientColor;
|
||||
float diff = max(dot(N, lightDir), 0.0);
|
||||
vec3 diffuse = diff * sunlightColor;
|
||||
|
||||
float r = clamp(roughness, 0.0, 1.0);
|
||||
float shininess = mix(512.0, 4.0, r);
|
||||
float ks = mix(0.8, 0.02, r);
|
||||
|
||||
float spec = 0.0;
|
||||
if (diff > 0.0) {
|
||||
spec = ks * pow(max(dot(N, H), 0.0), shininess);
|
||||
}
|
||||
vec3 specular = spec * sunlightColor * specularStrength;
|
||||
|
||||
vec3 tintedRefract;
|
||||
|
||||
if (enableDepthFade) {
|
||||
vec3 shallowColor = vec3(0.4, 0.75, 0.7);
|
||||
vec3 deepColor = vec3(0.0, 0.015, 0.045);
|
||||
|
||||
vec3 waterTint = mix(shallowColor, deepColor, depthFactor);
|
||||
|
||||
tintedRefract = mix(refractColor, refractColor * waterTint, depthFactor);
|
||||
tintedRefract = mix(tintedRefract, objectColor.rgb, 0.4); // Forcefully blend 40% texture color, independent of depth
|
||||
} else {
|
||||
tintedRefract = mix(refractColor, objectColor.rgb, 0.4);
|
||||
}
|
||||
|
||||
//vec3 baseWaterColor = (ambient + diffuse) * objectColor.rgb + specular;
|
||||
vec3 baseWaterColor = (ambient + diffuse) * tintedRefract + specular;
|
||||
|
||||
vec3 finalColor = mix(baseWaterColor, reflectColor, fresnel);
|
||||
|
||||
float alpha = objectColor.a;
|
||||
if (alpha < 1e-4) discard;
|
||||
|
||||
|
||||
float w = weight(v_depth, alpha);
|
||||
|
||||
accum = vec4(finalColor * alpha * w, alpha * w);
|
||||
|
||||
reveal = alpha;
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#version 460
|
||||
|
||||
layout (location = 0) in vec3 pos;
|
||||
layout (location = 1) in vec2 texCoord;
|
||||
layout (location = 2) in float layer;
|
||||
layout (location = 3) in vec3 aNormal;
|
||||
layout (location = 4) in float Roughness;
|
||||
|
||||
out vec2 tc;
|
||||
out vec3 normal;
|
||||
out vec3 vert_pos;
|
||||
out vec3 world_pos;
|
||||
flat out int tex_layer;
|
||||
out float roughness;
|
||||
out float v_depth;
|
||||
|
||||
uniform mat4 mv_matrix;
|
||||
uniform mat4 proj_matrix;
|
||||
uniform mat4 norm_matrix;
|
||||
|
||||
|
||||
void main(void) {
|
||||
vec4 view_pos = mv_matrix * vec4(pos, 1.0);
|
||||
world_pos = pos;
|
||||
vert_pos = view_pos.xyz;
|
||||
normal = mat3(norm_matrix) * aNormal;
|
||||
roughness = Roughness;
|
||||
tc = texCoord;
|
||||
tex_layer = int(layer);
|
||||
v_depth = -view_pos.z;
|
||||
gl_Position = proj_matrix * view_pos;
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
include(FetchContent)
|
||||
|
||||
# System packages
|
||||
find_package(OpenGL REQUIRED)
|
||||
find_package(Protobuf REQUIRED)
|
||||
find_package(absl REQUIRED)
|
||||
find_package(zstd REQUIRED)
|
||||
if (UNIX AND NOT APPLE)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(glfw3 REQUIRED)
|
||||
endif()
|
||||
|
||||
|
||||
# Third-party libraries
|
||||
FetchContent_Declare(
|
||||
glm
|
||||
GIT_REPOSITORY https://github.com/g-truc/glm.git
|
||||
GIT_TAG 1.0.3
|
||||
|
||||
)
|
||||
FetchContent_MakeAvailable(glm)
|
||||
FetchContent_Declare(
|
||||
soil2
|
||||
GIT_REPOSITORY https://github.com/SpartanJ/SOIL2.git
|
||||
GIT_TAG 1.31
|
||||
|
||||
)
|
||||
FetchContent_MakeAvailable(soil2)
|
||||
FetchContent_Declare(
|
||||
tomlplusplus
|
||||
GIT_REPOSITORY https://github.com/marzer/tomlplusplus.git
|
||||
GIT_TAG v3.4.0
|
||||
)
|
||||
FetchContent_MakeAvailable(tomlplusplus)
|
||||
|
||||
|
||||
if (WIN32)
|
||||
FetchContent_Declare(
|
||||
glfw
|
||||
GIT_REPOSITORY https://github.com/glfw/glfw.git
|
||||
GIT_TAG 3.4
|
||||
)
|
||||
|
||||
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_INSTALL OFF CACHE BOOL "" FORCE)
|
||||
set(GLFW_VULKAN_STATIC ON CACHE BOOL "" FORCE)
|
||||
set(BUILD_SHARED_LIBS ON CACHE BOOL "" FORCE)
|
||||
|
||||
FetchContent_MakeAvailable(glfw)
|
||||
|
||||
FetchContent_Declare(
|
||||
freetype
|
||||
GIT_REPOSITORY https://gitlab.freedesktop.org/freetype/freetype.git
|
||||
GIT_TAG VER-2-14-3
|
||||
)
|
||||
FetchContent_MakeAvailable(freetype)
|
||||
if(TARGET freetype)
|
||||
add_library(Freetype::Freetype ALIAS freetype)
|
||||
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()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
find_path(ZSTD_INCLUDE_DIRS
|
||||
NAMES zstd.h
|
||||
HINTS ${zstd_ROOT_DIR}/include)
|
||||
|
||||
find_library(ZSTD_LIBRARIES
|
||||
NAMES zstd
|
||||
HINTS ${zstd_ROOT_DIR}/lib)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(zstd DEFAULT_MSG ZSTD_LIBRARIES ZSTD_INCLUDE_DIRS)
|
||||
|
||||
mark_as_advanced(
|
||||
ZSTD_LIBRARIES
|
||||
ZSTD_INCLUDE_DIRS)
|
||||
|
||||
if(ZSTD_FOUND AND NOT (TARGET zstd::zstd))
|
||||
add_library (zstd::zstd UNKNOWN IMPORTED)
|
||||
set_target_properties(zstd::zstd
|
||||
PROPERTIES
|
||||
IMPORTED_LOCATION ${ZSTD_LIBRARIES}
|
||||
INTERFACE_INCLUDE_DIRECTORIES ${ZSTD_INCLUDE_DIRS})
|
||||
endif()
|
||||
@@ -1,10 +1,8 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/client_world.hpp"
|
||||
#include "Cubed/gameplay/network_server.hpp"
|
||||
#include "Cubed/gameplay/server_world.hpp"
|
||||
#define GLFW_INCLUDE_NONE
|
||||
#include "Cubed/camera.hpp"
|
||||
#include "Cubed/dev_panel.hpp"
|
||||
#include "Cubed/gameplay/world.hpp"
|
||||
#include "Cubed/renderer.hpp"
|
||||
#include "Cubed/texture_manager.hpp"
|
||||
#include "Cubed/window.hpp"
|
||||
@@ -12,13 +10,6 @@ namespace Cubed {
|
||||
|
||||
class App {
|
||||
public:
|
||||
struct Argument {
|
||||
bool is_client = false;
|
||||
int port = 25530;
|
||||
std::string ip{"127.0.0.1"};
|
||||
std::string player{"Unknown"};
|
||||
};
|
||||
|
||||
App();
|
||||
~App();
|
||||
static void cursor_position_callback(GLFWwindow* window, double xpos,
|
||||
@@ -45,20 +36,14 @@ public:
|
||||
Renderer& renderer();
|
||||
TextureManager& texture_manager();
|
||||
Window& window();
|
||||
ClientWorld& client_world();
|
||||
ServerWorld& server_world();
|
||||
const Argument& argument() const;
|
||||
World& world();
|
||||
|
||||
private:
|
||||
Camera m_camera;
|
||||
TextureManager m_texture_manager;
|
||||
NetworkServer m_server;
|
||||
std::shared_ptr<NetworkClient> m_client;
|
||||
ClientWorld m_client_world;
|
||||
|
||||
World m_world;
|
||||
DevPanel m_dev_panel{*this};
|
||||
Renderer m_renderer{m_camera, m_client_world, m_texture_manager,
|
||||
m_dev_panel};
|
||||
Renderer m_renderer{m_camera, m_world, m_texture_manager, m_dev_panel};
|
||||
|
||||
Window m_window{m_renderer};
|
||||
|
||||
@@ -68,10 +53,9 @@ private:
|
||||
inline static double fps_time_count = 0.0f;
|
||||
inline static int frame_count = 0;
|
||||
inline static int fps = 0;
|
||||
Argument m_argument;
|
||||
void init(int argc, char** argv);
|
||||
void handle_argument(int argc, char** argv);
|
||||
void handle_toml();
|
||||
|
||||
void init();
|
||||
|
||||
auto init_camera();
|
||||
auto init_texture();
|
||||
auto init_world();
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
class ClientPlayer;
|
||||
class Player;
|
||||
|
||||
class Camera {
|
||||
private:
|
||||
bool m_firse_mouse = true;
|
||||
ClientPlayer* m_player;
|
||||
Player* m_player;
|
||||
float m_last_mouse_x, m_last_mouse_y;
|
||||
glm::vec3 m_camera_pos;
|
||||
bool m_under_water = false;
|
||||
@@ -23,7 +23,7 @@ public:
|
||||
|
||||
void update_move_camera();
|
||||
|
||||
void camera_init(ClientPlayer* player);
|
||||
void camera_init(Player* player);
|
||||
void hot_reload();
|
||||
void reset_camera();
|
||||
void update_cursor_position_camera(double xpos, double ypos);
|
||||
@@ -32,7 +32,6 @@ public:
|
||||
const glm::vec3& get_camera_pos() const;
|
||||
|
||||
bool is_under_water() const;
|
||||
glm::vec3 get_camera_front() const;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
#pragma once
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
#include "Cubed/tools/toml.utils.hpp"
|
||||
|
||||
#include <toml++/toml.hpp>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
template <typename T>
|
||||
concept TomlValueType =
|
||||
std::same_as<T, int> || std::same_as<T, bool> || std::same_as<T, double> ||
|
||||
std::same_as<T, const char*> || std::same_as<T, toml::date> ||
|
||||
std::same_as<T, toml::time> || std::same_as<T, toml::date_time> ||
|
||||
std::same_as<T, std::string>;
|
||||
|
||||
class Config {
|
||||
public:
|
||||
Config();
|
||||
@@ -16,7 +24,7 @@ public:
|
||||
void load_or_create_config();
|
||||
void save_to_file();
|
||||
|
||||
template <TOML::TomlValueType T> T get(std::string_view key) const {
|
||||
template <TomlValueType T> T get(std::string_view key) const {
|
||||
size_t cur = 0;
|
||||
auto pos = key.find('.');
|
||||
const toml::table* table = &m_tbl;
|
||||
@@ -53,7 +61,7 @@ public:
|
||||
}
|
||||
}
|
||||
template <typename T> void set(std::string_view key, T&& val) {
|
||||
if constexpr (!TOML::TomlValueType<std::decay_t<T>>) {
|
||||
if constexpr (!TomlValueType<std::decay_t<T>>) {
|
||||
static_assert(false, "Type Not Support");
|
||||
}
|
||||
size_t cur = 0;
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
|
||||
#include <array>
|
||||
namespace Cubed {
|
||||
|
||||
constexpr int WORLD_SIZE_Y = 256;
|
||||
constexpr int CHUNK_SIZE = 16;
|
||||
constexpr int SEA_LEVEL = 63;
|
||||
constexpr int SEA_LEVEL = 64;
|
||||
|
||||
constexpr int MAX_UI_NUM = 1;
|
||||
constexpr int MAX_BLOCK_STATUS = 1;
|
||||
@@ -22,12 +23,12 @@ constexpr float DEFAULT_MAX_RUN_SPEED = 7.0f;
|
||||
constexpr float DEFAULT_ACCELERATION = 10.0f;
|
||||
constexpr float DEFAULT_DECELERATION = 15.0f;
|
||||
constexpr float DEFAULT_G = 22.5f;
|
||||
constexpr int SIZE_X = CHUNK_SIZE;
|
||||
constexpr int SIZE_Y = WORLD_SIZE_Y;
|
||||
constexpr int SIZE_Z = CHUNK_SIZE;
|
||||
constexpr int RESERVED_THREADS = 5;
|
||||
static constexpr int SIZE_X = CHUNK_SIZE;
|
||||
static constexpr int SIZE_Y = WORLD_SIZE_Y;
|
||||
static constexpr int SIZE_Z = CHUNK_SIZE;
|
||||
|
||||
constexpr float DEFAULT_CAVE_PROBABILITY = 0.035f;
|
||||
constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
|
||||
{1, 1}, {-1, 1}, {1, -1}, {-1, -1}};
|
||||
|
||||
using HeightMapArray = std::array<std::array<int, CHUNK_SIZE>, CHUNK_SIZE>;
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
namespace Cubed {
|
||||
|
||||
class App;
|
||||
class ClientPlayer;
|
||||
class Player;
|
||||
class DevPanel {
|
||||
struct ConfigView {
|
||||
float fov = 70.0f;
|
||||
@@ -26,6 +26,9 @@ class DevPanel {
|
||||
int gait = 0;
|
||||
float pos[3] = {0.0f, 0.0f, 0.0f};
|
||||
};
|
||||
struct TextEditing {
|
||||
bool perlin_seed = false;
|
||||
};
|
||||
|
||||
public:
|
||||
DevPanel(App& app);
|
||||
@@ -35,30 +38,20 @@ public:
|
||||
private:
|
||||
App& m_app;
|
||||
ConfigView m_config;
|
||||
ClientPlayer* m_player;
|
||||
Player* m_player;
|
||||
PlayerProfile m_player_profile;
|
||||
TextEditing m_text_editing;
|
||||
bool m_need_save_config = false;
|
||||
bool m_gen_thread_running = true;
|
||||
int m_theme = 0;
|
||||
int m_pre_set_day_tick = 0;
|
||||
int m_pre_set_tick_speed = 1;
|
||||
bool m_tick_frezze = false;
|
||||
int m_samples_idx = 1;
|
||||
int m_threads = 1;
|
||||
int m_chunk_style = 0;
|
||||
void show_about_table_bar();
|
||||
void show_biome_table_bar();
|
||||
void show_time_table_bar();
|
||||
void show_cave_table_bar();
|
||||
void show_river_table_bar();
|
||||
void show_chunk_table_bar();
|
||||
void show_settings_tab_item();
|
||||
void show_world_tab_item();
|
||||
void show_server_world_table_bar();
|
||||
void show_client_world_table_bar();
|
||||
void show_player_tab_item();
|
||||
void show_items_tab_item();
|
||||
void show_shader_tab_item();
|
||||
|
||||
void update_config_view();
|
||||
void update_player_profile();
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
#pragma once
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Cubed {
|
||||
@@ -80,32 +77,4 @@ DesertParams& desert_params();
|
||||
MountainParams& mountain_params();
|
||||
RiverParams& river_params();
|
||||
|
||||
inline BiomeType get_biome_from_id(int id) {
|
||||
using enum BiomeType;
|
||||
auto to = std::to_underlying<BiomeType>;
|
||||
if (id == to(PLAIN)) {
|
||||
return PLAIN;
|
||||
}
|
||||
if (id == to(FOREST)) {
|
||||
return FOREST;
|
||||
}
|
||||
if (id == to(DESERT)) {
|
||||
return DESERT;
|
||||
}
|
||||
if (id == to(MOUNTAIN)) {
|
||||
return MOUNTAIN;
|
||||
}
|
||||
if (id == to(RIVER)) {
|
||||
return RIVER;
|
||||
}
|
||||
if (id == to(SNOWY_PLAIN)) {
|
||||
return SNOWY_PLAIN;
|
||||
}
|
||||
if (id == to(OCEAN)) {
|
||||
return OCEAN;
|
||||
}
|
||||
ASSERT_MSG(false, "Unknown Biome Id");
|
||||
throw std::invalid_argument("Unknown Biome Id");
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -2,15 +2,12 @@
|
||||
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
using BlockType = uint8_t;
|
||||
using OptionalBlockVectorArray =
|
||||
std::array<std::optional<std::vector<BlockType>>, 4>;
|
||||
|
||||
struct BlockTexture {
|
||||
std::string name;
|
||||
@@ -53,15 +50,13 @@ struct BlockData {
|
||||
|
||||
bool is_discard = false;
|
||||
bool is_blend = false;
|
||||
bool is_transitional = false;
|
||||
float roughness = 1.0f;
|
||||
|
||||
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, float r)
|
||||
bool discard, bool blend)
|
||||
: 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), roughness(r) {}
|
||||
is_transparent(transparent), is_discard(discard), is_blend(blend) {}
|
||||
};
|
||||
|
||||
class BlockManager {
|
||||
@@ -82,8 +77,7 @@ public:
|
||||
|
||||
static bool is_discard(BlockType id);
|
||||
static bool is_blend(BlockType id);
|
||||
static bool is_transitional(BlockType id);
|
||||
static float roughness(BlockType id);
|
||||
|
||||
static BlockType cross_plane_index(BlockType id);
|
||||
|
||||
private:
|
||||
|
||||
@@ -9,10 +9,10 @@ public:
|
||||
virtual ChunkGenerator& get_chunk_generator() = 0;
|
||||
virtual void build_biome() = 0;
|
||||
virtual void build_vegetation() = 0;
|
||||
void ocean_water_build();
|
||||
|
||||
protected:
|
||||
void build_bottom();
|
||||
void place_grass();
|
||||
void ocean_water_build();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,26 +1,24 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/path.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
|
||||
#include "Cubed/gameplay/cave_path.hpp"
|
||||
namespace Cubed {
|
||||
class CaveCarver {
|
||||
|
||||
public:
|
||||
CaveCarver();
|
||||
|
||||
std::unordered_map<unsigned, CavePath>& paths();
|
||||
void init(unsigned world_seed);
|
||||
void reload(unsigned world_seed);
|
||||
bool has_origin_fast(const ChunkPos& pos) const;
|
||||
float cave_probability() const;
|
||||
PathOrigin get_origin(const ChunkPos& origin_chunk) const;
|
||||
int search_radius() const;
|
||||
unsigned world_seed() const;
|
||||
void add_path(const glm::vec3& pos, unsigned chunk_seed);
|
||||
void try_to_add_path(const ChunkPos& pos, unsigned chunk_seed);
|
||||
void cleanup_finished_caves();
|
||||
|
||||
int cave_sum() const;
|
||||
float& cave_probability();
|
||||
|
||||
private:
|
||||
std::atomic<unsigned> m_world_seed{0};
|
||||
std::atomic<float> m_cave_probability{DEFAULT_CAVE_PROBABILITY};
|
||||
std::unordered_map<unsigned, CavePath> m_paths;
|
||||
unsigned m_seed = 0;
|
||||
int m_sum = 0;
|
||||
Random m_random;
|
||||
float m_cave_probability = 0.035f;
|
||||
};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
#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>
|
||||
#include <unordered_set>
|
||||
namespace Cubed {
|
||||
|
||||
class CavePath {
|
||||
|
||||
public:
|
||||
CavePath(unsigned int chunk_seed, unsigned world_seed,
|
||||
const glm::vec3& start_pos);
|
||||
CavePath(unsigned int world_seed, int path_id, 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();
|
||||
@@ -22,7 +23,6 @@ public:
|
||||
static float& delta_angle_max();
|
||||
static int& step_min();
|
||||
static int& step_max();
|
||||
static int step_len();
|
||||
|
||||
private:
|
||||
static inline float m_radius_xz_min = 5.0f;
|
||||
@@ -33,16 +33,19 @@ private:
|
||||
static inline float m_delta_angle_max = 5.0f;
|
||||
static inline int m_step_min = 10;
|
||||
static inline int m_step_max = 400;
|
||||
static inline float m_step_len = 4.0f;
|
||||
|
||||
int m_path_id = 0;
|
||||
unsigned int m_seed = 0;
|
||||
float m_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;
|
||||
std::unordered_set<ChunkPos, ChunkPos::Hash> m_pending_chunks;
|
||||
void collect_path_points();
|
||||
void precompute_chunk_coverage();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,42 +1,64 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
|
||||
#include "Cubed/gameplay/biome.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/chunk_generator.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/vertex_data.hpp"
|
||||
#include "world/chunk_data.pb.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <mutex>
|
||||
namespace Cubed {
|
||||
class ClientWorld;
|
||||
struct ChunkRenderSnapshot {
|
||||
GLuint normal_vao;
|
||||
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;
|
||||
GLuint water_vao;
|
||||
size_t water_vertices_count;
|
||||
glm::vec3 center;
|
||||
glm::vec3 half_extents;
|
||||
};
|
||||
class ClientChunk {
|
||||
public:
|
||||
ClientChunk(ClientWorld& world);
|
||||
~ClientChunk();
|
||||
ClientChunk(const ClientChunk&) = delete;
|
||||
ClientChunk& operator=(const ClientChunk&) = delete;
|
||||
ClientChunk(ClientChunk&&) noexcept;
|
||||
ClientChunk& operator=(ClientChunk&&) noexcept;
|
||||
|
||||
static int index(int x, int y, int z);
|
||||
static int index(const glm::vec3& pos);
|
||||
class World;
|
||||
// if want to use, do init_chunk(), gen_vertex_data() and
|
||||
class Chunk {
|
||||
private:
|
||||
static constexpr int SIZE_X = CHUNK_SIZE;
|
||||
static constexpr int SIZE_Y = WORLD_SIZE_Y;
|
||||
static constexpr int SIZE_Z = CHUNK_SIZE;
|
||||
static constexpr int VERTEX_DATA_SUM = 4;
|
||||
std::atomic<bool> m_dirty{false};
|
||||
std::atomic<bool> m_need_upload{true};
|
||||
std::atomic<bool> m_is_on_gen_vertex_data{false};
|
||||
std::atomic<BiomeType> m_biome = BiomeType::PLAIN;
|
||||
std::mutex m_vertexs_data_mutex;
|
||||
|
||||
std::unique_ptr<ChunkGenerator> m_generator;
|
||||
|
||||
ChunkPos m_chunk_pos;
|
||||
World& m_world;
|
||||
HeightMapArray m_heightmap;
|
||||
// the index is a array of block id
|
||||
std::vector<BlockType> m_blocks;
|
||||
|
||||
/*
|
||||
0 - normal
|
||||
1 - cross_plane
|
||||
2 - normal_discard
|
||||
3 - transparent and blend
|
||||
*/
|
||||
std::vector<VertexData> m_vertex_data;
|
||||
float frequency = 0.01f;
|
||||
float height = 80;
|
||||
unsigned m_seed = 0;
|
||||
|
||||
BiomeConditions m_conditions;
|
||||
|
||||
void clear_dirty();
|
||||
void gen_vertices(
|
||||
const std::array<const std::vector<BlockType>*, 4>& neighbor_block);
|
||||
void gen_cross_plane_vertices(int world_x, int world_y, int world_z,
|
||||
BlockType id);
|
||||
|
||||
public:
|
||||
Chunk(World& world, ChunkPos chunk_pos);
|
||||
~Chunk();
|
||||
Chunk(const Chunk&) = delete;
|
||||
Chunk& operator=(const Chunk&) = delete;
|
||||
Chunk(Chunk&&) noexcept;
|
||||
Chunk& operator=(Chunk&&) noexcept;
|
||||
|
||||
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
|
||||
int world_z, int chunk_x,
|
||||
int chunk_z);
|
||||
@@ -49,9 +71,34 @@ public:
|
||||
BiomeType get_biome() const;
|
||||
ChunkPos get_chunk_pos() const;
|
||||
const std::vector<BlockType>& get_chunk_blocks() const;
|
||||
void receive_chunk(const ChunkDataRsp& data);
|
||||
void gen_vertex_data(const OptionalBlockVectorArray& neighbor_block);
|
||||
// Can only be called on the render thread
|
||||
HeightMapArray get_heightmap() const;
|
||||
static int index(int x, int y, int z);
|
||||
static int index(const glm::vec3& pos);
|
||||
// Init Chunk
|
||||
// Determine biome from temperature and humidity noise
|
||||
void gen_phase_one();
|
||||
// Resolve biome adjacency conflicts with neighbor chunks
|
||||
void gen_phase_two(const std::array<const Chunk*, 8>& adj_chunks);
|
||||
// Generate heightmap using biome-specific noise
|
||||
void gen_phase_three();
|
||||
// Blend heightmap with neighbors for smooth transitions
|
||||
void gen_phase_four(
|
||||
const std::array<std::optional<HeightMapArray>, 8>& neighbor_heightmap,
|
||||
const std::array<BiomeType, 8>& neighbor_biome);
|
||||
// Generate terrain blocks from heightmap and biome
|
||||
void gen_phase_five();
|
||||
// Blend surface blocks at chunk borders with neighbors
|
||||
void gen_phase_six(const std::array<std::optional<std::vector<BlockType>>,
|
||||
4>& neighbor_block);
|
||||
// Generate biome-specific vegetation/structures
|
||||
void gen_phase_seven();
|
||||
// void gen_vertex_data();
|
||||
// 0 : (1, 0)
|
||||
// 1 : (-1, 0)
|
||||
// 2 : (0, 1)
|
||||
// 3 : (0, -1)
|
||||
void gen_vertex_data(
|
||||
const std::array<const std::vector<BlockType>*, 4>& neighbor_block);
|
||||
void upload_to_gpu();
|
||||
|
||||
GLuint get_normal_vao() const;
|
||||
@@ -66,9 +113,6 @@ public:
|
||||
GLuint get_normal_blend_vao() const;
|
||||
size_t get_normal_blend_vertices_sum() const;
|
||||
|
||||
GLuint get_water_vao() const;
|
||||
size_t get_water_vertices_sum() const;
|
||||
|
||||
bool is_dirty() const;
|
||||
void mark_dirty();
|
||||
|
||||
@@ -76,58 +120,15 @@ public:
|
||||
void need_upload();
|
||||
|
||||
void set_chunk_block(int index, unsigned id);
|
||||
bool is_temp_chunk() const;
|
||||
|
||||
ChunkPos chunk_pos() const;
|
||||
BiomeType biome() const;
|
||||
void biome(BiomeType b);
|
||||
HeightMapArray& heightmap();
|
||||
std::vector<BlockType>& blocks();
|
||||
ClientWorld& world();
|
||||
World& world();
|
||||
unsigned seed() const;
|
||||
const ChunkRenderSnapshot* get_render_snapshot() const;
|
||||
|
||||
private:
|
||||
struct FaceKey {
|
||||
BlockType block_id = 0;
|
||||
int face = -1; // 0-5, used to index NORMALS/TANGENTS/TEX_COORDS
|
||||
|
||||
bool valid() const { return block_id != 0; }
|
||||
bool operator==(const FaceKey& o) const {
|
||||
return block_id == o.block_id && face == o.face;
|
||||
}
|
||||
bool operator!=(const FaceKey& o) const { return !(*this == o); }
|
||||
};
|
||||
|
||||
static constexpr int SIZE_X = CHUNK_SIZE;
|
||||
static constexpr int SIZE_Y = WORLD_SIZE_Y;
|
||||
static constexpr int SIZE_Z = CHUNK_SIZE;
|
||||
static constexpr int BLOCK_SIZE = SIZE_X * SIZE_Y * SIZE_Z;
|
||||
static constexpr int VERTEX_DATA_SUM = 5;
|
||||
std::atomic<bool> m_dirty{false};
|
||||
std::atomic<bool> m_need_upload{true};
|
||||
std::atomic<bool> m_is_on_gen_vertex_data{false};
|
||||
std::atomic<BiomeType> m_biome = BiomeType::PLAIN;
|
||||
std::mutex m_vertexs_data_mutex;
|
||||
ChunkPos m_chunk_pos;
|
||||
ClientWorld& m_world;
|
||||
// the index is a array of block id
|
||||
std::vector<BlockType> m_blocks;
|
||||
/*
|
||||
0 - normal
|
||||
1 - cross_plane
|
||||
2 - normal_discard
|
||||
3 - transparent and blend
|
||||
4 - water
|
||||
*/
|
||||
std::vector<VertexData> m_vertex_data;
|
||||
|
||||
ChunkRenderSnapshot m_render_snapshot;
|
||||
|
||||
unsigned m_seed = 0;
|
||||
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);
|
||||
void emit_quad(int axis, int face_dir, int layer, int i, int j, int w,
|
||||
int h, int u_axis, int v_axis, FaceKey key);
|
||||
BiomeConditions& conditions();
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -11,11 +11,11 @@
|
||||
#include <optional>
|
||||
namespace Cubed {
|
||||
|
||||
class ServerChunk;
|
||||
class Chunk;
|
||||
|
||||
class ChunkGenerator {
|
||||
public:
|
||||
ChunkGenerator(ServerChunk& chunk);
|
||||
ChunkGenerator(Chunk& chunk);
|
||||
|
||||
static void init();
|
||||
static void reload();
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
void assign_chunk_biome();
|
||||
// Adjust Biome
|
||||
void resolve_biome_adjacency_conflict(
|
||||
const std::array<const ServerChunk*, 8>& adj_chunks);
|
||||
const std::array<const Chunk*, 8>& adj_chunks);
|
||||
// Generate Heightmap
|
||||
void generate_heightmap();
|
||||
// Adjust Height
|
||||
@@ -42,10 +42,9 @@ public:
|
||||
// Generate Structure
|
||||
void generate_vegetation();
|
||||
BiomeType get_biome_at(float world_x, float world_z);
|
||||
ServerChunk& chunk();
|
||||
Chunk& chunk();
|
||||
Random& random();
|
||||
const std::array<BiomeType, 8>& neighbor_biome() const;
|
||||
void ocean_build();
|
||||
void generate_cave();
|
||||
void generate_river();
|
||||
|
||||
@@ -53,7 +52,7 @@ private:
|
||||
static inline std::atomic<bool> is_init{false};
|
||||
static inline unsigned m_generator_seed{0};
|
||||
static inline std::atomic<bool> is_seed_change{false};
|
||||
ServerChunk& m_chunk;
|
||||
Chunk& m_chunk;
|
||||
Random m_random;
|
||||
std::unique_ptr<BiomeBuilder> m_biome_builder{nullptr};
|
||||
bool is_cur_chunk_ins = false;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/constants.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Cubed {
|
||||
@@ -18,14 +16,7 @@ struct ChunkPos {
|
||||
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 {
|
||||
return ChunkPos{x + pos.x, z + pos.z};
|
||||
}
|
||||
@@ -36,29 +27,5 @@ struct ChunkPos {
|
||||
return *this;
|
||||
};
|
||||
};
|
||||
constexpr ChunkPos CHUNK_DIR[]{{1, 0}, {-1, 0}, {0, 1}, {0, -1},
|
||||
{1, 1}, {-1, 1}, {1, -1}, {-1, -1}};
|
||||
inline ChunkPos get_chunk_pos(int world_x, int world_z) {
|
||||
int chunk_x, chunk_z;
|
||||
if (world_x < 0) {
|
||||
chunk_x = (world_x + 1) / CHUNK_SIZE - 1;
|
||||
}
|
||||
if (world_x >= 0) {
|
||||
chunk_x = world_x / CHUNK_SIZE;
|
||||
}
|
||||
if (world_z < 0) {
|
||||
chunk_z = (world_z + 1) / CHUNK_SIZE - 1;
|
||||
}
|
||||
if (world_z >= 0) {
|
||||
chunk_z = world_z / CHUNK_SIZE;
|
||||
}
|
||||
return {chunk_x, chunk_z};
|
||||
}
|
||||
|
||||
inline float distance2(const ChunkPos& a, const ChunkPos& b) {
|
||||
float dx = static_cast<float>(a.x) - b.x;
|
||||
float dz = static_cast<float>(a.z) - b.z;
|
||||
return dx * dx + dz * dz;
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,138 +0,0 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/client_chunk.hpp"
|
||||
#include "Cubed/gameplay/client_player.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
#include "Cubed/gameplay/network_client.hpp"
|
||||
#include "Cubed/tools/priority_thread_pool.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <deque>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
namespace Cubed {
|
||||
|
||||
struct RemotePlayerInfo {
|
||||
std::string name;
|
||||
glm::vec3 render_pos;
|
||||
glm::vec3 target_pos;
|
||||
};
|
||||
|
||||
struct RemotePlayerRenderData {
|
||||
std::string name;
|
||||
glm::vec3 render_pos;
|
||||
};
|
||||
|
||||
class ClientWorld {
|
||||
public:
|
||||
ClientWorld();
|
||||
~ClientWorld();
|
||||
void init(std::string_view player_name,
|
||||
std::shared_ptr<NetworkClient> client);
|
||||
void update(float delta_time);
|
||||
const std::optional<LookBlock>& get_look_block_pos() const;
|
||||
ClientPlayer& get_player();
|
||||
int get_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;
|
||||
|
||||
void rebuild_world();
|
||||
|
||||
void push_delete_vbo(GLuint vbo);
|
||||
void push_delete_vao(GLuint vao);
|
||||
// void hot_reload();
|
||||
|
||||
// void rebuild_world();
|
||||
void report_block_change(const glm::ivec3& pos, unsigned id) const;
|
||||
void receive_block_change(const BlockChangeRsp& rsp);
|
||||
void receive_time(const UpdateTime& rsp);
|
||||
|
||||
void receive_remote_player(const PlayerInfoRsp& rsp);
|
||||
void receive_player_logout(const LogoutRsp& rsp);
|
||||
int rendering_distance() const;
|
||||
void rendering_distance(int rendering_distance);
|
||||
int get_chunk_task_id() const;
|
||||
void start_client_thread(std::string_view uuid);
|
||||
void stop_client_thread();
|
||||
|
||||
void start_thread_pool();
|
||||
void stop_thread_pool();
|
||||
void change_pool_threads(int threads);
|
||||
void hot_reload();
|
||||
void request_chunk();
|
||||
std::vector<glm::vec4>& planes();
|
||||
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
|
||||
const std::vector<RemotePlayerRenderData>& render_player_data() const;
|
||||
glm::vec3 sunlight_dir() const;
|
||||
void receive_chunk(std::vector<uint8_t> data, PacketHeader header);
|
||||
void request_exit();
|
||||
bool is_receive_exit();
|
||||
int chunk_size() const;
|
||||
static AABB get_block_aabb(const glm::ivec3& pos);
|
||||
template <typename Fn>
|
||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_timers.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::string(id)),
|
||||
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ChunkLoadStyle { RANDOM, CENTER };
|
||||
using ChunkHashMap =
|
||||
tbb::concurrent_hash_map<ChunkPos, std::shared_ptr<ClientChunk>,
|
||||
ChunkPos::TBBHash>;
|
||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||
using ChunkPosVector = std::vector<ChunkPos>;
|
||||
using OtherPlayerHashMap =
|
||||
std::unordered_map<std::string, RemotePlayerInfo>;
|
||||
using chunk_acc = ChunkHashMap::accessor;
|
||||
using chunk_cacc = ChunkHashMap::const_accessor;
|
||||
static constexpr int WORLD_EXIT_TIMEOUT = 200;
|
||||
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
|
||||
ClientPlayer m_player;
|
||||
OtherPlayerHashMap m_other_players;
|
||||
ChunkHashMap m_chunks;
|
||||
std::vector<glm::vec4> m_planes;
|
||||
std::jthread m_client_thread;
|
||||
|
||||
std::mutex m_delete_vbo_mutex;
|
||||
std::mutex m_delete_vao_mutex;
|
||||
mutable std::shared_mutex m_other_players_mutex;
|
||||
|
||||
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
|
||||
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
|
||||
|
||||
std::vector<GLuint> m_pending_delete_vbo;
|
||||
std::vector<GLuint> m_pending_delete_vao;
|
||||
|
||||
std::deque<ChunkPos> m_dirty_queue;
|
||||
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
|
||||
std::vector<RemotePlayerRenderData> m_render_player_data;
|
||||
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
|
||||
std::atomic<bool> m_game_running{false};
|
||||
std::atomic<bool> m_receive_exit{false};
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<TickType> m_game_ticks{0};
|
||||
std::atomic<TickType> m_day_tick{6000};
|
||||
std::atomic<bool> m_requesting_chunk{false};
|
||||
std::atomic<bool> m_is_rebuilding{false};
|
||||
std::atomic<int> m_chunk_task_id{0};
|
||||
std::shared_ptr<NetworkClient> m_client;
|
||||
ChunkLoadStyle m_chunk_load_style{ChunkLoadStyle::CENTER};
|
||||
|
||||
std::atomic<std::shared_ptr<PriorityThreadPool>> m_thread_pool;
|
||||
|
||||
void client_run(std::stop_token token);
|
||||
|
||||
void set_player_pos();
|
||||
|
||||
void report_player_pos();
|
||||
|
||||
void set_block(const glm::ivec3& pos, unsigned id);
|
||||
|
||||
void update_chunk(const ChunkPosSet& old, const ChunkPosSet& now);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// Prevent unsigned underflow issues in subtraction
|
||||
#include "Cubed/tools/cubed_assert.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
namespace Cubed {
|
||||
using TickType = long long;
|
||||
|
||||
constexpr int DEFAULT_PER_TICK_TIME = 50;
|
||||
|
||||
constexpr TickType DAY_TIME = 24000;
|
||||
|
||||
constexpr TickType PER_HOUR = 1000;
|
||||
|
||||
class Timer {
|
||||
public:
|
||||
template <typename Fn>
|
||||
Timer(TickType threshold, Fn&& f)
|
||||
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
|
||||
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
|
||||
}
|
||||
bool update() {
|
||||
if (++m_current >= m_threshold) {
|
||||
m_current = 0;
|
||||
m_fn();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void reset() { m_current = 0; }
|
||||
|
||||
private:
|
||||
std::function<void()> m_fn;
|
||||
TickType m_threshold;
|
||||
TickType m_current = 0;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,65 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/packet.hpp"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
namespace Cubed {
|
||||
using asio::ip::tcp;
|
||||
class ClientWorld;
|
||||
class NetworkClient : public std::enable_shared_from_this<NetworkClient> {
|
||||
public:
|
||||
NetworkClient(ClientWorld& world);
|
||||
~NetworkClient();
|
||||
void close();
|
||||
void stop();
|
||||
void send(Packet packet, int priority = 10);
|
||||
void start(std::string ip, int port = 25530);
|
||||
bool is_connected() const;
|
||||
bool is_connect_error() const;
|
||||
|
||||
private:
|
||||
struct Task {
|
||||
int priority = 10;
|
||||
std::uint64_t sequence = 0;
|
||||
Packet packet;
|
||||
Task(int p, std::uint64_t seq, Packet pac)
|
||||
: priority(p), sequence(seq), packet(std::move(pac)) {}
|
||||
};
|
||||
|
||||
struct TaskCompare {
|
||||
bool operator()(const Task& a, const Task& b) const {
|
||||
|
||||
if (a.priority != b.priority) {
|
||||
return a.priority > b.priority;
|
||||
}
|
||||
|
||||
return a.sequence > b.sequence;
|
||||
}
|
||||
};
|
||||
|
||||
asio::io_context m_io;
|
||||
|
||||
std::thread m_net_thread;
|
||||
static constexpr uint32_t MAX_PACKET_SIZE = 4 * 1024 * 1024;
|
||||
tcp::socket m_socket;
|
||||
std::vector<char> m_read_buffer;
|
||||
|
||||
std::priority_queue<Task, std::vector<Task>, TaskCompare> m_write_queue;
|
||||
|
||||
asio::strand<asio::io_context::executor_type> m_strand;
|
||||
std::atomic<bool> m_closed{false};
|
||||
std::atomic<bool> m_connected{false};
|
||||
std::atomic<bool> m_connect_error{false};
|
||||
// ClientWorld is managed by App
|
||||
ClientWorld& m_world;
|
||||
std::atomic_uint64_t m_sequence{0};
|
||||
|
||||
asio::awaitable<void> connect(std::string ip, int port);
|
||||
asio::awaitable<void> read_loop();
|
||||
|
||||
void do_write();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,33 +0,0 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/server_world.hpp"
|
||||
#include "Cubed/gameplay/session.hpp"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <thread>
|
||||
namespace Cubed {
|
||||
|
||||
class NetworkServer {
|
||||
public:
|
||||
NetworkServer(int port = 25530);
|
||||
~NetworkServer();
|
||||
void stop();
|
||||
|
||||
// Run in another thread after initialization is complete
|
||||
void start_server(int port = 25530);
|
||||
|
||||
int port() const;
|
||||
ServerWorld& server_world();
|
||||
|
||||
private:
|
||||
asio::io_context m_io;
|
||||
std::thread m_net_thread;
|
||||
int m_port = 25530;
|
||||
std::atomic<bool> m_stopped{false};
|
||||
std::atomic<bool> m_started{false};
|
||||
ServerWorld m_world;
|
||||
std::mutex m_session_mutex;
|
||||
std::unordered_map<std::string, std::shared_ptr<Session>> m_session;
|
||||
asio::awaitable<void> listen();
|
||||
void net_run();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,216 +0,0 @@
|
||||
#pragma once
|
||||
#include "Cubed/tools/compression.hpp"
|
||||
#include "packet.pb.h" // IWYU pragma: keep
|
||||
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#ifdef _WIN32
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <span>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
namespace Cubed {
|
||||
constexpr size_t HEADER_LEN =
|
||||
sizeof(uint16_t) + sizeof(uint16_t) + sizeof(uint32_t) + sizeof(uint32_t);
|
||||
constexpr size_t PACKET_COMPRESSION_THRESHOLD = 100;
|
||||
using Packet = std::shared_ptr<std::vector<uint8_t>>;
|
||||
enum class CompressType : uint16_t {
|
||||
NONE = 0,
|
||||
ZSTD = 1,
|
||||
};
|
||||
|
||||
inline CompressType get_compress_type(uint16_t id) {
|
||||
using enum CompressType;
|
||||
switch (id) {
|
||||
case std::to_underlying(NONE):
|
||||
return NONE;
|
||||
case std::to_underlying(ZSTD):
|
||||
return ZSTD;
|
||||
}
|
||||
throw std::runtime_error(std::format("Unknown CompressType {}", id));
|
||||
}
|
||||
|
||||
struct PacketHeader {
|
||||
uint16_t cmd{};
|
||||
CompressType compress_type{}; // 0=none 1=zlib
|
||||
uint32_t uncompressed_size{};
|
||||
uint32_t compressed_size{};
|
||||
};
|
||||
|
||||
enum class PacketEnum : uint16_t {
|
||||
LOGIN_REQ = 1001,
|
||||
LOGIN_RSP = 1002,
|
||||
LOGOUT_REQ = 1003,
|
||||
LOGOUT_RSP = 1004,
|
||||
PLAYER_INFO = 2001,
|
||||
PLAYER_POS = 2002,
|
||||
PLAYER_INFO_RSP = 2003,
|
||||
CHUNK_DATA_REQ = 3001,
|
||||
CHUNK_DATA_RSP = 3002,
|
||||
BLOCK_CHANGE_REQ = 3003,
|
||||
BLOCK_CHANGE_RSP = 3004,
|
||||
S2C_CLEAR_ALL_CHUNKS = 3005,
|
||||
UPDATE_TIME = 3006,
|
||||
PING = 9001,
|
||||
PONG = 9002
|
||||
|
||||
};
|
||||
|
||||
template <typename> struct always_false : std::false_type {}; // NOLINT
|
||||
|
||||
template <typename T> constexpr uint16_t get_packet_id() {
|
||||
static_assert(always_false<T>::value, "Unknown Type");
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <> constexpr uint16_t get_packet_id<LoginReq>() {
|
||||
return std::to_underlying(PacketEnum::LOGIN_REQ);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<LoginRsp>() {
|
||||
return std::to_underlying(PacketEnum::LOGIN_RSP);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<LogoutReq>() {
|
||||
return std::to_underlying(PacketEnum::LOGOUT_REQ);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<LogoutRsp>() {
|
||||
return std::to_underlying(PacketEnum::LOGOUT_RSP);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<PlayerInfo>() {
|
||||
return std::to_underlying(PacketEnum::PLAYER_INFO);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<PlayerPos>() {
|
||||
return std::to_underlying(PacketEnum::PLAYER_POS);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<PlayerInfoRsp>() {
|
||||
return std::to_underlying(PacketEnum::PLAYER_INFO_RSP);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<ChunkDataReq>() {
|
||||
return std::to_underlying(PacketEnum::CHUNK_DATA_REQ);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<ChunkDataRsp>() {
|
||||
return std::to_underlying(PacketEnum::CHUNK_DATA_RSP);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<BlockChangeReq>() {
|
||||
return std::to_underlying(PacketEnum::BLOCK_CHANGE_REQ);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<BlockChangeRsp>() {
|
||||
return std::to_underlying(PacketEnum::BLOCK_CHANGE_RSP);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2C_ClearAllChunks>() {
|
||||
return std::to_underlying(PacketEnum::S2C_CLEAR_ALL_CHUNKS);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<UpdateTime>() {
|
||||
return std::to_underlying(PacketEnum::UPDATE_TIME);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<Ping>() {
|
||||
return std::to_underlying(PacketEnum::PING);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<Pong>() {
|
||||
return std::to_underlying(PacketEnum::PONG);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::derived_from<T, google::protobuf::Message>
|
||||
Packet make_packet(const T& msg) {
|
||||
PacketHeader header{};
|
||||
header.cmd = get_packet_id<T>();
|
||||
uint32_t raw_size = static_cast<uint32_t>(msg.ByteSizeLong());
|
||||
std::vector<uint8_t> raw(raw_size);
|
||||
|
||||
if (!msg.SerializeToArray(raw.data(), raw_size)) {
|
||||
return {};
|
||||
}
|
||||
std::vector<uint8_t> payload;
|
||||
if (raw_size >= PACKET_COMPRESSION_THRESHOLD) {
|
||||
std::vector<uint8_t> compressed = compress_data(raw);
|
||||
if (compressed.size() < raw.size()) {
|
||||
payload = std::move(compressed);
|
||||
header.compress_type = CompressType::ZSTD;
|
||||
} else {
|
||||
payload = std::move(raw);
|
||||
header.compress_type = CompressType::NONE;
|
||||
}
|
||||
} else {
|
||||
payload = std::move(raw);
|
||||
header.compress_type = CompressType::NONE;
|
||||
}
|
||||
header.uncompressed_size = raw_size;
|
||||
header.compressed_size = static_cast<uint32_t>(payload.size());
|
||||
|
||||
auto packet =
|
||||
std::make_shared<std::vector<uint8_t>>(HEADER_LEN + payload.size());
|
||||
|
||||
uint16_t cmd_net = htons(header.cmd);
|
||||
uint16_t compress_type_net =
|
||||
htons(std::to_underlying(header.compress_type));
|
||||
uint32_t uncompressed_size_net = htonl(header.uncompressed_size);
|
||||
uint32_t compressed_size_net = htonl(header.compressed_size);
|
||||
|
||||
std::memcpy(packet->data(), &cmd_net, sizeof(cmd_net));
|
||||
|
||||
std::memcpy(packet->data() + 2, &compress_type_net,
|
||||
sizeof(compress_type_net));
|
||||
std::memcpy(packet->data() + 4, &uncompressed_size_net,
|
||||
sizeof(uncompressed_size_net));
|
||||
std::memcpy(packet->data() + 8, &compressed_size_net,
|
||||
sizeof(compressed_size_net));
|
||||
std::memcpy(packet->data() + HEADER_LEN, payload.data(), payload.size());
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
inline PacketHeader decode_packet_header(std::span<const uint8_t> header) {
|
||||
if (header.size() < HEADER_LEN)
|
||||
throw std::runtime_error("Invalid header");
|
||||
uint16_t cmd_net;
|
||||
uint16_t compress_type_net;
|
||||
uint32_t uncompressed_size_net;
|
||||
uint32_t compressed_size_net;
|
||||
std::memcpy(&cmd_net, header.data(), sizeof(cmd_net));
|
||||
std::memcpy(&compress_type_net, header.data() + 2,
|
||||
sizeof(compress_type_net));
|
||||
std::memcpy(&uncompressed_size_net, header.data() + 4,
|
||||
sizeof(uncompressed_size_net));
|
||||
std::memcpy(&compressed_size_net, header.data() + 8,
|
||||
sizeof(compressed_size_net));
|
||||
|
||||
return {ntohs(cmd_net), get_compress_type(ntohs(compress_type_net)),
|
||||
ntohl(uncompressed_size_net), ntohl(compressed_size_net)};
|
||||
}
|
||||
template <typename T>
|
||||
requires std::derived_from<T, google::protobuf::Message>
|
||||
bool decode_packet(T& message, std::span<const uint8_t> data,
|
||||
const PacketHeader& header) {
|
||||
if (data.size() != header.compressed_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.compress_type == CompressType::NONE &&
|
||||
header.uncompressed_size != header.compressed_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (header.compress_type) {
|
||||
case CompressType::NONE: {
|
||||
return message.ParseFromArray(
|
||||
data.data(), static_cast<int>(header.uncompressed_size));
|
||||
}
|
||||
case CompressType::ZSTD: {
|
||||
auto raw = decompress_data(data, header.uncompressed_size);
|
||||
return message.ParseFromArray(raw.data(), static_cast<int>(raw.size()));
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
struct PathOrigin {
|
||||
bool exists;
|
||||
glm::vec3 pos;
|
||||
unsigned seed;
|
||||
};
|
||||
@@ -6,29 +6,82 @@
|
||||
#include "Cubed/gameplay/game_mode.hpp"
|
||||
#include "Cubed/input.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
enum class Gait { WALK = 0, RUN };
|
||||
class ClientWorld;
|
||||
class ClientPlayer {
|
||||
|
||||
class World;
|
||||
|
||||
class Player {
|
||||
private:
|
||||
using enum GameMode;
|
||||
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
|
||||
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
|
||||
float m_acceleration = DEFAULT_ACCELERATION;
|
||||
float m_deceleration = DEFAULT_DECELERATION;
|
||||
float m_g = DEFAULT_G;
|
||||
|
||||
constexpr static float MAX_SPACE_ON_TIME = 0.3f;
|
||||
|
||||
float m_yaw = 0.0f;
|
||||
float m_pitch = 0.0f;
|
||||
|
||||
float m_sensitivity = 0.15f;
|
||||
|
||||
float m_max_speed = m_max_walk_speed;
|
||||
float m_y_speed = 0.0f;
|
||||
bool can_up = true;
|
||||
|
||||
float space_on_time = 0.0f;
|
||||
bool space_on = false;
|
||||
bool is_fly = false;
|
||||
|
||||
float m_xz_speed = 0.0f;
|
||||
|
||||
unsigned m_place_block = 1;
|
||||
|
||||
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
glm::vec3 move_distance{0.0f, 0.0f, 0.0f};
|
||||
// player is tow block tall, the pos is the lower pos
|
||||
|
||||
glm::vec3 m_player_pos{0.0f, 255.0f, 0.0f};
|
||||
ChunkPos m_player_chunk_pos{0, 0};
|
||||
|
||||
glm::vec3 m_front{0, 0, -1};
|
||||
glm::vec3 m_right{0, 0, 0};
|
||||
glm::vec3 m_size{0.6f, 1.8f, 0.6f};
|
||||
|
||||
Gait m_gait = Gait::WALK;
|
||||
MoveState m_move_state{};
|
||||
GameMode m_game_mode = CREATIVE;
|
||||
std::optional<LookBlock> m_look_block = std::nullopt;
|
||||
std::string m_name{};
|
||||
World& m_world;
|
||||
|
||||
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
|
||||
glm::ivec3& block_pos, glm::vec3& normal,
|
||||
float distance = 4.0f);
|
||||
|
||||
void check_player_chunk_transition();
|
||||
void update_direction();
|
||||
void update_lookup_block();
|
||||
void update_move(float delta_time);
|
||||
void update_x_move();
|
||||
void update_y_move();
|
||||
void update_z_move();
|
||||
|
||||
public:
|
||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||
ClientPlayer(ClientWorld& world);
|
||||
~ClientPlayer();
|
||||
|
||||
void update_chunk_set(const ChunkPosSet& set);
|
||||
const ChunkPosSet& get_chunk_pos_set() const;
|
||||
ChunkPosSet& get_chunk_pos_set();
|
||||
|
||||
static AABB get_aabb(const glm::vec3& pos);
|
||||
Player(World& world, const std::string& name);
|
||||
~Player();
|
||||
AABB get_aabb() const;
|
||||
const glm::vec3& get_front() const;
|
||||
const Gait& get_gait() const;
|
||||
const std::optional<LookBlock>& get_look_block_pos() const;
|
||||
// thread safe
|
||||
glm::vec3 get_player_pos() const;
|
||||
const glm::vec3& get_player_pos() const;
|
||||
const MoveState& get_move_state() const;
|
||||
|
||||
void change_mode(GameMode mode);
|
||||
@@ -46,80 +99,12 @@ public:
|
||||
float& acceleration();
|
||||
float& deceleration();
|
||||
float& g();
|
||||
float& fly_y_speed();
|
||||
|
||||
unsigned place_block() const;
|
||||
|
||||
Gait& gait();
|
||||
GameMode& game_mode();
|
||||
|
||||
const ClientWorld& get_world() const;
|
||||
|
||||
void set_uuid(std::string_view uuid);
|
||||
const std::string& get_uuid() const;
|
||||
const std::string& get_name() const;
|
||||
|
||||
void init(std::string_view name);
|
||||
|
||||
private:
|
||||
using enum GameMode;
|
||||
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
|
||||
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
|
||||
float m_acceleration = DEFAULT_ACCELERATION;
|
||||
float m_deceleration = DEFAULT_DECELERATION;
|
||||
float m_g = DEFAULT_G;
|
||||
constexpr static float MAX_SPACE_ON_TIME = 0.3f;
|
||||
|
||||
float m_yaw = 0.0f;
|
||||
float m_pitch = 0.0f;
|
||||
|
||||
float m_sensitivity = 0.15f;
|
||||
|
||||
float m_max_speed = m_max_walk_speed;
|
||||
float m_y_speed = 0.0f;
|
||||
float m_fly_y_speed = 7.5f;
|
||||
bool can_up = true;
|
||||
|
||||
float space_on_time = 0.0f;
|
||||
bool space_on = false;
|
||||
bool is_fly = false;
|
||||
|
||||
float m_xz_speed = 0.0f;
|
||||
|
||||
unsigned m_place_block = 1;
|
||||
|
||||
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
glm::vec3 move_distance{0.0f, 0.0f, 0.0f};
|
||||
// player is tow block tall, the pos is the lower pos
|
||||
|
||||
glm::vec3 m_player_pos{0.0f, 255.0f, 0.0f};
|
||||
ChunkPos m_last_chunk_pos{0, 0};
|
||||
|
||||
glm::vec3 m_front{0, 0, -1};
|
||||
glm::vec3 m_right{0, 0, 0};
|
||||
static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f};
|
||||
|
||||
Gait m_gait = Gait::WALK;
|
||||
MoveState m_move_state{};
|
||||
GameMode m_game_mode = CREATIVE;
|
||||
std::optional<LookBlock> m_look_block = std::nullopt;
|
||||
std::string m_name{};
|
||||
std::string m_uuid;
|
||||
ClientWorld& m_world;
|
||||
|
||||
mutable std::shared_mutex m_player_pos_mutex;
|
||||
mutable std::shared_mutex m_chunk_pos_mutex;
|
||||
ChunkPosSet m_player_chunk_pos_set;
|
||||
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
|
||||
glm::ivec3& block_pos, glm::vec3& normal,
|
||||
float distance = 4.0f);
|
||||
|
||||
void update_direction();
|
||||
void update_lookup_block();
|
||||
void update_move(float delta_time);
|
||||
void update_x_move(glm::vec3& player_pos);
|
||||
void update_y_move(glm::vec3& player_pos);
|
||||
void update_z_move(glm::vec3& player_pos);
|
||||
void update_player_chunk();
|
||||
const World& get_world() const;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,18 +1,20 @@
|
||||
#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>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Cubed {
|
||||
class RiverPath {
|
||||
|
||||
public:
|
||||
RiverPath(unsigned int chunk_seed, unsigned world_seed,
|
||||
const glm::vec3& start_pos);
|
||||
RiverPath(unsigned int world_seed, int path_id, 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();
|
||||
@@ -22,7 +24,6 @@ public:
|
||||
static float& delta_angle_max();
|
||||
static int& step_min();
|
||||
static int& step_max();
|
||||
static float step_len();
|
||||
|
||||
private:
|
||||
static inline float m_radius_xz_min = 5.0f;
|
||||
@@ -31,18 +32,21 @@ private:
|
||||
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_min = 150;
|
||||
static inline int m_step_max = 400;
|
||||
static inline float m_step_len = 4.0f;
|
||||
|
||||
int m_path_id = 0;
|
||||
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;
|
||||
std::unordered_set<ChunkPos, ChunkPos::Hash> m_pending_chunks;
|
||||
void collect_path_points();
|
||||
void precompute_chunk_coverage();
|
||||
};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -1,30 +1,31 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/path.hpp"
|
||||
#include "Cubed/gameplay/river.path.hpp"
|
||||
#include "Cubed/tools/cubed_random.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <unordered_map>
|
||||
namespace Cubed {
|
||||
|
||||
class RiverWorm {
|
||||
|
||||
public:
|
||||
RiverWorm();
|
||||
~RiverWorm();
|
||||
|
||||
std::unordered_map<unsigned, RiverPath>& 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();
|
||||
|
||||
PathOrigin get_origin(const ChunkPos& origin_chunk) const;
|
||||
int search_radius() const;
|
||||
unsigned world_seed() const;
|
||||
|
||||
float river_probability() const;
|
||||
bool has_origin_fast(const ChunkPos& pos) const;
|
||||
int river_sum() const;
|
||||
float& river_probability();
|
||||
|
||||
private:
|
||||
std::atomic<unsigned> m_world_seed{0};
|
||||
std::atomic<float> m_probability{0.01f};
|
||||
std::unordered_map<unsigned, RiverPath> m_paths;
|
||||
unsigned m_seed = 0;
|
||||
int m_sum = 0;
|
||||
Random m_random;
|
||||
float m_probability = 0.01f;
|
||||
};
|
||||
|
||||
}; // namespace Cubed
|
||||
@@ -1,97 +0,0 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
#include "Cubed/gameplay/biome.hpp"
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/chunk_generator.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class ServerChunk {
|
||||
public:
|
||||
ServerChunk(ServerWorld& world, ChunkPos chunk_pos,
|
||||
bool temp_chunk = false);
|
||||
ServerChunk(const ServerChunk&) = delete;
|
||||
ServerChunk(ServerChunk&&) noexcept;
|
||||
ServerChunk& operator=(const ServerChunk&) = delete;
|
||||
ServerChunk& operator=(ServerChunk&&) noexcept;
|
||||
|
||||
static std::tuple<int, int, int> world_to_block(int world_x, int world_y,
|
||||
int world_z, int chunk_x,
|
||||
int chunk_z);
|
||||
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
|
||||
int chunk_x, int chunk_z);
|
||||
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
|
||||
void set_chunk_block(int index, unsigned id);
|
||||
// ensure thread safe!
|
||||
void gen_chunk();
|
||||
|
||||
BiomeType get_biome() const;
|
||||
ChunkPos get_chunk_pos() const;
|
||||
const std::vector<BlockType>& get_chunk_blocks() const;
|
||||
HeightMapArray get_heightmap() const;
|
||||
bool is_temp_chunk() const;
|
||||
ChunkPos chunk_pos() const;
|
||||
BiomeType biome() const;
|
||||
void biome(BiomeType b);
|
||||
HeightMapArray& heightmap();
|
||||
std::vector<BlockType>& blocks();
|
||||
ServerWorld& world();
|
||||
unsigned seed() const;
|
||||
BiomeConditions& conditions();
|
||||
bool& has_cave();
|
||||
const OptionalBlockVectorArray& get_neightbor_blocks() const;
|
||||
static int index(int x, int y, int z);
|
||||
static int index(const glm::vec3& pos);
|
||||
|
||||
private:
|
||||
static constexpr int SIZE_X = CHUNK_SIZE;
|
||||
static constexpr int SIZE_Y = WORLD_SIZE_Y;
|
||||
static constexpr int SIZE_Z = CHUNK_SIZE;
|
||||
|
||||
std::atomic<bool> m_gening{false};
|
||||
std::atomic<bool> m_temp_chunk{false};
|
||||
|
||||
bool m_has_cave{false};
|
||||
|
||||
std::atomic<BiomeType> m_biome = BiomeType::PLAIN;
|
||||
|
||||
ChunkPos m_chunk_pos;
|
||||
ServerWorld& m_world;
|
||||
HeightMapArray m_heightmap;
|
||||
// the index is a array of block id
|
||||
std::vector<BlockType> m_blocks;
|
||||
OptionalBlockVectorArray m_neightbor_blocks;
|
||||
float frequency = 0.01f;
|
||||
float height = 80;
|
||||
unsigned m_seed = 0;
|
||||
|
||||
BiomeConditions m_conditions;
|
||||
|
||||
std::unique_ptr<ChunkGenerator> m_generator;
|
||||
|
||||
// Init Chunk
|
||||
// Determine biome from temperature and humidity noise
|
||||
void gen_phase_one();
|
||||
|
||||
// Generate heightmap using biome-specific noise
|
||||
void gen_phase_two();
|
||||
|
||||
// Generate terrain blocks from heightmap and biome
|
||||
void gen_phase_three();
|
||||
// Blend surface blocks at chunk borders with neighbors
|
||||
void gen_phase_four(const std::array<std::optional<std::vector<BlockType>>,
|
||||
4>& neighbor_block);
|
||||
// Generate biome-specific vegetation/structures
|
||||
void gen_phase_five();
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -1,54 +0,0 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <atomic>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class Session;
|
||||
class ServerPlayer {
|
||||
|
||||
public:
|
||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||
ServerPlayer(const ServerPlayer&) = delete;
|
||||
ServerPlayer(ServerPlayer&&) = delete;
|
||||
ServerPlayer& operator=(const ServerPlayer&) = delete;
|
||||
ServerPlayer& operator=(ServerPlayer&&) = delete;
|
||||
ServerPlayer(std::string_view name, std::string_view uuid,
|
||||
ServerWorld& m_world, std::shared_ptr<Session> session,
|
||||
TickType gametick);
|
||||
|
||||
const glm::vec3& get_pos() const;
|
||||
const std::string& get_name() const;
|
||||
const std::string& get_uuid() const;
|
||||
std::shared_ptr<Session> get_session() const;
|
||||
void update_pos(float x, float y, float z);
|
||||
void update_sync_gametick(TickType gametick);
|
||||
bool is_disconnect(TickType current_gametick) const;
|
||||
int task_id() const;
|
||||
void task_id(int id);
|
||||
bool has_player(ChunkPos pos) const;
|
||||
void update_chunk_set(const ChunkPosSet& set);
|
||||
const ChunkPosSet& get_chunk_pos_set() const;
|
||||
ChunkPosSet& get_chunk_pos_set();
|
||||
|
||||
private:
|
||||
static constexpr TickType TIMEOUT = 200;
|
||||
std::string m_name;
|
||||
std::string m_uuid;
|
||||
glm::vec3 m_pos{0.0f};
|
||||
ServerWorld& m_world;
|
||||
ChunkPos m_last_chunk_pos{0, 0};
|
||||
std::shared_ptr<Session> m_session;
|
||||
std::atomic<TickType> m_last_gametick{0};
|
||||
std::atomic<int> m_chunk_task_id{0};
|
||||
mutable std::shared_mutex m_chunk_pos_mutex;
|
||||
ChunkPosSet m_player_chunk_pos_set;
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,187 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/cave_carver.hpp"
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
#include "Cubed/gameplay/game_time.hpp"
|
||||
#include "Cubed/gameplay/river_worm.hpp"
|
||||
#include "Cubed/gameplay/server_chunk.hpp"
|
||||
#include "Cubed/gameplay/server_player.hpp"
|
||||
#include "Cubed/tools/priority_thread_pool.hpp"
|
||||
#include "Cubed/tools/recent_queue.hpp"
|
||||
#include "Cubed/tools/thread_pool.hpp"
|
||||
#include "world/block_change.pb.h"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <shared_mutex>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
#include <tbb/concurrent_unordered_map.h>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Session;
|
||||
class ServerWorld {
|
||||
public:
|
||||
enum class ThreadPoolKind { NET, GEN };
|
||||
ServerWorld();
|
||||
~ServerWorld();
|
||||
void stop();
|
||||
void handle_player_exit(const std::string& uuid);
|
||||
void init_world();
|
||||
void need_gen(std::string uuid);
|
||||
void update();
|
||||
void hot_reload();
|
||||
|
||||
int rendering_distance() const;
|
||||
void rendering_distance(int rendering_distance);
|
||||
void start_gen_thread();
|
||||
void start_server_thread();
|
||||
|
||||
void stop_gen_thread();
|
||||
void stop_server_thread();
|
||||
|
||||
void stop_thread_pool();
|
||||
void start_thread_pool();
|
||||
|
||||
void serever_run(std::stop_token stoken);
|
||||
|
||||
CaveCarver& cave_carcer();
|
||||
RiverWorm& river_worm();
|
||||
|
||||
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);
|
||||
|
||||
int gen_pool_threads() const;
|
||||
int max_threads() const;
|
||||
|
||||
void change_pool_threads(ThreadPoolKind kind, int threads);
|
||||
|
||||
int chunk_load_style() const;
|
||||
void set_chunk_load_style(int id);
|
||||
|
||||
bool set_block(const glm::ivec3& block_pos, unsigned id);
|
||||
|
||||
void sync_player_pos(const std::string& uuid, float x, float y, float z);
|
||||
void handle_player_login(const std::string& player_name,
|
||||
std::shared_ptr<Session> session);
|
||||
glm::vec3 get_player_pos(const std::string& uuid) const;
|
||||
|
||||
void handle_chunk_req(int task_id, const std::string& uuid, ChunkPos pos);
|
||||
void handle_block_change(const BlockChangeReq& req);
|
||||
|
||||
int chunk_size() const;
|
||||
template <typename Fn>
|
||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_timers.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(std::string(id)),
|
||||
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
|
||||
}
|
||||
|
||||
private:
|
||||
enum class ChunkState { NONE, GENERATING, READY, PENDING_DELETE };
|
||||
struct ChunkEntity {
|
||||
ChunkState state;
|
||||
std::shared_ptr<ServerChunk> chunk;
|
||||
uint32_t ref_count = 0;
|
||||
};
|
||||
|
||||
enum class ChunkLoadStyle { RANDOM, CENTER };
|
||||
struct PendingRequest {
|
||||
std::string uuid;
|
||||
int task_id;
|
||||
ChunkPos pos;
|
||||
};
|
||||
struct PendingChunk {
|
||||
ChunkPos pos;
|
||||
std::unique_ptr<ServerChunk> chunk;
|
||||
};
|
||||
|
||||
using ChunkHashMap =
|
||||
tbb::concurrent_hash_map<ChunkPos, ChunkEntity, ChunkPos::TBBHash>;
|
||||
using PlayerHashMap = std::unordered_map<std::string, ServerPlayer>;
|
||||
using NewChunkVector = std::vector<PendingChunk>;
|
||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
|
||||
|
||||
using chunk_acc = ChunkHashMap::accessor;
|
||||
using chunk_caac = ChunkHashMap::const_accessor;
|
||||
|
||||
using uuid_acc = PlayerUUIDMap::accessor;
|
||||
using uuid_cacc = PlayerUUIDMap::const_accessor;
|
||||
// key = uuid
|
||||
PlayerHashMap m_players;
|
||||
ChunkHashMap m_chunks;
|
||||
|
||||
CaveCarver m_cave_carcer;
|
||||
RiverWorm m_river_worm;
|
||||
|
||||
std::jthread m_gen_thread;
|
||||
std::jthread m_server_thread;
|
||||
|
||||
std::atomic<bool> m_chunk_gen_finished{false};
|
||||
std::atomic<bool> m_could_gen{true};
|
||||
std::atomic<bool> m_gen_running{false};
|
||||
std::atomic<bool> m_need_gen_chunk{false};
|
||||
std::atomic<bool> m_init{false};
|
||||
std::atomic<bool> m_stopped{false};
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<int> m_gen_pool_threads{0};
|
||||
std::atomic<int> m_net_pool_threads{0};
|
||||
std::atomic<int> m_max_threads{1};
|
||||
|
||||
std::atomic<TickType> m_game_ticks{0};
|
||||
std::atomic<TickType> m_day_tick{6000};
|
||||
std::atomic<bool> m_tick_running{true};
|
||||
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
|
||||
|
||||
mutable std::shared_mutex m_player_mutex;
|
||||
std::mutex m_need_gen_queue_mutex;
|
||||
std::condition_variable_any m_gen_cv;
|
||||
|
||||
RecentQueue<std::string> m_need_gen_queue;
|
||||
|
||||
std::atomic<std::shared_ptr<PriorityThreadPool>> m_gen_thread_pool;
|
||||
std::atomic<std::shared_ptr<ThreadPool>> m_net_thread_pool;
|
||||
|
||||
std::atomic<ChunkLoadStyle> m_chunk_load_style{ChunkLoadStyle::CENTER};
|
||||
|
||||
PlayerUUIDMap m_uuid_to_name;
|
||||
|
||||
tbb::concurrent_unordered_map<std::string, Timer> m_timers;
|
||||
tbb::concurrent_queue<PendingRequest> m_waiting_chunk_requests;
|
||||
tbb::concurrent_queue<std::unique_ptr<ServerChunk>> m_finished_queue;
|
||||
|
||||
void init_chunks();
|
||||
|
||||
void gen_chunks_internal(const std::string& uuid);
|
||||
|
||||
void compute_required_chunks(ChunkPosSet& required_chunks,
|
||||
const std::optional<std::string>& uuid);
|
||||
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
|
||||
const ChunkPosSet&);
|
||||
void submit_new_chunks(const std::string& uuid, NewChunkVector& new_chunks);
|
||||
// void wait_all_chunk_tasks();
|
||||
|
||||
void update_ref_count(const ChunkPosSet& old, const ChunkPosSet& now);
|
||||
|
||||
void send_time();
|
||||
|
||||
void send_chunk(int task_id, const std::string& uuid, ChunkPos pos);
|
||||
|
||||
int
|
||||
change_pool_threads(std::atomic<std::shared_ptr<ThreadPool>>& thread_pool,
|
||||
int threads);
|
||||
int change_pool_threads(
|
||||
std::atomic<std::shared_ptr<PriorityThreadPool>>& thread_pool,
|
||||
int threads);
|
||||
void send_server_stop();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,60 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/packet.hpp"
|
||||
|
||||
#include <asio.hpp>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
namespace Cubed {
|
||||
|
||||
using asio::ip::tcp;
|
||||
class ServerWorld;
|
||||
class Session : public std::enable_shared_from_this<Session> {
|
||||
|
||||
public:
|
||||
Session(tcp::socket socket, ServerWorld& server_world,
|
||||
asio::io_context& io);
|
||||
~Session();
|
||||
void start();
|
||||
void send(Packet packet, int priority = 10);
|
||||
|
||||
void close();
|
||||
const std::string& uuid() const;
|
||||
|
||||
private:
|
||||
struct Task {
|
||||
int priority = 10;
|
||||
std::uint64_t sequence = 0;
|
||||
Packet packet;
|
||||
Task(int p, std::uint64_t seq, Packet pac)
|
||||
: priority(p), sequence(seq), packet(std::move(pac)) {}
|
||||
};
|
||||
|
||||
struct TaskCompare {
|
||||
bool operator()(const Task& a, const Task& b) const {
|
||||
|
||||
if (a.priority != b.priority) {
|
||||
return a.priority > b.priority;
|
||||
}
|
||||
|
||||
return a.sequence > b.sequence;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr uint32_t MAX_PACKET_SIZE = 4 * 1024 * 1024;
|
||||
tcp::socket m_socket;
|
||||
std::vector<char> m_read_buffer;
|
||||
std::priority_queue<Task, std::vector<Task>, TaskCompare> m_write_queue;
|
||||
asio::strand<asio::io_context::executor_type> m_strand;
|
||||
std::string m_uuid;
|
||||
ServerWorld& m_server_world;
|
||||
std::atomic<bool> m_closed{false};
|
||||
|
||||
std::atomic_uint64_t m_sequence{0};
|
||||
|
||||
asio::awaitable<void> read_loop();
|
||||
|
||||
void do_write();
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -4,13 +4,13 @@
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
class ServerChunk;
|
||||
class Chunk;
|
||||
|
||||
struct TreeStructNode {
|
||||
glm::ivec3 offset{0, 0, 0};
|
||||
unsigned id = 0;
|
||||
};
|
||||
|
||||
bool build_tree(ServerChunk& chunk, const glm::ivec3& pos);
|
||||
bool build_tree(Chunk& chunk, const glm::ivec3& pos);
|
||||
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
#include <glad/glad.h>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class ClientWorld;
|
||||
class World;
|
||||
struct VertexData {
|
||||
std::vector<Vertex3D> m_vertices;
|
||||
std::vector<Vertex> m_vertices;
|
||||
GLuint m_vbo = 0;
|
||||
GLuint m_vao = 0;
|
||||
std::atomic<std::size_t> m_sum{0};
|
||||
ClientWorld& m_world;
|
||||
VertexData(ClientWorld& world);
|
||||
World& m_world;
|
||||
VertexData(World& world);
|
||||
~VertexData();
|
||||
VertexData(const VertexData&) = delete;
|
||||
VertexData(VertexData&&) noexcept;
|
||||
|
||||
126
include/Cubed/gameplay/world.hpp
Normal file
@@ -0,0 +1,126 @@
|
||||
#pragma once
|
||||
#include "Cubed/AABB.hpp"
|
||||
#include "Cubed/gameplay/cave_carver.hpp"
|
||||
#include "Cubed/gameplay/chunk.hpp"
|
||||
#include "Cubed/gameplay/river_worm.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
struct ChunkRenderSnapshot {
|
||||
GLuint normal_vao;
|
||||
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 half_extents;
|
||||
};
|
||||
|
||||
class Player;
|
||||
class TextureManager;
|
||||
class World {
|
||||
private:
|
||||
using ChunkPtrUpdateList = std::vector<std::pair<ChunkPos, Chunk*>>;
|
||||
using ChunkUpdateList = std::vector<std::pair<ChunkPos, Chunk>>;
|
||||
using ConstChunkMap =
|
||||
std::unordered_map<ChunkPos, const Chunk*, ChunkPos::Hash>;
|
||||
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
|
||||
using ChunkHashMap = std::unordered_map<ChunkPos, Chunk, ChunkPos::Hash>;
|
||||
glm::vec3 m_gen_player_pos{0.0f, 0.0f, 0.0f};
|
||||
ChunkHashMap m_chunks;
|
||||
std::unordered_map<std::size_t, Player> m_players;
|
||||
std::vector<glm::vec4> m_planes;
|
||||
|
||||
std::thread m_gen_thread;
|
||||
mutable std::mutex m_chunks_mutex;
|
||||
std::mutex m_gen_signal_mutex;
|
||||
std::mutex m_new_chunk_queue_mutex;
|
||||
std::mutex m_delete_vbo_mutex;
|
||||
std::mutex m_delete_vao_mutex;
|
||||
std::mutex m_gen_player_pos_mutex;
|
||||
std::vector<GLuint> m_pending_delete_vbo;
|
||||
std::vector<GLuint> m_pending_delete_vao;
|
||||
std::condition_variable m_gen_cv;
|
||||
std::atomic<bool> m_gen_running{false};
|
||||
std::atomic<bool> m_need_gen_chunk{false};
|
||||
std::atomic<bool> m_is_rebuilding{false};
|
||||
std::atomic<bool> m_chunk_gen_finished{false};
|
||||
std::atomic<bool> m_could_gen{true};
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<float> m_chunk_gen_fraction{0.0f};
|
||||
std::vector<ChunkPos> m_dirty_queue;
|
||||
std::vector<ChunkRenderSnapshot> m_render_snapshots;
|
||||
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk;
|
||||
std::vector<std::pair<ChunkPos, Chunk>> m_new_chunk_queue;
|
||||
|
||||
CaveCarver m_cave_carcer;
|
||||
RiverWorm m_river_worm;
|
||||
void init_chunks();
|
||||
|
||||
void gen_chunks_internal();
|
||||
void sync_player_pos(glm::vec3& player_pos);
|
||||
void compute_required_chunks(ChunkPosSet& required_chunks,
|
||||
ChunkHashMap& temp_neighbor);
|
||||
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
|
||||
const ChunkPosSet&);
|
||||
void
|
||||
build_neighbor_context_for_new_chunks(ConstChunkMap& new_chunks_neighbor,
|
||||
ChunkPtrUpdateList& affected_neighbor,
|
||||
const ChunkUpdateList& new_chunks);
|
||||
void build_neighbor_context_for_affected_neighbors(ChunkPtrUpdateList&,
|
||||
ConstChunkMap&);
|
||||
|
||||
public:
|
||||
World();
|
||||
~World();
|
||||
|
||||
bool can_move(const AABB& player_box) const;
|
||||
// const BlockRenderData& get_block_render_data(int x, int y ,int z);
|
||||
|
||||
const std::optional<LookBlock>&
|
||||
get_look_block_pos(const std::string& name) const;
|
||||
const Chunk* get_chunk(const ChunkPos& pos) const;
|
||||
|
||||
Player& get_player(const std::string& name);
|
||||
void init_world();
|
||||
int get_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);
|
||||
|
||||
void need_gen();
|
||||
|
||||
void set_block(const glm::ivec3& pos, unsigned id);
|
||||
void update(float delta_time);
|
||||
|
||||
void push_delete_vbo(GLuint vbo);
|
||||
void push_delete_vao(GLuint vao);
|
||||
void hot_reload();
|
||||
|
||||
void rebuild_world();
|
||||
|
||||
float chunk_gen_fraction() const;
|
||||
int rendering_distance() const;
|
||||
void rendering_distance(int rendering_distance);
|
||||
void start_gen_thread();
|
||||
void stop_gen_thread();
|
||||
|
||||
CaveCarver& cave_carcer();
|
||||
RiverWorm& river_worm();
|
||||
std::vector<glm::vec4>& planes();
|
||||
std::vector<ChunkRenderSnapshot>& render_snapshots();
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||