35 Commits

Author SHA1 Message Date
d31ec68f3c feat(packet): implement zstd compression and refactor packet decoding 2026-06-26 20:20:34 +08:00
c7c6ee9a61 refactor(network): extract packet header decoding into function 2026-06-26 19:20:14 +08:00
db7b67f265 refactor(packet): replace zlib with zstd compression and restructure header
Increase header length to 12 bytes, add CompressType and PacketHeader struct, and implement decode_packet_header. Update CMake to find zstd and link against it, adding Findzstd module.
2026-06-26 19:12:39 +08:00
7e42f595c9 build: add ZLIB dependency 2026-06-26 17:23:35 +08:00
11c902d2bf fix(dev_panel): add protobuf to about table libraries 2026-06-26 17:18:05 +08:00
e5e84848a9 feat(gameplay): add player logout and disconnect handling 2026-06-26 17:05:09 +08:00
25e9b0e0d3 feat(gameplay): add thread pool for asynchronous chunk data processing 2026-06-26 16:14:20 +08:00
48300b5fe2 feat(multiplayer): add rendering of other players as colored cubes 2026-06-26 15:52:22 +08:00
3c14a4ffe1 feat(app): add client/server CLI arguments and init guards 2026-06-26 13:48:07 +08:00
fbb6fe2fa6 feat(client): add center-based chunk loading priority 2026-06-26 11:32:52 +08:00
a858774b71 refactor(client_world): split pending queue into data and upload queues 2026-06-26 11:20:47 +08:00
4631bbe6d6 feat(gameplay): optimize chunk request with player chunk tracking
Add `update_player_chunk()` method to `ClientPlayer` that triggers a chunk request when the player moves more than 2 chunks away from the last recorded chunk. Introduce an atomic `m_requesting_chunk` flag in `ClientWorld` to prevent concurrent requests. Rename `m_player_chunk_pos` to `m_last_chunk_pos` for clarity.
2026-06-25 21:57:55 +08:00
db10d30dc2 fix: correct neighbor block indexing and add time update support 2026-06-25 21:37:19 +08:00
47552a1887 feat(gameplay): add time sync and network improvements 2026-06-25 20:13:51 +08:00
d7d47e9e7f refactor(gameplay): split world into client and server
Remove monolithic World, Player, Chunk classes. Introduce ClientWorld, ServerWorld, ClientPlayer, and related networked components. Add Abseil dependency for logging and checks. Rename old files to pre_remove_* and update all includes and references accordingly.
2026-06-25 17:54:32 +08:00
2c6dccfb53 refactor(server): replace Chunk with ServerChunk 2026-06-25 17:13:50 +08:00
b782b40133 feat(gameplay): add block change sync and coordinate utils 2026-06-25 17:09:41 +08:00
4a1ef54258 feat(gameplay): integrate network client and thread-safe player pos 2026-06-25 16:49:45 +08:00
cbc04f6ab0 feat(client_player): add name constructor param, remove chunk transition 2026-06-25 14:38:57 +08:00
df2e26e731 feat(client-chunk): add greedy meshing, receive_chunk, and biome field 2026-06-25 14:31:11 +08:00
c92e2249a4 feat(client): add client-side chunk, player, world and network classes 2026-06-24 21:49:30 +08:00
4e6f29bedd fix(gameplay): correct unknown error log condition and missing chunk pos update 2026-06-24 18:50:02 +08:00
0223fefa6b fix(network-server): stop accept loop on shutdown flag 2026-06-24 18:03:40 +08:00
8d86a71c3f fix(session): catch std::exception in read_loop and log error 2026-06-24 17:59:32 +08:00
af8812916d refactor(server): enhance thread safety and session management 2026-06-24 17:56:11 +08:00
85341651b3 feat(server): add block change packet handling and increase reserved threads 2026-06-24 17:02:21 +08:00
9d33b240c6 feat(world): add player-based chunk loading and UUID support
Restructure world generation to trigger chunk loading based on player
movement. Replace player name with UUID for identification. Implement
chunk request/response protocol. Improve thread management for gen
thread.
2026-06-24 14:51:48 +08:00
6f39ff771d refactor(packet): serialize directly into packet buffer with ByteSizeLong 2026-06-23 22:00:19 +08:00
2953e8a233 feat(protocol): add packet serialization and login handling
Introduce packet header and ID mapping for protobuf messages.
Refactor session and server_world to use new packet wrapper.
Fix missing semicolons in proto files.
2026-06-23 21:58:35 +08:00
678fc02998 refactor(proto): restructure protobuf definitions and update build system 2026-06-23 21:10:40 +08:00
4303c1cd32 feat(network): integrate protobuf for player sync and session management
Add Protobuf dependency, define proto messages for player requests, positions, and chunk data. Refactor Session to use strand and async write. Implement player join/exit and position sync in ServerWorld.
2026-06-23 20:08:40 +08:00
1acfeef9e6 feat(gameplay): add Session class and UUID generation utility 2026-06-23 15:54:01 +08:00
27e3cd6851 refactor(gameplay): split Chunk into server/client variants and add networking 2026-06-23 14:49:16 +08:00
d4a25bdf4b feat(dev_panel): add Asio to about table 2026-06-23 10:49:25 +08:00
7c07110afa build: add asio library 2026-06-23 10:42:13 +08:00
283 changed files with 7878 additions and 76656 deletions

View File

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

View File

@@ -1,161 +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 }}
env:
VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}/.vcpkg-cache,readwrite
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: Cache vcpkg
uses: actions/cache@v4
with:
path: .vcpkg-cache
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
restore-keys: |
${{ runner.os }}-vcpkg-
- 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
View File

@@ -41,7 +41,5 @@ CMakeError.log
*~
.DS_Store
assets/config.toml
assets/server-config.toml
.venv/
pyout/
vcpkg_installed/
pyout/

View File

@@ -1,9 +1,4 @@
cmake_minimum_required(VERSION 3.21)
if(APPLE)
message(FATAL_ERROR "This game does not support macOS")
endif()
cmake_minimum_required(VERSION 3.14...3.24)
project(Cubed LANGUAGES C CXX)
set(CMAKE_CXX_STANDARD 23)
@@ -11,124 +6,197 @@ 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()
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
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(PkgConfig REQUIRED)
pkg_check_modules(EGL REQUIRED egl)
pkg_check_modules(Wayland REQUIRED wayland-client wayland-egl)
find_package(glfw3 REQUIRED)
endif()
add_library(glad STATIC third_party/glad/src/glad.c)
target_include_directories(glad PUBLIC third_party/glad/include)
include(FetchContent)
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(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()
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()
FetchContent_Declare(
glm
GIT_REPOSITORY https://github.com/g-truc/glm.git
GIT_TAG 1.0.3
)
include(Dependencies)
add_subdirectory(third_party/glad)
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)
add_subdirectory(third_party/imgui)
add_subdirectory(third_party/dr_libs)
add_executable(${PROJECT_NAME})
add_subdirectory(src)
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_generator.cpp
src/gameplay/tree.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
src/gameplay/network_server.cpp
src/gameplay/server_world.cpp
src/gameplay/client_world.cpp
src/gameplay/server_chunk.cpp
src/gameplay/client_chunk.cpp
src/gameplay/server_player.cpp
src/gameplay/client_player.cpp
src/gameplay/session.cpp
src/gameplay/network_client.cpp
)
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
)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message(STATUS "Building with AddressSanitizer enabled for target: ${PROJECT_NAME}")
configure_file(
src/version.hpp.in
${CMAKE_BINARY_DIR}/generated/version.hpp
@ONLY
)
target_compile_options(${PROJECT_NAME} PRIVATE
#-fsanitize=address
#-fsanitize=thread
-fno-omit-frame-pointer
-g
)
target_compile_options(${PROJECT_NAME}
PRIVATE
$<$<CONFIG:Debug>:-fno-omit-frame-pointer>
$<$<CONFIG:Debug>:-g>
#$<$<CONFIG:Debug>:-fsanitize=address>
#$<$<CONFIG:Debug>:-fsanitize=thread>
target_link_options(${PROJECT_NAME} PRIVATE
#-fsanitize=address
#-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/>"
target_compile_definitions(${PROJECT_NAME} PRIVATE DEBUG_MODE)
target_compile_definitions(${PROJECT_NAME} PRIVATE
ASSETS_PATH="${CMAKE_SOURCE_DIR}/assets/"
)
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/"
)
endif()
target_compile_definitions(${PROJECT_NAME} PRIVATE
ASSETS_PATH="./assets/"
)
endif()
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
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_SOURCE_DIR}/include)
target_include_directories(${PROJECT_NAME} PRIVATE
${CMAKE_SOURCE_DIR}/third_party/asio/include
)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src)
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
target_compile_definitions(${PROJECT_NAME} PRIVATE
ASIO_STANDALONE
ASIO_NO_DEPRECATED
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
SDL3::SDL3
glfw
glad
glm::glm
OpenGL::GL
@@ -136,29 +204,47 @@ target_link_libraries(${PROJECT_NAME}
Freetype::Freetype
tomlplusplus::tomlplusplus
imgui
dr_libs
tbb
protobuf::libprotobuf
absl::log
absl::check
absl::base
absl::strings
absl::flat_hash_map
zstd::zstd
OpenAL::OpenAL
harfbuzz::harfbuzz
Opus::Opus
$<$<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}
)
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()
if (WIN32)
foreach(TBB_LIB IN ITEMS tbb tbbmalloc tbbmalloc_proxy)
if(TARGET ${TBB_LIB})
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:${TBB_LIB}>
$<TARGET_FILE_DIR:${PROJECT_NAME}>
COMMENT "Copying ${TBB_LIB}.dll"
)
else()
message(STATUS "Target ${TBB_LIB} not found, skipping copy")
endif()
endforeach()
endif()

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@@ -1,39 +0,0 @@
{
"menu.settings": "Settings",
"menu.main.host_game": "Host Game",
"menu.main.join_game": "Join Game",
"menu.main.credits": "Credits",
"menu.main.quit": "Quit",
"menu.main.player_name": "Player: {name}",
"menu.main.cubed_version": "Cubed: {version}",
"menu.pause.pause_menu": "Pause Menu",
"menu.pause.back_to_game": "Back To Game",
"menu.pause.return_to_main_menu": "Return to Menu",
"button.done": "Done",
"button.return": "Return",
"settings.title": "Settings",
"settings.fov": "Fov: {fov}",
"settings.mouse_sensitivity": "Sensitivity: {sensitivity}",
"settings.distance": "Distance: {distance}",
"settings.music_volume": "Music: {music}",
"settings.sfx_volume": "SFX: {sfx}",
"settings.player_voice": "Player Voice: {volume}",
"settings.fullscreen": "fullscreen: {state}",
"settings.aniso": "Aniso: {aniso}",
"settings.vsync": "V-Sync: {state}",
"settings.language": "Language: {lang}",
"settings.restart_game_info": "You need to restart the game to apply this changes.",
"settings.voice_input": "Voice Input: {type}",
"voice_input.ptt": "PTT",
"voice_input.always": "always",
"common.on": "on",
"common.off": "off",
"hostgame.create_a_new_world": "Create A New World",
"hostgame.world_seed": "World Seed",
"hostgame.port": "Port",
"hostgame.create_world": "Create World",
"joingame.join_a_world": "Join A World",
"joingame.server_ip": "Server Ip",
"joingame.join_world": "Join World",
"error.disable_voice": "Server Disable Voice Chat"
}

View File

@@ -1,39 +0,0 @@
{
"menu.settings": "设置",
"menu.main.host_game": "创建游戏",
"menu.main.join_game": "加入游戏",
"menu.main.credits": "制作人员",
"menu.main.quit": "退出",
"menu.main.player_name": "玩家:{name}",
"menu.main.cubed_version": "Cubed{version}",
"menu.pause.pause_menu": "暂停菜单",
"menu.pause.back_to_game": "返回游戏",
"menu.pause.return_to_main_menu": "返回主菜单",
"button.done": "完成",
"button.return": "返回",
"settings.title": "设置",
"settings.fov": "视野:{fov}",
"settings.mouse_sensitivity": "灵敏度:{sensitivity}",
"settings.distance": "距离:{distance}",
"settings.music_volume": "音乐:{music}",
"settings.sfx_volume": "音效:{sfx}",
"settings.player_voice": "玩家语音:{volume}",
"settings.fullscreen": "全屏:{state}",
"settings.aniso": "各向异性过滤:{aniso}",
"settings.vsync": "垂直同步: {state}",
"settings.language": "语言: {lang}",
"settings.restart_game_info": "您需要重启游戏以应用这些更改。",
"settings.voice_input": "语音输入: {type}",
"voice_input.ptt": "按键通话",
"voice_input.always": "始终",
"common.on": "开",
"common.off": "关",
"hostgame.create_a_new_world": "创建新世界",
"hostgame.world_seed": "世界种子",
"hostgame.port": "端口",
"hostgame.create_world": "创建世界",
"joingame.join_a_world": "加入世界",
"joingame.server_ip": "服务器IP",
"joingame.join_world": "加入世界",
"error.disable_voice": "服务器已禁用语音聊天"
}

View File

@@ -1,90 +0,0 @@
The SIL Open Font License version 1.1 is copied below, and is also
available with a FAQ at http://scripts.sil.org/OFL.
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@@ -1,437 +0,0 @@
GNU LIBRARY GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the library GPL. It is
numbered 2 because it goes with version 2 of the ordinary GPL.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Library General Public License, applies to some
specially designated Free Software Foundation software, and to any
other libraries whose authors decide to use it. You can use it for
your libraries, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if
you distribute copies of the library, or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link a program with the library, you must provide
complete object files to the recipients so that they can relink them
with the library, after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
Our method of protecting your rights has two steps: (1) copyright
the library, and (2) offer you this license which gives you legal
permission to copy, distribute and/or modify the library.
Also, for each distributor's protection, we want to make certain
that everyone understands that there is no warranty for this free
library. If the library is modified by someone else and passed on, we
want its recipients to know that what they have is not the original
version, so that any problems introduced by others will not reflect on
the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that companies distributing free
software will individually obtain patent licenses, thus in effect
transforming the program into proprietary software. To prevent this,
we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
Most GNU software, including some libraries, is covered by the ordinary
GNU General Public License, which was designed for utility programs. This
license, the GNU Library General Public License, applies to certain
designated libraries. This license is quite different from the ordinary
one; be sure to read it in full, and don't assume that anything in it is
the same as in the ordinary license.
The reason we have a separate public license for some libraries is that
they blur the distinction we usually make between modifying or adding to a
program and simply using it. Linking a program with a library, without
changing the library, is in some sense simply using the library, and is
analogous to running a utility program or application program. However, in
a textual and legal sense, the linked executable is a combined work, a
derivative of the original library, and the ordinary General Public License
treats it as such.
Because of this blurred distinction, using the ordinary General
Public License for libraries did not effectively promote software
sharing, because most developers did not use the libraries. We
concluded that weaker conditions might promote sharing better.
However, unrestricted linking of non-free programs would deprive the
users of those programs of all benefit from the free status of the
libraries themselves. This Library General Public License is intended to
permit developers of non-free programs to use free libraries, while
preserving your freedom as a user of such programs to change the free
libraries that are incorporated in them. (We have not seen how to achieve
this as regards changes in header files, but we have achieved it as regards
changes in the actual functions of the Library.) The hope is that this
will lead to faster development of free libraries.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, while the latter only
works together with the library.
Note that it is possible for a library to be covered by the ordinary
General Public License rather than by this special one.
GNU LIBRARY GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library which
contains a notice placed by the copyright holder or other authorized
party saying it may be distributed under the terms of this Library
General Public License (also called "this License"). Each licensee is
addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also compile or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
c) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
d) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the source code distributed need not include anything that is normally
distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Library General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS

View File

@@ -29,8 +29,254 @@ uniform bool flipY;
uniform int renderDistance;
uniform vec3 skyColor;
#include "shadow.glsl"
#include "normal.glsl"
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);
}
void main(void) {
vec4 objectColor = texture(samp, vec3(tc, tex_layer));

View File

@@ -1,7 +0,0 @@
#version 460
in vec2 tc;
void main() {
}

View File

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

View File

@@ -1,11 +0,0 @@
#version 460
in vec2 tc;
out vec4 color;
layout (binding = 0) uniform sampler2D samp;
void main(void) {
color = texture(samp, tc);
}

View File

@@ -1,14 +0,0 @@
#version 460
layout (location = 0) in vec2 pos;
layout (location = 1) in vec2 texCoord;
out vec2 tc;
uniform mat4 model_matrix;
uniform mat4 proj_matrix;
void main(void) {
gl_Position = proj_matrix * model_matrix * vec4(pos, 0.0, 1.0);
tc = texCoord;
}

View File

@@ -1,10 +0,0 @@
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);
}

View File

@@ -1,47 +1,8 @@
#version 460
in vec2 tc;
in vec3 normal;
in vec3 vert_pos;
in vec4 FragPosLightSpace;
out vec4 color;
layout (binding = 0) uniform sampler2D shadowMap;
layout (binding = 1) uniform sampler2D samp;
uniform float ambientStrength;
uniform vec3 sunlightColor;
uniform vec3 ambientColor;
uniform vec3 sunlightDir;
uniform bool shader_on;
uniform int shadowMode;
uniform float lightSizeUV;
uniform float minRadius;
uniform float maxRadius;
#include "shadow.glsl"
in vec2 tc;
void main() {
vec4 objectColor = texture(samp, tc);
if (!shader_on) {
color = objectColor;
return;
}
vec3 lightDir = normalize(-sunlightDir);
vec3 norm = normalize(normal);
vec3 ambient = ambientStrength * ambientColor;
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * sunlightColor;
float shadow = ShadowCalculation(FragPosLightSpace, norm, lightDir);
//float shadow = 0.0;
//color = vec4(vec3(shadow),1);
//color = vec4(vec3(diff),1);
color = vec4((ambient + (1.0 - shadow) * (diffuse)) * objectColor.rgb, objectColor.a);
}
color = vec4(0.0, 0.0, 1.0, 1.0);
}

View File

@@ -2,26 +2,14 @@
layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 aNormal;
layout (location = 3) in vec3 aTangent;
uniform mat4 mv_matrix;
uniform mat4 proj_matrix;
uniform mat4 norm_matrix;
uniform mat4 lightSpaceMatrix;
uniform mat4 modelMatrix;
out vec4 FragPosLightSpace;
out vec3 normal;
out vec2 tc;
out vec3 vert_pos;
out vec2 tc;
void main() {
vec4 worldPos = modelMatrix * vec4(pos, 1.0);
FragPosLightSpace = lightSpaceMatrix * worldPos;
vec4 viewPos = mv_matrix * vec4(pos, 1.0);
tc = texCoord;
vert_pos = pos;
normal = normalize(mat3(norm_matrix) * aNormal);
gl_Position = proj_matrix * viewPos;
}

View File

@@ -1,9 +0,0 @@
#version 460
out vec4 color;
uniform vec4 inColor;
void main(void) {
color = inColor;
}

View File

@@ -1,11 +0,0 @@
#version 460
layout (location = 0) in vec2 pos;
uniform mat4 model_matrix;
uniform mat4 proj_matrix;
void main(void) {
gl_Position = proj_matrix * model_matrix * vec4(pos, 0.0, 1.0);
}

View File

@@ -1,238 +0,0 @@
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;
}

View File

@@ -0,0 +1,12 @@
#version 460
in vec2 tc;
flat in int tex_layer;
out vec4 color;
layout (binding = 0) uniform sampler2DArray samp;
void main(void) {
color = texture(samp, vec3(tc, tex_layer));
}

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

View File

@@ -1,56 +0,0 @@
include(FetchContent)
# System packages
find_package(OpenGL REQUIRED)
find_package(Protobuf REQUIRED)
find_package(absl REQUIRED)
find_package(zstd REQUIRED)
find_package(OpenAL REQUIRED)
find_package(harfbuzz REQUIRED)
find_package(Freetype REQUIRED)
find_package(SDL3 REQUIRED)
find_package(Opus REQUIRED)
# 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)
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()

View File

@@ -1,25 +0,0 @@
include(FindPackageHandleStandardArgs)
find_path(OPUS_INCLUDE_DIR
NAMES opus/opus.h opus.h
)
find_library(OPUS_LIBRARY
NAMES opus
)
find_package_handle_standard_args(
Opus
REQUIRED_VARS
OPUS_INCLUDE_DIR
OPUS_LIBRARY
)
if(Opus_FOUND)
add_library(Opus::Opus UNKNOWN IMPORTED)
set_target_properties(Opus::Opus PROPERTIES
IMPORTED_LOCATION "${OPUS_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${OPUS_INCLUDE_DIR}"
)
endif()

View File

@@ -1,70 +1,72 @@
#pragma once
#include "Cubed/argument.hpp"
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp"
#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/render/renderer.hpp"
#include "Cubed/scene/scene_manager.hpp"
#include "Cubed/renderer.hpp"
#include "Cubed/texture_manager.hpp"
#include "Cubed/window.hpp"
namespace Cubed {
class App {
public:
struct Argument {
bool is_client = false;
int port = 25530;
std::string ip{"127.0.0.1"};
};
App();
~App();
void handle_mouse_move(float xpos, float ypos, float xrel, float yrel);
void handle_sdl_key(SDL_Event& e);
void handle_sdl_mouse_button(SDL_Event& e);
void handle_window_focus(bool focused);
void handle_window_resize(int new_width, int new_height);
void handle_framebuffer_resize(int new_width, int new_height);
void handle_mouse_scroll(float xoffset, float yoffset);
void handle_text_input(const char* text);
static void cursor_position_callback(GLFWwindow* window, double xpos,
double ypos);
static void key_callback(GLFWwindow* window, int key, int scancode,
int action, int mods);
static void mouse_button_callback(GLFWwindow* window, int button,
int action, int mods);
static void window_focus_callback(GLFWwindow* window, int focused);
static void window_reshape_callback(GLFWwindow* window, int new_width,
int new_height);
static void mouse_scroll_callback(GLFWwindow* window, double xoffset,
double yoffset);
static void cursor_enter_callback(GLFWwindow* window, int entered);
static void char_callback(GLFWwindow* window, unsigned int ch);
static int start_cubed_application(int argc, char** argv);
static unsigned int seed();
static float delta_time();
static float delte_time();
static float get_fps();
Camera& camera();
DevPanel& dev_panel();
Renderer& renderer();
TextureManager& texture_manager();
Window& window();
Config& config();
const Argument& argument() const;
AudioEngine& audio();
std::string get_clipboard_text();
void start_text_input();
void stop_text_input();
void update_text_input_area(const glm::vec4& textbox,
float cursor_position_x);
ClientWorld& client_world();
ServerWorld& server_world();
private:
Config m_game_config;
Window m_window;
Camera m_camera;
TextureManager m_texture_manager;
NetworkServer m_server;
std::shared_ptr<NetworkClient> m_client;
ClientWorld m_client_world;
AudioEngine m_audio;
DevPanel m_dev_panel{*this};
Renderer m_renderer{m_camera, m_client_world, m_texture_manager,
m_dev_panel};
Renderer m_renderer;
Window m_window{m_renderer};
SceneManager m_scene_manager;
inline static uint64_t last_tick = 0;
inline static uint64_t current_tick = 0;
inline static float dt = 0.0f;
inline static float fps_time_count = 0.0f;
inline static double last_time = glfwGetTime();
inline static double current_time = glfwGetTime();
inline static double delta_time = 0.0f;
inline static double fps_time_count = 0.0f;
inline static int frame_count = 0;
inline static int fps = 0;
Argument m_argument;
bool m_running = true;
bool m_opengl_init = false;
void init(int argc, char** argv);
void handle_argument(int argc, char** argv);
auto init_camera();
@@ -74,10 +76,6 @@ private:
void render();
void run();
void update();
void handle_sdl_event(SDL_Event& e);
void dispatch_event(const Event& e);
};
} // namespace Cubed

View File

@@ -1,17 +0,0 @@
#pragma once
#include <optional>
#include <string>
namespace Cubed {
struct Argument {
std::optional<int> port;
std::optional<std::string> ip;
std::optional<std::string> player;
std::optional<std::string> language;
std::optional<std::string> video_driver;
std::optional<bool> enable_exclusive;
std::optional<std::string> logs_path;
std::optional<int> log_level;
std::optional<bool> enable_filelog;
std::optional<bool> enable_consolelog;
};
} // namespace Cubed

View File

@@ -1,24 +0,0 @@
#pragma once
#include "Cubed/audio/audio_data.hpp"
#include <AL/al.h>
namespace Cubed {
class AudioBuffer {
public:
AudioBuffer(const AudioData& data);
AudioBuffer(const AudioBuffer&) = delete;
AudioBuffer(AudioBuffer&&) = delete;
AudioBuffer& operator=(const AudioBuffer&) = delete;
AudioBuffer& operator=(AudioBuffer&&) = delete;
~AudioBuffer();
ALuint buffer() const;
float duration() const;
uint32_t channels() const;
private:
ALuint m_buffer = 0;
float m_duration = 0.0f;
uint32_t m_channels = 0;
void set_data(const AudioData& data);
};
} // namespace Cubed

View File

@@ -1,10 +0,0 @@
#pragma once
#include <cstdint>
#include <vector>
namespace Cubed {
struct AudioData {
std::vector<int16_t> pcm;
uint32_t channels;
uint32_t sample_rate;
};
} // namespace Cubed

View File

@@ -1,19 +0,0 @@
#pragma once
#include <AL/al.h>
namespace Cubed {
class AudioEffect {
public:
AudioEffect();
AudioEffect(const AudioEffect&) = delete;
AudioEffect(AudioEffect&&) = delete;
AudioEffect& operator=(const AudioEffect&) = default;
AudioEffect& operator=(AudioEffect&&) = delete;
~AudioEffect();
void set_reverb(float decay_time, float reverb_gain, float gain_hf,
float density = 1.0f, float diffusion = 1.0f);
ALuint effect() const;
private:
ALuint m_effect = 0;
};
} // namespace Cubed

View File

@@ -1,18 +0,0 @@
#pragma once
#include "Cubed/audio/audio_effect.hpp"
namespace Cubed {
class AudioEffectSlot {
public:
AudioEffectSlot();
AudioEffectSlot(const AudioEffectSlot&) = delete;
AudioEffectSlot(AudioEffectSlot&&) = delete;
AudioEffectSlot& operator=(const AudioEffectSlot&) = delete;
AudioEffectSlot& operator=(AudioEffectSlot&&) = delete;
~AudioEffectSlot();
void set_effect(const AudioEffect& effect);
ALuint slot() const;
private:
ALuint m_slot = 0;
};
} // namespace Cubed

View File

@@ -1,82 +0,0 @@
#pragma once
#include "Cubed/audio/audio_fade.hpp"
#include "Cubed/audio/audio_recording.hpp"
#include "Cubed/audio/audio_source.hpp"
#include "Cubed/audio/audio_stream_source.hpp"
#include "Cubed/audio/sound_manager.hpp"
#include "Cubed/audio/source_pool.hpp"
#include "Cubed/config.hpp"
#include <AL/al.h>
#include <AL/alc.h>
#include <glm/glm.hpp>
#include <memory>
#include <opus/opus.h>
#include <string>
#include <unordered_map>
namespace Cubed {
class NetworkClient;
class AudioEngine {
public:
AudioEngine(Config& config);
AudioEngine(const AudioEngine&) = delete;
AudioEngine(AudioEngine&&) = delete;
AudioEngine& operator=(const AudioEngine&) = delete;
AudioEngine& operator=(AudioEngine&&) = delete;
~AudioEngine();
void init();
void play_bgm();
void stop_bgm();
void change_bgm(const std::string& sound);
void play_3d(const std::string& sound, const glm::vec3& pos,
bool check = false);
void play_2d(const std::string& sound, bool check = false);
void update_listener(const glm::vec3& pos, const glm::vec3& forward,
const glm::vec3& up);
void update();
void reload_config();
void underwater_change(bool underwater);
float& bgm_target_volume();
void set_client(std::weak_ptr<NetworkClient> client);
void send_voice(const std::array<int16_t, AudioRecording::FRAME_SAMPLES>&);
void receive_voice(std::span<const char> opus, const glm::vec3& pos);
AudioRecording& audio_recording();
const AudioRecording& audio_recording() const;
private:
using FadeMap = std::unordered_map<std::string, AudioFade>;
bool m_init{false};
ALCdevice* device{nullptr};
ALCcontext* context{nullptr};
OpusEncoder* m_encoder{nullptr};
OpusDecoder* m_decoder{nullptr};
AudioRecording m_recording;
std::weak_ptr<NetworkClient> m_client;
glm::vec3 listener_pos;
std::unique_ptr<AudioSource> m_bgm;
FadeMap m_fade_map;
SoundManager m_sounds;
Config& m_config;
std::shared_ptr<SourcePool> m_pool;
bool m_efx_supported = false;
bool m_underwater = false;
float m_music_volume = 1.0f;
float m_sfx_volume = 1.0f;
float m_player_voice_volume = 1.0f;
std::unique_ptr<AudioStreamSource> m_voice_source;
std::unique_ptr<AudioFilter> m_low_pass_filter;
std::unique_ptr<AudioEffect> m_underwater_effect;
std::unique_ptr<AudioEffectSlot> m_underwater_slot;
};
} // namespace Cubed

View File

@@ -1,16 +0,0 @@
#pragma once
#include "Cubed/tools/log.hpp"
#include <AL/al.h>
#include <source_location>
namespace Cubed {
inline void check_al_error(
std::source_location loaction = std::source_location::current()) {
ALenum error = alGetError();
if (error != AL_NO_ERROR) {
Logger::error("File {} Line {} Function {} OpenAL Error {} ",
loaction.file_name(), loaction.line(),
loaction.function_name(), alGetString(error));
}
}
} // namespace Cubed

View File

@@ -1,19 +0,0 @@
#pragma once
#include "Cubed/audio/audio_source.hpp"
namespace Cubed {
class AudioFade {
public:
AudioFade(AudioSource* source, float fade_in = 0.0f, float fade_out = 0.0f);
~AudioFade();
void update();
void reset();
private:
AudioSource* m_source = nullptr;
float m_in_duration = 0.0f;
float m_out_duration = 0.0f;
float m_start_gain = 0.0f;
bool m_fade_in = true;
bool m_active = true;
};
} // namespace Cubed

View File

@@ -1,19 +0,0 @@
#pragma once
#include <AL/al.h>
namespace Cubed {
class AudioFilter {
public:
AudioFilter();
AudioFilter(const AudioFilter&) = delete;
AudioFilter(AudioFilter&&) = delete;
AudioFilter& operator=(const AudioFilter&) = delete;
AudioFilter& operator=(AudioFilter&&) = delete;
~AudioFilter();
void set_lowpass(float gain, float gain_hf);
ALuint filter() const;
private:
ALuint m_filter = 0;
};
} // namespace Cubed

View File

@@ -1,17 +0,0 @@
#pragma once
#include "Cubed/audio/audio_data.hpp"
#include <filesystem>
namespace Cubed {
class AudioLoader {
public:
static AudioData load(const std::filesystem::path& path);
private:
static AudioData load_wav(const std::filesystem::path& path);
static AudioData load_mp3(const std::filesystem::path& path);
static AudioData load_flac(const std::filesystem::path& path);
static AudioData load_ogg(const std::filesystem::path& path);
};
} // namespace Cubed

View File

@@ -1,33 +0,0 @@
#pragma once
#include <alc.h>
#include <array>
#include <cstdint>
namespace Cubed {
class AudioEngine;
class AudioRecording {
public:
static constexpr int SAMPLE_RATE = 48000;
static constexpr int FRAME_MS = 20;
static constexpr int FRAME_SAMPLES = SAMPLE_RATE * FRAME_MS / 1000;
AudioRecording(AudioEngine& engine);
AudioRecording(const AudioRecording&) = delete;
AudioRecording(AudioRecording&&) = delete;
AudioRecording& operator=(const AudioRecording&) = delete;
AudioRecording& operator=(AudioRecording&&) = delete;
~AudioRecording();
void update();
void init();
void start();
void stop();
bool is_recording() const;
private:
AudioEngine& m_engine;
ALCdevice* m_capture = nullptr;
bool m_recording = false;
void send_voice(const std::array<int16_t, FRAME_SAMPLES>& pcm);
};
} // namespace Cubed

View File

@@ -1,54 +0,0 @@
#pragma once
#include "Cubed/audio/audio_buffer.hpp"
#include "Cubed/audio/audio_effect_slot.hpp"
#include "Cubed/audio/audio_filter.hpp"
#include <AL/al.h>
#include <glm/glm.hpp>
#include <memory>
namespace Cubed {
enum class AudioState { INITIAL, PLAYING, PAUSED, STOPPED };
class AudioSource {
public:
AudioSource(float volume = 1.0f);
~AudioSource();
void set_buffer_2d(const AudioBuffer& buffer);
void set_buffer_3d(const AudioBuffer& buffer, const glm::vec3& vec3);
void set_loop(bool on = true);
void set_volume(float volume);
void set_pitch(float pitch);
void play();
void play_2d(const AudioBuffer& buffer);
void play_2d(std::unique_ptr<AudioBuffer> buffer);
void play_3d(const AudioBuffer& buffer, const glm::vec3& pos);
void play_3d(std::unique_ptr<AudioBuffer> buffer, const glm::vec3& pos);
void stop();
void pause();
float duration() const;
float current_time() const;
float target_volume() const;
float& target_volume();
void set_target_volume(float volume);
AudioState state() const;
void mark_in_use();
bool in_use() const;
void reset();
void set_filter(const AudioFilter& filter);
void clear_filter();
void set_effect_slot(const AudioEffectSlot& slot);
void clear_effect_slot();
private:
std::unique_ptr<AudioBuffer> m_buffer;
ALuint m_source = 0;
float m_target_volume = 1.0f;
float m_duration = 0.0f;
bool m_using;
};
} // namespace Cubed

View File

@@ -1,46 +0,0 @@
#pragma once
#include "Cubed/audio/audio_effect_slot.hpp"
#include "Cubed/audio/audio_filter.hpp"
#include "glm/ext/vector_float3.hpp"
#include <al.h>
#include <array>
#include <cstdint>
#include <span>
#include <vector>
namespace Cubed {
class AudioStreamSource {
public:
static constexpr int NUM_BUFFERS = 4;
AudioStreamSource();
AudioStreamSource(const AudioStreamSource&) = delete;
AudioStreamSource(AudioStreamSource&&) = delete;
AudioStreamSource& operator=(const AudioStreamSource&) = delete;
AudioStreamSource& operator=(AudioStreamSource&&) = delete;
~AudioStreamSource();
void push_pcm(std::span<const int16_t> pcm, ALsizei sample_rate);
void stop();
bool is_playing() const;
void reset();
void set_volume(float volume);
void set_pitch(float pitch);
void set_pos(glm::vec3 pos);
void set_filter(const AudioFilter& filter);
void clear_filter();
void set_effect_slot(const AudioEffectSlot& slot);
void clear_effect_slot();
private:
ALuint m_source = 0;
std::array<ALuint, NUM_BUFFERS> m_buffers{};
std::vector<ALuint> m_free_buffers;
bool m_playing = false;
void unqueue_processed();
};
} // namespace Cubed

View File

@@ -1,19 +0,0 @@
#pragma once
#include "Cubed/audio/audio_buffer.hpp"
#include <string>
#include <unordered_map>
namespace Cubed {
class SoundManager {
public:
SoundManager();
~SoundManager();
const AudioBuffer& load(const std::string& name);
const AudioBuffer& get_buffer(const std::string& name);
void init();
void clear();
private:
std::unordered_map<std::string, AudioBuffer> m_buffers;
};
} // namespace Cubed

View File

@@ -1,24 +0,0 @@
#pragma once
#include "Cubed/audio/audio_source.hpp"
namespace Cubed {
class SourcePool {
private:
std::vector<std::unique_ptr<AudioSource>> m_sources;
public:
explicit SourcePool(size_t size);
~SourcePool();
SourcePool(const SourcePool&) = delete;
SourcePool(SourcePool&&) = delete;
SourcePool& operator=(const SourcePool&) = delete;
SourcePool& operator=(SourcePool&&) = delete;
void update();
[[nodiscard]]
AudioSource* acquire();
std::vector<std::unique_ptr<AudioSource>>& sources();
};
} // namespace Cubed

View File

@@ -1,7 +1,7 @@
#pragma once
#include "Cubed/input/event.hpp"
#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
@@ -12,23 +12,11 @@ class ClientPlayer;
class Camera {
private:
enum class Perspective {
FIRST_PERSON,
THIRD_PERSON_BACK,
THIRD_PERSON_FRONT,
};
bool m_firse_mouse = true;
ClientPlayer* m_player;
float m_last_mouse_x, m_last_mouse_y;
glm::vec3 m_camera_pos;
bool m_under_water = false;
Perspective m_perspective = Perspective::FIRST_PERSON;
glm::vec3 m_front;
glm::vec3 camera_collision(glm::vec3 start, glm::vec3 end,
float radius = 0.2f);
bool handle_key_event(const KeyEvent& e);
bool handle_mouse_move_event(const MouseMoveEvent& e);
public:
Camera();
@@ -37,17 +25,14 @@ public:
void camera_init(ClientPlayer* player);
void hot_reload();
void update_cursor_position_camera(float offset_x, float offset_y);
void reset_camera();
void update_cursor_position_camera(double xpos, double ypos);
const glm::mat4 get_camera_lookat() const;
const glm::vec3& get_camera_pos() const;
bool is_under_water() const;
glm::vec3 get_camera_front() const;
void change_perspective();
bool is_first_person() const;
bool handle_event(const Event& e);
ClientPlayer* player();
};
} // namespace Cubed

View File

@@ -1,89 +1,107 @@
#pragma once
#include "Cubed/tools/toml.utils.hpp"
#include "Cubed/tools/cubed_assert.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:
explicit Config(std::string_view path);
Config(const Config&) = delete;
Config(Config&&) = delete;
Config& operator=(const Config&) = delete;
Config& operator=(Config&&) = delete;
Config();
~Config();
static Config& get();
toml::table& table();
void load_config();
void load_or_create_config();
void save_to_file();
template <TOML::TomlValueType T>
T get(std::string_view key, T default_value) {
if (!m_init) {
load_config();
template <TomlValueType T> T get(std::string_view key) const {
size_t cur = 0;
auto pos = key.find('.');
const toml::table* table = &m_tbl;
while (pos != std::string_view::npos) {
std::string_view s = key.substr(cur, pos - cur);
if (s.empty()) {
Logger::error("Empty key/table name in path '{}'", key);
ASSERT(false);
std::abort();
}
cur = pos + 1;
pos = key.find('.', cur);
if (auto* next = (*table)[s].as_table()) {
table = next;
} else {
Logger::error("Can't find table {}", s);
ASSERT(false);
std::abort();
}
}
if (auto* node = find_node(m_tbl, key)) {
if (auto value = node->value<T>())
return *value;
std::string_view n_key = key.substr(cur);
if (n_key.empty()) {
Logger::error("Trailing dot in path '{}'", key);
ASSERT(false);
std::abort();
}
set(key, default_value);
return default_value;
}
template <TOML::TomlValueType T> void set(std::string_view key, T&& value) {
if (!m_init) {
load_config();
}
auto pos = key.rfind('.');
toml::table* table;
std::string_view name;
if (pos == std::string_view::npos) {
table = &m_tbl;
name = key;
auto opt = (*table)[n_key].value<T>();
if (opt) {
return *opt;
} else {
table = find_or_create_table(key.substr(0, pos));
name = key.substr(pos + 1);
Logger::error("Can't find key {}", n_key);
ASSERT(false);
std::abort();
}
// Insert node at the last level's table
table->insert_or_assign(name, std::forward<T>(value));
}
template <typename T> void set(std::string_view key, T&& val) {
if constexpr (!TomlValueType<std::decay_t<T>>) {
static_assert(false, "Type Not Support");
}
size_t cur = 0;
auto pos = key.find('.');
toml::table* table = &m_tbl;
while (pos != std::string_view::npos) {
std::string_view s = key.substr(cur, pos - cur);
if (s.empty()) {
Logger::error("Empty key/table name in path '{}'", key);
ASSERT(false);
std::abort();
}
cur = pos + 1;
pos = key.find('.', cur);
if (auto* next = (*table)[s].as_table()) {
table = next;
} else {
auto [it, inserted] = table->insert_or_assign(s, toml::table{});
table = it->second.as_table();
}
}
std::string_view n_key = key.substr(cur);
if (n_key.empty()) {
Logger::error("Trailing dot in path '{}'", key);
ASSERT(false);
std::abort();
}
table->insert_or_assign(n_key, std::forward<T>(val));
}
template <typename T> void set_and_save(std::string_view key, T&& val) {
set(key, std::forward(val));
save_to_file();
}
std::string get(std::string_view key, const char* default_value) {
return get(key, std::string(default_value));
}
void set(std::string_view key, const char* val) {
set(key, std::string(val));
}
toml::node_view<toml::node> val_view(std::string_view key);
private:
toml::table m_tbl;
bool m_init = false;
const std::string CONGIF_PATH;
const toml::node* find_node(const toml::table& root,
std::string_view path) const;
// Follow the path to find the last-level toml::table, creating it if it
// does not exist
toml::table* find_or_create_table(std::string_view path);
constexpr static inline std::string_view CONGIF_PATH =
ASSETS_PATH "config.toml";
void create_config();
};
template <>
inline float Config::get(std::string_view key, float default_value) {
return static_cast<float>(
Config::get<double>(key, static_cast<double>(default_value)));
}
template <> inline void Config::set(std::string_view key, float&& value) {
Config::set<double>(key, static_cast<double>(value));
}
} // namespace Cubed

View File

@@ -1,11 +1,7 @@
#pragma once
#include "Cubed/ui/column_layout.hpp"
#include "Cubed/ui/label.hpp"
#include "Cubed/ui/widget.hpp"
#include "Cubed/ui/text.hpp"
#include <format>
#include <memory>
#include <unordered_map>
namespace Cubed {
@@ -13,25 +9,16 @@ namespace Cubed {
class DebugCollector {
public:
static DebugCollector& get();
static void destory();
DebugCollector();
void report(const std::string& name, std::string_view content);
void init(int width, int height);
Widget& get_widget();
bool handle_event(const Event& e);
std::unordered_map<std::size_t, Text>& all_texts();
Text& text(std::string_view name);
void report(std::string_view name, std::string_view content);
void init_text();
private:
ColumnLayout m_widget;
std::unordered_map<std::string, Label*> m_component;
static std::unique_ptr<DebugCollector>& get_ptr();
std::unordered_map<std::size_t, Text> m_texts;
};
template <typename... Args>
void d_rep(const std::string& key, std::format_string<Args...> fmt,
Args&&... args) {
std::string msg = std::vformat(fmt.get(), std::make_format_args(args...));
DebugCollector::get().report(key, msg);
}
} // namespace Cubed

View File

@@ -1,14 +1,11 @@
#pragma once
#include "Cubed/config.hpp"
#include <toml++/toml.hpp>
namespace Cubed {
class WorldScene;
class ClientPlayer;
class App;
class ClientPlayer;
class DevPanel {
struct ConfigView {
float fov = 70.0f;
@@ -23,27 +20,27 @@ class DevPanel {
bool is_enable_aniso = false;
bool is_support_aniso = true;
bool is_reload = true;
float volume_music = 1.0f;
float volume_sfx = 1.0f;
};
struct PlayerProfile {
int game_mode = 0;
int gait = 0;
float pos[3] = {0.0f, 0.0f, 0.0f};
};
struct TextEditing {
bool perlin_seed = false;
};
public:
DevPanel(WorldScene& app);
DevPanel(App& app);
void init();
void render();
private:
App& m_app;
WorldScene& m_world_scene;
Config& m_config;
ConfigView m_config_view;
ConfigView m_config;
ClientPlayer* 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;
@@ -53,6 +50,7 @@ private:
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();
@@ -60,8 +58,6 @@ private:
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();

View File

@@ -1,15 +0,0 @@
#pragma once
#include "Cubed/ui/color.hpp"
#include <cstdint>
#include <string>
namespace Cubed {
struct ChatMessage {
std::string player;
std::string text;
Color color;
bool system_msg = false;
uint64_t time = 0;
};
} // namespace Cubed

View File

@@ -18,7 +18,6 @@ public:
ChunkGenerator(ServerChunk& chunk);
static void init();
static void init(unsigned seed);
static void reload();
static const unsigned& seed();
static void seed(unsigned s);

View File

@@ -51,7 +51,6 @@ public:
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
void upload_to_gpu();
GLuint get_normal_vao() const;
@@ -76,7 +75,6 @@ public:
void need_upload();
void set_chunk_block(int index, unsigned id);
BlockType get_chunk_block(int index);
bool is_temp_chunk() const;
ChunkPos chunk_pos() const;
BiomeType biome() const;
@@ -84,7 +82,6 @@ public:
std::vector<BlockType>& blocks();
ClientWorld& world();
unsigned seed() const;
const ChunkRenderSnapshot* get_render_snapshot() const;
private:
struct FaceKey {
@@ -120,9 +117,6 @@ private:
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);

View File

@@ -4,51 +4,34 @@
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/player.hpp"
#include "Cubed/input/event.hpp"
#include "Cubed/input/input.hpp"
#include "Cubed/input.hpp"
#include <absl/container/flat_hash_set.h>
#include <glm/glm.hpp>
#include <optional>
#include <shared_mutex>
namespace Cubed {
enum class Gait { WALK = 0, RUN };
class ClientWorld;
class ClientPlayer {
public:
static constexpr float WALK_SOUND_INTERVAL = 0.45f;
static constexpr float RUN_SOUND_INTERVAL = 0.3f;
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
ClientPlayer(ClientWorld& world);
~ClientPlayer();
bool handle_mouse_button_event(const MouseButtonEvent& e);
bool handle_key_event(const KeyEvent& e);
bool handle_mouse_wheel_event(const MouseWheelEvent& e);
void update_front_vec(float offset_x, float offset_y);
bool update_player_move_state(Key key, KeyAction action);
bool update_scroll(float yoffset);
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);
AABB get_aabb(const glm::vec3& pos) const;
const glm::vec3& get_front() const;
Gait get_gait() const;
const Gait& get_gait() const;
const std::optional<LookBlock>& get_look_block_pos() const;
// thread safe
glm::vec3 get_player_pos() const;
const MoveState& get_move_state() const;
void change_mode(GameMode mode);
void reload_config();
void hot_reload();
void set_player_pos(const glm::vec3& pos);
void set_place_block(unsigned id);
void update(float delta_time);
void update_front_vec(float offset_x, float offset_y);
void update_player_move_state(int key, int action);
void update_scroll(double yoffset);
float& max_walk_speed();
float& max_run_speed();
@@ -58,29 +41,18 @@ public:
float& g();
float& fly_y_speed();
unsigned get_current_block() const;
unsigned place_block() const;
void set_gait(Gait gait);
Gait& gait();
GameMode& game_mode();
ClientWorld& get_world();
const ClientWorld& get_world() const;
void set_uuid(std::string_view uuid);
std::string get_uuid() const;
const std::string& get_uuid() const;
const std::string& get_name() const;
void reset_key_status();
void init(std::string_view name);
float yaw() const;
float pitch() const;
float& angle();
float& walk_time();
bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
glm::ivec3& block_pos, glm::vec3& normal,
float distance = 4.0f);
bool is_underwater() const;
void set_underwater(bool u);
void place_block(float dt);
void init(std::string_view name);
private:
using enum GameMode;
@@ -90,10 +62,9 @@ private:
float m_deceleration = DEFAULT_DECELERATION;
float m_g = DEFAULT_G;
constexpr static float MAX_SPACE_ON_TIME = 0.3f;
constexpr static float PLACE_BLOCK_INTERVAL = 0.2f;
float m_place_time = PLACE_BLOCK_INTERVAL;
std::atomic<float> m_yaw = 0.0f;
std::atomic<float> m_pitch = 0.0f;
float m_yaw = 0.0f;
float m_pitch = 0.0f;
float m_sensitivity = 0.15f;
@@ -110,10 +81,6 @@ private:
unsigned m_place_block = 1;
bool m_moving = false;
bool m_sprinting = false;
bool m_underwater = false;
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
@@ -123,39 +90,28 @@ private:
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};
glm::vec3 m_size{0.6f, 1.8f, 0.6f};
std::atomic<Gait> m_gait = Gait::STOP;
Gait m_gait = Gait::WALK;
MoveState m_move_state{};
MouseState m_mouse_state{};
GameMode m_game_mode = CREATIVE;
std::optional<LookBlock> m_look_block = std::nullopt;
std::string m_name{};
mutable std::shared_mutex m_uuid_mutex;
std::string m_uuid;
ClientWorld& m_world;
float m_angle{0.0f};
float m_walk_time{0.0f};
std::unordered_map<std::string, Timer> m_timers;
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();
void play_walk_sound(float dt);
Gait compute_gait() const;
};
} // namespace Cubed

View File

@@ -1,69 +1,43 @@
#pragma once
#include "Cubed/audio/audio_engine.hpp"
#include "Cubed/config.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chat_message.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/input/event.hpp"
#include "Cubed/tools/cubed_random.hpp"
#include "Cubed/tools/priority_thread_pool.hpp"
#include "Cubed/tools/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 PlayerInfo {
struct OtherPlayerInfo {
std::string name;
std::string uuid;
glm::vec3 render_pos;
glm::vec3 target_pos;
float render_yaw;
float yaw;
float render_pitch;
float pitch;
Gait gait;
float angle = 0.0f;
float walk_time = 0.0f;
float moving_time = 0.0f;
glm::vec3 pos;
};
struct PlayerRenderData {
struct RenderPlayerData {
std::string name;
std::string uuid;
glm::vec3 render_pos;
float yaw;
float pitch;
Gait gait;
float angle;
glm::vec3 pos;
};
class WorldScene;
class ClientWorld {
public:
ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene);
ClientWorld();
~ClientWorld();
void init(std::string_view player_name,
std::shared_ptr<NetworkClient> client);
void update(float delta_time);
bool handle_event(const Event& e);
const std::optional<LookBlock>& get_look_block_pos() const;
const std::optional<LookBlock>&
get_look_block_pos(const std::string& name) const;
ClientPlayer& get_player();
const ClientPlayer& get_player() const;
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(std::unique_ptr<VertexBuffer>& vbo);
void push_delete_vao(std::unique_ptr<VertexArray>& vao);
void push_delete_vbo(GLuint vbo);
void push_delete_vao(GLuint vao);
// void hot_reload();
// void rebuild_world();
@@ -71,127 +45,75 @@ public:
void receive_block_change(const BlockChangeRsp& rsp);
void receive_time(const UpdateTime& rsp);
void receive_remote_player(const PlayerInfoRsp& rsp);
void receive_other_player(const PlayerInfoRsp& rsp);
void receive_player_logout(const LogoutRsp& rsp);
void receive_player_water_sound(const PlayerWaterSound& rsp);
void send_player_water_sound(bool underwater, const glm::vec3& pos);
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 receive_login_rsp(LoginRsp& rsp);
void stop_client_thread();
void start_thread_pool();
void stop_thread_pool();
void change_pool_threads(int threads);
void reload_config(bool chunk_build = true);
void hot_reload();
void request_chunk();
void reset_key_status();
std::vector<glm::vec4>& planes();
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
const std::vector<PlayerRenderData>& render_player_data() const;
std::vector<PlayerRenderData>& render_player_data();
const std::vector<ChunkRenderSnapshot>& render_snapshots() const;
const std::vector<RenderPlayerData>& render_player_data() const;
glm::vec3 sunlight_dir() const;
bool sphere_collide_world(glm::vec3 center, float radius) 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);
AudioEngine& get_audio();
const AudioEngine& get_audio() const;
Config& get_config();
WorldScene& world_scene();
void set_direct_exit();
void receive_chat_message(ChatMsg& msg);
void send_chat_message(ChatMessage& message);
void receive_voice_message(VoiceMsg& msg);
bool enable_voice_chat() const;
void receive_chunk(const ChunkDataRsp& data);
void exit();
template <typename Fn>
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
m_ticktimers.emplace(
std::piecewise_construct, std::forward_as_tuple(std::string(id)),
std::forward_as_tuple(threshold, std::forward<Fn>(f)));
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:
struct VoiceMessage {
std::string data;
glm::vec3 pos;
};
std::atomic<bool> m_is_pending_delete_queue_free{false};
std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
std::vector<std::unique_ptr<VertexBuffer>> m_pending_delete_vbo;
std::vector<std::unique_ptr<VertexArray>> m_pending_delete_vao;
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>;
tbb::concurrent_unordered_map<ChunkPos, ClientChunk, ChunkPos::Hash>;
using ChunkPosSet = std::unordered_set<ChunkPos, ChunkPos::Hash>;
using ChunkPosVector = std::vector<ChunkPos>;
using OtherPlayerHashMap = std::unordered_map<std::string, PlayerInfo>;
using chunk_acc = ChunkHashMap::accessor;
using chunk_cacc = ChunkHashMap::const_accessor;
struct PendingSound {
std::string sound;
glm::vec3 sound_pos;
};
static constexpr int WORLD_EXIT_TIMEOUT = 200;
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
using OtherPlayerHashMap = std::unordered_map<std::string, OtherPlayerInfo>;
ClientPlayer m_player;
OtherPlayerHashMap m_player_info;
OtherPlayerHashMap m_other_players;
ChunkHashMap m_chunks;
AudioEngine& m_audio;
Config& m_config;
WorldScene& m_world_scene;
std::vector<glm::vec4> m_planes;
std::jthread m_client_thread;
mutable std::shared_mutex m_player_info_mutex;
mutable std::shared_mutex m_chunks_mutex;
std::mutex m_delete_vbo_mutex;
std::mutex m_delete_vao_mutex;
std::mutex m_pending_upload_queue_mutex;
std::mutex m_other_players_mutex;
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
tbb::concurrent_queue<PendingSound> m_pending_sound;
tbb::concurrent_queue<ChatMessage> m_message_queue;
tbb::concurrent_queue<VoiceMessage> m_voice_queue;
std::deque<ClientChunk> m_pending_upload_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<PlayerRenderData> m_render_player_data;
tbb::concurrent_unordered_map<std::string, TickTimer> m_ticktimers;
std::unordered_map<std::string, Timer> m_timers;
std::atomic<bool> m_exit_direct{false};
std::vector<ChunkRenderSnapshot> m_render_snapshots;
std::vector<RenderPlayerData> 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::atomic<bool> m_voice_chat{true};
std::shared_ptr<NetworkClient> m_client;
ChunkLoadStyle m_chunk_load_style{ChunkLoadStyle::CENTER};
std::atomic<std::shared_ptr<PriorityThreadPool>> m_thread_pool;
Random m_random;
std::atomic<std::shared_ptr<ThreadPool>> m_thread_pool;
void client_run(std::stop_token token);
void report_player_info();
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

View File

@@ -6,9 +6,7 @@
#include <functional>
#include <utility>
namespace Cubed {
using TickType = long long;
using TimeType = float;
constexpr int DEFAULT_PER_TICK_TIME = 50;
@@ -16,10 +14,10 @@ constexpr TickType DAY_TIME = 24000;
constexpr TickType PER_HOUR = 1000;
class TickTimer {
class Timer {
public:
template <typename Fn>
TickTimer(TickType threshold, Fn&& f)
Timer(TickType threshold, Fn&& f)
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
}
@@ -39,37 +37,4 @@ private:
TickType m_current = 0;
};
class Timer {
public:
template <typename Fn>
Timer(TimeType threshold, Fn&& f)
: m_fn(std::forward<Fn>(f)), m_threshold(threshold) {
ASSERT_MSG(threshold > 0, "Threshold Must Rreater Than 0");
}
bool update(TimeType dt) {
m_current += dt;
bool triggered = false;
while (m_current >= m_threshold) {
m_current -= m_threshold;
m_fn();
triggered = true;
}
return triggered;
}
void reset() { m_current = 0.0f; }
void set_threshold(TimeType threshold) {
ASSERT_MSG(threshold > 0, "Threshold must be greater than 0");
m_threshold = threshold;
}
private:
std::function<void()> m_fn;
TimeType m_threshold;
TimeType m_current = 0;
};
} // namespace Cubed

View File

@@ -3,7 +3,6 @@
#include "Cubed/gameplay/packet.hpp"
#include <asio.hpp>
#include <queue>
#include <string>
#include <thread>
namespace Cubed {
@@ -15,57 +14,29 @@ public:
~NetworkClient();
void close();
void stop();
void send(Packet packet, int priority = 10);
void send(Packet packet);
void start(std::string ip, int port = 25530);
bool is_connected() const;
bool is_connect_error() const;
std::string get_error_string() const;
void clear_error();
ClientWorld& world();
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;
std::deque<Packet> 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};
mutable std::mutex m_error_string_mutex;
std::string m_error_string;
// 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();
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;
void do_write();
void set_error(std::string_view error);
};
} // namespace Cubed

View File

@@ -1,5 +1,4 @@
#pragma once
#include "Cubed/config.hpp"
#include "Cubed/gameplay/server_world.hpp"
#include "Cubed/gameplay/session.hpp"
@@ -9,19 +8,17 @@ namespace Cubed {
class NetworkServer {
public:
explicit NetworkServer();
NetworkServer(int port = 25530);
~NetworkServer();
void stop();
// Run in another thread after initialization is complete
void start_server(int port);
void start_server();
void start_server(int port = 25530);
int port() const;
ServerWorld& server_world();
private:
Config m_config;
asio::io_context m_io;
std::thread m_net_thread;
int m_port = 25530;

View File

@@ -6,14 +6,7 @@
#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>
@@ -30,10 +23,11 @@ enum class CompressType : uint16_t {
inline CompressType get_compress_type(uint16_t id) {
using enum CompressType;
constexpr auto& to = std::to_underlying<CompressType>;
switch (id) {
case std::to_underlying(NONE):
case to(NONE):
return NONE;
case std::to_underlying(ZSTD):
case to(ZSTD):
return ZSTD;
}
throw std::runtime_error(std::format("Unknown CompressType {}", id));
@@ -52,82 +46,56 @@ enum class PacketEnum : uint16_t {
LOGOUT_REQ = 1003,
LOGOUT_RSP = 1004,
PLAYER_INFO = 2001,
C2S_PLAYER_INFO = 2002,
PLAYER_POS = 2002,
PLAYER_INFO_RSP = 2003,
PLAYER_WATER_SOUND = 2004,
CHUNK_DATA_REQ = 3001,
CHUNK_DATA_RSP = 3002,
BLOCK_CHANGE_REQ = 3003,
BLOCK_CHANGE_RSP = 3004,
S2C_CLEAR_ALL_CHUNKS = 3005,
UPDATE_TIME = 3006,
CHAT_MSG = 4001,
VOICE_MSG = 4002,
UPDATE_TIME = 3005,
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;
}
using enum PacketEnum;
using std::is_same_v;
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<C2S_PlayerInfo>() {
return std::to_underlying(PacketEnum::C2S_PLAYER_INFO);
}
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 <> constexpr uint16_t get_packet_id<PlayerWaterSound>() {
return std::to_underlying(PacketEnum::PLAYER_WATER_SOUND);
}
template <> constexpr uint16_t get_packet_id<ChatMsg>() {
return std::to_underlying(PacketEnum::CHAT_MSG);
}
template <> constexpr uint16_t get_packet_id<VoiceMsg>() {
return std::to_underlying(PacketEnum::VOICE_MSG);
using U = std::decay_t<T>;
constexpr auto& to_num = std::to_underlying<PacketEnum>;
if constexpr (is_same_v<U, LoginReq>) {
return to_num(LOGIN_REQ);
} else if constexpr (is_same_v<U, LoginRsp>) {
return to_num(LOGIN_RSP);
} else if constexpr (is_same_v<U, LogoutReq>) {
return to_num(LOGOUT_REQ);
} else if constexpr (is_same_v<U, LogoutRsp>) {
return to_num(LOGOUT_RSP);
} else if constexpr (is_same_v<U, PlayerInfo>) {
return to_num(PLAYER_INFO);
} else if constexpr (is_same_v<U, PlayerPos>) {
return to_num(PLAYER_POS);
} else if constexpr (is_same_v<U, PlayerInfoRsp>) {
return to_num(PLAYER_INFO_RSP);
} else if constexpr (is_same_v<U, ChunkDataReq>) {
return to_num(CHUNK_DATA_REQ);
} else if constexpr (is_same_v<U, ChunkDataRsp>) {
return to_num(CHUNK_DATA_RSP);
} else if constexpr (is_same_v<U, BlockChangeReq>) {
return to_num(BLOCK_CHANGE_REQ);
} else if constexpr (is_same_v<U, BlockChangeRsp>) {
return to_num(BLOCK_CHANGE_RSP);
} else if constexpr (is_same_v<U, UpdateTime>) {
return to_num(UPDATE_TIME);
} else if constexpr (is_same_v<U, Ping>) {
return to_num(PING);
} else if constexpr (is_same_v<U, Pong>) {
return to_num(PONG);
} else {
static_assert(always_false<U>::value, "Unknown Type");
}
}
template <typename T>

View File

@@ -1,21 +0,0 @@
#pragma once
#include <stdexcept>
#include <utility>
namespace Cubed {
enum class Gait { STOP = 0, WALK = 1, RUN = 2 };
constexpr int get_gait_id(Gait gait) { return std::to_underlying(gait); }
inline Gait get_gait_from_id(int id) {
switch (id) {
case std::to_underlying(Gait::STOP):
return Gait::STOP;
case std::to_underlying(Gait::WALK):
return Gait::WALK;
case std::to_underlying(Gait::RUN):
return Gait::RUN;
default:
throw std::runtime_error("Unknown Gait");
}
}
} // namespace Cubed

View File

@@ -0,0 +1,171 @@
#pragma once
#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 <atomic>
#include <mutex>
namespace Cubed {
struct ChunkInfo {
ChunkPos pos{0, 0};
unsigned seed{0};
BiomeType biome{BiomeType::NONE};
unsigned first_random{0};
bool has_cave_start{false};
bool has_cave{false};
};
class World;
// if want to use, do init_chunk(), gen_vertex_data() and
class Chunk {
private:
using OptionalBlockVectorArray =
std::array<std::optional<std::vector<BlockType>>, 4>;
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 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<bool> m_gening{false};
std::atomic<bool> m_temp_chunk{false};
bool m_has_cave{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
4 - water
*/
std::vector<VertexData> m_vertex_data;
float frequency = 0.01f;
float height = 80;
unsigned m_seed = 0;
BiomeConditions m_conditions;
ChunkInfo m_info;
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);
public:
Chunk(World& world, ChunkPos chunk_pos, bool temp_chunk = false);
~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);
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);
BiomeType get_biome() const;
ChunkPos get_chunk_pos() const;
const std::vector<BlockType>& get_chunk_blocks() const;
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 OptionalBlockVectorArray& neighbor_block);
void upload_to_gpu();
GLuint get_normal_vao() const;
size_t get_normal_vertices_sum() const;
GLuint get_cross_vao() const;
size_t get_cross_vertices_sum() const;
GLuint get_normal_discard_vao() const;
size_t get_normal_discard_vertices_sum() const;
GLuint get_normal_blend_vao() const;
size_t get_normal_blend_vertices_sum() const;
GLuint get_water_vao() const;
size_t get_water_vertices_sum() const;
bool is_dirty() const;
void mark_dirty();
bool is_need_upload() const;
void need_upload();
void set_chunk_block(int index, unsigned id);
// ensure thread safe!
void gen_chunk();
bool is_temp_chunk() const;
ChunkPos chunk_pos() const;
BiomeType biome() const;
void biome(BiomeType b);
HeightMapArray& heightmap();
std::vector<BlockType>& blocks();
World& world();
unsigned seed() const;
BiomeConditions& conditions();
ChunkInfo get_info() const;
bool& has_cave();
};
} // namespace Cubed

View File

@@ -0,0 +1,112 @@
#pragma once
#include "Cubed/AABB.hpp"
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/input.hpp"
#include <glm/glm.hpp>
#include <optional>
#include <string>
namespace Cubed {
enum class Gait { WALK = 0, RUN };
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;
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_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:
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;
const glm::vec3& get_player_pos() const;
const MoveState& get_move_state() const;
void change_mode(GameMode mode);
void hot_reload();
void set_player_pos(const glm::vec3& pos);
void set_place_block(unsigned id);
void update(float delta_time);
void update_front_vec(float offset_x, float offset_y);
void update_player_move_state(int key, int action);
void update_scroll(double yoffset);
float& max_walk_speed();
float& max_run_speed();
float& max_speed();
float& acceleration();
float& deceleration();
float& g();
float& fly_y_speed();
unsigned place_block() const;
Gait& gait();
GameMode& game_mode();
const World& get_world() const;
};
} // namespace Cubed

View File

@@ -0,0 +1,173 @@
#pragma once
#include "Cubed/AABB.hpp"
#include "Cubed/gameplay/cave_carver.hpp"
#include "Cubed/gameplay/chunk.hpp"
#include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/river_worm.hpp"
#include "Cubed/tools/thread_pool.hpp"
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
#include <shared_mutex>
#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;
GLuint water_vao;
size_t water_vertices_count;
glm::vec3 center;
glm::vec3 half_extents;
};
class Player;
class TextureManager;
class World {
private:
enum class ChunkLoadStyle { RANDOM, CENTER };
struct PendingChunk {
Chunk chunk;
std::future<void> future;
};
using OptionalBlockVectorArray =
std::array<std::optional<std::vector<BlockType>>, 4>;
using ChunkPtrUpdateList = std::vector<std::pair<ChunkPos, Chunk*>>;
using ChunkPairVector = std::vector<std::pair<ChunkPos, Chunk>>;
using ChunkPairQueue = std::queue<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>;
using PendingChunkHashMap =
std::unordered_map<ChunkPos, PendingChunk, 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;
std::thread m_server_thread;
std::atomic<std::shared_ptr<ThreadPool>> m_gen_thread_pool;
std::stop_source m_server_stop_source;
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms
std::atomic<TickType> m_day_tick = 6000;
mutable std::shared_mutex m_chunks_mutex;
std::mutex m_gen_signal_mutex;
std::mutex m_new_chunk_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<bool> m_tick_running{true};
std::atomic<int> m_rendering_distance{24};
std::atomic<int> m_pool_threads{0};
std::atomic<int> m_max_threads{1};
std::atomic<TickType> m_game_ticks{0};
std::atomic<ChunkLoadStyle> m_chunk_load_style{ChunkLoadStyle::RANDOM};
std::vector<ChunkPos> m_dirty_queue;
std::vector<ChunkRenderSnapshot> m_render_snapshots;
std::vector<std::pair<ChunkPos, Chunk>> m_new_finished_chunk;
// Can only be used in the gen thread
PendingChunkHashMap new_chunks;
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);
void sync_and_collect_missing_chunks(std::vector<ChunkPos>&,
const ChunkPosSet&);
void submit_new_chunks();
void poll_finished_chunks();
void wait_all_chunk_tasks();
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 get_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();
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();
std::vector<glm::vec4>& planes();
std::vector<ChunkRenderSnapshot>& render_snapshots();
glm::vec3 sunlight_dir() const;
TickType game_tick() const;
TickType day_tick() const;
void day_tick(TickType tick);
int per_tick_time() const;
void per_tick_time(int ms);
bool is_tick_running() const;
void tick_running(bool run);
int pool_threads() const;
int max_threads() const;
void change_pool_threads(int threads);
int chunk_load_style() const;
void set_chunk_load_style(int id);
ChunkInfo get_chunk_info(const glm::vec3& world_pos) const;
};
} // namespace Cubed

View File

@@ -93,5 +93,4 @@ private:
// Generate biome-specific vegetation/structures
void gen_phase_five();
};
} // namespace Cubed

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