Merge branch 'headless-mode' into Fix-usage-vcpkg-for-windows-and-add-cmake-preset-for-vscode
This commit is contained in:
commit
3b96c13857
4
.github/workflows/appimage.yml
vendored
4
.github/workflows/appimage.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ "main", "release-**"]
|
branches: [ "main", "release-**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "headless-mode" ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-appimage:
|
build-appimage:
|
||||||
@ -24,7 +24,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \
|
sudo apt-get install -y build-essential libglfw3-dev libglfw3 libglew-dev \
|
||||||
libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools tree
|
libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev libgtest-dev cmake squashfs-tools
|
||||||
# fix luajit paths
|
# fix luajit paths
|
||||||
sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a
|
sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a
|
||||||
sudo ln -s /usr/include/luajit-2.1 /usr/include/lua
|
sudo ln -s /usr/include/luajit-2.1 /usr/include/lua
|
||||||
|
|||||||
2
.github/workflows/macos.yml
vendored
2
.github/workflows/macos.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ "main", "release-**"]
|
branches: [ "main", "release-**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "headless-mode" ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-dmg:
|
build-dmg:
|
||||||
|
|||||||
4
.github/workflows/windows-clang.yml
vendored
4
.github/workflows/windows-clang.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ "main", "release-**"]
|
branches: [ "main", "release-**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "headless-mode" ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
@ -31,7 +31,7 @@ jobs:
|
|||||||
mingw-w64-clang-x86_64-cmake
|
mingw-w64-clang-x86_64-cmake
|
||||||
mingw-w64-clang-x86_64-make
|
mingw-w64-clang-x86_64-make
|
||||||
mingw-w64-clang-x86_64-luajit
|
mingw-w64-clang-x86_64-luajit
|
||||||
git tree
|
git
|
||||||
- name: Set up vcpkg
|
- name: Set up vcpkg
|
||||||
shell: msys2 {0}
|
shell: msys2 {0}
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [ "main", "release-**"]
|
branches: [ "main", "release-**"]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main" ]
|
branches: [ "main", "headless-mode" ]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-windows:
|
build-windows:
|
||||||
|
|||||||
27
dev/tests/chunks.lua
Normal file
27
dev/tests/chunks.lua
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
test.set_setting("chunks.load-distance", 3)
|
||||||
|
test.set_setting("chunks.load-speed", 1)
|
||||||
|
|
||||||
|
test.reconfig_packs({"base"}, {})
|
||||||
|
test.new_world("demo", "2019", "core:default")
|
||||||
|
|
||||||
|
local pid1 = player.create("Xerxes")
|
||||||
|
assert(player.get_name(pid1) == "Xerxes")
|
||||||
|
|
||||||
|
local pid2 = player.create("Segfault")
|
||||||
|
assert(player.get_name(pid2) == "Segfault")
|
||||||
|
|
||||||
|
local seed = math.floor(math.random() * 1e6)
|
||||||
|
print("random seed", seed)
|
||||||
|
math.randomseed(seed)
|
||||||
|
|
||||||
|
for i=1,25 do
|
||||||
|
if i % 5 == 0 then
|
||||||
|
print(tostring(i*4).." % done")
|
||||||
|
print("chunks loaded", world.count_chunks())
|
||||||
|
end
|
||||||
|
player.set_pos(pid1, math.random() * 100 - 50, 100, math.random() * 100 - 50)
|
||||||
|
player.set_pos(pid2, math.random() * 200 - 100, 100, math.random() * 200 - 100)
|
||||||
|
test.tick()
|
||||||
|
end
|
||||||
|
|
||||||
|
test.close_world(true)
|
||||||
@ -1,19 +0,0 @@
|
|||||||
test.set_setting("chunks.load-distance", 3)
|
|
||||||
test.set_setting("chunks.load-speed", 16)
|
|
||||||
|
|
||||||
test.reconfig_packs({"base"}, {})
|
|
||||||
test.new_world("demo", "2019", "core:default")
|
|
||||||
local pid = player.create("Xerxes")
|
|
||||||
assert(player.get_name(pid) == "Xerxes")
|
|
||||||
test.sleep_until(function() return world.count_chunks() >= 9 end, 1000)
|
|
||||||
print(world.count_chunks())
|
|
||||||
|
|
||||||
for i=1,3 do
|
|
||||||
print("---")
|
|
||||||
timeit(1000000, block.get, 0, 0, 0)
|
|
||||||
timeit(1000000, block.get_slow, 0, 0, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
block.destruct(0, 0, 0, pid)
|
|
||||||
assert(block.get(0, 0, 0) == 0)
|
|
||||||
test.close_world(true)
|
|
||||||
@ -60,7 +60,7 @@ Library **audio** contains available Audio API in Lua scripts.
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream(
|
audio.play_stream(
|
||||||
-- audio file location
|
-- audio file location (without entry point, but with extension included)
|
||||||
name: string,
|
name: string,
|
||||||
-- audio source world position
|
-- audio source world position
|
||||||
x: number, y: number, z: number,
|
x: number, y: number, z: number,
|
||||||
@ -79,7 +79,7 @@ Plays streaming audio from the specified file at the specified world position. R
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream_2d(
|
audio.play_stream_2d(
|
||||||
-- audio file location
|
-- audio file location (without entry point, but with extension included)
|
||||||
name: string,
|
name: string,
|
||||||
-- audio gain (0.0 - 1.0)
|
-- audio gain (0.0 - 1.0)
|
||||||
volume: number
|
volume: number
|
||||||
|
|||||||
@ -52,6 +52,7 @@ The main properties described in the configuration file:
|
|||||||
- **biomes-bpd** - number of blocks per point of the biome selection parameter map. Default: 4.
|
- **biomes-bpd** - number of blocks per point of the biome selection parameter map. Default: 4.
|
||||||
- **heights-bpd** - number of blocks per point of the height map. Default: 4.
|
- **heights-bpd** - number of blocks per point of the height map. Default: 4.
|
||||||
- **wide-structs-chunks-radius** - maximum radius for placing 'wide' structures, measured in chunks.
|
- **wide-structs-chunks-radius** - maximum radius for placing 'wide' structures, measured in chunks.
|
||||||
|
- **heightmap-inputs** - an array of parameter map numbers that will be passed by the inputs table to the height map generation function.
|
||||||
|
|
||||||
## Global variables
|
## Global variables
|
||||||
|
|
||||||
|
|||||||
@ -61,7 +61,7 @@
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream(
|
audio.play_stream(
|
||||||
-- путь к аудио-файлу
|
-- путь к аудио-файлу (без точки входа, но с указанием расширения)
|
||||||
name: string,
|
name: string,
|
||||||
-- позиция источника аудио в мире
|
-- позиция источника аудио в мире
|
||||||
x: number, y: number, z: number,
|
x: number, y: number, z: number,
|
||||||
@ -80,7 +80,7 @@ audio.play_stream(
|
|||||||
|
|
||||||
```lua
|
```lua
|
||||||
audio.play_stream_2d(
|
audio.play_stream_2d(
|
||||||
-- путь к аудио-файлу
|
-- путь к аудио-файлу (без точки входа, но с указанием расширения)
|
||||||
name: string,
|
name: string,
|
||||||
-- громкость аудио (от 0.0 до 1.0)
|
-- громкость аудио (от 0.0 до 1.0)
|
||||||
volume: number
|
volume: number
|
||||||
|
|||||||
@ -52,6 +52,7 @@
|
|||||||
- **biomes-bpd** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 4.
|
- **biomes-bpd** - количество блоков на точку карты параметра выбора биомов. По-умолчанию: 4.
|
||||||
- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 4.
|
- **heights-bpd** - количество блоков на точку карты высот. По-умолчанию: 4.
|
||||||
- **wide-structs-chunks-radius** - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
|
- **wide-structs-chunks-radius** - масимальный радиус размещения 'широких' структур, измеряемый в чанках.
|
||||||
|
- **heightmap-inputs** - массив номеров карт параметров, которые будут переданы таблицей inputs в функцию генерации карты высот.
|
||||||
|
|
||||||
## Глобальные переменные
|
## Глобальные переменные
|
||||||
|
|
||||||
|
|||||||
@ -9,16 +9,42 @@ function sleep(timesec)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function tb_frame_tostring(frame)
|
||||||
|
local s = frame.short_src
|
||||||
|
if frame.what ~= "C" then
|
||||||
|
s = s .. ":" .. tostring(frame.currentline)
|
||||||
|
end
|
||||||
|
if frame.what == "main" then
|
||||||
|
s = s .. ": in main chunk"
|
||||||
|
elseif frame.name then
|
||||||
|
s = s .. ": in function " .. utf8.escape(frame.name)
|
||||||
|
end
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
if test then
|
if test then
|
||||||
test.sleep = sleep
|
test.sleep = sleep
|
||||||
test.name = __VC_TEST_NAME
|
test.name = __VC_TEST_NAME
|
||||||
test.new_world = core.new_world
|
test.new_world = core.new_world
|
||||||
test.open_world = core.open_world
|
test.open_world = core.open_world
|
||||||
test.close_world = core.close_world
|
test.close_world = core.close_world
|
||||||
|
test.reopen_world = core.reopen_world
|
||||||
|
test.delete_world = core.delete_world
|
||||||
test.reconfig_packs = core.reconfig_packs
|
test.reconfig_packs = core.reconfig_packs
|
||||||
test.set_setting = core.set_setting
|
test.set_setting = core.set_setting
|
||||||
test.tick = coroutine.yield
|
test.tick = coroutine.yield
|
||||||
|
|
||||||
|
function test.quit()
|
||||||
|
local tb = debug.get_traceback(1)
|
||||||
|
local s = "test.quit() traceback:"
|
||||||
|
for i, frame in ipairs(tb) do
|
||||||
|
s = s .. "\n\t"..tb_frame_tostring(frame)
|
||||||
|
end
|
||||||
|
debug.log(s)
|
||||||
|
core.quit()
|
||||||
|
coroutine.yield()
|
||||||
|
end
|
||||||
|
|
||||||
function test.sleep_until(predicate, max_ticks)
|
function test.sleep_until(predicate, max_ticks)
|
||||||
max_ticks = max_ticks or 1e9
|
max_ticks = max_ticks or 1e9
|
||||||
local ticks = 0
|
local ticks = 0
|
||||||
@ -382,7 +408,9 @@ end
|
|||||||
function __vc_stop_coroutine(id)
|
function __vc_stop_coroutine(id)
|
||||||
local co = __vc_coroutines[id]
|
local co = __vc_coroutines[id]
|
||||||
if co then
|
if co then
|
||||||
|
if coroutine.close then
|
||||||
coroutine.close(co)
|
coroutine.close(co)
|
||||||
|
end
|
||||||
__vc_coroutines[id] = nil
|
__vc_coroutines[id] = nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -74,7 +74,7 @@ function table.deep_copy(t)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return copied
|
return setmetatable(copied, getmetatable(t))
|
||||||
end
|
end
|
||||||
|
|
||||||
function table.count_pairs(t)
|
function table.count_pairs(t)
|
||||||
|
|||||||
@ -20,14 +20,14 @@ void Mainloop::run() {
|
|||||||
// destroy LevelScreen and run quit callbacks
|
// destroy LevelScreen and run quit callbacks
|
||||||
engine.setScreen(nullptr);
|
engine.setScreen(nullptr);
|
||||||
// create and go to menu screen
|
// create and go to menu screen
|
||||||
engine.setScreen(std::make_shared<MenuScreen>(&engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
} else {
|
} else {
|
||||||
engine.setScreen(std::make_shared<LevelScreen>(&engine, std::move(level)));
|
engine.setScreen(std::make_shared<LevelScreen>(engine, std::move(level)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
logger.info() << "starting menu screen";
|
logger.info() << "starting menu screen";
|
||||||
engine.setScreen(std::make_shared<MenuScreen>(&engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
|
|
||||||
logger.info() << "main loop started";
|
logger.info() << "main loop started";
|
||||||
while (!Window::isShouldClose()){
|
while (!Window::isShouldClose()){
|
||||||
|
|||||||
85
src/ServerMainloop.cpp
Normal file
85
src/ServerMainloop.cpp
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#include "ServerMainloop.hpp"
|
||||||
|
|
||||||
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
#include "logic/LevelController.hpp"
|
||||||
|
#include "interfaces/Process.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
#include "world/Level.hpp"
|
||||||
|
#include "world/World.hpp"
|
||||||
|
#include "util/platform.hpp"
|
||||||
|
#include "engine.hpp"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
static debug::Logger logger("mainloop");
|
||||||
|
|
||||||
|
inline constexpr int TPS = 20;
|
||||||
|
|
||||||
|
ServerMainloop::ServerMainloop(Engine& engine) : engine(engine) {
|
||||||
|
}
|
||||||
|
|
||||||
|
ServerMainloop::~ServerMainloop() = default;
|
||||||
|
|
||||||
|
void ServerMainloop::run() {
|
||||||
|
const auto& coreParams = engine.getCoreParameters();
|
||||||
|
auto& time = engine.getTime();
|
||||||
|
|
||||||
|
if (coreParams.scriptFile.empty()) {
|
||||||
|
logger.info() << "nothing to do";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
engine.setLevelConsumer([this](auto level) {
|
||||||
|
setLevel(std::move(level));
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info() << "starting test " << coreParams.scriptFile;
|
||||||
|
auto process = scripting::start_coroutine(coreParams.scriptFile);
|
||||||
|
|
||||||
|
double targetDelta = 1.0 / static_cast<double>(TPS);
|
||||||
|
double delta = targetDelta;
|
||||||
|
auto begin = system_clock::now();
|
||||||
|
auto startupTime = begin;
|
||||||
|
|
||||||
|
while (process->isActive()) {
|
||||||
|
if (engine.isQuitSignal()) {
|
||||||
|
process->terminate();
|
||||||
|
logger.info() << "script has been terminated due to quit signal";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (coreParams.testMode) {
|
||||||
|
time.step(delta);
|
||||||
|
} else {
|
||||||
|
auto now = system_clock::now();
|
||||||
|
time.update(
|
||||||
|
duration_cast<microseconds>(now - startupTime).count() / 1e6);
|
||||||
|
delta = time.getDelta();
|
||||||
|
}
|
||||||
|
process->update();
|
||||||
|
if (controller) {
|
||||||
|
controller->getLevel()->getWorld()->updateTimers(delta);
|
||||||
|
controller->update(glm::min(delta, 0.2), false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!coreParams.testMode) {
|
||||||
|
auto end = system_clock::now();
|
||||||
|
platform::sleep(targetDelta * 1000 -
|
||||||
|
duration_cast<microseconds>(end - begin).count() / 1000);
|
||||||
|
begin = system_clock::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info() << "test finished";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ServerMainloop::setLevel(std::unique_ptr<Level> level) {
|
||||||
|
if (level == nullptr) {
|
||||||
|
controller->onWorldQuit();
|
||||||
|
engine.getPaths().setCurrentWorldFolder(fs::path());
|
||||||
|
controller = nullptr;
|
||||||
|
} else {
|
||||||
|
controller = std::make_unique<LevelController>(
|
||||||
|
&engine, std::move(level), nullptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,12 +6,12 @@ class Level;
|
|||||||
class LevelController;
|
class LevelController;
|
||||||
class Engine;
|
class Engine;
|
||||||
|
|
||||||
class TestMainloop {
|
class ServerMainloop {
|
||||||
Engine& engine;
|
Engine& engine;
|
||||||
std::unique_ptr<LevelController> controller;
|
std::unique_ptr<LevelController> controller;
|
||||||
public:
|
public:
|
||||||
TestMainloop(Engine& engine);
|
ServerMainloop(Engine& engine);
|
||||||
~TestMainloop();
|
~ServerMainloop();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
#include "TestMainloop.hpp"
|
|
||||||
|
|
||||||
#include "logic/scripting/scripting.hpp"
|
|
||||||
#include "logic/LevelController.hpp"
|
|
||||||
#include "interfaces/Process.hpp"
|
|
||||||
#include "debug/Logger.hpp"
|
|
||||||
#include "world/Level.hpp"
|
|
||||||
#include "world/World.hpp"
|
|
||||||
#include "engine.hpp"
|
|
||||||
|
|
||||||
static debug::Logger logger("mainloop");
|
|
||||||
|
|
||||||
inline constexpr int TPS = 20;
|
|
||||||
|
|
||||||
TestMainloop::TestMainloop(Engine& engine) : engine(engine) {
|
|
||||||
}
|
|
||||||
|
|
||||||
TestMainloop::~TestMainloop() = default;
|
|
||||||
|
|
||||||
void TestMainloop::run() {
|
|
||||||
const auto& coreParams = engine.getCoreParameters();
|
|
||||||
auto& time = engine.getTime();
|
|
||||||
|
|
||||||
if (coreParams.testFile.empty()) {
|
|
||||||
logger.info() << "nothing to do";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
engine.setLevelConsumer([this](auto level) {
|
|
||||||
setLevel(std::move(level));
|
|
||||||
});
|
|
||||||
|
|
||||||
logger.info() << "starting test " << coreParams.testFile;
|
|
||||||
auto process = scripting::start_coroutine(coreParams.testFile);
|
|
||||||
while (process->isActive()) {
|
|
||||||
time.step(1.0f / static_cast<float>(TPS));
|
|
||||||
process->update();
|
|
||||||
if (controller) {
|
|
||||||
float delta = time.getDelta();
|
|
||||||
controller->getLevel()->getWorld()->updateTimers(delta);
|
|
||||||
controller->update(glm::min(delta, 0.2f), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.info() << "test finished";
|
|
||||||
}
|
|
||||||
|
|
||||||
void TestMainloop::setLevel(std::unique_ptr<Level> level) {
|
|
||||||
if (level == nullptr) {
|
|
||||||
controller->onWorldQuit();
|
|
||||||
engine.getPaths()->setCurrentWorldFolder(fs::path());
|
|
||||||
controller = nullptr;
|
|
||||||
} else {
|
|
||||||
controller = std::make_unique<LevelController>(&engine, std::move(level));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -35,6 +35,11 @@ inline constexpr int CHUNK_D = 16;
|
|||||||
inline constexpr uint VOXEL_USER_BITS = 8;
|
inline constexpr uint VOXEL_USER_BITS = 8;
|
||||||
inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS;
|
inline constexpr uint VOXEL_USER_BITS_OFFSET = sizeof(blockstate_t)*8-VOXEL_USER_BITS;
|
||||||
|
|
||||||
|
/// @brief % unordered map max average buckets load factor.
|
||||||
|
/// Low value gives significant performance impact by minimizing collisions and
|
||||||
|
/// lookup latency. Default value (1.0) shows x2 slower work.
|
||||||
|
inline constexpr float CHUNKS_MAP_MAX_LOAD_FACTOR = 0.1f;
|
||||||
|
|
||||||
/// @brief chunk volume (count of voxels per Chunk)
|
/// @brief chunk volume (count of voxels per Chunk)
|
||||||
inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D);
|
inline constexpr int CHUNK_VOL = (CHUNK_W * CHUNK_H * CHUNK_D);
|
||||||
|
|
||||||
|
|||||||
@ -220,11 +220,11 @@ void ContentLoader::loadBlock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// block model
|
// block model
|
||||||
std::string modelTypeName;
|
std::string modelTypeName = to_string(def.model);
|
||||||
root.at("model").get(modelTypeName);
|
root.at("model").get(modelTypeName);
|
||||||
root.at("model-name").get(def.modelName);
|
root.at("model-name").get(def.modelName);
|
||||||
if (auto model = BlockModel_from(modelTypeName)) {
|
if (auto model = BlockModel_from(modelTypeName)) {
|
||||||
if (*model == BlockModel::custom) {
|
if (*model == BlockModel::custom && def.customModelRaw == nullptr) {
|
||||||
if (root.has("model-primitives")) {
|
if (root.has("model-primitives")) {
|
||||||
def.customModelRaw = root["model-primitives"];
|
def.customModelRaw = root["model-primitives"];
|
||||||
} else if (def.modelName.empty()) {
|
} else if (def.modelName.empty()) {
|
||||||
@ -246,7 +246,7 @@ void ContentLoader::loadBlock(
|
|||||||
root.at("material").get(def.material);
|
root.at("material").get(def.material);
|
||||||
|
|
||||||
// rotation profile
|
// rotation profile
|
||||||
std::string profile = "none";
|
std::string profile = def.rotations.name;
|
||||||
root.at("rotation").get(profile);
|
root.at("rotation").get(profile);
|
||||||
|
|
||||||
def.rotatable = profile != "none";
|
def.rotatable = profile != "none";
|
||||||
@ -285,8 +285,6 @@ void ContentLoader::loadBlock(
|
|||||||
);
|
);
|
||||||
aabb.b += aabb.a;
|
aabb.b += aabb.a;
|
||||||
def.hitboxes = {aabb};
|
def.hitboxes = {aabb};
|
||||||
} else {
|
|
||||||
def.hitboxes = {AABB()};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// block light emission [r, g, b] where r,g,b in range [0..15]
|
// block light emission [r, g, b] where r,g,b in range [0..15]
|
||||||
|
|||||||
@ -23,10 +23,16 @@ Logger::Logger(std::string name) : name(std::move(name)) {
|
|||||||
void Logger::log(
|
void Logger::log(
|
||||||
LogLevel level, const std::string& name, const std::string& message
|
LogLevel level, const std::string& name, const std::string& message
|
||||||
) {
|
) {
|
||||||
|
if (level == LogLevel::print) {
|
||||||
|
std::cout << "[" << name << "] " << message << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
switch (level) {
|
switch (level) {
|
||||||
|
case LogLevel::print:
|
||||||
case LogLevel::debug:
|
case LogLevel::debug:
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -5,7 +5,7 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
namespace debug {
|
namespace debug {
|
||||||
enum class LogLevel { debug, info, warning, error };
|
enum class LogLevel { print, debug, info, warning, error };
|
||||||
|
|
||||||
class Logger;
|
class Logger;
|
||||||
|
|
||||||
@ -60,5 +60,10 @@ namespace debug {
|
|||||||
LogMessage warning() {
|
LogMessage warning() {
|
||||||
return LogMessage(this, LogLevel::warning);
|
return LogMessage(this, LogLevel::warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// @brief Print-debugging tool (printed without header)
|
||||||
|
LogMessage print() {
|
||||||
|
return LogMessage(this, LogLevel::print);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@
|
|||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "Mainloop.hpp"
|
#include "Mainloop.hpp"
|
||||||
#include "TestMainloop.hpp"
|
#include "ServerMainloop.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -87,7 +87,7 @@ Engine::Engine(CoreParameters coreParameters)
|
|||||||
|
|
||||||
auto resdir = paths.getResourcesFolder();
|
auto resdir = paths.getResourcesFolder();
|
||||||
|
|
||||||
controller = std::make_unique<EngineController>(this);
|
controller = std::make_unique<EngineController>(*this);
|
||||||
if (!params.headless) {
|
if (!params.headless) {
|
||||||
if (Window::initialize(&settings.display)){
|
if (Window::initialize(&settings.display)){
|
||||||
throw initialize_error("could not initialize window");
|
throw initialize_error("could not initialize window");
|
||||||
@ -101,7 +101,7 @@ Engine::Engine(CoreParameters coreParameters)
|
|||||||
|
|
||||||
gui = std::make_unique<gui::GUI>();
|
gui = std::make_unique<gui::GUI>();
|
||||||
if (ENGINE_DEBUG_BUILD) {
|
if (ENGINE_DEBUG_BUILD) {
|
||||||
menus::create_version_label(this);
|
menus::create_version_label(*this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
audio::initialize(settings.audio.enabled.get() && !params.headless);
|
audio::initialize(settings.audio.enabled.get() && !params.headless);
|
||||||
@ -118,7 +118,7 @@ Engine::Engine(CoreParameters coreParameters)
|
|||||||
paths.getResourcesFolder()
|
paths.getResourcesFolder()
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
keepAlive(settings.ui.language.observe([=](auto lang) {
|
keepAlive(settings.ui.language.observe([this](auto lang) {
|
||||||
setLanguage(lang);
|
setLanguage(lang);
|
||||||
}, true));
|
}, true));
|
||||||
|
|
||||||
@ -173,7 +173,7 @@ void Engine::saveScreenshot() {
|
|||||||
|
|
||||||
void Engine::run() {
|
void Engine::run() {
|
||||||
if (params.headless) {
|
if (params.headless) {
|
||||||
TestMainloop(*this).run();
|
ServerMainloop(*this).run();
|
||||||
} else {
|
} else {
|
||||||
Mainloop(*this).run();
|
Mainloop(*this).run();
|
||||||
}
|
}
|
||||||
@ -447,7 +447,7 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
|
|||||||
void Engine::setLanguage(std::string locale) {
|
void Engine::setLanguage(std::string locale) {
|
||||||
langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks);
|
langs::setup(paths.getResourcesFolder(), std::move(locale), contentPacks);
|
||||||
if (gui) {
|
if (gui) {
|
||||||
gui->getMenu()->setPageLoader(menus::create_page_loader(this));
|
gui->getMenu()->setPageLoader(menus::create_page_loader(*this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,6 +461,17 @@ void Engine::onWorldClosed() {
|
|||||||
levelConsumer(nullptr);
|
levelConsumer(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Engine::quit() {
|
||||||
|
quitSignal = true;
|
||||||
|
if (!isHeadless()) {
|
||||||
|
Window::setShouldClose(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::isQuitSignal() const {
|
||||||
|
return quitSignal;
|
||||||
|
}
|
||||||
|
|
||||||
gui::GUI* Engine::getGUI() {
|
gui::GUI* Engine::getGUI() {
|
||||||
return gui.get();
|
return gui.get();
|
||||||
}
|
}
|
||||||
@ -491,8 +502,8 @@ std::vector<std::string>& Engine::getBasePacks() {
|
|||||||
return basePacks;
|
return basePacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnginePaths* Engine::getPaths() {
|
EnginePaths& Engine::getPaths() {
|
||||||
return &paths;
|
return paths;
|
||||||
}
|
}
|
||||||
|
|
||||||
ResPaths* Engine::getResPaths() {
|
ResPaths* Engine::getResPaths() {
|
||||||
|
|||||||
@ -48,9 +48,10 @@ public:
|
|||||||
|
|
||||||
struct CoreParameters {
|
struct CoreParameters {
|
||||||
bool headless = false;
|
bool headless = false;
|
||||||
|
bool testMode = false;
|
||||||
std::filesystem::path resFolder {"res"};
|
std::filesystem::path resFolder {"res"};
|
||||||
std::filesystem::path userFolder {"."};
|
std::filesystem::path userFolder {"."};
|
||||||
std::filesystem::path testFile;
|
std::filesystem::path scriptFile;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Engine : public util::ObjectsKeeper {
|
class Engine : public util::ObjectsKeeper {
|
||||||
@ -73,6 +74,7 @@ class Engine : public util::ObjectsKeeper {
|
|||||||
std::unique_ptr<gui::GUI> gui;
|
std::unique_ptr<gui::GUI> gui;
|
||||||
Time time;
|
Time time;
|
||||||
consumer<std::unique_ptr<Level>> levelConsumer;
|
consumer<std::unique_ptr<Level>> levelConsumer;
|
||||||
|
bool quitSignal = false;
|
||||||
|
|
||||||
void loadControls();
|
void loadControls();
|
||||||
void loadSettings();
|
void loadSettings();
|
||||||
@ -129,7 +131,7 @@ public:
|
|||||||
EngineSettings& getSettings();
|
EngineSettings& getSettings();
|
||||||
|
|
||||||
/// @brief Get engine filesystem paths source
|
/// @brief Get engine filesystem paths source
|
||||||
EnginePaths* getPaths();
|
EnginePaths& getPaths();
|
||||||
|
|
||||||
/// @brief Get engine resource paths controller
|
/// @brief Get engine resource paths controller
|
||||||
ResPaths* getResPaths();
|
ResPaths* getResPaths();
|
||||||
@ -137,6 +139,10 @@ public:
|
|||||||
void onWorldOpen(std::unique_ptr<Level> level);
|
void onWorldOpen(std::unique_ptr<Level> level);
|
||||||
void onWorldClosed();
|
void onWorldClosed();
|
||||||
|
|
||||||
|
void quit();
|
||||||
|
|
||||||
|
bool isQuitSignal() const;
|
||||||
|
|
||||||
/// @brief Get current Content instance
|
/// @brief Get current Content instance
|
||||||
const Content* getContent() const;
|
const Content* getContent() const;
|
||||||
|
|
||||||
|
|||||||
@ -132,7 +132,7 @@ std::filesystem::path EnginePaths::getSettingsFile() const {
|
|||||||
return userFilesFolder / SETTINGS_FILE;
|
return userFilesFolder / SETTINGS_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::filesystem::path> EnginePaths::scanForWorlds() {
|
std::vector<std::filesystem::path> EnginePaths::scanForWorlds() const {
|
||||||
std::vector<std::filesystem::path> folders;
|
std::vector<std::filesystem::path> folders;
|
||||||
|
|
||||||
auto folder = getWorldsFolder();
|
auto folder = getWorldsFolder();
|
||||||
@ -189,7 +189,7 @@ std::tuple<std::string, std::string> EnginePaths::parsePath(std::string_view pat
|
|||||||
|
|
||||||
std::filesystem::path EnginePaths::resolve(
|
std::filesystem::path EnginePaths::resolve(
|
||||||
const std::string& path, bool throwErr
|
const std::string& path, bool throwErr
|
||||||
) {
|
) const {
|
||||||
auto [prefix, filename] = EnginePaths::parsePath(path);
|
auto [prefix, filename] = EnginePaths::parsePath(path);
|
||||||
if (prefix.empty()) {
|
if (prefix.empty()) {
|
||||||
throw files_access_error("no entry point specified");
|
throw files_access_error("no entry point specified");
|
||||||
|
|||||||
@ -39,9 +39,9 @@ public:
|
|||||||
|
|
||||||
void setContentPacks(std::vector<ContentPack>* contentPacks);
|
void setContentPacks(std::vector<ContentPack>* contentPacks);
|
||||||
|
|
||||||
std::vector<std::filesystem::path> scanForWorlds();
|
std::vector<std::filesystem::path> scanForWorlds() const;
|
||||||
|
|
||||||
std::filesystem::path resolve(const std::string& path, bool throwErr = true);
|
std::filesystem::path resolve(const std::string& path, bool throwErr = true) const;
|
||||||
|
|
||||||
static std::tuple<std::string, std::string> parsePath(std::string_view view);
|
static std::tuple<std::string, std::string> parsePath(std::string_view view);
|
||||||
|
|
||||||
|
|||||||
@ -42,7 +42,7 @@ static std::shared_ptr<Label> create_label(wstringsupplier supplier) {
|
|||||||
// TODO: move to xml
|
// TODO: move to xml
|
||||||
// TODO: move to xml finally
|
// TODO: move to xml finally
|
||||||
std::shared_ptr<UINode> create_debug_panel(
|
std::shared_ptr<UINode> create_debug_panel(
|
||||||
Engine* engine,
|
Engine& engine,
|
||||||
Level& level,
|
Level& level,
|
||||||
Player& player,
|
Player& player,
|
||||||
bool allowDebugCheats
|
bool allowDebugCheats
|
||||||
@ -56,8 +56,8 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
static int fpsMax = fps;
|
static int fpsMax = fps;
|
||||||
static std::wstring fpsString = L"";
|
static std::wstring fpsString = L"";
|
||||||
|
|
||||||
panel->listenInterval(0.016f, [engine]() {
|
panel->listenInterval(0.016f, [&engine]() {
|
||||||
fps = 1.0f / engine->getTime().getDelta();
|
fps = 1.0f / engine.getTime().getDelta();
|
||||||
fpsMin = std::min(fps, fpsMin);
|
fpsMin = std::min(fps, fpsMin);
|
||||||
fpsMax = std::max(fps, fpsMax);
|
fpsMax = std::max(fps, fpsMax);
|
||||||
});
|
});
|
||||||
@ -84,8 +84,8 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
panel->add(create_label([]() {
|
panel->add(create_label([]() {
|
||||||
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
|
return L"lua-stack: " + std::to_wstring(scripting::get_values_on_stack());
|
||||||
}));
|
}));
|
||||||
panel->add(create_label([=]() {
|
panel->add(create_label([&engine]() {
|
||||||
auto& settings = engine->getSettings();
|
auto& settings = engine.getSettings();
|
||||||
bool culling = settings.graphics.frustumCulling.get();
|
bool culling = settings.graphics.frustumCulling.get();
|
||||||
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
|
return L"frustum-culling: "+std::wstring(culling ? L"on" : L"off");
|
||||||
}));
|
}));
|
||||||
@ -96,7 +96,7 @@ std::shared_ptr<UINode> create_debug_panel(
|
|||||||
std::to_wstring(ParticlesRenderer::aliveEmitters);
|
std::to_wstring(ParticlesRenderer::aliveEmitters);
|
||||||
}));
|
}));
|
||||||
panel->add(create_label([&]() {
|
panel->add(create_label([&]() {
|
||||||
return L"chunks: "+std::to_wstring(level.chunksStorage->size())+
|
return L"chunks: "+std::to_wstring(level.chunks->size())+
|
||||||
L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks);
|
L" visible: "+std::to_wstring(ChunksRenderer::visibleChunks);
|
||||||
}));
|
}));
|
||||||
panel->add(create_label([&]() {
|
panel->add(create_label([&]() {
|
||||||
|
|||||||
@ -47,6 +47,7 @@
|
|||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -56,11 +57,13 @@
|
|||||||
|
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
|
static debug::Logger logger("hud");
|
||||||
|
|
||||||
bool Hud::showGeneratorMinimap = false;
|
bool Hud::showGeneratorMinimap = false;
|
||||||
|
|
||||||
// implemented in debug_panel.cpp
|
// implemented in debug_panel.cpp
|
||||||
extern std::shared_ptr<UINode> create_debug_panel(
|
std::shared_ptr<UINode> create_debug_panel(
|
||||||
Engine* engine,
|
Engine& engine,
|
||||||
Level& level,
|
Level& level,
|
||||||
Player& player,
|
Player& player,
|
||||||
bool allowDebugCheats
|
bool allowDebugCheats
|
||||||
@ -106,7 +109,7 @@ std::shared_ptr<UINode> HudElement::getNode() const {
|
|||||||
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
||||||
auto content = frontend.getLevel().content;
|
auto content = frontend.getLevel().content;
|
||||||
auto indices = content->getIndices();
|
auto indices = content->getIndices();
|
||||||
auto inventory = player->getInventory();
|
auto inventory = player.getInventory();
|
||||||
|
|
||||||
size_t itemsCount = indices->items.count();
|
size_t itemsCount = indices->items.count();
|
||||||
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
auto accessInventory = std::make_shared<Inventory>(0, itemsCount);
|
||||||
@ -120,7 +123,7 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
|||||||
inventory->move(copy, indices);
|
inventory->move(copy, indices);
|
||||||
},
|
},
|
||||||
[=](uint, ItemStack& item) {
|
[=](uint, ItemStack& item) {
|
||||||
inventory->getSlot(player->getChosenSlot()).set(item);
|
inventory->getSlot(player.getChosenSlot()).set(item);
|
||||||
});
|
});
|
||||||
|
|
||||||
InventoryBuilder builder;
|
InventoryBuilder builder;
|
||||||
@ -132,7 +135,7 @@ std::shared_ptr<InventoryView> Hud::createContentAccess() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<InventoryView> Hud::createHotbar() {
|
std::shared_ptr<InventoryView> Hud::createHotbar() {
|
||||||
auto inventory = player->getInventory();
|
auto inventory = player.getInventory();
|
||||||
auto content = frontend.getLevel().content;
|
auto content = frontend.getLevel().content;
|
||||||
|
|
||||||
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
|
SlotLayout slotLayout(-1, glm::vec2(), false, false, nullptr, nullptr, nullptr);
|
||||||
@ -148,10 +151,10 @@ std::shared_ptr<InventoryView> Hud::createHotbar() {
|
|||||||
|
|
||||||
static constexpr uint WORLDGEN_IMG_SIZE = 128U;
|
static constexpr uint WORLDGEN_IMG_SIZE = 128U;
|
||||||
|
|
||||||
Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player)
|
Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
|
||||||
: engine(engine),
|
: engine(engine),
|
||||||
assets(engine->getAssets()),
|
assets(*engine.getAssets()),
|
||||||
gui(engine->getGUI()),
|
gui(engine.getGUI()),
|
||||||
frontend(frontend),
|
frontend(frontend),
|
||||||
player(player),
|
player(player),
|
||||||
debugImgWorldGen(std::make_unique<ImageData>(
|
debugImgWorldGen(std::make_unique<ImageData>(
|
||||||
@ -177,7 +180,7 @@ Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player)
|
|||||||
uicamera->flipped = true;
|
uicamera->flipped = true;
|
||||||
|
|
||||||
debugPanel = create_debug_panel(
|
debugPanel = create_debug_panel(
|
||||||
engine, frontend.getLevel(), *player, allowDebugCheats
|
engine, frontend.getLevel(), player, allowDebugCheats
|
||||||
);
|
);
|
||||||
debugPanel->setZIndex(2);
|
debugPanel->setZIndex(2);
|
||||||
gui->add(debugPanel);
|
gui->add(debugPanel);
|
||||||
@ -191,7 +194,7 @@ Hud::Hud(Engine* engine, LevelFrontend& frontend, Player* player)
|
|||||||
dplotter->setInteractive(false);
|
dplotter->setInteractive(false);
|
||||||
add(HudElement(hud_element_mode::permanent, nullptr, dplotter, true));
|
add(HudElement(hud_element_mode::permanent, nullptr, dplotter, true));
|
||||||
|
|
||||||
assets->store(Texture::from(debugImgWorldGen.get()), DEBUG_WORLDGEN_IMAGE);
|
assets.store(Texture::from(debugImgWorldGen.get()), DEBUG_WORLDGEN_IMAGE);
|
||||||
|
|
||||||
debugMinimap = guiutil::create(
|
debugMinimap = guiutil::create(
|
||||||
"<image src='"+DEBUG_WORLDGEN_IMAGE+
|
"<image src='"+DEBUG_WORLDGEN_IMAGE+
|
||||||
@ -246,12 +249,12 @@ void Hud::processInput(bool visible) {
|
|||||||
|
|
||||||
void Hud::updateHotbarControl() {
|
void Hud::updateHotbarControl() {
|
||||||
if (!inventoryOpen && Events::scroll) {
|
if (!inventoryOpen && Events::scroll) {
|
||||||
int slot = player->getChosenSlot();
|
int slot = player.getChosenSlot();
|
||||||
slot = (slot - Events::scroll) % 10;
|
slot = (slot - Events::scroll) % 10;
|
||||||
if (slot < 0) {
|
if (slot < 0) {
|
||||||
slot += 10;
|
slot += 10;
|
||||||
}
|
}
|
||||||
player->setChosenSlot(slot);
|
player.setChosenSlot(slot);
|
||||||
}
|
}
|
||||||
for (
|
for (
|
||||||
int i = static_cast<int>(keycode::NUM_1);
|
int i = static_cast<int>(keycode::NUM_1);
|
||||||
@ -259,17 +262,17 @@ void Hud::updateHotbarControl() {
|
|||||||
i++
|
i++
|
||||||
) {
|
) {
|
||||||
if (Events::jpressed(i)) {
|
if (Events::jpressed(i)) {
|
||||||
player->setChosenSlot(i - static_cast<int>(keycode::NUM_1));
|
player.setChosenSlot(i - static_cast<int>(keycode::NUM_1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (Events::jpressed(keycode::NUM_0)) {
|
if (Events::jpressed(keycode::NUM_0)) {
|
||||||
player->setChosenSlot(9);
|
player.setChosenSlot(9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::updateWorldGenDebugVisualization() {
|
void Hud::updateWorldGenDebugVisualization() {
|
||||||
auto& level = frontend.getLevel();
|
auto& level = frontend.getLevel();
|
||||||
auto& chunks = *level.chunks;
|
const auto& chunks = *player.chunks;
|
||||||
auto generator =
|
auto generator =
|
||||||
frontend.getController()->getChunksController()->getGenerator();
|
frontend.getController()->getChunksController()->getGenerator();
|
||||||
auto debugInfo = generator->createDebugInfo();
|
auto debugInfo = generator->createDebugInfo();
|
||||||
@ -296,7 +299,7 @@ void Hud::updateWorldGenDebugVisualization() {
|
|||||||
data[(flippedZ * width + x) * 4 + 1] =
|
data[(flippedZ * width + x) * 4 + 1] =
|
||||||
chunks.getChunk(ax + ox, az + oz) ? 255 : 0;
|
chunks.getChunk(ax + ox, az + oz) ? 255 : 0;
|
||||||
data[(flippedZ * width + x) * 4 + 0] =
|
data[(flippedZ * width + x) * 4 + 0] =
|
||||||
level.chunksStorage->fetch(ax + ox, az + oz) ? 255 : 0;
|
level.chunks->fetch(ax + ox, az + oz) ? 255 : 0;
|
||||||
|
|
||||||
if (ax < 0 || az < 0 ||
|
if (ax < 0 || az < 0 ||
|
||||||
ax >= areaWidth || az >= areaHeight) {
|
ax >= areaWidth || az >= areaHeight) {
|
||||||
@ -312,15 +315,16 @@ void Hud::updateWorldGenDebugVisualization() {
|
|||||||
data[(flippedZ * width + x) * 4 + 3] = 150;
|
data[(flippedZ * width + x) * 4 + 3] = 150;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto texture = assets->get<Texture>(DEBUG_WORLDGEN_IMAGE);
|
auto& texture = assets.require<Texture>(DEBUG_WORLDGEN_IMAGE);
|
||||||
texture->reload(*debugImgWorldGen);
|
texture.reload(*debugImgWorldGen);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Hud::update(bool visible) {
|
void Hud::update(bool visible) {
|
||||||
const auto& level = frontend.getLevel();
|
const auto& level = frontend.getLevel();
|
||||||
|
const auto& chunks = *player.chunks;
|
||||||
auto menu = gui->getMenu();
|
auto menu = gui->getMenu();
|
||||||
|
|
||||||
debugPanel->setVisible(player->debug && visible);
|
debugPanel->setVisible(player.debug && visible);
|
||||||
|
|
||||||
if (!visible && inventoryOpen) {
|
if (!visible && inventoryOpen) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
@ -337,7 +341,7 @@ void Hud::update(bool visible) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (blockUI) {
|
if (blockUI) {
|
||||||
voxel* vox = level.chunks->get(blockPos.x, blockPos.y, blockPos.z);
|
voxel* vox = chunks.get(blockPos.x, blockPos.y, blockPos.z);
|
||||||
if (vox == nullptr || vox->id != currentblockid) {
|
if (vox == nullptr || vox->id != currentblockid) {
|
||||||
closeInventory();
|
closeInventory();
|
||||||
}
|
}
|
||||||
@ -355,7 +359,7 @@ void Hud::update(bool visible) {
|
|||||||
|
|
||||||
if (visible) {
|
if (visible) {
|
||||||
for (auto& element : elements) {
|
for (auto& element : elements) {
|
||||||
element.update(pause, inventoryOpen, player->debug);
|
element.update(pause, inventoryOpen, player.debug);
|
||||||
if (element.isRemoved()) {
|
if (element.isRemoved()) {
|
||||||
onRemove(element);
|
onRemove(element);
|
||||||
}
|
}
|
||||||
@ -363,8 +367,8 @@ void Hud::update(bool visible) {
|
|||||||
}
|
}
|
||||||
cleanup();
|
cleanup();
|
||||||
|
|
||||||
debugMinimap->setVisible(player->debug && showGeneratorMinimap);
|
debugMinimap->setVisible(player.debug && showGeneratorMinimap);
|
||||||
if (player->debug && showGeneratorMinimap) {
|
if (player.debug && showGeneratorMinimap) {
|
||||||
updateWorldGenDebugVisualization();
|
updateWorldGenDebugVisualization();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -375,8 +379,8 @@ void Hud::openInventory() {
|
|||||||
showExchangeSlot();
|
showExchangeSlot();
|
||||||
|
|
||||||
inventoryOpen = true;
|
inventoryOpen = true;
|
||||||
auto inventory = player->getInventory();
|
auto inventory = player.getInventory();
|
||||||
auto inventoryDocument = assets->get<UiDocument>("core:inventory");
|
auto inventoryDocument = assets.get<UiDocument>("core:inventory");
|
||||||
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
inventoryView = std::dynamic_pointer_cast<InventoryView>(inventoryDocument->getRoot());
|
||||||
inventoryView->bind(inventory, content);
|
inventoryView->bind(inventory, content);
|
||||||
add(HudElement(hud_element_mode::inventory_bound, inventoryDocument, inventoryView, false));
|
add(HudElement(hud_element_mode::inventory_bound, inventoryDocument, inventoryView, false));
|
||||||
@ -422,7 +426,9 @@ void Hud::openInventory(
|
|||||||
closeInventory();
|
closeInventory();
|
||||||
}
|
}
|
||||||
auto& level = frontend.getLevel();
|
auto& level = frontend.getLevel();
|
||||||
|
const auto& chunks = *player.chunks;
|
||||||
auto content = level.content;
|
auto content = level.content;
|
||||||
|
|
||||||
blockUI = std::dynamic_pointer_cast<InventoryView>(doc->getRoot());
|
blockUI = std::dynamic_pointer_cast<InventoryView>(doc->getRoot());
|
||||||
if (blockUI == nullptr) {
|
if (blockUI == nullptr) {
|
||||||
throw std::runtime_error("block UI root element must be 'inventory'");
|
throw std::runtime_error("block UI root element must be 'inventory'");
|
||||||
@ -436,10 +442,10 @@ void Hud::openInventory(
|
|||||||
if (blockinv == nullptr) {
|
if (blockinv == nullptr) {
|
||||||
blockinv = level.inventories->createVirtual(blockUI->getSlotsCount());
|
blockinv = level.inventories->createVirtual(blockUI->getSlotsCount());
|
||||||
}
|
}
|
||||||
level.chunks->getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true;
|
chunks.getChunkByVoxel(block.x, block.y, block.z)->flags.unsaved = true;
|
||||||
blockUI->bind(blockinv, content);
|
blockUI->bind(blockinv, content);
|
||||||
blockPos = block;
|
blockPos = block;
|
||||||
currentblockid = level.chunks->get(block.x, block.y, block.z)->id;
|
currentblockid = chunks.require(block.x, block.y, block.z).id;
|
||||||
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
|
add(HudElement(hud_element_mode::inventory_bound, doc, blockUI, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -481,12 +487,37 @@ void Hud::openPermanent(UiDocument* doc) {
|
|||||||
|
|
||||||
auto invview = std::dynamic_pointer_cast<InventoryView>(root);
|
auto invview = std::dynamic_pointer_cast<InventoryView>(root);
|
||||||
if (invview) {
|
if (invview) {
|
||||||
invview->bind(player->getInventory(), frontend.getLevel().content);
|
invview->bind(player.getInventory(), frontend.getLevel().content);
|
||||||
}
|
}
|
||||||
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
add(HudElement(hud_element_mode::permanent, doc, doc->getRoot(), false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Hud::dropExchangeSlot() {
|
||||||
|
auto slotView = std::dynamic_pointer_cast<SlotView>(
|
||||||
|
gui->get(SlotView::EXCHANGE_SLOT_NAME)
|
||||||
|
);
|
||||||
|
if (slotView == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ItemStack& stack = slotView->getStack();
|
||||||
|
|
||||||
|
auto indices = frontend.getLevel().content->getIndices();
|
||||||
|
if (auto invView = std::dynamic_pointer_cast<InventoryView>(blockUI)) {
|
||||||
|
invView->getInventory()->move(stack, indices);
|
||||||
|
}
|
||||||
|
if (stack.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
player.getInventory()->move(stack, indices);
|
||||||
|
if (!stack.isEmpty()) {
|
||||||
|
logger.warning() << "discard item [" << stack.getItemId() << ":"
|
||||||
|
<< stack.getCount();
|
||||||
|
stack.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Hud::closeInventory() {
|
void Hud::closeInventory() {
|
||||||
|
dropExchangeSlot();
|
||||||
gui->remove(SlotView::EXCHANGE_SLOT_NAME);
|
gui->remove(SlotView::EXCHANGE_SLOT_NAME);
|
||||||
exchangeSlot = nullptr;
|
exchangeSlot = nullptr;
|
||||||
exchangeSlotInv = nullptr;
|
exchangeSlotInv = nullptr;
|
||||||
@ -566,15 +597,15 @@ void Hud::draw(const DrawContext& ctx){
|
|||||||
auto batch = ctx.getBatch2D();
|
auto batch = ctx.getBatch2D();
|
||||||
batch->begin();
|
batch->begin();
|
||||||
|
|
||||||
auto uishader = assets->get<Shader>("ui");
|
auto& uishader = assets.require<Shader>("ui");
|
||||||
uishader->use();
|
uishader.use();
|
||||||
uishader->uniformMatrix("u_projview", uicamera->getProjView());
|
uishader.uniformMatrix("u_projview", uicamera->getProjView());
|
||||||
|
|
||||||
// Crosshair
|
// Crosshair
|
||||||
if (!pause && !inventoryOpen && !player->debug) {
|
if (!pause && !inventoryOpen && !player.debug) {
|
||||||
DrawContext chctx = ctx.sub(batch);
|
DrawContext chctx = ctx.sub(batch);
|
||||||
chctx.setBlendMode(BlendMode::inversion);
|
chctx.setBlendMode(BlendMode::inversion);
|
||||||
auto texture = assets->get<Texture>("gui/crosshair");
|
auto texture = assets.get<Texture>("gui/crosshair");
|
||||||
batch->texture(texture);
|
batch->texture(texture);
|
||||||
int chsizex = texture != nullptr ? texture->getWidth() : 16;
|
int chsizex = texture != nullptr ? texture->getWidth() : 16;
|
||||||
int chsizey = texture != nullptr ? texture->getHeight() : 16;
|
int chsizey = texture != nullptr ? texture->getHeight() : 16;
|
||||||
@ -629,7 +660,7 @@ void Hud::updateElementsPosition(const Viewport& viewport) {
|
|||||||
exchangeSlot->setPos(glm::vec2(Events::cursor));
|
exchangeSlot->setPos(glm::vec2(Events::cursor));
|
||||||
}
|
}
|
||||||
hotbarView->setPos(glm::vec2(width/2, height-65));
|
hotbarView->setPos(glm::vec2(width/2, height-65));
|
||||||
hotbarView->setSelected(player->getChosenSlot());
|
hotbarView->setSelected(player.getChosenSlot());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Hud::isInventoryOpen() const {
|
bool Hud::isInventoryOpen() const {
|
||||||
@ -661,7 +692,7 @@ void Hud::setPause(bool pause) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Player* Hud::getPlayer() const {
|
Player* Hud::getPlayer() const {
|
||||||
return player;
|
return &player;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Inventory> Hud::getBlockInventory() {
|
std::shared_ptr<Inventory> Hud::getBlockInventory() {
|
||||||
@ -684,7 +715,7 @@ void Hud::setDebugCheats(bool flag) {
|
|||||||
|
|
||||||
gui->remove(debugPanel);
|
gui->remove(debugPanel);
|
||||||
debugPanel = create_debug_panel(
|
debugPanel = create_debug_panel(
|
||||||
engine, frontend.getLevel(), *player, allowDebugCheats
|
engine, frontend.getLevel(), player, allowDebugCheats
|
||||||
);
|
);
|
||||||
debugPanel->setZIndex(2);
|
debugPanel->setZIndex(2);
|
||||||
gui->add(debugPanel);
|
gui->add(debugPanel);
|
||||||
|
|||||||
@ -70,12 +70,12 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class Hud : public util::ObjectsKeeper {
|
class Hud : public util::ObjectsKeeper {
|
||||||
Engine* engine;
|
Engine& engine;
|
||||||
Assets* assets;
|
Assets& assets;
|
||||||
std::unique_ptr<Camera> uicamera;
|
std::unique_ptr<Camera> uicamera;
|
||||||
gui::GUI* gui;
|
gui::GUI* gui;
|
||||||
LevelFrontend& frontend;
|
LevelFrontend& frontend;
|
||||||
Player* player;
|
Player& player;
|
||||||
|
|
||||||
/// @brief Is any overlay/inventory open
|
/// @brief Is any overlay/inventory open
|
||||||
bool inventoryOpen = false;
|
bool inventoryOpen = false;
|
||||||
@ -128,10 +128,13 @@ class Hud : public util::ObjectsKeeper {
|
|||||||
void updateHotbarControl();
|
void updateHotbarControl();
|
||||||
void cleanup();
|
void cleanup();
|
||||||
|
|
||||||
|
/// @brief Perform exchange slot removal when it's not empty.
|
||||||
|
void dropExchangeSlot();
|
||||||
|
|
||||||
void showExchangeSlot();
|
void showExchangeSlot();
|
||||||
void updateWorldGenDebugVisualization();
|
void updateWorldGenDebugVisualization();
|
||||||
public:
|
public:
|
||||||
Hud(Engine* engine, LevelFrontend& frontend, Player* player);
|
Hud(Engine& engine, LevelFrontend& frontend, Player& player);
|
||||||
~Hud();
|
~Hud();
|
||||||
|
|
||||||
void update(bool hudVisible);
|
void update(bool hudVisible);
|
||||||
|
|||||||
@ -24,8 +24,8 @@
|
|||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
using namespace gui;
|
using namespace gui;
|
||||||
|
|
||||||
void menus::create_version_label(Engine* engine) {
|
void menus::create_version_label(Engine& engine) {
|
||||||
auto gui = engine->getGUI();
|
auto gui = engine.getGUI();
|
||||||
auto text = ENGINE_VERSION_STRING+" debug build";
|
auto text = ENGINE_VERSION_STRING+" debug build";
|
||||||
gui->add(guiutil::create(
|
gui->add(guiutil::create(
|
||||||
"<label z-index='1000' color='#FFFFFF80' gravity='top-right' margin='4'>"
|
"<label z-index='1000' color='#FFFFFF80' gravity='top-right' margin='4'>"
|
||||||
@ -34,8 +34,8 @@ void menus::create_version_label(Engine* engine) {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
gui::page_loader_func menus::create_page_loader(Engine* engine) {
|
gui::page_loader_func menus::create_page_loader(Engine& engine) {
|
||||||
return [=](const std::string& query) {
|
return [&](const std::string& query) {
|
||||||
std::vector<dv::value> args;
|
std::vector<dv::value> args;
|
||||||
|
|
||||||
std::string name;
|
std::string name;
|
||||||
@ -58,75 +58,79 @@ gui::page_loader_func menus::create_page_loader(Engine* engine) {
|
|||||||
name = query;
|
name = query;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto file = engine->getResPaths()->find("layouts/pages/"+name+".xml");
|
auto file = engine.getResPaths()->find("layouts/pages/"+name+".xml");
|
||||||
auto fullname = "core:pages/"+name;
|
auto fullname = "core:pages/"+name;
|
||||||
|
|
||||||
auto document_ptr = UiDocument::read(
|
auto documentPtr = UiDocument::read(
|
||||||
scripting::get_root_environment(),
|
scripting::get_root_environment(),
|
||||||
fullname,
|
fullname,
|
||||||
file,
|
file,
|
||||||
"core:layouts/pages/" + name
|
"core:layouts/pages/" + name
|
||||||
);
|
);
|
||||||
auto document = document_ptr.get();
|
auto document = documentPtr.get();
|
||||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
engine.getAssets()->store(std::move(documentPtr), fullname);
|
||||||
scripting::on_ui_open(document, std::move(args));
|
scripting::on_ui_open(document, std::move(args));
|
||||||
return document->getRoot();
|
return document->getRoot();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool menus::call(Engine* engine, runnable func) {
|
bool menus::call(Engine& engine, runnable func) {
|
||||||
|
if (engine.isHeadless()) {
|
||||||
|
throw std::runtime_error("menus::call(...) in headless mode");
|
||||||
|
}
|
||||||
|
auto gui = engine.getGUI();
|
||||||
try {
|
try {
|
||||||
func();
|
func();
|
||||||
return true;
|
return true;
|
||||||
} catch (const contentpack_error& error) {
|
} catch (const contentpack_error& error) {
|
||||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
// could not to find or read pack
|
// could not to find or read pack
|
||||||
guiutil::alert(
|
guiutil::alert(
|
||||||
engine->getGUI(), langs::get(L"error.pack-not-found")+L": "+
|
gui, langs::get(L"error.pack-not-found")+L": "+
|
||||||
util::str2wstr_utf8(error.getPackId())
|
util::str2wstr_utf8(error.getPackId())
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
} catch (const assetload::error& error) {
|
} catch (const assetload::error& error) {
|
||||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
guiutil::alert(
|
guiutil::alert(
|
||||||
engine->getGUI(), langs::get(L"Assets Load Error", L"menu")+L":\n"+
|
gui, langs::get(L"Assets Load Error", L"menu")+L":\n"+
|
||||||
util::str2wstr_utf8(error.what())
|
util::str2wstr_utf8(error.what())
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
} catch (const parsing_error& error) {
|
} catch (const parsing_error& error) {
|
||||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
guiutil::alert(engine->getGUI(), util::str2wstr_utf8(error.errorLog()));
|
guiutil::alert(gui, util::str2wstr_utf8(error.errorLog()));
|
||||||
return false;
|
return false;
|
||||||
} catch (const std::runtime_error& error) {
|
} catch (const std::runtime_error& error) {
|
||||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
guiutil::alert(
|
guiutil::alert(
|
||||||
engine->getGUI(), langs::get(L"Content Error", L"menu")+L":\n"+
|
gui, langs::get(L"Content Error", L"menu")+L":\n"+
|
||||||
util::str2wstr_utf8(error.what())
|
util::str2wstr_utf8(error.what())
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UiDocument* menus::show(Engine* engine, const std::string& name, std::vector<dv::value> args) {
|
UiDocument* menus::show(Engine& engine, const std::string& name, std::vector<dv::value> args) {
|
||||||
auto menu = engine->getGUI()->getMenu();
|
auto menu = engine.getGUI()->getMenu();
|
||||||
auto file = engine->getResPaths()->find("layouts/"+name+".xml");
|
auto file = engine.getResPaths()->find("layouts/"+name+".xml");
|
||||||
auto fullname = "core:layouts/"+name;
|
auto fullname = "core:layouts/"+name;
|
||||||
|
|
||||||
auto document_ptr = UiDocument::read(
|
auto documentPtr = UiDocument::read(
|
||||||
scripting::get_root_environment(), fullname, file, "core:layouts/"+name
|
scripting::get_root_environment(), fullname, file, "core:layouts/"+name
|
||||||
);
|
);
|
||||||
auto document = document_ptr.get();
|
auto document = documentPtr.get();
|
||||||
engine->getAssets()->store(std::move(document_ptr), fullname);
|
engine.getAssets()->store(std::move(documentPtr), fullname);
|
||||||
scripting::on_ui_open(document, std::move(args));
|
scripting::on_ui_open(document, std::move(args));
|
||||||
menu->addPage(name, document->getRoot());
|
menu->addPage(name, document->getRoot());
|
||||||
menu->setPage(name);
|
menu->setPage(name);
|
||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
void menus::show_process_panel(Engine* engine, const std::shared_ptr<Task>& task, const std::wstring& text) {
|
void menus::show_process_panel(Engine& engine, const std::shared_ptr<Task>& task, const std::wstring& text) {
|
||||||
uint initialWork = task->getWorkTotal();
|
uint initialWork = task->getWorkTotal();
|
||||||
|
|
||||||
auto menu = engine->getGUI()->getMenu();
|
auto menu = engine.getGUI()->getMenu();
|
||||||
menu->reset();
|
menu->reset();
|
||||||
auto doc = menus::show(engine, "process", {
|
auto doc = menus::show(engine, "process", {
|
||||||
util::wstr2str_utf8(langs::get(text))
|
util::wstr2str_utf8(langs::get(text))
|
||||||
|
|||||||
@ -14,17 +14,17 @@ class UiDocument;
|
|||||||
|
|
||||||
namespace menus {
|
namespace menus {
|
||||||
/// @brief Create development version label at the top-right screen corner
|
/// @brief Create development version label at the top-right screen corner
|
||||||
void create_version_label(Engine* engine);
|
void create_version_label(Engine& engine);
|
||||||
|
|
||||||
gui::page_loader_func create_page_loader(Engine* engine);
|
gui::page_loader_func create_page_loader(Engine& engine);
|
||||||
|
|
||||||
UiDocument* show(
|
UiDocument* show(
|
||||||
Engine* engine,
|
Engine& engine,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
std::vector<dv::value> args
|
std::vector<dv::value> args
|
||||||
);
|
);
|
||||||
|
|
||||||
void show_process_panel(Engine* engine, const std::shared_ptr<Task>& task, const std::wstring& text=L"");
|
void show_process_panel(Engine& engine, const std::shared_ptr<Task>& task, const std::wstring& text=L"");
|
||||||
|
|
||||||
bool call(Engine* engine, runnable func);
|
bool call(Engine& engine, runnable func);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,47 +34,47 @@
|
|||||||
|
|
||||||
static debug::Logger logger("level-screen");
|
static debug::Logger logger("level-screen");
|
||||||
|
|
||||||
LevelScreen::LevelScreen(Engine* engine, std::unique_ptr<Level> levelPtr)
|
LevelScreen::LevelScreen(Engine& engine, std::unique_ptr<Level> levelPtr)
|
||||||
: Screen(engine), postProcessing(std::make_unique<PostProcessing>())
|
: Screen(engine), postProcessing(std::make_unique<PostProcessing>())
|
||||||
{
|
{
|
||||||
Level* level = levelPtr.get();
|
Level* level = levelPtr.get();
|
||||||
|
|
||||||
auto& settings = engine->getSettings();
|
auto& settings = engine.getSettings();
|
||||||
auto& assets = *engine->getAssets();
|
auto& assets = *engine.getAssets();
|
||||||
auto menu = engine->getGUI()->getMenu();
|
auto menu = engine.getGUI()->getMenu();
|
||||||
menu->reset();
|
menu->reset();
|
||||||
|
|
||||||
controller = std::make_unique<LevelController>(engine, std::move(levelPtr));
|
|
||||||
|
|
||||||
auto player = level->players->get(0);
|
auto player = level->players->get(0);
|
||||||
|
controller =
|
||||||
|
std::make_unique<LevelController>(&engine, std::move(levelPtr), player);
|
||||||
playerController = std::make_unique<PlayerController>(
|
playerController = std::make_unique<PlayerController>(
|
||||||
settings,
|
settings,
|
||||||
level,
|
*level,
|
||||||
player,
|
*player,
|
||||||
controller->getBlocksController()
|
*controller->getBlocksController()
|
||||||
);
|
);
|
||||||
|
|
||||||
frontend = std::make_unique<LevelFrontend>(
|
frontend = std::make_unique<LevelFrontend>(
|
||||||
player, controller.get(), assets
|
player, controller.get(), assets
|
||||||
);
|
);
|
||||||
worldRenderer = std::make_unique<WorldRenderer>(
|
worldRenderer = std::make_unique<WorldRenderer>(
|
||||||
engine, *frontend, player
|
engine, *frontend, *player
|
||||||
);
|
);
|
||||||
hud = std::make_unique<Hud>(engine, *frontend, player);
|
hud = std::make_unique<Hud>(engine, *frontend, *player);
|
||||||
|
|
||||||
decorator = std::make_unique<Decorator>(
|
decorator = std::make_unique<Decorator>(
|
||||||
*engine, *controller, *worldRenderer, assets, *player
|
engine, *controller, *worldRenderer, assets, *player
|
||||||
);
|
);
|
||||||
|
|
||||||
keepAlive(settings.graphics.backlight.observe([=](bool) {
|
keepAlive(settings.graphics.backlight.observe([=](bool) {
|
||||||
controller->getLevel()->chunks->saveAndClear();
|
player->chunks->saveAndClear();
|
||||||
worldRenderer->clear();
|
worldRenderer->clear();
|
||||||
}));
|
}));
|
||||||
keepAlive(settings.camera.fov.observe([=](double value) {
|
keepAlive(settings.camera.fov.observe([=](double value) {
|
||||||
player->fpCamera->setFov(glm::radians(value));
|
player->fpCamera->setFov(glm::radians(value));
|
||||||
}));
|
}));
|
||||||
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
|
keepAlive(Events::getBinding(BIND_CHUNKS_RELOAD).onactived.add([=](){
|
||||||
controller->getLevel()->chunks->saveAndClear();
|
player->chunks->saveAndClear();
|
||||||
worldRenderer->clear();
|
worldRenderer->clear();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
@ -111,15 +111,15 @@ LevelScreen::~LevelScreen() {
|
|||||||
// unblock all bindings
|
// unblock all bindings
|
||||||
Events::enableBindings();
|
Events::enableBindings();
|
||||||
controller->onWorldQuit();
|
controller->onWorldQuit();
|
||||||
engine->getPaths()->setCurrentWorldFolder(fs::path());
|
engine.getPaths().setCurrentWorldFolder(fs::path());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelScreen::saveWorldPreview() {
|
void LevelScreen::saveWorldPreview() {
|
||||||
try {
|
try {
|
||||||
logger.info() << "saving world preview";
|
logger.info() << "saving world preview";
|
||||||
auto paths = engine->getPaths();
|
const auto& paths = engine.getPaths();
|
||||||
auto player = playerController->getPlayer();
|
auto player = playerController->getPlayer();
|
||||||
auto& settings = engine->getSettings();
|
auto& settings = engine.getSettings();
|
||||||
int previewSize = settings.ui.worldPreviewSize.get();
|
int previewSize = settings.ui.worldPreviewSize.get();
|
||||||
|
|
||||||
// camera special copy for world preview
|
// camera special copy for world preview
|
||||||
@ -134,7 +134,7 @@ void LevelScreen::saveWorldPreview() {
|
|||||||
worldRenderer->draw(ctx, camera, false, true, 0.0f, postProcessing.get());
|
worldRenderer->draw(ctx, camera, false, true, 0.0f, postProcessing.get());
|
||||||
auto image = postProcessing->toImage();
|
auto image = postProcessing->toImage();
|
||||||
image->flipY();
|
image->flipY();
|
||||||
imageio::write(paths->resolve("world:preview.png").u8string(), image.get());
|
imageio::write(paths.resolve("world:preview.png").u8string(), image.get());
|
||||||
} catch (const std::exception& err) {
|
} catch (const std::exception& err) {
|
||||||
logger.error() << err.what();
|
logger.error() << err.what();
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ void LevelScreen::saveWorldPreview() {
|
|||||||
|
|
||||||
void LevelScreen::updateHotkeys() {
|
void LevelScreen::updateHotkeys() {
|
||||||
auto player = playerController->getPlayer();
|
auto player = playerController->getPlayer();
|
||||||
auto& settings = engine->getSettings();
|
auto& settings = engine.getSettings();
|
||||||
if (Events::jpressed(keycode::O)) {
|
if (Events::jpressed(keycode::O)) {
|
||||||
settings.graphics.frustumCulling.toggle();
|
settings.graphics.frustumCulling.toggle();
|
||||||
}
|
}
|
||||||
@ -155,7 +155,7 @@ void LevelScreen::updateHotkeys() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void LevelScreen::update(float delta) {
|
void LevelScreen::update(float delta) {
|
||||||
gui::GUI* gui = engine->getGUI();
|
gui::GUI* gui = engine.getGUI();
|
||||||
|
|
||||||
bool inputLocked = hud->isPause() ||
|
bool inputLocked = hud->isPause() ||
|
||||||
hud->isInventoryOpen() ||
|
hud->isInventoryOpen() ||
|
||||||
@ -181,7 +181,7 @@ void LevelScreen::update(float delta) {
|
|||||||
glm::vec3(0, 1, 0)
|
glm::vec3(0, 1, 0)
|
||||||
);
|
);
|
||||||
auto level = controller->getLevel();
|
auto level = controller->getLevel();
|
||||||
const auto& settings = engine->getSettings();
|
const auto& settings = engine.getSettings();
|
||||||
|
|
||||||
if (!hud->isPause()) {
|
if (!hud->isPause()) {
|
||||||
level->getWorld()->updateTimers(delta);
|
level->getWorld()->updateTimers(delta);
|
||||||
@ -204,7 +204,7 @@ void LevelScreen::draw(float delta) {
|
|||||||
DrawContext ctx(nullptr, viewport, batch.get());
|
DrawContext ctx(nullptr, viewport, batch.get());
|
||||||
|
|
||||||
if (!hud->isPause()) {
|
if (!hud->isPause()) {
|
||||||
scripting::on_entities_render(engine->getTime().getDelta());
|
scripting::on_entities_render(engine.getTime().getDelta());
|
||||||
}
|
}
|
||||||
worldRenderer->draw(
|
worldRenderer->draw(
|
||||||
ctx, *camera, hudVisible, hud->isPause(), delta, postProcessing.get()
|
ctx, *camera, hudVisible, hud->isPause(), delta, postProcessing.get()
|
||||||
|
|||||||
@ -33,7 +33,7 @@ class LevelScreen : public Screen {
|
|||||||
void initializeContent();
|
void initializeContent();
|
||||||
void initializePack(ContentPackRuntime* pack);
|
void initializePack(ContentPackRuntime* pack);
|
||||||
public:
|
public:
|
||||||
LevelScreen(Engine* engine, std::unique_ptr<Level> level);
|
LevelScreen(Engine& engine, std::unique_ptr<Level> level);
|
||||||
~LevelScreen();
|
~LevelScreen();
|
||||||
|
|
||||||
void update(float delta) override;
|
void update(float delta) override;
|
||||||
|
|||||||
@ -10,10 +10,10 @@
|
|||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
|
|
||||||
MenuScreen::MenuScreen(Engine* engine) : Screen(engine) {
|
MenuScreen::MenuScreen(Engine& engine) : Screen(engine) {
|
||||||
engine->resetContent();
|
engine.resetContent();
|
||||||
|
|
||||||
auto menu = engine->getGUI()->getMenu();
|
auto menu = engine.getGUI()->getMenu();
|
||||||
menu->reset();
|
menu->reset();
|
||||||
menu->setPage("main");
|
menu->setPage("main");
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ void MenuScreen::update(float delta) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void MenuScreen::draw(float delta) {
|
void MenuScreen::draw(float delta) {
|
||||||
auto assets = engine->getAssets();
|
auto assets = engine.getAssets();
|
||||||
|
|
||||||
Window::clear();
|
Window::clear();
|
||||||
Window::setBgColor(glm::vec3(0.2f));
|
Window::setBgColor(glm::vec3(0.2f));
|
||||||
|
|||||||
@ -10,7 +10,7 @@ class Engine;
|
|||||||
class MenuScreen : public Screen {
|
class MenuScreen : public Screen {
|
||||||
std::unique_ptr<Camera> uicamera;
|
std::unique_ptr<Camera> uicamera;
|
||||||
public:
|
public:
|
||||||
MenuScreen(Engine* engine);
|
MenuScreen(Engine& engine);
|
||||||
~MenuScreen();
|
~MenuScreen();
|
||||||
|
|
||||||
void update(float delta) override;
|
void update(float delta) override;
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include "graphics/core/Batch2D.hpp"
|
#include "graphics/core/Batch2D.hpp"
|
||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
|
|
||||||
Screen::Screen(Engine* engine)
|
Screen::Screen(Engine& engine)
|
||||||
: engine(engine),
|
: engine(engine),
|
||||||
batch(std::make_unique<Batch2D>(1024)) {
|
batch(std::make_unique<Batch2D>(1024)) {
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,10 @@ class Batch2D;
|
|||||||
/// @brief Screen is a mainloop state
|
/// @brief Screen is a mainloop state
|
||||||
class Screen : public util::ObjectsKeeper {
|
class Screen : public util::ObjectsKeeper {
|
||||||
protected:
|
protected:
|
||||||
Engine* engine;
|
Engine& engine;
|
||||||
std::unique_ptr<Batch2D> batch;
|
std::unique_ptr<Batch2D> batch;
|
||||||
public:
|
public:
|
||||||
Screen(Engine* engine);
|
Screen(Engine& engine);
|
||||||
virtual ~Screen();
|
virtual ~Screen();
|
||||||
virtual void update(float delta) = 0;
|
virtual void update(float delta) = 0;
|
||||||
virtual void draw(float delta) = 0;
|
virtual void draw(float delta) = 0;
|
||||||
|
|||||||
@ -14,15 +14,18 @@
|
|||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
||||||
BlockWrapsRenderer::BlockWrapsRenderer(const Assets& assets, const Level& level)
|
BlockWrapsRenderer::BlockWrapsRenderer(
|
||||||
: assets(assets), level(level), batch(std::make_unique<MainBatch>(1024)) {
|
const Assets& assets, const Level& level, const Chunks& chunks
|
||||||
|
)
|
||||||
|
: assets(assets),
|
||||||
|
level(level),
|
||||||
|
chunks(chunks),
|
||||||
|
batch(std::make_unique<MainBatch>(1024)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockWrapsRenderer::~BlockWrapsRenderer() = default;
|
BlockWrapsRenderer::~BlockWrapsRenderer() = default;
|
||||||
|
|
||||||
void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) {
|
void BlockWrapsRenderer::draw(const BlockWrapper& wrapper) {
|
||||||
const auto& chunks = *level.chunks;
|
|
||||||
|
|
||||||
auto textureRegion = util::get_texture_region(assets, wrapper.texture, "");
|
auto textureRegion = util::get_texture_region(assets, wrapper.texture, "");
|
||||||
|
|
||||||
auto& shader = assets.require<Shader>("entity");
|
auto& shader = assets.require<Shader>("entity");
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
class Assets;
|
class Assets;
|
||||||
class Player;
|
class Player;
|
||||||
class Level;
|
class Level;
|
||||||
|
class Chunks;
|
||||||
class DrawContext;
|
class DrawContext;
|
||||||
|
|
||||||
struct BlockWrapper {
|
struct BlockWrapper {
|
||||||
@ -20,6 +21,7 @@ struct BlockWrapper {
|
|||||||
class BlockWrapsRenderer {
|
class BlockWrapsRenderer {
|
||||||
const Assets& assets;
|
const Assets& assets;
|
||||||
const Level& level;
|
const Level& level;
|
||||||
|
const Chunks& chunks;
|
||||||
std::unique_ptr<MainBatch> batch;
|
std::unique_ptr<MainBatch> batch;
|
||||||
|
|
||||||
std::unordered_map<u64id_t, std::unique_ptr<BlockWrapper>> wrappers;
|
std::unordered_map<u64id_t, std::unique_ptr<BlockWrapper>> wrappers;
|
||||||
@ -27,7 +29,9 @@ class BlockWrapsRenderer {
|
|||||||
|
|
||||||
void draw(const BlockWrapper& wrapper);
|
void draw(const BlockWrapper& wrapper);
|
||||||
public:
|
public:
|
||||||
BlockWrapsRenderer(const Assets& assets, const Level& level);
|
BlockWrapsRenderer(
|
||||||
|
const Assets& assets, const Level& level, const Chunks& chunks
|
||||||
|
);
|
||||||
~BlockWrapsRenderer();
|
~BlockWrapsRenderer();
|
||||||
|
|
||||||
void draw(const DrawContext& ctx, const Player& player);
|
void draw(const DrawContext& ctx, const Player& player);
|
||||||
|
|||||||
@ -20,19 +20,22 @@ size_t ChunksRenderer::visibleChunks = 0;
|
|||||||
|
|
||||||
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
class RendererWorker : public util::Worker<std::shared_ptr<Chunk>, RendererResult> {
|
||||||
const Level& level;
|
const Level& level;
|
||||||
|
const Chunks& chunks;
|
||||||
BlocksRenderer renderer;
|
BlocksRenderer renderer;
|
||||||
public:
|
public:
|
||||||
RendererWorker(
|
RendererWorker(
|
||||||
const Level& level,
|
const Level& level,
|
||||||
|
const Chunks& chunks,
|
||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
) : level(level),
|
) : level(level),
|
||||||
|
chunks(chunks),
|
||||||
renderer(settings.graphics.chunkMaxVertices.get(),
|
renderer(settings.graphics.chunkMaxVertices.get(),
|
||||||
*level.content, cache, settings)
|
*level.content, cache, settings)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
|
RendererResult operator()(const std::shared_ptr<Chunk>& chunk) override {
|
||||||
renderer.build(chunk.get(), level.chunks.get());
|
renderer.build(chunk.get(), &chunks);
|
||||||
if (renderer.isCancelled()) {
|
if (renderer.isCancelled()) {
|
||||||
return RendererResult {
|
return RendererResult {
|
||||||
glm::ivec2(chunk->x, chunk->z), true, MeshData()};
|
glm::ivec2(chunk->x, chunk->z), true, MeshData()};
|
||||||
@ -45,28 +48,35 @@ public:
|
|||||||
|
|
||||||
ChunksRenderer::ChunksRenderer(
|
ChunksRenderer::ChunksRenderer(
|
||||||
const Level* level,
|
const Level* level,
|
||||||
|
const Chunks& chunks,
|
||||||
const Assets& assets,
|
const Assets& assets,
|
||||||
const Frustum& frustum,
|
const Frustum& frustum,
|
||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
const EngineSettings& settings
|
const EngineSettings& settings
|
||||||
) : level(*level),
|
)
|
||||||
|
: level(*level),
|
||||||
|
chunks(chunks),
|
||||||
assets(assets),
|
assets(assets),
|
||||||
frustum(frustum),
|
frustum(frustum),
|
||||||
settings(settings),
|
settings(settings),
|
||||||
threadPool(
|
threadPool(
|
||||||
"chunks-render-pool",
|
"chunks-render-pool",
|
||||||
[&](){return std::make_shared<RendererWorker>(*level, cache, settings);},
|
[&]() {
|
||||||
|
return std::make_shared<RendererWorker>(
|
||||||
|
*level, chunks, cache, settings
|
||||||
|
);
|
||||||
|
},
|
||||||
[&](RendererResult& result) {
|
[&](RendererResult& result) {
|
||||||
if (!result.cancelled) {
|
if (!result.cancelled) {
|
||||||
auto meshData = std::move(result.meshData);
|
auto meshData = std::move(result.meshData);
|
||||||
meshes[result.key] = ChunkMesh {
|
meshes[result.key] = ChunkMesh {
|
||||||
std::make_unique<Mesh>(meshData.mesh),
|
std::make_unique<Mesh>(meshData.mesh),
|
||||||
std::move(meshData.sortingMesh)
|
std::move(meshData.sortingMesh)};
|
||||||
};
|
|
||||||
}
|
}
|
||||||
inwork.erase(result.key);
|
inwork.erase(result.key);
|
||||||
}, settings.graphics.chunkMaxRenderers.get())
|
},
|
||||||
{
|
settings.graphics.chunkMaxRenderers.get()
|
||||||
|
) {
|
||||||
threadPool.setStopOnFail(false);
|
threadPool.setStopOnFail(false);
|
||||||
renderer = std::make_unique<BlocksRenderer>(
|
renderer = std::make_unique<BlocksRenderer>(
|
||||||
settings.graphics.chunkMaxVertices.get(),
|
settings.graphics.chunkMaxVertices.get(),
|
||||||
@ -83,7 +93,7 @@ const Mesh* ChunksRenderer::render(
|
|||||||
) {
|
) {
|
||||||
chunk->flags.modified = false;
|
chunk->flags.modified = false;
|
||||||
if (important) {
|
if (important) {
|
||||||
auto mesh = renderer->render(chunk.get(), level.chunks.get());
|
auto mesh = renderer->render(chunk.get(), &chunks);
|
||||||
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
meshes[glm::ivec2(chunk->x, chunk->z)] = ChunkMesh {
|
||||||
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
std::move(mesh.mesh), std::move(mesh.sortingMeshData)
|
||||||
};
|
};
|
||||||
@ -131,7 +141,7 @@ void ChunksRenderer::update() {
|
|||||||
const Mesh* ChunksRenderer::retrieveChunk(
|
const Mesh* ChunksRenderer::retrieveChunk(
|
||||||
size_t index, const Camera& camera, Shader& shader, bool culling
|
size_t index, const Camera& camera, Shader& shader, bool culling
|
||||||
) {
|
) {
|
||||||
auto chunk = level.chunks->getChunks()[index];
|
auto chunk = chunks.getChunks()[index];
|
||||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@ -163,7 +173,6 @@ const Mesh* ChunksRenderer::retrieveChunk(
|
|||||||
void ChunksRenderer::drawChunks(
|
void ChunksRenderer::drawChunks(
|
||||||
const Camera& camera, Shader& shader
|
const Camera& camera, Shader& shader
|
||||||
) {
|
) {
|
||||||
const auto& chunks = *level.chunks;
|
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
|
|
||||||
atlas.getTexture()->bind();
|
atlas.getTexture()->bind();
|
||||||
@ -232,7 +241,7 @@ void ChunksRenderer::drawSortedMeshes(const Camera& camera, Shader& shader) {
|
|||||||
frameid++;
|
frameid++;
|
||||||
|
|
||||||
bool culling = settings.graphics.frustumCulling.get();
|
bool culling = settings.graphics.frustumCulling.get();
|
||||||
const auto& chunks = level.chunks->getChunks();
|
const auto& chunks = this->chunks.getChunks();
|
||||||
const auto& cameraPos = camera.position;
|
const auto& cameraPos = camera.position;
|
||||||
const auto& atlas = assets.require<Atlas>("blocks");
|
const auto& atlas = assets.require<Atlas>("blocks");
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ class Level;
|
|||||||
class Camera;
|
class Camera;
|
||||||
class Shader;
|
class Shader;
|
||||||
class Assets;
|
class Assets;
|
||||||
|
class Chunks;
|
||||||
class Frustum;
|
class Frustum;
|
||||||
class BlocksRenderer;
|
class BlocksRenderer;
|
||||||
class ContentGfxCache;
|
class ContentGfxCache;
|
||||||
@ -42,6 +43,7 @@ struct RendererResult {
|
|||||||
|
|
||||||
class ChunksRenderer {
|
class ChunksRenderer {
|
||||||
const Level& level;
|
const Level& level;
|
||||||
|
const Chunks& chunks;
|
||||||
const Assets& assets;
|
const Assets& assets;
|
||||||
const Frustum& frustum;
|
const Frustum& frustum;
|
||||||
const EngineSettings& settings;
|
const EngineSettings& settings;
|
||||||
@ -57,6 +59,7 @@ class ChunksRenderer {
|
|||||||
public:
|
public:
|
||||||
ChunksRenderer(
|
ChunksRenderer(
|
||||||
const Level* level,
|
const Level* level,
|
||||||
|
const Chunks& chunks,
|
||||||
const Assets& assets,
|
const Assets& assets,
|
||||||
const Frustum& frustum,
|
const Frustum& frustum,
|
||||||
const ContentGfxCache& cache,
|
const ContentGfxCache& cache,
|
||||||
|
|||||||
@ -85,7 +85,7 @@ void Decorator::update(
|
|||||||
int index = currentIndex;
|
int index = currentIndex;
|
||||||
currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS;
|
currentIndex = (currentIndex + BIG_PRIME) % UPDATE_BLOCKS;
|
||||||
|
|
||||||
const auto& chunks = *level.chunks;
|
const auto& chunks = *player.chunks;
|
||||||
const auto& indices = *level.content->getIndices();
|
const auto& indices = *level.content->getIndices();
|
||||||
|
|
||||||
int lx = index % UPDATE_AREA_DIAMETER;
|
int lx = index % UPDATE_AREA_DIAMETER;
|
||||||
@ -108,7 +108,7 @@ void Decorator::update(float delta, const Camera& camera) {
|
|||||||
for (int i = 0; i < ITERATIONS; i++) {
|
for (int i = 0; i < ITERATIONS; i++) {
|
||||||
update(delta, pos, camera.position);
|
update(delta, pos, camera.position);
|
||||||
}
|
}
|
||||||
const auto& chunks = *level.chunks;
|
const auto& chunks = *player.chunks;
|
||||||
const auto& indices = *level.content->getIndices();
|
const auto& indices = *level.content->getIndices();
|
||||||
auto iter = blockEmitters.begin();
|
auto iter = blockEmitters.begin();
|
||||||
while (iter != blockEmitters.end()) {
|
while (iter != blockEmitters.end()) {
|
||||||
|
|||||||
@ -16,12 +16,17 @@ size_t ParticlesRenderer::visibleParticles = 0;
|
|||||||
size_t ParticlesRenderer::aliveEmitters = 0;
|
size_t ParticlesRenderer::aliveEmitters = 0;
|
||||||
|
|
||||||
ParticlesRenderer::ParticlesRenderer(
|
ParticlesRenderer::ParticlesRenderer(
|
||||||
const Assets& assets, const Level& level, const GraphicsSettings* settings
|
const Assets& assets,
|
||||||
|
const Level& level,
|
||||||
|
const Chunks& chunks,
|
||||||
|
const GraphicsSettings* settings
|
||||||
)
|
)
|
||||||
: batch(std::make_unique<MainBatch>(4096)),
|
: batch(std::make_unique<MainBatch>(4096)),
|
||||||
level(level),
|
level(level),
|
||||||
|
chunks(chunks),
|
||||||
assets(assets),
|
assets(assets),
|
||||||
settings(settings) {}
|
settings(settings) {
|
||||||
|
}
|
||||||
|
|
||||||
ParticlesRenderer::~ParticlesRenderer() = default;
|
ParticlesRenderer::~ParticlesRenderer() = default;
|
||||||
|
|
||||||
@ -44,7 +49,6 @@ void ParticlesRenderer::renderParticles(const Camera& camera, float delta) {
|
|||||||
const auto& right = camera.right;
|
const auto& right = camera.right;
|
||||||
const auto& up = camera.up;
|
const auto& up = camera.up;
|
||||||
|
|
||||||
const auto& chunks = *level.chunks;
|
|
||||||
bool backlight = settings->backlight.get();
|
bool backlight = settings->backlight.get();
|
||||||
|
|
||||||
std::vector<const Texture*> unusedTextures;
|
std::vector<const Texture*> unusedTextures;
|
||||||
|
|||||||
@ -10,12 +10,14 @@
|
|||||||
class Texture;
|
class Texture;
|
||||||
class Assets;
|
class Assets;
|
||||||
class Camera;
|
class Camera;
|
||||||
|
class Chunks;
|
||||||
class MainBatch;
|
class MainBatch;
|
||||||
class Level;
|
class Level;
|
||||||
struct GraphicsSettings;
|
struct GraphicsSettings;
|
||||||
|
|
||||||
class ParticlesRenderer {
|
class ParticlesRenderer {
|
||||||
const Level& level;
|
const Level& level;
|
||||||
|
const Chunks& chunks;
|
||||||
const Assets& assets;
|
const Assets& assets;
|
||||||
const GraphicsSettings* settings;
|
const GraphicsSettings* settings;
|
||||||
std::unordered_map<const Texture*, std::vector<Particle>> particles;
|
std::unordered_map<const Texture*, std::vector<Particle>> particles;
|
||||||
@ -29,6 +31,7 @@ public:
|
|||||||
ParticlesRenderer(
|
ParticlesRenderer(
|
||||||
const Assets& assets,
|
const Assets& assets,
|
||||||
const Level& level,
|
const Level& level,
|
||||||
|
const Chunks& chunks,
|
||||||
const GraphicsSettings* settings
|
const GraphicsSettings* settings
|
||||||
);
|
);
|
||||||
~ParticlesRenderer();
|
~ParticlesRenderer();
|
||||||
|
|||||||
@ -13,6 +13,7 @@
|
|||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "engine.hpp"
|
#include "engine.hpp"
|
||||||
#include "frontend/LevelFrontend.hpp"
|
#include "frontend/LevelFrontend.hpp"
|
||||||
|
#include "frontend/ContentGfxCache.hpp"
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
#include "items/ItemDef.hpp"
|
#include "items/ItemDef.hpp"
|
||||||
#include "items/ItemStack.hpp"
|
#include "items/ItemStack.hpp"
|
||||||
@ -58,37 +59,40 @@ bool WorldRenderer::showChunkBorders = false;
|
|||||||
bool WorldRenderer::showEntitiesDebug = false;
|
bool WorldRenderer::showEntitiesDebug = false;
|
||||||
|
|
||||||
WorldRenderer::WorldRenderer(
|
WorldRenderer::WorldRenderer(
|
||||||
Engine* engine, LevelFrontend& frontend, Player* player
|
Engine& engine, LevelFrontend& frontend, Player& player
|
||||||
)
|
)
|
||||||
: engine(engine),
|
: engine(engine),
|
||||||
level(frontend.getLevel()),
|
level(frontend.getLevel()),
|
||||||
player(player),
|
player(player),
|
||||||
assets(*engine->getAssets()),
|
assets(*engine.getAssets()),
|
||||||
frustumCulling(std::make_unique<Frustum>()),
|
frustumCulling(std::make_unique<Frustum>()),
|
||||||
lineBatch(std::make_unique<LineBatch>()),
|
lineBatch(std::make_unique<LineBatch>()),
|
||||||
batch3d(std::make_unique<Batch3D>(BATCH3D_CAPACITY)),
|
batch3d(std::make_unique<Batch3D>(BATCH3D_CAPACITY)),
|
||||||
modelBatch(std::make_unique<ModelBatch>(
|
modelBatch(std::make_unique<ModelBatch>(
|
||||||
MODEL_BATCH_CAPACITY, assets, *level.chunks, engine->getSettings()
|
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
|
||||||
)),
|
)),
|
||||||
particles(std::make_unique<ParticlesRenderer>(
|
particles(std::make_unique<ParticlesRenderer>(
|
||||||
assets, level, &engine->getSettings().graphics
|
assets, level, *player.chunks, &engine.getSettings().graphics
|
||||||
)),
|
)),
|
||||||
texts(std::make_unique<TextsRenderer>(*batch3d, assets, *frustumCulling)),
|
texts(std::make_unique<TextsRenderer>(*batch3d, assets, *frustumCulling)),
|
||||||
guides(std::make_unique<GuidesRenderer>()),
|
guides(std::make_unique<GuidesRenderer>()),
|
||||||
chunks(std::make_unique<ChunksRenderer>(
|
chunks(std::make_unique<ChunksRenderer>(
|
||||||
&level,
|
&level,
|
||||||
|
*player.chunks,
|
||||||
assets,
|
assets,
|
||||||
*frustumCulling,
|
*frustumCulling,
|
||||||
frontend.getContentGfxCache(),
|
frontend.getContentGfxCache(),
|
||||||
engine->getSettings()
|
engine.getSettings()
|
||||||
)),
|
)),
|
||||||
blockWraps(std::make_unique<BlockWrapsRenderer>(assets, level)) {
|
blockWraps(
|
||||||
auto& settings = engine->getSettings();
|
std::make_unique<BlockWrapsRenderer>(assets, level, *player.chunks)
|
||||||
|
) {
|
||||||
|
auto& settings = engine.getSettings();
|
||||||
level.events->listen(
|
level.events->listen(
|
||||||
EVT_CHUNK_HIDDEN,
|
EVT_CHUNK_HIDDEN,
|
||||||
[this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); }
|
[this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); }
|
||||||
);
|
);
|
||||||
auto assets = engine->getAssets();
|
auto assets = engine.getAssets();
|
||||||
skybox = std::make_unique<Skybox>(
|
skybox = std::make_unique<Skybox>(
|
||||||
settings.graphics.skyboxResolution.get(),
|
settings.graphics.skyboxResolution.get(),
|
||||||
assets->require<Shader>("skybox_gen")
|
assets->require<Shader>("skybox_gen")
|
||||||
@ -119,8 +123,8 @@ void WorldRenderer::setupWorldShader(
|
|||||||
auto indices = level.content->getIndices();
|
auto indices = level.content->getIndices();
|
||||||
// Light emission when an emissive item is chosen
|
// Light emission when an emissive item is chosen
|
||||||
{
|
{
|
||||||
auto inventory = player->getInventory();
|
auto inventory = player.getInventory();
|
||||||
ItemStack& stack = inventory->getSlot(player->getChosenSlot());
|
ItemStack& stack = inventory->getSlot(player.getChosenSlot());
|
||||||
auto& item = indices->items.require(stack.getItemId());
|
auto& item = indices->items.require(stack.getItemId());
|
||||||
float multiplier = 0.5f;
|
float multiplier = 0.5f;
|
||||||
shader.uniform3f(
|
shader.uniform3f(
|
||||||
@ -143,7 +147,7 @@ void WorldRenderer::renderLevel(
|
|||||||
) {
|
) {
|
||||||
texts->render(ctx, camera, settings, hudVisible, false);
|
texts->render(ctx, camera, settings, hudVisible, false);
|
||||||
|
|
||||||
bool culling = engine->getSettings().graphics.frustumCulling.get();
|
bool culling = engine.getSettings().graphics.frustumCulling.get();
|
||||||
float fogFactor =
|
float fogFactor =
|
||||||
15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
|
15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
|
||||||
|
|
||||||
@ -172,7 +176,7 @@ void WorldRenderer::renderLevel(
|
|||||||
setupWorldShader(shader, camera, settings, fogFactor);
|
setupWorldShader(shader, camera, settings, fogFactor);
|
||||||
|
|
||||||
chunks->drawChunks(camera, shader);
|
chunks->drawChunks(camera, shader);
|
||||||
blockWraps->draw(ctx, *player);
|
blockWraps->draw(ctx, player);
|
||||||
|
|
||||||
if (hudVisible) {
|
if (hudVisible) {
|
||||||
renderLines(camera, linesShader, ctx);
|
renderLines(camera, linesShader, ctx);
|
||||||
@ -188,11 +192,11 @@ void WorldRenderer::renderLevel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderBlockSelection() {
|
void WorldRenderer::renderBlockSelection() {
|
||||||
const auto& selection = player->selection;
|
const auto& selection = player.selection;
|
||||||
auto indices = level.content->getIndices();
|
auto indices = level.content->getIndices();
|
||||||
blockid_t id = selection.vox.id;
|
blockid_t id = selection.vox.id;
|
||||||
auto& block = indices->blocks.require(id);
|
auto& block = indices->blocks.require(id);
|
||||||
const glm::ivec3 pos = player->selection.position;
|
const glm::ivec3 pos = player.selection.position;
|
||||||
const glm::vec3 point = selection.hitPosition;
|
const glm::vec3 point = selection.hitPosition;
|
||||||
const glm::vec3 norm = selection.normal;
|
const glm::vec3 norm = selection.normal;
|
||||||
|
|
||||||
@ -207,7 +211,7 @@ void WorldRenderer::renderBlockSelection() {
|
|||||||
lineBatch->box(
|
lineBatch->box(
|
||||||
center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 0.5f)
|
center, size + glm::vec3(0.01), glm::vec4(0.f, 0.f, 0.f, 0.5f)
|
||||||
);
|
);
|
||||||
if (player->debug) {
|
if (player.debug) {
|
||||||
lineBatch->line(
|
lineBatch->line(
|
||||||
point, point + norm * 0.5f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f)
|
point, point + norm * 0.5f, glm::vec4(1.0f, 0.0f, 1.0f, 1.0f)
|
||||||
);
|
);
|
||||||
@ -221,12 +225,12 @@ void WorldRenderer::renderLines(
|
|||||||
) {
|
) {
|
||||||
linesShader.use();
|
linesShader.use();
|
||||||
linesShader.uniformMatrix("u_projview", camera.getProjView());
|
linesShader.uniformMatrix("u_projview", camera.getProjView());
|
||||||
if (player->selection.vox.id != BLOCK_VOID) {
|
if (player.selection.vox.id != BLOCK_VOID) {
|
||||||
renderBlockSelection();
|
renderBlockSelection();
|
||||||
}
|
}
|
||||||
if (player->debug && showEntitiesDebug) {
|
if (player.debug && showEntitiesDebug) {
|
||||||
auto ctx = pctx.sub(lineBatch.get());
|
auto ctx = pctx.sub(lineBatch.get());
|
||||||
bool culling = engine->getSettings().graphics.frustumCulling.get();
|
bool culling = engine.getSettings().graphics.frustumCulling.get();
|
||||||
level.entities->renderDebug(
|
level.entities->renderDebug(
|
||||||
*lineBatch, culling ? frustumCulling.get() : nullptr, ctx
|
*lineBatch, culling ? frustumCulling.get() : nullptr, ctx
|
||||||
);
|
);
|
||||||
@ -240,8 +244,8 @@ void WorldRenderer::renderHands(
|
|||||||
auto indices = level.content->getIndices();
|
auto indices = level.content->getIndices();
|
||||||
|
|
||||||
// get current chosen item
|
// get current chosen item
|
||||||
const auto& inventory = player->getInventory();
|
const auto& inventory = player.getInventory();
|
||||||
int slot = player->getChosenSlot();
|
int slot = player.getChosenSlot();
|
||||||
const ItemStack& stack = inventory->getSlot(slot);
|
const ItemStack& stack = inventory->getSlot(slot);
|
||||||
const auto& def = indices->items.require(stack.getItemId());
|
const auto& def = indices->items.require(stack.getItemId());
|
||||||
|
|
||||||
@ -270,8 +274,8 @@ void WorldRenderer::renderHands(
|
|||||||
glm::mat4(1.0f), -glm::pi<float>() * 0.5f, glm::vec3(0, 1, 0)
|
glm::mat4(1.0f), -glm::pi<float>() * 0.5f, glm::vec3(0, 1, 0)
|
||||||
);
|
);
|
||||||
prevRotation = rotation;
|
prevRotation = rotation;
|
||||||
auto offset = -(camera.position - player->getPosition());
|
auto offset = -(camera.position - player.getPosition());
|
||||||
float angle = glm::radians(player->cam.x - 90);
|
float angle = glm::radians(player.cam.x - 90);
|
||||||
float cos = glm::cos(angle);
|
float cos = glm::cos(angle);
|
||||||
float sin = glm::sin(angle);
|
float sin = glm::sin(angle);
|
||||||
|
|
||||||
@ -289,7 +293,7 @@ void WorldRenderer::renderHands(
|
|||||||
nullptr
|
nullptr
|
||||||
);
|
);
|
||||||
Window::clearDepth();
|
Window::clearDepth();
|
||||||
setupWorldShader(entityShader, hudcam, engine->getSettings(), 0.0f);
|
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
|
||||||
skybox->bind();
|
skybox->bind();
|
||||||
modelBatch->render();
|
modelBatch->render();
|
||||||
modelBatch->setLightsOffset(glm::vec3());
|
modelBatch->setLightsOffset(glm::vec3());
|
||||||
@ -309,12 +313,12 @@ void WorldRenderer::draw(
|
|||||||
const Viewport& vp = pctx.getViewport();
|
const Viewport& vp = pctx.getViewport();
|
||||||
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
|
camera.aspect = vp.getWidth() / static_cast<float>(vp.getHeight());
|
||||||
|
|
||||||
const auto& settings = engine->getSettings();
|
const auto& settings = engine.getSettings();
|
||||||
const auto& worldInfo = world->getInfo();
|
const auto& worldInfo = world->getInfo();
|
||||||
|
|
||||||
skybox->refresh(pctx, worldInfo.daytime, 1.0f + worldInfo.fog * 2.0f, 4);
|
skybox->refresh(pctx, worldInfo.daytime, 1.0f + worldInfo.fog * 2.0f, 4);
|
||||||
|
|
||||||
const auto& assets = *engine->getAssets();
|
const auto& assets = *engine.getAssets();
|
||||||
auto& linesShader = assets.require<Shader>("lines");
|
auto& linesShader = assets.require<Shader>("lines");
|
||||||
|
|
||||||
/* World render scope with diegetic HUD included */ {
|
/* World render scope with diegetic HUD included */ {
|
||||||
@ -333,12 +337,12 @@ void WorldRenderer::draw(
|
|||||||
renderLevel(ctx, camera, settings, delta, pause, hudVisible);
|
renderLevel(ctx, camera, settings, delta, pause, hudVisible);
|
||||||
// Debug lines
|
// Debug lines
|
||||||
if (hudVisible) {
|
if (hudVisible) {
|
||||||
if (player->debug) {
|
if (player.debug) {
|
||||||
guides->renderDebugLines(
|
guides->renderDebugLines(
|
||||||
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
ctx, camera, *lineBatch, linesShader, showChunkBorders
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (player->currentCamera == player->fpCamera) {
|
if (player.currentCamera == player.fpCamera) {
|
||||||
renderHands(camera, delta * !pause);
|
renderHands(camera, delta * !pause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -359,10 +363,10 @@ void WorldRenderer::draw(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
||||||
int x = std::floor(player->currentCamera->position.x);
|
int x = std::floor(player.currentCamera->position.x);
|
||||||
int y = std::floor(player->currentCamera->position.y);
|
int y = std::floor(player.currentCamera->position.y);
|
||||||
int z = std::floor(player->currentCamera->position.z);
|
int z = std::floor(player.currentCamera->position.z);
|
||||||
auto block = level.chunks->get(x, y, z);
|
auto block = player.chunks->get(x, y, z);
|
||||||
if (block && block->id) {
|
if (block && block->id) {
|
||||||
const auto& def =
|
const auto& def =
|
||||||
level.content->getIndices()->blocks.require(block->id);
|
level.content->getIndices()->blocks.require(block->id);
|
||||||
@ -381,7 +385,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
|||||||
batch3d->begin();
|
batch3d->begin();
|
||||||
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
|
shader.uniformMatrix("u_projview", glm::mat4(1.0f));
|
||||||
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
|
shader.uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||||
auto light = level.chunks->getLight(x, y, z);
|
auto light = player.chunks->getLight(x, y, z);
|
||||||
float s = Lightmap::extract(light, 3) / 15.0f;
|
float s = Lightmap::extract(light, 3) / 15.0f;
|
||||||
glm::vec4 tint(
|
glm::vec4 tint(
|
||||||
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
|
glm::min(1.0f, Lightmap::extract(light, 0) / 15.0f + s),
|
||||||
|
|||||||
@ -32,9 +32,9 @@ class Assets;
|
|||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
|
||||||
class WorldRenderer {
|
class WorldRenderer {
|
||||||
Engine* engine;
|
Engine& engine;
|
||||||
const Level& level;
|
const Level& level;
|
||||||
Player* player;
|
Player& player;
|
||||||
const Assets& assets;
|
const Assets& assets;
|
||||||
std::unique_ptr<Frustum> frustumCulling;
|
std::unique_ptr<Frustum> frustumCulling;
|
||||||
std::unique_ptr<LineBatch> lineBatch;
|
std::unique_ptr<LineBatch> lineBatch;
|
||||||
@ -74,7 +74,7 @@ public:
|
|||||||
static bool showChunkBorders;
|
static bool showChunkBorders;
|
||||||
static bool showEntitiesDebug;
|
static bool showEntitiesDebug;
|
||||||
|
|
||||||
WorldRenderer(Engine* engine, LevelFrontend& frontend, Player* player);
|
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
|
||||||
~WorldRenderer();
|
~WorldRenderer();
|
||||||
|
|
||||||
void draw(
|
void draw(
|
||||||
|
|||||||
@ -612,7 +612,7 @@ static std::shared_ptr<UINode> readInventory(UiXmlReader& reader, const xml::xml
|
|||||||
static std::shared_ptr<UINode> readPageBox(UiXmlReader& reader, const xml::xmlelement& element) {
|
static std::shared_ptr<UINode> readPageBox(UiXmlReader& reader, const xml::xmlelement& element) {
|
||||||
auto menu = std::make_shared<Menu>();
|
auto menu = std::make_shared<Menu>();
|
||||||
// fixme
|
// fixme
|
||||||
menu->setPageLoader(menus::create_page_loader(scripting::engine));
|
menu->setPageLoader(menus::create_page_loader(*scripting::engine));
|
||||||
_readContainer(reader, element, *menu);
|
_readContainer(reader, element, *menu);
|
||||||
|
|
||||||
return menu;
|
return menu;
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
#include "BlocksController.hpp"
|
#include "BlocksController.hpp"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
@ -11,17 +13,19 @@
|
|||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "objects/Player.hpp"
|
||||||
|
#include "objects/Players.hpp"
|
||||||
|
|
||||||
BlocksController::BlocksController(const Level& level, uint padding)
|
BlocksController::BlocksController(const Level& level, Lighting* lighting)
|
||||||
: level(level),
|
: level(level),
|
||||||
chunks(*level.chunks),
|
chunks(*level.chunks),
|
||||||
lighting(*level.lighting),
|
lighting(lighting),
|
||||||
randTickClock(20, 3),
|
randTickClock(20, 3),
|
||||||
blocksTickClock(20, 1),
|
blocksTickClock(20, 1),
|
||||||
worldTickClock(20, 1),
|
worldTickClock(20, 1) {
|
||||||
padding(padding) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::updateSides(int x, int y, int z) {
|
void BlocksController::updateSides(int x, int y, int z) {
|
||||||
@ -34,7 +38,7 @@ void BlocksController::updateSides(int x, int y, int z) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::updateSides(int x, int y, int z, int w, int h, int d) {
|
void BlocksController::updateSides(int x, int y, int z, int w, int h, int d) {
|
||||||
voxel* vox = chunks.get(x, y, z);
|
voxel* vox = blocks_agent::get(chunks, x, y, z);
|
||||||
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||||
const auto& rot = def.rotations.variants[vox->state.rotation];
|
const auto& rot = def.rotations.variants[vox->state.rotation];
|
||||||
const auto& xaxis = rot.axisX;
|
const auto& xaxis = rot.axisX;
|
||||||
@ -62,8 +66,10 @@ void BlocksController::breakBlock(
|
|||||||
onBlockInteraction(
|
onBlockInteraction(
|
||||||
player, glm::ivec3(x, y, z), def, BlockInteraction::destruction
|
player, glm::ivec3(x, y, z), def, BlockInteraction::destruction
|
||||||
);
|
);
|
||||||
chunks.set(x, y, z, 0, {});
|
blocks_agent::set(chunks, x, y, z, 0, {});
|
||||||
lighting.onBlockSet(x, y, z, 0);
|
if (lighting) {
|
||||||
|
lighting->onBlockSet(x, y, z, 0);
|
||||||
|
}
|
||||||
scripting::on_block_broken(player, def, glm::ivec3(x, y, z));
|
scripting::on_block_broken(player, def, glm::ivec3(x, y, z));
|
||||||
if (def.rt.extended) {
|
if (def.rt.extended) {
|
||||||
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
||||||
@ -75,7 +81,7 @@ void BlocksController::breakBlock(
|
|||||||
void BlocksController::placeBlock(
|
void BlocksController::placeBlock(
|
||||||
Player* player, const Block& def, blockstate state, int x, int y, int z
|
Player* player, const Block& def, blockstate state, int x, int y, int z
|
||||||
) {
|
) {
|
||||||
auto voxel = chunks.get(x, y, z);
|
auto voxel = blocks_agent::get(chunks, x, y, z);
|
||||||
if (voxel == nullptr) {
|
if (voxel == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -85,8 +91,10 @@ void BlocksController::placeBlock(
|
|||||||
onBlockInteraction(
|
onBlockInteraction(
|
||||||
player, glm::ivec3(x, y, z), def, BlockInteraction::placing
|
player, glm::ivec3(x, y, z), def, BlockInteraction::placing
|
||||||
);
|
);
|
||||||
chunks.set(x, y, z, def.rt.id, state);
|
blocks_agent::set(chunks, x, y, z, def.rt.id, state);
|
||||||
lighting.onBlockSet(x, y, z, def.rt.id);
|
if (lighting) {
|
||||||
|
lighting->onBlockSet(x, y, z, def.rt.id);
|
||||||
|
}
|
||||||
scripting::on_block_placed(player, def, glm::ivec3(x, y, z));
|
scripting::on_block_placed(player, def, glm::ivec3(x, y, z));
|
||||||
if (def.rt.extended) {
|
if (def.rt.extended) {
|
||||||
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
updateSides(x, y, z , def.size.x, def.size.y, def.size.z);
|
||||||
@ -96,12 +104,12 @@ void BlocksController::placeBlock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::updateBlock(int x, int y, int z) {
|
void BlocksController::updateBlock(int x, int y, int z) {
|
||||||
voxel* vox = chunks.get(x, y, z);
|
voxel* vox = blocks_agent::get(chunks, x, y, z);
|
||||||
if (vox == nullptr) return;
|
if (vox == nullptr) return;
|
||||||
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
const auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||||
if (def.grounded) {
|
if (def.grounded) {
|
||||||
const auto& vec = get_ground_direction(def, vox->state.rotation);
|
const auto& vec = get_ground_direction(def, vox->state.rotation);
|
||||||
if (!chunks.isSolidBlock(x + vec.x, y + vec.y, z + vec.z)) {
|
if (!blocks_agent::is_solid_at(chunks, x + vec.x, y + vec.y, z + vec.z)) {
|
||||||
breakBlock(nullptr, def, x, y, z);
|
breakBlock(nullptr, def, x, y, z);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -111,9 +119,9 @@ void BlocksController::updateBlock(int x, int y, int z) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::update(float delta) {
|
void BlocksController::update(float delta, uint padding) {
|
||||||
if (randTickClock.update(delta)) {
|
if (randTickClock.update(delta)) {
|
||||||
randomTick(randTickClock.getPart(), randTickClock.getParts());
|
randomTick(randTickClock.getPart(), randTickClock.getParts(), padding);
|
||||||
}
|
}
|
||||||
if (blocksTickClock.update(delta)) {
|
if (blocksTickClock.update(delta)) {
|
||||||
onBlocksTick(blocksTickClock.getPart(), blocksTickClock.getParts());
|
onBlocksTick(blocksTickClock.getPart(), blocksTickClock.getParts());
|
||||||
@ -160,8 +168,15 @@ void BlocksController::randomTick(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::randomTick(int tickid, int parts) {
|
void BlocksController::randomTick(int tickid, int parts, uint padding) {
|
||||||
auto indices = level.content->getIndices();
|
auto indices = level.content->getIndices();
|
||||||
|
|
||||||
|
std::set<uint64_t> chunksIterated;
|
||||||
|
|
||||||
|
for (const auto& [pid, player] : *level.players) {
|
||||||
|
const auto& chunks = *player->chunks;
|
||||||
|
int offsetX = chunks.getOffsetX();
|
||||||
|
int offsetY = chunks.getOffsetY();
|
||||||
int width = chunks.getWidth();
|
int width = chunks.getWidth();
|
||||||
int height = chunks.getHeight();
|
int height = chunks.getHeight();
|
||||||
int segments = 4;
|
int segments = 4;
|
||||||
@ -176,14 +191,27 @@ void BlocksController::randomTick(int tickid, int parts) {
|
|||||||
if (chunk == nullptr || !chunk->flags.lighted) {
|
if (chunk == nullptr || !chunk->flags.lighted) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
union {
|
||||||
|
int32_t pos[2];
|
||||||
|
uint64_t key;
|
||||||
|
} posU;
|
||||||
|
posU.pos[0] = x + offsetX;
|
||||||
|
posU.pos[1] = z + offsetY;
|
||||||
|
if (chunksIterated.find(posU.key) != chunksIterated.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
chunksIterated.insert(posU.key);
|
||||||
randomTick(*chunk, segments, indices);
|
randomTick(*chunk, segments, indices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
||||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
auto chunk = blocks_agent::get_chunk(
|
||||||
if (chunk == nullptr) {
|
chunks, floordiv<CHUNK_W>(x), floordiv<CHUNK_D>(z)
|
||||||
|
);
|
||||||
|
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
int lx = x - chunk->x * CHUNK_W;
|
int lx = x - chunk->x * CHUNK_W;
|
||||||
@ -203,7 +231,9 @@ int64_t BlocksController::createBlockInventory(int x, int y, int z) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
||||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
auto chunk = blocks_agent::get_chunk(
|
||||||
|
chunks, floordiv<CHUNK_W>(x), floordiv<CHUNK_D>(z)
|
||||||
|
);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
throw std::runtime_error("block does not exists");
|
throw std::runtime_error("block does not exists");
|
||||||
}
|
}
|
||||||
@ -216,7 +246,9 @@ void BlocksController::bindInventory(int64_t invid, int x, int y, int z) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BlocksController::unbindInventory(int x, int y, int z) {
|
void BlocksController::unbindInventory(int x, int y, int z) {
|
||||||
auto chunk = chunks.getChunkByVoxel(x, y, z);
|
auto chunk = blocks_agent::get_chunk(
|
||||||
|
chunks, floordiv<CHUNK_W>(x), floordiv<CHUNK_D>(z)
|
||||||
|
);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
throw std::runtime_error("block does not exists");
|
throw std::runtime_error("block does not exists");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,27 +14,27 @@ class Level;
|
|||||||
class Chunk;
|
class Chunk;
|
||||||
class Chunks;
|
class Chunks;
|
||||||
class Lighting;
|
class Lighting;
|
||||||
|
class GlobalChunks;
|
||||||
class ContentIndices;
|
class ContentIndices;
|
||||||
|
|
||||||
enum class BlockInteraction { step, destruction, placing };
|
enum class BlockInteraction { step, destruction, placing };
|
||||||
|
|
||||||
/// @brief Player argument is nullable
|
/// @brief Player argument is nullable
|
||||||
using on_block_interaction = std::function<
|
using on_block_interaction = std::function<
|
||||||
void(Player*, const glm::ivec3&, const Block&, BlockInteraction type)>;
|
void(Player*, const glm::ivec3&, const Block&, BlockInteraction)>;
|
||||||
|
|
||||||
/// BlocksController manages block updates and data (inventories, metadata)
|
/// BlocksController manages block updates and data (inventories, metadata)
|
||||||
class BlocksController {
|
class BlocksController {
|
||||||
const Level& level;
|
const Level& level;
|
||||||
Chunks& chunks;
|
GlobalChunks& chunks;
|
||||||
Lighting& lighting;
|
Lighting* lighting;
|
||||||
util::Clock randTickClock;
|
util::Clock randTickClock;
|
||||||
util::Clock blocksTickClock;
|
util::Clock blocksTickClock;
|
||||||
util::Clock worldTickClock;
|
util::Clock worldTickClock;
|
||||||
uint padding;
|
|
||||||
FastRandom random {};
|
FastRandom random {};
|
||||||
std::vector<on_block_interaction> blockInteractionCallbacks;
|
std::vector<on_block_interaction> blockInteractionCallbacks;
|
||||||
public:
|
public:
|
||||||
BlocksController(const Level& level, uint padding);
|
BlocksController(const Level& level, Lighting* lighting);
|
||||||
|
|
||||||
void updateSides(int x, int y, int z);
|
void updateSides(int x, int y, int z);
|
||||||
void updateSides(int x, int y, int z, int w, int h, int d);
|
void updateSides(int x, int y, int z, int w, int h, int d);
|
||||||
@ -45,11 +45,11 @@ public:
|
|||||||
Player* player, const Block& def, blockstate state, int x, int y, int z
|
Player* player, const Block& def, blockstate state, int x, int y, int z
|
||||||
);
|
);
|
||||||
|
|
||||||
void update(float delta);
|
void update(float delta, uint padding);
|
||||||
void randomTick(
|
void randomTick(
|
||||||
const Chunk& chunk, int segments, const ContentIndices* indices
|
const Chunk& chunk, int segments, const ContentIndices* indices
|
||||||
);
|
);
|
||||||
void randomTick(int tickid, int parts);
|
void randomTick(int tickid, int parts, uint padding);
|
||||||
void onBlocksTick(int tickid, int parts);
|
void onBlocksTick(int tickid, int parts);
|
||||||
int64_t createBlockInventory(int x, int y, int z);
|
int64_t createBlockInventory(int x, int y, int z);
|
||||||
void bindInventory(int64_t invid, int x, int y, int z);
|
void bindInventory(int64_t invid, int x, int y, int z);
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
#include "ChunksController.hpp"
|
#include "ChunksController.hpp"
|
||||||
|
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
@ -11,6 +9,7 @@
|
|||||||
#include "lighting/Lighting.hpp"
|
#include "lighting/Lighting.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "util/timeutil.hpp"
|
#include "util/timeutil.hpp"
|
||||||
|
#include "objects/Player.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
@ -22,11 +21,8 @@
|
|||||||
const uint MAX_WORK_PER_FRAME = 128;
|
const uint MAX_WORK_PER_FRAME = 128;
|
||||||
const uint MIN_SURROUNDING = 9;
|
const uint MIN_SURROUNDING = 9;
|
||||||
|
|
||||||
ChunksController::ChunksController(Level& level, uint padding)
|
ChunksController::ChunksController(Level& level)
|
||||||
: level(level),
|
: level(level),
|
||||||
chunks(*level.chunks),
|
|
||||||
lighting(*level.lighting),
|
|
||||||
padding(padding),
|
|
||||||
generator(std::make_unique<WorldGenerator>(
|
generator(std::make_unique<WorldGenerator>(
|
||||||
level.content->generators.require(level.getWorld()->getGenerator()),
|
level.content->generators.require(level.getWorld()->getGenerator()),
|
||||||
level.content,
|
level.content,
|
||||||
@ -36,15 +32,19 @@ ChunksController::ChunksController(Level& level, uint padding)
|
|||||||
ChunksController::~ChunksController() = default;
|
ChunksController::~ChunksController() = default;
|
||||||
|
|
||||||
void ChunksController::update(
|
void ChunksController::update(
|
||||||
int64_t maxDuration, int loadDistance, int centerX, int centerY
|
int64_t maxDuration, int loadDistance, uint padding, Player& player
|
||||||
) {
|
) const {
|
||||||
|
const auto& position = player.getPosition();
|
||||||
|
int centerX = floordiv<CHUNK_W>(position.x);
|
||||||
|
int centerY = floordiv<CHUNK_D>(position.z);
|
||||||
|
|
||||||
generator->update(centerX, centerY, loadDistance);
|
generator->update(centerX, centerY, loadDistance);
|
||||||
|
|
||||||
int64_t mcstotal = 0;
|
int64_t mcstotal = 0;
|
||||||
|
|
||||||
for (uint i = 0; i < MAX_WORK_PER_FRAME; i++) {
|
for (uint i = 0; i < MAX_WORK_PER_FRAME; i++) {
|
||||||
timeutil::Timer timer;
|
timeutil::Timer timer;
|
||||||
if (loadVisible()) {
|
if (loadVisible(player, padding)) {
|
||||||
int64_t mcs = timer.stop();
|
int64_t mcs = timer.stop();
|
||||||
if (mcstotal + mcs < maxDuration * 1000) {
|
if (mcstotal + mcs < maxDuration * 1000) {
|
||||||
mcstotal += mcs;
|
mcstotal += mcs;
|
||||||
@ -55,7 +55,8 @@ void ChunksController::update(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunksController::loadVisible() {
|
bool ChunksController::loadVisible(const Player& player, uint padding) const {
|
||||||
|
const auto& chunks = *player.chunks;
|
||||||
int sizeX = chunks.getWidth();
|
int sizeX = chunks.getWidth();
|
||||||
int sizeY = chunks.getHeight();
|
int sizeY = chunks.getHeight();
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ bool ChunksController::loadVisible() {
|
|||||||
auto& chunk = chunks.getChunks()[index];
|
auto& chunk = chunks.getChunks()[index];
|
||||||
if (chunk != nullptr) {
|
if (chunk != nullptr) {
|
||||||
if (chunk->flags.loaded && !chunk->flags.lighted) {
|
if (chunk->flags.loaded && !chunk->flags.lighted) {
|
||||||
if (buildLights(chunk)) {
|
if (buildLights(player, chunk)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,32 +94,35 @@ bool ChunksController::loadVisible() {
|
|||||||
}
|
}
|
||||||
int offsetX = chunks.getOffsetX();
|
int offsetX = chunks.getOffsetX();
|
||||||
int offsetY = chunks.getOffsetY();
|
int offsetY = chunks.getOffsetY();
|
||||||
createChunk(nearX + offsetX, nearZ + offsetY);
|
createChunk(player, nearX + offsetX, nearZ + offsetY);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ChunksController::buildLights(const std::shared_ptr<Chunk>& chunk) {
|
bool ChunksController::buildLights(const Player& player, const std::shared_ptr<Chunk>& chunk) const {
|
||||||
int surrounding = 0;
|
int surrounding = 0;
|
||||||
for (int oz = -1; oz <= 1; oz++) {
|
for (int oz = -1; oz <= 1; oz++) {
|
||||||
for (int ox = -1; ox <= 1; ox++) {
|
for (int ox = -1; ox <= 1; ox++) {
|
||||||
if (chunks.getChunk(chunk->x + ox, chunk->z + oz)) surrounding++;
|
if (player.chunks->getChunk(chunk->x + ox, chunk->z + oz))
|
||||||
|
surrounding++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (surrounding == MIN_SURROUNDING) {
|
if (surrounding == MIN_SURROUNDING) {
|
||||||
|
if (lighting) {
|
||||||
bool lightsCache = chunk->flags.loadedLights;
|
bool lightsCache = chunk->flags.loadedLights;
|
||||||
if (!lightsCache) {
|
if (!lightsCache) {
|
||||||
lighting.buildSkyLight(chunk->x, chunk->z);
|
lighting->buildSkyLight(chunk->x, chunk->z);
|
||||||
|
}
|
||||||
|
lighting->onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
||||||
}
|
}
|
||||||
lighting.onChunkLoaded(chunk->x, chunk->z, !lightsCache);
|
|
||||||
chunk->flags.lighted = true;
|
chunk->flags.lighted = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChunksController::createChunk(int x, int z) {
|
void ChunksController::createChunk(const Player& player, int x, int z) const {
|
||||||
auto chunk = level.chunksStorage->create(x, z);
|
auto chunk = level.chunks->create(x, z);
|
||||||
chunks.putChunk(chunk);
|
player.chunks->putChunk(chunk);
|
||||||
auto& chunkFlags = chunk->flags;
|
auto& chunkFlags = chunk->flags;
|
||||||
|
|
||||||
if (!chunkFlags.loaded) {
|
if (!chunkFlags.loaded) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
class Level;
|
class Level;
|
||||||
class Chunk;
|
class Chunk;
|
||||||
class Chunks;
|
class Chunks;
|
||||||
|
class Player;
|
||||||
class Lighting;
|
class Lighting;
|
||||||
class WorldGenerator;
|
class WorldGenerator;
|
||||||
|
|
||||||
@ -14,25 +15,22 @@ class WorldGenerator;
|
|||||||
class ChunksController {
|
class ChunksController {
|
||||||
private:
|
private:
|
||||||
Level& level;
|
Level& level;
|
||||||
Chunks& chunks;
|
|
||||||
Lighting& lighting;
|
|
||||||
uint padding;
|
|
||||||
std::unique_ptr<WorldGenerator> generator;
|
std::unique_ptr<WorldGenerator> generator;
|
||||||
|
|
||||||
/// @brief Process one chunk: load it or calculate lights for it
|
/// @brief Process one chunk: load it or calculate lights for it
|
||||||
bool loadVisible();
|
bool loadVisible(const Player& player, uint padding) const;
|
||||||
bool buildLights(const std::shared_ptr<Chunk>& chunk);
|
bool buildLights(const Player& player, const std::shared_ptr<Chunk>& chunk) const;
|
||||||
void createChunk(int x, int y);
|
void createChunk(const Player& player, int x, int y) const;
|
||||||
public:
|
public:
|
||||||
ChunksController(Level& level, uint padding);
|
std::unique_ptr<Lighting> lighting;
|
||||||
|
|
||||||
|
ChunksController(Level& level);
|
||||||
~ChunksController();
|
~ChunksController();
|
||||||
|
|
||||||
/// @param maxDuration milliseconds reserved for chunks loading
|
/// @param maxDuration milliseconds reserved for chunks loading
|
||||||
void update(
|
void update(
|
||||||
int64_t maxDuration,
|
int64_t maxDuration, int loadDistance, uint padding, Player& player
|
||||||
int loadDistance,
|
) const;
|
||||||
int centerX,
|
|
||||||
int centerY);
|
|
||||||
|
|
||||||
const WorldGenerator* getGenerator() const {
|
const WorldGenerator* getGenerator() const {
|
||||||
return generator.get();
|
return generator.get();
|
||||||
|
|||||||
@ -27,13 +27,13 @@ namespace fs = std::filesystem;
|
|||||||
|
|
||||||
static debug::Logger logger("engine-control");
|
static debug::Logger logger("engine-control");
|
||||||
|
|
||||||
EngineController::EngineController(Engine* engine) : engine(engine) {
|
EngineController::EngineController(Engine& engine) : engine(engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineController::deleteWorld(const std::string& name) {
|
void EngineController::deleteWorld(const std::string& name) {
|
||||||
fs::path folder = engine->getPaths()->getWorldFolderByName(name);
|
fs::path folder = engine.getPaths().getWorldFolderByName(name);
|
||||||
guiutil::confirm(
|
guiutil::confirm(
|
||||||
engine->getGUI(),
|
engine.getGUI(),
|
||||||
langs::get(L"delete-confirm", L"world") + L" (" +
|
langs::get(L"delete-confirm", L"world") + L" (" +
|
||||||
util::str2wstr_utf8(folder.u8string()) + L")",
|
util::str2wstr_utf8(folder.u8string()) + L")",
|
||||||
[=]() {
|
[=]() {
|
||||||
@ -44,7 +44,7 @@ void EngineController::deleteWorld(const std::string& name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Task> create_converter(
|
std::shared_ptr<Task> create_converter(
|
||||||
Engine* engine,
|
Engine& engine,
|
||||||
const std::shared_ptr<WorldFiles>& worldFiles,
|
const std::shared_ptr<WorldFiles>& worldFiles,
|
||||||
const Content* content,
|
const Content* content,
|
||||||
const std::shared_ptr<ContentReport>& report,
|
const std::shared_ptr<ContentReport>& report,
|
||||||
@ -62,25 +62,25 @@ std::shared_ptr<Task> create_converter(
|
|||||||
worldFiles,
|
worldFiles,
|
||||||
content,
|
content,
|
||||||
report,
|
report,
|
||||||
[=]() {
|
[&engine, postRunnable]() {
|
||||||
auto menu = engine->getGUI()->getMenu();
|
auto menu = engine.getGUI()->getMenu();
|
||||||
menu->reset();
|
menu->reset();
|
||||||
menu->setPage("main", false);
|
menu->setPage("main", false);
|
||||||
engine->getGUI()->postRunnable([=]() { postRunnable(); });
|
engine.getGUI()->postRunnable([=]() { postRunnable(); });
|
||||||
},
|
},
|
||||||
mode,
|
mode,
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_convert_request(
|
static void show_convert_request(
|
||||||
Engine* engine,
|
Engine& engine,
|
||||||
const Content* content,
|
const Content* content,
|
||||||
const std::shared_ptr<ContentReport>& report,
|
const std::shared_ptr<ContentReport>& report,
|
||||||
const std::shared_ptr<WorldFiles>& worldFiles,
|
const std::shared_ptr<WorldFiles>& worldFiles,
|
||||||
const runnable& postRunnable
|
const runnable& postRunnable
|
||||||
) {
|
) {
|
||||||
auto on_confirm = [=]() {
|
auto on_confirm = [&engine, worldFiles, content, report, postRunnable]() {
|
||||||
auto converter =
|
auto converter =
|
||||||
create_converter(engine, worldFiles, content, report, postRunnable);
|
create_converter(engine, worldFiles, content, report, postRunnable);
|
||||||
menus::show_process_panel(
|
menus::show_process_panel(
|
||||||
@ -101,7 +101,7 @@ void show_convert_request(
|
|||||||
text += util::str2wstr_utf8(line) + L"\n";
|
text += util::str2wstr_utf8(line) + L"\n";
|
||||||
}
|
}
|
||||||
guiutil::confirmWithMemo(
|
guiutil::confirmWithMemo(
|
||||||
engine->getGUI(),
|
engine.getGUI(),
|
||||||
langs::get(message),
|
langs::get(message),
|
||||||
text,
|
text,
|
||||||
on_confirm,
|
on_confirm,
|
||||||
@ -111,7 +111,7 @@ void show_convert_request(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
guiutil::confirm(
|
guiutil::confirm(
|
||||||
engine->getGUI(),
|
engine.getGUI(),
|
||||||
langs::get(message),
|
langs::get(message),
|
||||||
on_confirm,
|
on_confirm,
|
||||||
L"",
|
L"",
|
||||||
@ -120,7 +120,7 @@ void show_convert_request(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void show_content_missing(
|
static void show_content_missing(
|
||||||
Engine* engine, const std::shared_ptr<ContentReport>& report
|
Engine& engine, const std::shared_ptr<ContentReport>& report
|
||||||
) {
|
) {
|
||||||
auto root = dv::object();
|
auto root = dv::object();
|
||||||
auto& contentEntries = root.list("content");
|
auto& contentEntries = root.list("content");
|
||||||
@ -133,28 +133,30 @@ static void show_content_missing(
|
|||||||
menus::show(engine, "reports/missing_content", {std::move(root)});
|
menus::show(engine, "reports/missing_content", {std::move(root)});
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool load_world_content(Engine* engine, const fs::path& folder) {
|
static bool load_world_content(Engine& engine, const fs::path& folder) {
|
||||||
if (engine->isHeadless()) {
|
if (engine.isHeadless()) {
|
||||||
engine->loadWorldContent(folder);
|
engine.loadWorldContent(folder);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return menus::call(engine, [engine, folder]() {
|
return menus::call(engine, [&engine, folder]() {
|
||||||
engine->loadWorldContent(folder);
|
engine.loadWorldContent(folder);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void load_world(Engine* engine, const std::shared_ptr<WorldFiles>& worldFiles) {
|
static void load_world(
|
||||||
|
Engine& engine, const std::shared_ptr<WorldFiles>& worldFiles
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
auto content = engine->getContent();
|
auto content = engine.getContent();
|
||||||
auto& packs = engine->getContentPacks();
|
auto& packs = engine.getContentPacks();
|
||||||
auto& settings = engine->getSettings();
|
auto& settings = engine.getSettings();
|
||||||
|
|
||||||
auto level = World::load(worldFiles, settings, content, packs);
|
auto level = World::load(worldFiles, settings, content, packs);
|
||||||
engine->onWorldOpen(std::move(level));
|
engine.onWorldOpen(std::move(level));
|
||||||
} catch (const world_load_error& error) {
|
} catch (const world_load_error& error) {
|
||||||
guiutil::alert(
|
guiutil::alert(
|
||||||
engine->getGUI(),
|
engine.getGUI(),
|
||||||
langs::get(L"Error") + L": " + util::str2wstr_utf8(error.what())
|
langs::get(L"Error") + L": " + util::str2wstr_utf8(error.what())
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
@ -162,8 +164,8 @@ static void load_world(Engine* engine, const std::shared_ptr<WorldFiles>& worldF
|
|||||||
}
|
}
|
||||||
|
|
||||||
void EngineController::openWorld(const std::string& name, bool confirmConvert) {
|
void EngineController::openWorld(const std::string& name, bool confirmConvert) {
|
||||||
auto paths = engine->getPaths();
|
auto& paths = engine.getPaths();
|
||||||
auto folder = paths->getWorldsFolder() / fs::u8path(name);
|
auto folder = paths.getWorldsFolder() / fs::u8path(name);
|
||||||
auto worldFile = folder / fs::u8path("world.json");
|
auto worldFile = folder / fs::u8path("world.json");
|
||||||
if (!fs::exists(worldFile)) {
|
if (!fs::exists(worldFile)) {
|
||||||
throw std::runtime_error(worldFile.u8string() + " does not exists");
|
throw std::runtime_error(worldFile.u8string() + " does not exists");
|
||||||
@ -173,12 +175,12 @@ void EngineController::openWorld(const std::string& name, bool confirmConvert) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* content = engine->getContent();
|
const Content* content = engine.getContent();
|
||||||
auto worldFiles = std::make_shared<WorldFiles>(
|
auto worldFiles = std::make_shared<WorldFiles>(
|
||||||
folder, engine->getSettings().debug);
|
folder, engine.getSettings().debug);
|
||||||
if (auto report = World::checkIndices(worldFiles, content)) {
|
if (auto report = World::checkIndices(worldFiles, content)) {
|
||||||
if (report->hasMissingContent()) {
|
if (report->hasMissingContent()) {
|
||||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
show_content_missing(engine, report);
|
show_content_missing(engine, report);
|
||||||
} else {
|
} else {
|
||||||
if (confirmConvert) {
|
if (confirmConvert) {
|
||||||
@ -225,15 +227,15 @@ void EngineController::createWorld(
|
|||||||
) {
|
) {
|
||||||
uint64_t seed = str2seed(seedstr);
|
uint64_t seed = str2seed(seedstr);
|
||||||
|
|
||||||
EnginePaths* paths = engine->getPaths();
|
EnginePaths& paths = engine.getPaths();
|
||||||
auto folder = paths->getWorldsFolder() / fs::u8path(name);
|
auto folder = paths.getWorldsFolder() / fs::u8path(name);
|
||||||
|
|
||||||
if (engine->isHeadless()) {
|
if (engine.isHeadless()) {
|
||||||
engine->loadContent();
|
engine.loadContent();
|
||||||
paths->setCurrentWorldFolder(folder);
|
paths.setCurrentWorldFolder(folder);
|
||||||
} else if (!menus::call(engine, [this, paths, folder]() {
|
} else if (!menus::call(engine, [this, &paths, folder]() {
|
||||||
engine->loadContent();
|
engine.loadContent();
|
||||||
paths->setCurrentWorldFolder(folder);
|
paths.setCurrentWorldFolder(folder);
|
||||||
})) {
|
})) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -242,20 +244,20 @@ void EngineController::createWorld(
|
|||||||
generatorID,
|
generatorID,
|
||||||
folder,
|
folder,
|
||||||
seed,
|
seed,
|
||||||
engine->getSettings(),
|
engine.getSettings(),
|
||||||
engine->getContent(),
|
engine.getContent(),
|
||||||
engine->getContentPacks()
|
engine.getContentPacks()
|
||||||
);
|
);
|
||||||
if (!engine->isHeadless()) {
|
if (!engine.isHeadless()) {
|
||||||
level->players->create();
|
level->players->create();
|
||||||
}
|
}
|
||||||
engine->onWorldOpen(std::move(level));
|
engine.onWorldOpen(std::move(level));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineController::reopenWorld(World* world) {
|
void EngineController::reopenWorld(World* world) {
|
||||||
std::string wname = world->wfile->getFolder().filename().u8string();
|
std::string wname = world->wfile->getFolder().filename().u8string();
|
||||||
engine->setScreen(nullptr);
|
engine.setScreen(nullptr);
|
||||||
engine->setScreen(std::make_shared<MenuScreen>(engine));
|
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||||
openWorld(wname, true);
|
openWorld(wname, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +266,7 @@ void EngineController::reconfigPacks(
|
|||||||
const std::vector<std::string>& packsToAdd,
|
const std::vector<std::string>& packsToAdd,
|
||||||
const std::vector<std::string>& packsToRemove
|
const std::vector<std::string>& packsToRemove
|
||||||
) {
|
) {
|
||||||
auto content = engine->getContent();
|
auto content = engine.getContent();
|
||||||
bool hasIndices = false;
|
bool hasIndices = false;
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
@ -281,13 +283,12 @@ void EngineController::reconfigPacks(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
runnable removeFunc = [=]() {
|
runnable removeFunc = [this, controller, packsToAdd, packsToRemove]() {
|
||||||
if (controller == nullptr) {
|
if (controller == nullptr) {
|
||||||
try {
|
try {
|
||||||
auto manager = engine->createPacksManager(fs::path(""));
|
auto manager = engine.createPacksManager(fs::path(""));
|
||||||
manager.scan();
|
manager.scan();
|
||||||
std::vector<std::string> names =
|
auto names = PacksManager::getNames(engine.getContentPacks());
|
||||||
PacksManager::getNames(engine->getContentPacks());
|
|
||||||
for (const auto& id : packsToAdd) {
|
for (const auto& id : packsToAdd) {
|
||||||
names.push_back(id);
|
names.push_back(id);
|
||||||
}
|
}
|
||||||
@ -296,7 +297,7 @@ void EngineController::reconfigPacks(
|
|||||||
names.erase(std::find(names.begin(), names.end(), id));
|
names.erase(std::find(names.begin(), names.end(), id));
|
||||||
}
|
}
|
||||||
names = manager.assembly(names);
|
names = manager.assembly(names);
|
||||||
engine->getContentPacks() = manager.getAll(names);
|
engine.getContentPacks() = manager.getAll(names);
|
||||||
} catch (const contentpack_error& err) {
|
} catch (const contentpack_error& err) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
std::string(err.what()) + " [" + err.getPackId() + "]"
|
std::string(err.what()) + " [" + err.getPackId() + "]"
|
||||||
@ -304,9 +305,9 @@ void EngineController::reconfigPacks(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto world = controller->getLevel()->getWorld();
|
auto world = controller->getLevel()->getWorld();
|
||||||
auto wfile = world->wfile.get();
|
auto& wfile = *world->wfile;
|
||||||
controller->saveWorld();
|
controller->saveWorld();
|
||||||
auto manager = engine->createPacksManager(wfile->getFolder());
|
auto manager = engine.createPacksManager(wfile.getFolder());
|
||||||
manager.scan();
|
manager.scan();
|
||||||
|
|
||||||
auto names = PacksManager::getNames(world->getPacks());
|
auto names = PacksManager::getNames(world->getPacks());
|
||||||
@ -317,15 +318,15 @@ void EngineController::reconfigPacks(
|
|||||||
manager.exclude(id);
|
manager.exclude(id);
|
||||||
names.erase(std::find(names.begin(), names.end(), id));
|
names.erase(std::find(names.begin(), names.end(), id));
|
||||||
}
|
}
|
||||||
wfile->removeIndices(packsToRemove);
|
wfile.removeIndices(packsToRemove);
|
||||||
wfile->writePacks(manager.getAll(names));
|
wfile.writePacks(manager.getAll(names));
|
||||||
reopenWorld(world);
|
reopenWorld(world);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (hasIndices) {
|
if (hasIndices) {
|
||||||
guiutil::confirm(
|
guiutil::confirm(
|
||||||
engine->getGUI(),
|
engine.getGUI(),
|
||||||
langs::get(L"remove-confirm", L"pack") + L" (" +
|
langs::get(L"remove-confirm", L"pack") + L" (" +
|
||||||
util::str2wstr_utf8(ss.str()) + L")",
|
util::str2wstr_utf8(ss.str()) + L")",
|
||||||
[=]() { removeFunc(); }
|
[=]() { removeFunc(); }
|
||||||
|
|||||||
@ -8,9 +8,9 @@ class World;
|
|||||||
class LevelController;
|
class LevelController;
|
||||||
|
|
||||||
class EngineController {
|
class EngineController {
|
||||||
Engine* engine;
|
Engine& engine;
|
||||||
public:
|
public:
|
||||||
EngineController(Engine* engine);
|
EngineController(Engine& engine);
|
||||||
|
|
||||||
/// @brief Load world, convert if required and set to LevelScreen.
|
/// @brief Load world, convert if required and set to LevelScreen.
|
||||||
/// @param name world name
|
/// @param name world name
|
||||||
|
|||||||
@ -10,43 +10,91 @@
|
|||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
|
#include "voxels/Chunks.hpp"
|
||||||
#include "scripting/scripting.hpp"
|
#include "scripting/scripting.hpp"
|
||||||
|
#include "lighting/Lighting.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
|
||||||
static debug::Logger logger("level-control");
|
static debug::Logger logger("level-control");
|
||||||
|
|
||||||
LevelController::LevelController(Engine* engine, std::unique_ptr<Level> levelPtr)
|
LevelController::LevelController(
|
||||||
|
Engine* engine, std::unique_ptr<Level> levelPtr, Player* clientPlayer
|
||||||
|
)
|
||||||
: settings(engine->getSettings()),
|
: settings(engine->getSettings()),
|
||||||
level(std::move(levelPtr)),
|
level(std::move(levelPtr)),
|
||||||
blocks(std::make_unique<BlocksController>(
|
chunks(std::make_unique<ChunksController>(*level)),
|
||||||
*level, settings.chunks.padding.get()
|
playerTickClock(20, 3) {
|
||||||
)),
|
if (clientPlayer) {
|
||||||
chunks(std::make_unique<ChunksController>(
|
chunks->lighting = std::make_unique<Lighting>(
|
||||||
*level, settings.chunks.padding.get()
|
level->content, clientPlayer->chunks.get()
|
||||||
)) {
|
);
|
||||||
|
}
|
||||||
|
blocks = std::make_unique<BlocksController>(
|
||||||
|
*level, chunks ? chunks->lighting.get() : nullptr
|
||||||
|
);
|
||||||
scripting::on_world_load(this);
|
scripting::on_world_load(this);
|
||||||
|
|
||||||
|
// TODO: do something to players added later
|
||||||
|
int confirmed;
|
||||||
|
do {
|
||||||
|
confirmed = 0;
|
||||||
|
for (const auto& [_, player] : *level->players) {
|
||||||
|
glm::vec3 position = player->getPosition();
|
||||||
|
player->chunks->configure(
|
||||||
|
std::floor(position.x), std::floor(position.z), 1
|
||||||
|
);
|
||||||
|
chunks->update(16, 1, 0, *player);
|
||||||
|
if (player->chunks->get(
|
||||||
|
std::floor(position.x),
|
||||||
|
std::floor(position.y),
|
||||||
|
std::floor(position.z)
|
||||||
|
)) {
|
||||||
|
confirmed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (confirmed < level->players->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LevelController::update(float delta, bool pause) {
|
void LevelController::update(float delta, bool pause) {
|
||||||
for (const auto& [uid, player] : *level->players) {
|
for (const auto& [_, player] : *level->players) {
|
||||||
glm::vec3 position = player->getPosition();
|
glm::vec3 position = player->getPosition();
|
||||||
level->loadMatrix(
|
player->chunks->configure(
|
||||||
position.x,
|
position.x,
|
||||||
position.z,
|
position.z,
|
||||||
settings.chunks.loadDistance.get() + settings.chunks.padding.get() * 2
|
settings.chunks.loadDistance.get() + settings.chunks.padding.get()
|
||||||
);
|
);
|
||||||
chunks->update(
|
chunks->update(
|
||||||
settings.chunks.loadSpeed.get(), settings.chunks.loadDistance.get(),
|
settings.chunks.loadSpeed.get(),
|
||||||
floordiv(position.x, CHUNK_W), floordiv(position.z, CHUNK_D)
|
settings.chunks.loadDistance.get(),
|
||||||
|
settings.chunks.padding.get(),
|
||||||
|
*player
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!pause) {
|
if (!pause) {
|
||||||
// update all objects that needed
|
// update all objects that needed
|
||||||
blocks->update(delta);
|
blocks->update(delta, settings.chunks.padding.get());
|
||||||
level->entities->updatePhysics(delta);
|
level->entities->updatePhysics(delta);
|
||||||
level->entities->update(delta);
|
level->entities->update(delta);
|
||||||
|
for (const auto& [_, player] : *level->players) {
|
||||||
|
if (playerTickClock.update(delta)) {
|
||||||
|
if (player->getId() % playerTickClock.getParts() ==
|
||||||
|
playerTickClock.getPart()) {
|
||||||
|
|
||||||
|
const auto& position = player->getPosition();
|
||||||
|
if (!player->chunks->get(
|
||||||
|
std::floor(position.x),
|
||||||
|
std::floor(position.y),
|
||||||
|
std::floor(position.z)
|
||||||
|
)){
|
||||||
|
scripting::on_player_tick(
|
||||||
|
player.get(), playerTickClock.getTickRate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
level->entities->clean();
|
level->entities->clean();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "BlocksController.hpp"
|
#include "BlocksController.hpp"
|
||||||
#include "ChunksController.hpp"
|
#include "ChunksController.hpp"
|
||||||
|
#include "util/Clock.hpp"
|
||||||
|
|
||||||
class Engine;
|
class Engine;
|
||||||
class Level;
|
class Level;
|
||||||
@ -17,8 +18,10 @@ class LevelController {
|
|||||||
// Sub-controllers
|
// Sub-controllers
|
||||||
std::unique_ptr<BlocksController> blocks;
|
std::unique_ptr<BlocksController> blocks;
|
||||||
std::unique_ptr<ChunksController> chunks;
|
std::unique_ptr<ChunksController> chunks;
|
||||||
|
|
||||||
|
util::Clock playerTickClock;
|
||||||
public:
|
public:
|
||||||
LevelController(Engine* engine, std::unique_ptr<Level> level);
|
LevelController(Engine* engine, std::unique_ptr<Level> level, Player* clientPlayer);
|
||||||
|
|
||||||
/// @param delta time elapsed since the last update
|
/// @param delta time elapsed since the last update
|
||||||
/// @param pause is world and player simulation paused
|
/// @param pause is world and player simulation paused
|
||||||
|
|||||||
@ -41,20 +41,20 @@ const float C_ZOOM = 0.1f;
|
|||||||
const float CROUCH_SHIFT_Y = -0.2f;
|
const float CROUCH_SHIFT_Y = -0.2f;
|
||||||
|
|
||||||
CameraControl::CameraControl(
|
CameraControl::CameraControl(
|
||||||
Player* player, const CameraSettings& settings
|
Player& player, const CameraSettings& settings
|
||||||
)
|
)
|
||||||
: player(player),
|
: player(player),
|
||||||
camera(player->fpCamera),
|
camera(player.fpCamera),
|
||||||
settings(settings),
|
settings(settings),
|
||||||
offset(0.0f, 0.7f, 0.0f) {
|
offset(0.0f, 0.7f, 0.0f) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraControl::refresh() {
|
void CameraControl::refresh() {
|
||||||
camera->position = player->getPosition() + offset;
|
camera->position = player.getPosition() + offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraControl::updateMouse(PlayerInput& input) {
|
void CameraControl::updateMouse(PlayerInput& input) {
|
||||||
glm::vec3& cam = player->cam;
|
glm::vec3& cam = player.cam;
|
||||||
|
|
||||||
float sensitivity =
|
float sensitivity =
|
||||||
(input.zoom ? settings.sensitivity.get() / 4.f
|
(input.zoom ? settings.sensitivity.get() / 4.f
|
||||||
@ -131,28 +131,31 @@ void CameraControl::updateFovEffects(
|
|||||||
// more extensible but uglier
|
// more extensible but uglier
|
||||||
void CameraControl::switchCamera() {
|
void CameraControl::switchCamera() {
|
||||||
const std::vector<std::shared_ptr<Camera>> playerCameras {
|
const std::vector<std::shared_ptr<Camera>> playerCameras {
|
||||||
camera, player->tpCamera, player->spCamera};
|
camera, player.tpCamera, player.spCamera
|
||||||
|
};
|
||||||
|
|
||||||
auto index = std::distance(
|
auto index = std::distance(
|
||||||
playerCameras.begin(),
|
playerCameras.begin(),
|
||||||
std::find_if(
|
std::find_if(
|
||||||
playerCameras.begin(),
|
playerCameras.begin(),
|
||||||
playerCameras.end(),
|
playerCameras.end(),
|
||||||
[=](auto ptr) { return ptr.get() == player->currentCamera.get(); }
|
[this](auto& ptr) { return ptr.get() == player.currentCamera.get(); }
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if (static_cast<size_t>(index) != playerCameras.size()) {
|
if (static_cast<size_t>(index) != playerCameras.size()) {
|
||||||
index = (index + 1) % playerCameras.size();
|
index = (index + 1) % playerCameras.size();
|
||||||
player->currentCamera = playerCameras.at(index);
|
player.currentCamera = playerCameras.at(index);
|
||||||
} else {
|
} else {
|
||||||
player->currentCamera = camera;
|
player.currentCamera = camera;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CameraControl::update(PlayerInput input, float delta, Chunks* chunks) {
|
void CameraControl::update(
|
||||||
|
PlayerInput input, float delta, const Chunks& chunks
|
||||||
|
) {
|
||||||
offset = glm::vec3(0.0f, 0.0f, 0.0f);
|
offset = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
if (auto hitbox = player->getHitbox()) {
|
if (auto hitbox = player.getHitbox()) {
|
||||||
offset.y += hitbox->halfsize.y * (0.7f / 0.9f);
|
offset.y += hitbox->halfsize.y * (0.7f / 0.9f);
|
||||||
if (settings.shaking.get() && !input.cheat) {
|
if (settings.shaking.get() && !input.cheat) {
|
||||||
offset += updateCameraShaking(*hitbox, delta);
|
offset += updateCameraShaking(*hitbox, delta);
|
||||||
@ -165,45 +168,44 @@ void CameraControl::update(PlayerInput input, float delta, Chunks* chunks) {
|
|||||||
switchCamera();
|
switchCamera();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto spCamera = player->spCamera;
|
auto& spCamera = player.spCamera;
|
||||||
auto tpCamera = player->tpCamera;
|
auto& tpCamera = player.tpCamera;
|
||||||
|
|
||||||
refresh();
|
refresh();
|
||||||
|
|
||||||
camera->updateVectors();
|
camera->updateVectors();
|
||||||
if (player->currentCamera == spCamera) {
|
if (player.currentCamera == spCamera) {
|
||||||
spCamera->position =
|
spCamera->position =
|
||||||
chunks->rayCastToObstacle(camera->position, camera->front, 3.0f) -
|
chunks.rayCastToObstacle(camera->position, camera->front, 3.0f) -
|
||||||
0.4f * camera->front;
|
0.4f * camera->front;
|
||||||
spCamera->dir = -camera->dir;
|
spCamera->dir = -camera->dir;
|
||||||
spCamera->front = -camera->front;
|
spCamera->front = -camera->front;
|
||||||
spCamera->right = -camera->right;
|
spCamera->right = -camera->right;
|
||||||
} else if (player->currentCamera == tpCamera) {
|
} else if (player.currentCamera == tpCamera) {
|
||||||
tpCamera->position =
|
tpCamera->position =
|
||||||
chunks->rayCastToObstacle(camera->position, -camera->front, 3.0f) +
|
chunks.rayCastToObstacle(camera->position, -camera->front, 3.0f) +
|
||||||
0.4f * camera->front;
|
0.4f * camera->front;
|
||||||
tpCamera->dir = camera->dir;
|
tpCamera->dir = camera->dir;
|
||||||
tpCamera->front = camera->front;
|
tpCamera->front = camera->front;
|
||||||
tpCamera->right = camera->right;
|
tpCamera->right = camera->right;
|
||||||
}
|
}
|
||||||
if (player->currentCamera == spCamera ||
|
if (player.currentCamera == spCamera ||
|
||||||
player->currentCamera == tpCamera || player->currentCamera == camera) {
|
player.currentCamera == tpCamera || player.currentCamera == camera) {
|
||||||
player->currentCamera->setFov(glm::radians(settings.fov.get()));
|
player.currentCamera->setFov(glm::radians(settings.fov.get()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerController::PlayerController(
|
PlayerController::PlayerController(
|
||||||
const EngineSettings& settings,
|
const EngineSettings& settings,
|
||||||
Level* level,
|
Level& level,
|
||||||
Player* player,
|
Player& player,
|
||||||
BlocksController* blocksController
|
BlocksController& blocksController
|
||||||
)
|
)
|
||||||
: settings(settings),
|
: settings(settings),
|
||||||
level(level),
|
level(level),
|
||||||
player(player),
|
player(player),
|
||||||
camControl(player, settings.camera),
|
camControl(player, settings.camera),
|
||||||
blocksController(blocksController),
|
blocksController(blocksController) {
|
||||||
playerTickClock(20, 3) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
void PlayerController::onFootstep(const Hitbox& hitbox) {
|
||||||
@ -215,14 +217,14 @@ void PlayerController::onFootstep(const Hitbox& hitbox) {
|
|||||||
int x = std::floor(pos.x + half.x * offsetX);
|
int x = std::floor(pos.x + half.x * offsetX);
|
||||||
int y = std::floor(pos.y - half.y * 1.1f);
|
int y = std::floor(pos.y - half.y * 1.1f);
|
||||||
int z = std::floor(pos.z + half.z * offsetZ);
|
int z = std::floor(pos.z + half.z * offsetZ);
|
||||||
auto vox = level->chunks->get(x, y, z);
|
auto vox = player.chunks->get(x, y, z);
|
||||||
if (vox) {
|
if (vox) {
|
||||||
auto& def = level->content->getIndices()->blocks.require(vox->id);
|
auto& def = level.content->getIndices()->blocks.require(vox->id);
|
||||||
if (!def.obstacle) {
|
if (!def.obstacle) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
blocksController->onBlockInteraction(
|
blocksController.onBlockInteraction(
|
||||||
player,
|
&player,
|
||||||
glm::ivec3(x, y, z),
|
glm::ivec3(x, y, z),
|
||||||
def,
|
def,
|
||||||
BlockInteraction::step
|
BlockInteraction::step
|
||||||
@ -234,7 +236,7 @@ void PlayerController::onFootstep(const Hitbox& hitbox) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::updateFootsteps(float delta) {
|
void PlayerController::updateFootsteps(float delta) {
|
||||||
auto hitbox = player->getHitbox();
|
auto hitbox = player.getHitbox();
|
||||||
if (hitbox && hitbox->grounded) {
|
if (hitbox && hitbox->grounded) {
|
||||||
const glm::vec3& vel = hitbox->velocity;
|
const glm::vec3& vel = hitbox->velocity;
|
||||||
float f = glm::length(glm::vec2(vel.x, vel.z));
|
float f = glm::length(glm::vec2(vel.x, vel.z));
|
||||||
@ -251,18 +253,11 @@ void PlayerController::updateFootsteps(float delta) {
|
|||||||
void PlayerController::update(float delta, bool input) {
|
void PlayerController::update(float delta, bool input) {
|
||||||
if (input) {
|
if (input) {
|
||||||
updateKeyboard();
|
updateKeyboard();
|
||||||
player->updateSelectedEntity();
|
player.updateSelectedEntity();
|
||||||
} else {
|
} else {
|
||||||
resetKeyboard();
|
resetKeyboard();
|
||||||
}
|
}
|
||||||
updatePlayer(delta);
|
updatePlayer(delta);
|
||||||
|
|
||||||
if (playerTickClock.update(delta)) {
|
|
||||||
if (player->getId() % playerTickClock.getParts() ==
|
|
||||||
playerTickClock.getPart()) {
|
|
||||||
scripting::on_player_tick(player, playerTickClock.getTickRate());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::postUpdate(float delta, bool input, bool pause) {
|
void PlayerController::postUpdate(float delta, bool input, bool pause) {
|
||||||
@ -273,12 +268,12 @@ void PlayerController::postUpdate(float delta, bool input, bool pause) {
|
|||||||
if (!pause && input) {
|
if (!pause && input) {
|
||||||
camControl.updateMouse(this->input);
|
camControl.updateMouse(this->input);
|
||||||
}
|
}
|
||||||
player->postUpdate();
|
player.postUpdate();
|
||||||
camControl.update(this->input, pause ? 0.0f : delta, level->chunks.get());
|
camControl.update(this->input, pause ? 0.0f : delta, *player.chunks);
|
||||||
if (input) {
|
if (input) {
|
||||||
updateInteraction(delta);
|
updateInteraction(delta);
|
||||||
} else {
|
} else {
|
||||||
player->selection = {};
|
player.selection = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,8 +305,8 @@ void PlayerController::resetKeyboard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::updatePlayer(float delta) {
|
void PlayerController::updatePlayer(float delta) {
|
||||||
player->updateEntity();
|
player.updateEntity();
|
||||||
player->updateInput(input, delta);
|
player.updateInput(input, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int determine_rotation(
|
static int determine_rotation(
|
||||||
@ -347,16 +342,20 @@ static int determine_rotation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void pick_block(
|
static void pick_block(
|
||||||
ContentIndices* indices, Chunks* chunks, Player* player, int x, int y, int z
|
ContentIndices* indices,
|
||||||
|
const Block& block,
|
||||||
|
Player& player,
|
||||||
|
int x,
|
||||||
|
int y,
|
||||||
|
int z
|
||||||
) {
|
) {
|
||||||
auto& block = indices->blocks.require(chunks->get(x, y, z)->id);
|
|
||||||
itemid_t id = block.rt.pickingItem;
|
itemid_t id = block.rt.pickingItem;
|
||||||
auto inventory = player->getInventory();
|
auto inventory = player.getInventory();
|
||||||
size_t slotid = inventory->findSlotByItem(id, 0, 10);
|
size_t slotid = inventory->findSlotByItem(id, 0, 10);
|
||||||
if (slotid == Inventory::npos) {
|
if (slotid == Inventory::npos) {
|
||||||
slotid = player->getChosenSlot();
|
slotid = player.getChosenSlot();
|
||||||
} else {
|
} else {
|
||||||
player->setChosenSlot(slotid);
|
player.setChosenSlot(slotid);
|
||||||
}
|
}
|
||||||
ItemStack& stack = inventory->getSlot(slotid);
|
ItemStack& stack = inventory->getSlot(slotid);
|
||||||
if (stack.getItemId() != id) {
|
if (stack.getItemId() != id) {
|
||||||
@ -365,15 +364,15 @@ static void pick_block(
|
|||||||
}
|
}
|
||||||
|
|
||||||
voxel* PlayerController::updateSelection(float maxDistance) {
|
voxel* PlayerController::updateSelection(float maxDistance) {
|
||||||
auto indices = level->content->getIndices();
|
auto indices = level.content->getIndices();
|
||||||
auto chunks = level->chunks.get();
|
auto& chunks = *player.chunks;
|
||||||
auto camera = player->fpCamera.get();
|
auto camera = player.fpCamera.get();
|
||||||
auto& selection = player->selection;
|
auto& selection = player.selection;
|
||||||
|
|
||||||
glm::vec3 end;
|
glm::vec3 end;
|
||||||
glm::ivec3 iend;
|
glm::ivec3 iend;
|
||||||
glm::ivec3 norm;
|
glm::ivec3 norm;
|
||||||
voxel* vox = chunks->rayCast(
|
voxel* vox = chunks.rayCast(
|
||||||
camera->position, camera->front, maxDistance, end, norm, iend
|
camera->position, camera->front, maxDistance, end, norm, iend
|
||||||
);
|
);
|
||||||
if (vox) {
|
if (vox) {
|
||||||
@ -382,8 +381,8 @@ voxel* PlayerController::updateSelection(float maxDistance) {
|
|||||||
auto prevEntity = selection.entity;
|
auto prevEntity = selection.entity;
|
||||||
selection.entity = ENTITY_NONE;
|
selection.entity = ENTITY_NONE;
|
||||||
selection.actualPosition = iend;
|
selection.actualPosition = iend;
|
||||||
if (auto result = level->entities->rayCast(
|
if (auto result = level.entities->rayCast(
|
||||||
camera->position, camera->front, maxDistance, player->getEntity()
|
camera->position, camera->front, maxDistance, player.getEntity()
|
||||||
)) {
|
)) {
|
||||||
selection.entity = result->entity;
|
selection.entity = result->entity;
|
||||||
selection.hitPosition =
|
selection.hitPosition =
|
||||||
@ -394,13 +393,13 @@ voxel* PlayerController::updateSelection(float maxDistance) {
|
|||||||
}
|
}
|
||||||
if (selection.entity != prevEntity) {
|
if (selection.entity != prevEntity) {
|
||||||
if (prevEntity != ENTITY_NONE) {
|
if (prevEntity != ENTITY_NONE) {
|
||||||
if (auto pentity = level->entities->get(prevEntity)) {
|
if (auto pentity = level.entities->get(prevEntity)) {
|
||||||
scripting::on_aim_off(*pentity, player);
|
scripting::on_aim_off(*pentity, &player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (selection.entity != ENTITY_NONE) {
|
if (selection.entity != ENTITY_NONE) {
|
||||||
if (auto pentity = level->entities->get(selection.entity)) {
|
if (auto pentity = level.entities->get(selection.entity)) {
|
||||||
scripting::on_aim_on(*pentity, player);
|
scripting::on_aim_on(*pentity, &player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,12 +410,12 @@ voxel* PlayerController::updateSelection(float maxDistance) {
|
|||||||
blockstate selectedState = vox->state;
|
blockstate selectedState = vox->state;
|
||||||
selection.vox = *vox;
|
selection.vox = *vox;
|
||||||
if (selectedState.segment) {
|
if (selectedState.segment) {
|
||||||
selection.position = chunks->seekOrigin(
|
selection.position = chunks.seekOrigin(
|
||||||
iend, indices->blocks.require(selection.vox.id), selectedState
|
iend, indices->blocks.require(selection.vox.id), selectedState
|
||||||
);
|
);
|
||||||
auto origin = chunks->get(selection.position);
|
auto origin = chunks.get(selection.position);
|
||||||
if (origin && origin->id != vox->id) {
|
if (origin && origin->id != vox->id) {
|
||||||
chunks->set(iend.x, iend.y, iend.z, 0, {});
|
chunks.set(iend.x, iend.y, iend.z, 0, {});
|
||||||
return updateSelection(maxDistance);
|
return updateSelection(maxDistance);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -428,16 +427,16 @@ voxel* PlayerController::updateSelection(float maxDistance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::processRightClick(const Block& def, const Block& target) {
|
void PlayerController::processRightClick(const Block& def, const Block& target) {
|
||||||
const auto& selection = player->selection;
|
const auto& selection = player.selection;
|
||||||
auto chunks = level->chunks.get();
|
auto& chunks = *player.chunks;
|
||||||
auto camera = player->fpCamera.get();
|
auto camera = player.fpCamera.get();
|
||||||
|
|
||||||
blockstate state {};
|
blockstate state {};
|
||||||
state.rotation = determine_rotation(&def, selection.normal, camera->dir);
|
state.rotation = determine_rotation(&def, selection.normal, camera->dir);
|
||||||
|
|
||||||
if (!input.shift && target.rt.funcsset.oninteract) {
|
if (!input.shift && target.rt.funcsset.oninteract) {
|
||||||
if (scripting::on_block_interact(
|
if (scripting::on_block_interact(
|
||||||
player, target, selection.actualPosition
|
&player, target, selection.actualPosition
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -453,33 +452,33 @@ void PlayerController::processRightClick(const Block& def, const Block& target)
|
|||||||
if (def.obstacle) {
|
if (def.obstacle) {
|
||||||
const auto& hitboxes = def.rt.hitboxes[state.rotation];
|
const auto& hitboxes = def.rt.hitboxes[state.rotation];
|
||||||
for (const AABB& blockAABB : hitboxes) {
|
for (const AABB& blockAABB : hitboxes) {
|
||||||
if (level->entities->hasBlockingInside(blockAABB.translated(coord))) {
|
if (level.entities->hasBlockingInside(blockAABB.translated(coord))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto vox = chunks->get(coord);
|
auto vox = chunks.get(coord);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!chunks->checkReplaceability(def, state, coord)) {
|
if (!chunks.checkReplaceability(def, state, coord)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (def.grounded) {
|
if (def.grounded) {
|
||||||
const auto& vec = get_ground_direction(def, state.rotation);
|
const auto& vec = get_ground_direction(def, state.rotation);
|
||||||
if (!chunks->isSolidBlock(
|
if (!chunks.isSolidBlock(
|
||||||
coord.x + vec.x, coord.y + vec.y, coord.z + vec.z
|
coord.x + vec.x, coord.y + vec.y, coord.z + vec.z
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chosenBlock != vox->id && chosenBlock) {
|
if (chosenBlock != vox->id && chosenBlock) {
|
||||||
if (!player->isInfiniteItems()) {
|
if (!player.isInfiniteItems()) {
|
||||||
auto& slot = player->getInventory()->getSlot(player->getChosenSlot());
|
auto& slot = player.getInventory()->getSlot(player.getChosenSlot());
|
||||||
slot.setCount(slot.getCount()-1);
|
slot.setCount(slot.getCount()-1);
|
||||||
}
|
}
|
||||||
blocksController->placeBlock(
|
blocksController.placeBlock(
|
||||||
player, def, state, coord.x, coord.y, coord.z
|
&player, def, state, coord.x, coord.y, coord.z
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -487,23 +486,23 @@ void PlayerController::processRightClick(const Block& def, const Block& target)
|
|||||||
void PlayerController::updateEntityInteraction(
|
void PlayerController::updateEntityInteraction(
|
||||||
entityid_t eid, bool lclick, bool rclick
|
entityid_t eid, bool lclick, bool rclick
|
||||||
) {
|
) {
|
||||||
auto entityOpt = level->entities->get(eid);
|
auto entityOpt = level.entities->get(eid);
|
||||||
if (!entityOpt.has_value()) {
|
if (!entityOpt.has_value()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto entity = *entityOpt;
|
auto entity = *entityOpt;
|
||||||
if (lclick) {
|
if (lclick) {
|
||||||
scripting::on_attacked(entity, player, player->getEntity());
|
scripting::on_attacked(entity, &player, player.getEntity());
|
||||||
}
|
}
|
||||||
if (rclick) {
|
if (rclick) {
|
||||||
scripting::on_entity_used(entity, player);
|
scripting::on_entity_used(entity, &player);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerController::updateInteraction(float delta) {
|
void PlayerController::updateInteraction(float delta) {
|
||||||
auto indices = level->content->getIndices();
|
auto indices = level.content->getIndices();
|
||||||
auto chunks = level->chunks.get();
|
auto chunks = player.chunks.get();
|
||||||
const auto& selection = player->selection;
|
const auto& selection = player.selection;
|
||||||
|
|
||||||
if (interactionTimer > 0.0f) {
|
if (interactionTimer > 0.0f) {
|
||||||
interactionTimer -= delta;
|
interactionTimer -= delta;
|
||||||
@ -520,14 +519,14 @@ void PlayerController::updateInteraction(float delta) {
|
|||||||
interactionTimer = INTERACTION_RELOAD;
|
interactionTimer = INTERACTION_RELOAD;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto inventory = player->getInventory();
|
auto inventory = player.getInventory();
|
||||||
const ItemStack& stack = inventory->getSlot(player->getChosenSlot());
|
const ItemStack& stack = inventory->getSlot(player.getChosenSlot());
|
||||||
auto& item = indices->items.require(stack.getItemId());
|
auto& item = indices->items.require(stack.getItemId());
|
||||||
|
|
||||||
auto vox = updateSelection(maxDistance);
|
auto vox = updateSelection(maxDistance);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
if (rclick && item.rt.funcsset.on_use) {
|
if (rclick && item.rt.funcsset.on_use) {
|
||||||
scripting::on_item_use(player, item);
|
scripting::on_item_use(&player, item);
|
||||||
}
|
}
|
||||||
if (selection.entity) {
|
if (selection.entity) {
|
||||||
updateEntityInteraction(selection.entity, lattack, rclick);
|
updateEntityInteraction(selection.entity, lattack, rclick);
|
||||||
@ -538,16 +537,16 @@ void PlayerController::updateInteraction(float delta) {
|
|||||||
auto iend = selection.position;
|
auto iend = selection.position;
|
||||||
if (lclick && !input.shift && item.rt.funcsset.on_block_break_by) {
|
if (lclick && !input.shift && item.rt.funcsset.on_block_break_by) {
|
||||||
if (scripting::on_item_break_block(
|
if (scripting::on_item_break_block(
|
||||||
player, item, iend.x, iend.y, iend.z
|
&player, item, iend.x, iend.y, iend.z
|
||||||
)) {
|
)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto& target = indices->blocks.require(vox->id);
|
auto& target = indices->blocks.require(vox->id);
|
||||||
if (lclick) {
|
if (lclick) {
|
||||||
if (player->isInstantDestruction() && target.breakable) {
|
if (player.isInstantDestruction() && target.breakable) {
|
||||||
blocksController->breakBlock(
|
blocksController.breakBlock(
|
||||||
player, target, iend.x, iend.y, iend.z
|
&player, target, iend.x, iend.y, iend.z
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -555,10 +554,10 @@ void PlayerController::updateInteraction(float delta) {
|
|||||||
bool preventDefault = false;
|
bool preventDefault = false;
|
||||||
if (item.rt.funcsset.on_use_on_block) {
|
if (item.rt.funcsset.on_use_on_block) {
|
||||||
preventDefault = scripting::on_item_use_on_block(
|
preventDefault = scripting::on_item_use_on_block(
|
||||||
player, item, iend, selection.normal
|
&player, item, iend, selection.normal
|
||||||
);
|
);
|
||||||
} else if (item.rt.funcsset.on_use) {
|
} else if (item.rt.funcsset.on_use) {
|
||||||
preventDefault = scripting::on_item_use(player, item);
|
preventDefault = scripting::on_item_use(&player, item);
|
||||||
}
|
}
|
||||||
if (preventDefault) {
|
if (preventDefault) {
|
||||||
return;
|
return;
|
||||||
@ -570,10 +569,10 @@ void PlayerController::updateInteraction(float delta) {
|
|||||||
}
|
}
|
||||||
if (Events::jactive(BIND_PLAYER_PICK)) {
|
if (Events::jactive(BIND_PLAYER_PICK)) {
|
||||||
auto coord = selection.actualPosition;
|
auto coord = selection.actualPosition;
|
||||||
pick_block(indices, chunks, player, coord.x, coord.y, coord.z);
|
pick_block(indices, target, player, coord.x, coord.y, coord.z);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Player* PlayerController::getPlayer() {
|
Player* PlayerController::getPlayer() {
|
||||||
return player;
|
return &player;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,7 +18,7 @@ struct CameraSettings;
|
|||||||
struct EngineSettings;
|
struct EngineSettings;
|
||||||
|
|
||||||
class CameraControl {
|
class CameraControl {
|
||||||
Player* player;
|
Player& player;
|
||||||
std::shared_ptr<Camera> camera;
|
std::shared_ptr<Camera> camera;
|
||||||
const CameraSettings& settings;
|
const CameraSettings& settings;
|
||||||
glm::vec3 offset;
|
glm::vec3 offset;
|
||||||
@ -39,24 +39,21 @@ class CameraControl {
|
|||||||
/// @brief Switch active player camera
|
/// @brief Switch active player camera
|
||||||
void switchCamera();
|
void switchCamera();
|
||||||
public:
|
public:
|
||||||
CameraControl(
|
CameraControl(Player& player, const CameraSettings& settings);
|
||||||
Player* player, const CameraSettings& settings
|
|
||||||
);
|
|
||||||
void updateMouse(PlayerInput& input);
|
void updateMouse(PlayerInput& input);
|
||||||
void update(PlayerInput input, float delta, Chunks* chunks);
|
void update(PlayerInput input, float delta, const Chunks& chunks);
|
||||||
void refresh();
|
void refresh();
|
||||||
};
|
};
|
||||||
|
|
||||||
class PlayerController {
|
class PlayerController {
|
||||||
const EngineSettings& settings;
|
const EngineSettings& settings;
|
||||||
Level* level;
|
Level& level;
|
||||||
Player* player;
|
Player& player;
|
||||||
PlayerInput input {};
|
PlayerInput input {};
|
||||||
CameraControl camControl;
|
CameraControl camControl;
|
||||||
BlocksController* blocksController;
|
BlocksController& blocksController;
|
||||||
util::Clock playerTickClock;
|
|
||||||
|
|
||||||
float interactionTimer = 0.0f;
|
float interactionTimer = 0.0f;
|
||||||
|
|
||||||
void updateKeyboard();
|
void updateKeyboard();
|
||||||
void resetKeyboard();
|
void resetKeyboard();
|
||||||
void updatePlayer(float delta);
|
void updatePlayer(float delta);
|
||||||
@ -72,9 +69,9 @@ class PlayerController {
|
|||||||
public:
|
public:
|
||||||
PlayerController(
|
PlayerController(
|
||||||
const EngineSettings& settings,
|
const EngineSettings& settings,
|
||||||
Level* level,
|
Level& level,
|
||||||
Player* player,
|
Player& player,
|
||||||
BlocksController* blocksController
|
BlocksController& blocksController
|
||||||
);
|
);
|
||||||
|
|
||||||
/// @brief Called after blocks update if not paused
|
/// @brief Called after blocks update if not paused
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "data/StructLayout.hpp"
|
#include "data/StructLayout.hpp"
|
||||||
@ -15,13 +16,13 @@
|
|||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static const Block* require_block(lua::State* L) {
|
static inline const Block* require_block(lua::State* L) {
|
||||||
auto indices = content->getIndices();
|
auto indices = content->getIndices();
|
||||||
auto id = lua::tointeger(L, 1);
|
auto id = lua::tointeger(L, 1);
|
||||||
return indices->blocks.get(id);
|
return indices->blocks.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_def(lua::State* L) {
|
static inline int l_get_def(lua::State* L) {
|
||||||
if (auto def = require_block(L)) {
|
if (auto def = require_block(L)) {
|
||||||
return lua::pushstring(L, def->name);
|
return lua::pushstring(L, def->name);
|
||||||
}
|
}
|
||||||
@ -39,8 +40,9 @@ static int l_is_solid_at(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
|
return lua::pushboolean(
|
||||||
return lua::pushboolean(L, level->chunks->isSolidBlock(x, y, z));
|
L, blocks_agent::is_solid_at(*level->chunks, x, y, z)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_count(lua::State* L) {
|
static int l_count(lua::State* L) {
|
||||||
@ -70,7 +72,7 @@ static int l_is_segment(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
const auto& vox = level->chunks->require(x, y, z);
|
const auto& vox = blocks_agent::require(*level->chunks, x, y, z);
|
||||||
return lua::pushboolean(L, vox.state.segment);
|
return lua::pushboolean(L, vox.state.segment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,10 +80,10 @@ static int l_seek_origin(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
const auto& vox = level->chunks->require(x, y, z);
|
const auto& vox = blocks_agent::require(*level->chunks, x, y, z);
|
||||||
auto& def = indices->blocks.require(vox.id);
|
auto& def = indices->blocks.require(vox.id);
|
||||||
return lua::pushivec_stack(
|
return lua::pushivec_stack(
|
||||||
L, level->chunks->seekOrigin({x, y, z}, def, vox.state)
|
L, blocks_agent::seek_origin(*level->chunks, {x, y, z}, def, vox.state)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,11 +97,19 @@ static int l_set(lua::State* L) {
|
|||||||
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!level->chunks->get(x, y, z)) {
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
|
if (!blocks_agent::get_chunk(*level->chunks, cx, cz)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
level->chunks->set(x, y, z, id, int2blockstate(state));
|
blocks_agent::set(*level->chunks, x, y, z, id, int2blockstate(state));
|
||||||
level->lighting->onBlockSet(x, y, z, id);
|
|
||||||
|
auto chunksController = controller->getChunksController();
|
||||||
|
if (chunksController == nullptr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Lighting& lighting = *chunksController->lighting;
|
||||||
|
lighting.onBlockSet(x, y, z, id);
|
||||||
if (!noupdate) {
|
if (!noupdate) {
|
||||||
blocks->updateSides(x, y, z);
|
blocks->updateSides(x, y, z);
|
||||||
}
|
}
|
||||||
@ -110,16 +120,7 @@ static int l_get(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = level->chunks->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
int id = vox == nullptr ? -1 : vox->id;
|
|
||||||
return lua::pushinteger(L, id);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int l_get_slow(lua::State* L) {
|
|
||||||
auto x = lua::tointeger(L, 1);
|
|
||||||
auto y = lua::tointeger(L, 2);
|
|
||||||
auto z = lua::tointeger(L, 3);
|
|
||||||
auto vox = level->chunksStorage->get(x, y, z);
|
|
||||||
int id = vox == nullptr ? -1 : vox->id;
|
int id = vox == nullptr ? -1 : vox->id;
|
||||||
return lua::pushinteger(L, id);
|
return lua::pushinteger(L, id);
|
||||||
}
|
}
|
||||||
@ -128,7 +129,7 @@ static int l_get_x(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = level->chunksStorage->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushivec_stack(L, glm::ivec3(1, 0, 0));
|
return lua::pushivec_stack(L, glm::ivec3(1, 0, 0));
|
||||||
}
|
}
|
||||||
@ -145,7 +146,7 @@ static int l_get_y(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = level->chunksStorage->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushivec_stack(L, glm::ivec3(0, 1, 0));
|
return lua::pushivec_stack(L, glm::ivec3(0, 1, 0));
|
||||||
}
|
}
|
||||||
@ -162,7 +163,7 @@ static int l_get_z(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = level->chunksStorage->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushivec_stack(L, glm::ivec3(0, 0, 1));
|
return lua::pushivec_stack(L, glm::ivec3(0, 0, 1));
|
||||||
}
|
}
|
||||||
@ -179,7 +180,7 @@ static int l_get_rotation(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
voxel* vox = level->chunksStorage->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
int rotation = vox == nullptr ? 0 : vox->state.rotation;
|
int rotation = vox == nullptr ? 0 : vox->state.rotation;
|
||||||
return lua::pushinteger(L, rotation);
|
return lua::pushinteger(L, rotation);
|
||||||
}
|
}
|
||||||
@ -189,7 +190,7 @@ static int l_set_rotation(lua::State* L) {
|
|||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto value = lua::tointeger(L, 4);
|
auto value = lua::tointeger(L, 4);
|
||||||
level->chunks->setRotation(x, y, z, value);
|
blocks_agent::set_rotation(*level->chunks, x, y, z, value);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,7 +198,7 @@ static int l_get_states(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto vox = level->chunksStorage->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
int states = vox == nullptr ? 0 : blockstate2int(vox->state);
|
int states = vox == nullptr ? 0 : blockstate2int(vox->state);
|
||||||
return lua::pushinteger(L, states);
|
return lua::pushinteger(L, states);
|
||||||
}
|
}
|
||||||
@ -207,13 +208,18 @@ static int l_set_states(lua::State* L) {
|
|||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto states = lua::tointeger(L, 4);
|
auto states = lua::tointeger(L, 4);
|
||||||
|
if (y < 0 || y >= CHUNK_H) {
|
||||||
auto chunk = level->chunks->getChunkByVoxel(x, y, z);
|
return 0;
|
||||||
|
}
|
||||||
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
|
auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz);
|
||||||
if (chunk == nullptr) {
|
if (chunk == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto vox = level->chunks->get(x, y, z);
|
int lx = x - cx * CHUNK_W;
|
||||||
vox->state = int2blockstate(states);
|
int lz = z - cz * CHUNK_D;
|
||||||
|
chunk->voxels[vox_index(lx, y, lz)].state = int2blockstate(states);
|
||||||
chunk->setModifiedAndUnsaved();
|
chunk->setModifiedAndUnsaved();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -226,14 +232,16 @@ static int l_get_user_bits(lua::State* L) {
|
|||||||
auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
auto offset = lua::tointeger(L, 4) + VOXEL_USER_BITS_OFFSET;
|
||||||
auto bits = lua::tointeger(L, 5);
|
auto bits = lua::tointeger(L, 5);
|
||||||
|
|
||||||
auto vox = level->chunks->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = content->getIndices()->blocks.require(vox->id);
|
||||||
if (def.rt.extended) {
|
if (def.rt.extended) {
|
||||||
auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state);
|
auto origin = blocks_agent::seek_origin(
|
||||||
vox = level->chunks->get(origin.x, origin.y, origin.z);
|
*level->chunks, {x, y, z}, def, vox->state
|
||||||
|
);
|
||||||
|
vox = blocks_agent::get(*level->chunks, origin.x, origin.y, origin.z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return lua::pushinteger(L, 0);
|
return lua::pushinteger(L, 0);
|
||||||
}
|
}
|
||||||
@ -244,6 +252,7 @@ static int l_get_user_bits(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_user_bits(lua::State* L) {
|
static int l_set_user_bits(lua::State* L) {
|
||||||
|
auto& chunks = *level->chunks;
|
||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
@ -253,18 +262,19 @@ static int l_set_user_bits(lua::State* L) {
|
|||||||
size_t mask = ((1 << bits) - 1) << offset;
|
size_t mask = ((1 << bits) - 1) << offset;
|
||||||
auto value = (lua::tointeger(L, 6) << offset) & mask;
|
auto value = (lua::tointeger(L, 6) << offset) & mask;
|
||||||
|
|
||||||
auto chunk = level->chunks->getChunkByVoxel(x, y, z);
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
if (chunk == nullptr) {
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
return 0;
|
auto chunk = blocks_agent::get_chunk(chunks, cx, cz);
|
||||||
}
|
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
||||||
auto vox = level->chunks->get(x, y, z);
|
|
||||||
if (vox == nullptr) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
int lx = x - cx * CHUNK_W;
|
||||||
|
int lz = z - cz * CHUNK_D;
|
||||||
|
auto vox = &chunk->voxels[vox_index(lx, y, lz)];
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = content->getIndices()->blocks.require(vox->id);
|
||||||
if (def.rt.extended) {
|
if (def.rt.extended) {
|
||||||
auto origin = level->chunks->seekOrigin({x, y, z}, def, vox->state);
|
auto origin = blocks_agent::seek_origin(chunks, {x, y, z}, def, vox->state);
|
||||||
vox = level->chunks->get(origin);
|
vox = blocks_agent::get(chunks, origin.x, origin.y, origin.z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -278,7 +288,9 @@ static int l_is_replaceable_at(lua::State* L) {
|
|||||||
auto x = lua::tointeger(L, 1);
|
auto x = lua::tointeger(L, 1);
|
||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
return lua::pushboolean(L, level->chunks->isReplaceableBlock(x, y, z));
|
return lua::pushboolean(
|
||||||
|
L, blocks_agent::is_replaceable_at(*level->chunks, x, y, z)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_caption(lua::State* L) {
|
static int l_caption(lua::State* L) {
|
||||||
@ -352,7 +364,7 @@ static int l_place(lua::State* L) {
|
|||||||
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
if (static_cast<size_t>(id) >= indices->blocks.count()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!level->chunks->get(x, y, z)) {
|
if (!blocks_agent::get(*level->chunks, x, y, z)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const auto def = level->content->getIndices()->blocks.get(id);
|
const auto def = level->content->getIndices()->blocks.get(id);
|
||||||
@ -373,11 +385,11 @@ static int l_destruct(lua::State* L) {
|
|||||||
auto y = lua::tointeger(L, 2);
|
auto y = lua::tointeger(L, 2);
|
||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1;
|
auto playerid = lua::gettop(L) >= 4 ? lua::tointeger(L, 4) : -1;
|
||||||
auto voxel = level->chunks->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
if (voxel == nullptr) {
|
if (vox == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
auto& def = level->content->getIndices()->blocks.require(voxel->id);
|
auto& def = level->content->getIndices()->blocks.require(vox->id);
|
||||||
auto player = level->players->get(playerid);
|
auto player = level->players->get(playerid);
|
||||||
controller->getBlocksController()->breakBlock(player, def, x, y, z);
|
controller->getBlocksController()->breakBlock(player, def, x, y, z);
|
||||||
return 0;
|
return 0;
|
||||||
@ -407,8 +419,15 @@ static int l_raycast(lua::State* L) {
|
|||||||
glm::vec3 end;
|
glm::vec3 end;
|
||||||
glm::ivec3 normal;
|
glm::ivec3 normal;
|
||||||
glm::ivec3 iend;
|
glm::ivec3 iend;
|
||||||
if (auto voxel = level->chunks->rayCast(
|
if (auto voxel = blocks_agent::raycast(
|
||||||
start, dir, maxDistance, end, normal, iend, filteredBlocks
|
*level->chunks,
|
||||||
|
start,
|
||||||
|
dir,
|
||||||
|
maxDistance,
|
||||||
|
end,
|
||||||
|
normal,
|
||||||
|
iend,
|
||||||
|
filteredBlocks
|
||||||
)) {
|
)) {
|
||||||
if (lua::gettop(L) >= 4 && !lua::isnil(L, 4)) {
|
if (lua::gettop(L) >= 4 && !lua::isnil(L, 4)) {
|
||||||
lua::pushvalue(L, 4);
|
lua::pushvalue(L, 4);
|
||||||
@ -504,12 +523,15 @@ static int l_get_field(lua::State* L) {
|
|||||||
}
|
}
|
||||||
auto cx = floordiv(x, CHUNK_W);
|
auto cx = floordiv(x, CHUNK_W);
|
||||||
auto cz = floordiv(z, CHUNK_D);
|
auto cz = floordiv(z, CHUNK_D);
|
||||||
auto chunk = level->chunks->getChunk(cx, cz);
|
auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz);
|
||||||
|
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
auto lx = x - cx * CHUNK_W;
|
auto lx = x - cx * CHUNK_W;
|
||||||
auto lz = z - cz * CHUNK_W;
|
auto lz = z - cz * CHUNK_W;
|
||||||
size_t voxelIndex = vox_index(lx, y, lz);
|
size_t voxelIndex = vox_index(lx, y, lz);
|
||||||
|
|
||||||
const auto& vox = level->chunks->require(x, y, z);
|
const auto& vox = chunk->voxels[voxelIndex];
|
||||||
const auto& def = content->getIndices()->blocks.require(vox.id);
|
const auto& def = content->getIndices()->blocks.require(vox.id);
|
||||||
if (def.dataStruct == nullptr) {
|
if (def.dataStruct == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
@ -568,15 +590,18 @@ static int l_set_field(lua::State* L) {
|
|||||||
if (lua::gettop(L) >= 6) {
|
if (lua::gettop(L) >= 6) {
|
||||||
index = lua::tointeger(L, 6);
|
index = lua::tointeger(L, 6);
|
||||||
}
|
}
|
||||||
auto vox = level->chunks->get(x, y, z);
|
|
||||||
auto cx = floordiv(x, CHUNK_W);
|
auto cx = floordiv(x, CHUNK_W);
|
||||||
auto cz = floordiv(z, CHUNK_D);
|
auto cz = floordiv(z, CHUNK_D);
|
||||||
auto chunk = level->chunks->getChunk(cx, cz);
|
|
||||||
auto lx = x - cx * CHUNK_W;
|
auto lx = x - cx * CHUNK_W;
|
||||||
auto lz = z - cz * CHUNK_W;
|
auto lz = z - cz * CHUNK_W;
|
||||||
|
auto chunk = blocks_agent::get_chunk(*level->chunks, cx, cz);
|
||||||
|
if (chunk == nullptr || y < 0 || y >= CHUNK_H) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
size_t voxelIndex = vox_index(lx, y, lz);
|
size_t voxelIndex = vox_index(lx, y, lz);
|
||||||
|
const auto& vox = chunk->voxels[voxelIndex];
|
||||||
|
|
||||||
const auto& def = content->getIndices()->blocks.require(vox->id);
|
const auto& def = content->getIndices()->blocks.require(vox.id);
|
||||||
if (def.dataStruct == nullptr) {
|
if (def.dataStruct == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -608,7 +633,6 @@ const luaL_Reg blocklib[] = {
|
|||||||
{"is_replaceable_at", lua::wrap<l_is_replaceable_at>},
|
{"is_replaceable_at", lua::wrap<l_is_replaceable_at>},
|
||||||
{"set", lua::wrap<l_set>},
|
{"set", lua::wrap<l_set>},
|
||||||
{"get", lua::wrap<l_get>},
|
{"get", lua::wrap<l_get>},
|
||||||
{"get_slow", lua::wrap<l_get_slow>},
|
|
||||||
{"get_X", lua::wrap<l_get_x>},
|
{"get_X", lua::wrap<l_get_x>},
|
||||||
{"get_Y", lua::wrap<l_get_y>},
|
{"get_Y", lua::wrap<l_get_y>},
|
||||||
{"get_Z", lua::wrap<l_get_z>},
|
{"get_Z", lua::wrap<l_get_z>},
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include "logic/EngineController.hpp"
|
#include "logic/EngineController.hpp"
|
||||||
#include "logic/LevelController.hpp"
|
#include "logic/LevelController.hpp"
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
|
#include "util/platform.hpp"
|
||||||
#include "window/Events.hpp"
|
#include "window/Events.hpp"
|
||||||
#include "window/Window.hpp"
|
#include "window/Window.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
@ -55,6 +56,9 @@ static int l_open_world(lua::State* L) {
|
|||||||
/// @brief Reopen world
|
/// @brief Reopen world
|
||||||
static int l_reopen_world(lua::State*) {
|
static int l_reopen_world(lua::State*) {
|
||||||
auto controller = engine->getController();
|
auto controller = engine->getController();
|
||||||
|
if (level == nullptr) {
|
||||||
|
throw std::runtime_error("no world open");
|
||||||
|
}
|
||||||
controller->reopenWorld(level->getWorld());
|
controller->reopenWorld(level->getWorld());
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -220,17 +224,15 @@ static int l_load_texture(lua::State* L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "util/platform.hpp"
|
|
||||||
|
|
||||||
static int l_open_folder(lua::State* L) {
|
static int l_open_folder(lua::State* L) {
|
||||||
auto path = engine->getPaths()->resolve(lua::require_string(L, 1));
|
auto path = engine->getPaths().resolve(lua::require_string(L, 1));
|
||||||
platform::open_folder(path);
|
platform::open_folder(path);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Quit the game
|
/// @brief Quit the game
|
||||||
static int l_quit(lua::State*) {
|
static int l_quit(lua::State*) {
|
||||||
Window::setShouldClose(true);
|
engine->quit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +241,7 @@ static int l_blank(lua::State*) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const luaL_Reg corelib[] = {
|
const luaL_Reg corelib[] = {
|
||||||
{"nop", lua::wrap<l_blank>},
|
{"blank", lua::wrap<l_blank>},
|
||||||
{"get_version", lua::wrap<l_get_version>},
|
{"get_version", lua::wrap<l_get_version>},
|
||||||
{"new_world", lua::wrap<l_new_world>},
|
{"new_world", lua::wrap<l_new_world>},
|
||||||
{"open_world", lua::wrap<l_open_world>},
|
{"open_world", lua::wrap<l_open_world>},
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
@ -149,8 +150,15 @@ static int l_raycast(lua::State* L) {
|
|||||||
|
|
||||||
blockid_t block = BLOCK_VOID;
|
blockid_t block = BLOCK_VOID;
|
||||||
|
|
||||||
if (auto voxel = level->chunks->rayCast(
|
if (auto voxel = blocks_agent::raycast(
|
||||||
start, dir, maxDistance, end, normal, iend, filteredBlocks
|
*level->chunks,
|
||||||
|
start,
|
||||||
|
dir,
|
||||||
|
maxDistance,
|
||||||
|
end,
|
||||||
|
normal,
|
||||||
|
iend,
|
||||||
|
filteredBlocks
|
||||||
)) {
|
)) {
|
||||||
maxDistance = glm::distance(start, end);
|
maxDistance = glm::distance(start, end);
|
||||||
block = voxel->id;
|
block = voxel->id;
|
||||||
|
|||||||
@ -14,14 +14,14 @@ namespace fs = std::filesystem;
|
|||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static fs::path resolve_path(const std::string& path) {
|
static fs::path resolve_path(const std::string& path) {
|
||||||
return engine->getPaths()->resolve(path);
|
return engine->getPaths().resolve(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fs::path resolve_path_soft(const std::string& path) {
|
static fs::path resolve_path_soft(const std::string& path) {
|
||||||
if (path.find(':') == std::string::npos) {
|
if (path.find(':') == std::string::npos) {
|
||||||
return fs::u8path("");
|
return fs::u8path("");
|
||||||
}
|
}
|
||||||
return engine->getPaths()->resolve(path, false);
|
return engine->getPaths().resolve(path, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_find(lua::State* L) {
|
static int l_find(lua::State* L) {
|
||||||
|
|||||||
@ -12,9 +12,9 @@
|
|||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static int l_save_fragment(lua::State* L) {
|
static int l_save_fragment(lua::State* L) {
|
||||||
auto paths = engine->getPaths();
|
const auto& paths = engine->getPaths();
|
||||||
auto fragment = lua::touserdata<lua::LuaVoxelFragment>(L, 1);
|
auto fragment = lua::touserdata<lua::LuaVoxelFragment>(L, 1);
|
||||||
auto file = paths->resolve(lua::require_string(L, 2), true);
|
auto file = paths.resolve(lua::require_string(L, 2), true);
|
||||||
auto map = fragment->getFragment()->serialize();
|
auto map = fragment->getFragment()->serialize();
|
||||||
auto bytes = json::to_binary(map, true);
|
auto bytes = json::to_binary(map, true);
|
||||||
files::write_bytes(file, bytes.data(), bytes.size());
|
files::write_bytes(file, bytes.data(), bytes.size());
|
||||||
@ -28,16 +28,16 @@ static int l_create_fragment(lua::State* L) {
|
|||||||
bool saveEntities = lua::toboolean(L, 4);
|
bool saveEntities = lua::toboolean(L, 4);
|
||||||
|
|
||||||
auto fragment =
|
auto fragment =
|
||||||
VoxelFragment::create(level, pointA, pointB, crop, saveEntities);
|
VoxelFragment::create(*level, pointA, pointB, crop, saveEntities);
|
||||||
return lua::newuserdata<lua::LuaVoxelFragment>(
|
return lua::newuserdata<lua::LuaVoxelFragment>(
|
||||||
L, std::shared_ptr<VoxelFragment>(std::move(fragment))
|
L, std::shared_ptr<VoxelFragment>(std::move(fragment))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_load_fragment(lua::State* L) {
|
static int l_load_fragment(lua::State* L) {
|
||||||
auto paths = engine->getPaths();
|
const auto& paths = engine->getPaths();
|
||||||
auto filename = lua::require_string(L, 1);
|
auto filename = lua::require_string(L, 1);
|
||||||
auto path = paths->resolve(filename);
|
auto path = paths.resolve(filename);
|
||||||
if (!std::filesystem::exists(path)) {
|
if (!std::filesystem::exists(path)) {
|
||||||
throw std::runtime_error("file "+path.u8string()+" does not exist");
|
throw std::runtime_error("file "+path.u8string()+" does not exist");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "api_lua.hpp"
|
#include "api_lua.hpp"
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ static int l_open_block(lua::State* L) {
|
|||||||
auto z = lua::tointeger(L, 3);
|
auto z = lua::tointeger(L, 3);
|
||||||
bool playerInventory = !lua::toboolean(L, 4);
|
bool playerInventory = !lua::toboolean(L, 4);
|
||||||
|
|
||||||
auto vox = level->chunks->get(x, y, z);
|
auto vox = blocks_agent::get(*level->chunks, x, y, z);
|
||||||
if (vox == nullptr) {
|
if (vox == nullptr) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"block does not exists at " + std::to_string(x) + " " +
|
"block does not exists at " + std::to_string(x) + " " +
|
||||||
|
|||||||
@ -125,7 +125,7 @@ static void resetPackBindings(fs::path& packFolder) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_reset_bindings(lua::State*) {
|
static int l_reset_bindings(lua::State*) {
|
||||||
auto resFolder = engine->getPaths()->getResourcesFolder();
|
auto resFolder = engine->getPaths().getResourcesFolder();
|
||||||
resetPackBindings(resFolder);
|
resetPackBindings(resFolder);
|
||||||
for (auto& pack : engine->getContentPacks()) {
|
for (auto& pack : engine->getContentPacks()) {
|
||||||
resetPackBindings(pack.folder);
|
resetPackBindings(pack.folder);
|
||||||
|
|||||||
@ -16,6 +16,8 @@
|
|||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
|
#include "logic/LevelController.hpp"
|
||||||
|
#include "logic/ChunksController.hpp"
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
@ -32,8 +34,8 @@ static int l_is_open(lua::State* L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_get_list(lua::State* L) {
|
static int l_get_list(lua::State* L) {
|
||||||
auto paths = engine->getPaths();
|
const auto& paths = engine->getPaths();
|
||||||
auto worlds = paths->scanForWorlds();
|
auto worlds = paths.scanForWorlds();
|
||||||
|
|
||||||
lua::createtable(L, worlds.size(), 0);
|
lua::createtable(L, worlds.size(), 0);
|
||||||
for (size_t i = 0; i < worlds.size(); i++) {
|
for (size_t i = 0; i < worlds.size(); i++) {
|
||||||
@ -102,7 +104,7 @@ static int l_get_seed(lua::State* L) {
|
|||||||
|
|
||||||
static int l_exists(lua::State* L) {
|
static int l_exists(lua::State* L) {
|
||||||
auto name = lua::require_string(L, 1);
|
auto name = lua::require_string(L, 1);
|
||||||
auto worldsDir = engine->getPaths()->getWorldFolderByName(name);
|
auto worldsDir = engine->getPaths().getWorldFolderByName(name);
|
||||||
return lua::pushboolean(L, fs::is_directory(worldsDir));
|
return lua::pushboolean(L, fs::is_directory(worldsDir));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,30 +205,37 @@ static int l_set_chunk_data(lua::State* L) {
|
|||||||
} else {
|
} else {
|
||||||
chunk->decode(buffer->data().data());
|
chunk->decode(buffer->data().data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto chunksController = controller->getChunksController();
|
||||||
|
if (chunksController == nullptr) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Lighting& lighting = *chunksController->lighting;
|
||||||
chunk->updateHeights();
|
chunk->updateHeights();
|
||||||
level->lighting->buildSkyLight(x, y);
|
lighting.buildSkyLight(x, y);
|
||||||
chunk->flags.modified = true;
|
chunk->flags.modified = true;
|
||||||
level->lighting->onChunkLoaded(x, y, true);
|
lighting.onChunkLoaded(x, y, true);
|
||||||
|
|
||||||
chunk = level->chunks->getChunk(x - 1, y);
|
chunk = level->chunks->getChunk(x - 1, y);
|
||||||
if (chunk != nullptr) {
|
if (chunk != nullptr) {
|
||||||
chunk->flags.modified = true;
|
chunk->flags.modified = true;
|
||||||
level->lighting->onChunkLoaded(x - 1, y, true);
|
lighting.onChunkLoaded(x - 1, y, true);
|
||||||
}
|
}
|
||||||
chunk = level->chunks->getChunk(x + 1, y);
|
chunk = level->chunks->getChunk(x + 1, y);
|
||||||
if (chunk != nullptr) {
|
if (chunk != nullptr) {
|
||||||
chunk->flags.modified = true;
|
chunk->flags.modified = true;
|
||||||
level->lighting->onChunkLoaded(x + 1, y, true);
|
lighting.onChunkLoaded(x + 1, y, true);
|
||||||
}
|
}
|
||||||
chunk = level->chunks->getChunk(x, y - 1);
|
chunk = level->chunks->getChunk(x, y - 1);
|
||||||
if (chunk != nullptr) {
|
if (chunk != nullptr) {
|
||||||
chunk->flags.modified = true;
|
chunk->flags.modified = true;
|
||||||
level->lighting->onChunkLoaded(x, y - 1, true);
|
lighting.onChunkLoaded(x, y - 1, true);
|
||||||
}
|
}
|
||||||
chunk = level->chunks->getChunk(x, y + 1);
|
chunk = level->chunks->getChunk(x, y + 1);
|
||||||
if (chunk != nullptr) {
|
if (chunk != nullptr) {
|
||||||
chunk->flags.modified = true;
|
chunk->flags.modified = true;
|
||||||
level->lighting->onChunkLoaded(x, y + 1, true);
|
lighting.onChunkLoaded(x, y + 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -236,7 +245,7 @@ static int l_count_chunks(lua::State* L) {
|
|||||||
if (level == nullptr) {
|
if (level == nullptr) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return lua::pushinteger(L, level->chunksStorage->size());
|
return lua::pushinteger(L, level->chunks->size());
|
||||||
}
|
}
|
||||||
|
|
||||||
const luaL_Reg worldlib[] = {
|
const luaL_Reg worldlib[] = {
|
||||||
|
|||||||
@ -121,7 +121,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) {
|
|||||||
main_thread = create_state(
|
main_thread = create_state(
|
||||||
paths, params.headless ? StateType::TEST : StateType::BASE
|
paths, params.headless ? StateType::TEST : StateType::BASE
|
||||||
);
|
);
|
||||||
lua::pushstring(main_thread, params.testFile.stem().u8string());
|
lua::pushstring(main_thread, params.scriptFile.stem().u8string());
|
||||||
lua::setglobal(main_thread, "__VC_TEST_NAME");
|
lua::setglobal(main_thread, "__VC_TEST_NAME");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -50,9 +50,9 @@ const float* LuaHeightmap::getValues() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int l_dump(lua::State* L) {
|
static int l_dump(lua::State* L) {
|
||||||
auto paths = scripting::engine->getPaths();
|
const auto& paths = scripting::engine->getPaths();
|
||||||
if (auto heightmap = touserdata<LuaHeightmap>(L, 1)) {
|
if (auto heightmap = touserdata<LuaHeightmap>(L, 1)) {
|
||||||
auto file = paths->resolve(require_string(L, 2));
|
auto file = paths.resolve(require_string(L, 2));
|
||||||
uint w = heightmap->getWidth();
|
uint w = heightmap->getWidth();
|
||||||
uint h = heightmap->getHeight();
|
uint h = heightmap->getHeight();
|
||||||
ImageData image(ImageFormat::rgb888, w, h);
|
ImageData image(ImageFormat::rgb888, w, h);
|
||||||
|
|||||||
@ -41,8 +41,8 @@ BlocksController* scripting::blocks = nullptr;
|
|||||||
LevelController* scripting::controller = nullptr;
|
LevelController* scripting::controller = nullptr;
|
||||||
|
|
||||||
void scripting::load_script(const fs::path& name, bool throwable) {
|
void scripting::load_script(const fs::path& name, bool throwable) {
|
||||||
auto paths = scripting::engine->getPaths();
|
const auto& paths = scripting::engine->getPaths();
|
||||||
fs::path file = paths->getResourcesFolder() / fs::path("scripts") / name;
|
fs::path file = paths.getResourcesFolder() / fs::path("scripts") / name;
|
||||||
std::string src = files::read_string(file);
|
std::string src = files::read_string(file);
|
||||||
auto L = lua::get_main_state();
|
auto L = lua::get_main_state();
|
||||||
lua::loadbuffer(L, 0, src, "core:scripts/"+name.u8string());
|
lua::loadbuffer(L, 0, src, "core:scripts/"+name.u8string());
|
||||||
@ -66,7 +66,7 @@ int scripting::load_script(
|
|||||||
|
|
||||||
void scripting::initialize(Engine* engine) {
|
void scripting::initialize(Engine* engine) {
|
||||||
scripting::engine = engine;
|
scripting::engine = engine;
|
||||||
lua::initialize(*engine->getPaths(), engine->getCoreParameters());
|
lua::initialize(engine->getPaths(), engine->getCoreParameters());
|
||||||
|
|
||||||
load_script(fs::path("stdlib.lua"), true);
|
load_script(fs::path("stdlib.lua"), true);
|
||||||
load_script(fs::path("classes.lua"), true);
|
load_script(fs::path("classes.lua"), true);
|
||||||
|
|||||||
@ -18,7 +18,7 @@ Hud* scripting::hud = nullptr;
|
|||||||
WorldRenderer* scripting::renderer = nullptr;
|
WorldRenderer* scripting::renderer = nullptr;
|
||||||
|
|
||||||
static void load_script(const std::string& name) {
|
static void load_script(const std::string& name) {
|
||||||
auto file = engine->getPaths()->getResourcesFolder() / "scripts" / name;
|
auto file = engine->getPaths().getResourcesFolder() / "scripts" / name;
|
||||||
std::string src = files::read_string(file);
|
std::string src = files::read_string(file);
|
||||||
logger.info() << "loading script " << file.u8string();
|
logger.info() << "loading script " << file.u8string();
|
||||||
|
|
||||||
|
|||||||
@ -255,7 +255,7 @@ std::unique_ptr<GeneratorScript> scripting::load_generator(
|
|||||||
const fs::path& file,
|
const fs::path& file,
|
||||||
const std::string& dirPath
|
const std::string& dirPath
|
||||||
) {
|
) {
|
||||||
auto L = create_state(*engine->getPaths(), StateType::GENERATOR);
|
auto L = create_state(engine->getPaths(), StateType::GENERATOR);
|
||||||
|
|
||||||
return std::make_unique<LuaGeneratorScript>(L, def, file, dirPath);
|
return std::make_unique<LuaGeneratorScript>(L, def, file, dirPath);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -459,7 +459,7 @@ void Entities::updatePhysics(float delta) {
|
|||||||
float vel = glm::length(prevVel);
|
float vel = glm::length(prevVel);
|
||||||
int substeps = static_cast<int>(delta * vel * 20);
|
int substeps = static_cast<int>(delta * vel * 20);
|
||||||
substeps = std::min(100, std::max(2, substeps));
|
substeps = std::min(100, std::max(2, substeps));
|
||||||
physics->step(level->chunks.get(), &hitbox, delta, substeps, eid.uid);
|
physics->step(*level->chunks, &hitbox, delta, substeps, eid.uid);
|
||||||
hitbox.linearDamping = hitbox.grounded * 24;
|
hitbox.linearDamping = hitbox.grounded * 24;
|
||||||
transform.setPos(hitbox.position);
|
transform.setPos(hitbox.position);
|
||||||
if (hitbox.grounded && !grounded) {
|
if (hitbox.grounded && !grounded) {
|
||||||
|
|||||||
@ -44,6 +44,9 @@ Player::Player(
|
|||||||
position(position),
|
position(position),
|
||||||
inventory(std::move(inv)),
|
inventory(std::move(inv)),
|
||||||
eid(eid),
|
eid(eid),
|
||||||
|
chunks(std::make_unique<Chunks>(
|
||||||
|
3, 3, 0, 0, level->events.get(), level->content->getIndices()
|
||||||
|
)),
|
||||||
fpCamera(level->getCamera("core:first-person")),
|
fpCamera(level->getCamera("core:first-person")),
|
||||||
spCamera(level->getCamera("core:third-person-front")),
|
spCamera(level->getCamera("core:third-person-front")),
|
||||||
tpCamera(level->getCamera("core:third-person-back")),
|
tpCamera(level->getCamera("core:third-person-back")),
|
||||||
@ -202,12 +205,12 @@ void Player::attemptToFindSpawnpoint() {
|
|||||||
position.z + (rand() % 200 - 100)
|
position.z + (rand() % 200 - 100)
|
||||||
);
|
);
|
||||||
while (newpos.y > 0 &&
|
while (newpos.y > 0 &&
|
||||||
!level->chunks->isObstacleBlock(newpos.x, newpos.y - 2, newpos.z)) {
|
!chunks->isObstacleBlock(newpos.x, newpos.y - 2, newpos.z)) {
|
||||||
newpos.y--;
|
newpos.y--;
|
||||||
}
|
}
|
||||||
|
|
||||||
voxel* headvox = level->chunks->get(newpos.x, newpos.y + 1, newpos.z);
|
voxel* headvox = chunks->get(newpos.x, newpos.y + 1, newpos.z);
|
||||||
if (level->chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) ||
|
if (chunks->isObstacleBlock(newpos.x, newpos.y, newpos.z) ||
|
||||||
headvox == nullptr || headvox->id != 0) {
|
headvox == nullptr || headvox->id != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
|
|
||||||
|
class Chunks;
|
||||||
class Camera;
|
class Camera;
|
||||||
class Inventory;
|
class Inventory;
|
||||||
class ContentReport;
|
class ContentReport;
|
||||||
@ -55,6 +56,7 @@ class Player : public Serializable {
|
|||||||
entityid_t eid;
|
entityid_t eid;
|
||||||
entityid_t selectedEid = 0;
|
entityid_t selectedEid = 0;
|
||||||
public:
|
public:
|
||||||
|
std::unique_ptr<Chunks> chunks; // not in use yet
|
||||||
std::shared_ptr<Camera> fpCamera, spCamera, tpCamera;
|
std::shared_ptr<Camera> fpCamera, spCamera, tpCamera;
|
||||||
std::shared_ptr<Camera> currentCamera;
|
std::shared_ptr<Camera> currentCamera;
|
||||||
bool debug = false;
|
bool debug = false;
|
||||||
|
|||||||
@ -36,4 +36,8 @@ public:
|
|||||||
auto end() const {
|
auto end() const {
|
||||||
return players.end();
|
return players.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t size() const {
|
||||||
|
return players.size();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "maths/aabb.hpp"
|
#include "maths/aabb.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
#include "voxels/voxel.hpp"
|
#include "voxels/voxel.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -18,7 +18,7 @@ PhysicsSolver::PhysicsSolver(glm::vec3 gravity) : gravity(gravity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSolver::step(
|
void PhysicsSolver::step(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
Hitbox* hitbox,
|
Hitbox* hitbox,
|
||||||
float delta,
|
float delta,
|
||||||
uint substeps,
|
uint substeps,
|
||||||
@ -63,7 +63,7 @@ void PhysicsSolver::step(
|
|||||||
float x = (px-half.x+E) + ix * s;
|
float x = (px-half.x+E) + ix * s;
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++){
|
for (int iz = 0; iz <= (half.z-E)*2/s; iz++){
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
float z = (pos.z-half.z+E) + iz * s;
|
||||||
if (chunks->isObstacleAt(x,y,z)){
|
if (chunks.isObstacleAt(x,y,z)){
|
||||||
hitbox->grounded = true;
|
hitbox->grounded = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ void PhysicsSolver::step(
|
|||||||
float x = (pos.x-half.x+E) + ix * s;
|
float x = (pos.x-half.x+E) + ix * s;
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++){
|
for (int iz = 0; iz <= (half.z-E)*2/s; iz++){
|
||||||
float z = (pz-half.z+E) + iz * s;
|
float z = (pz-half.z+E) + iz * s;
|
||||||
if (chunks->isObstacleAt(x,y,z)){
|
if (chunks.isObstacleAt(x,y,z)){
|
||||||
hitbox->grounded = true;
|
hitbox->grounded = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -119,8 +119,8 @@ void PhysicsSolver::step(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static float calc_step_height(
|
static float calc_step_height(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& half,
|
const glm::vec3& half,
|
||||||
float stepHeight,
|
float stepHeight,
|
||||||
float s
|
float s
|
||||||
@ -130,7 +130,7 @@ static float calc_step_height(
|
|||||||
float x = (pos.x-half.x+E) + ix * s;
|
float x = (pos.x-half.x+E) + ix * s;
|
||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
float z = (pos.z-half.z+E) + iz * s;
|
||||||
if (chunks->isObstacleAt(x, pos.y+half.y+stepHeight, z)) {
|
if (chunks.isObstacleAt(x, pos.y+half.y+stepHeight, z)) {
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ static float calc_step_height(
|
|||||||
|
|
||||||
template <int nx, int ny, int nz>
|
template <int nx, int ny, int nz>
|
||||||
static bool calc_collision_neg(
|
static bool calc_collision_neg(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
glm::vec3& pos,
|
glm::vec3& pos,
|
||||||
glm::vec3& vel,
|
glm::vec3& vel,
|
||||||
const glm::vec3& half,
|
const glm::vec3& half,
|
||||||
@ -159,7 +159,7 @@ static bool calc_collision_neg(
|
|||||||
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
||||||
coord[nx] = (pos[nx]-half[nx]-E);
|
coord[nx] = (pos[nx]-half[nx]-E);
|
||||||
|
|
||||||
if (const auto aabb = chunks->isObstacleAt(coord.x, coord.y, coord.z)) {
|
if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) {
|
||||||
vel[nx] = 0.0f;
|
vel[nx] = 0.0f;
|
||||||
float newx = std::floor(coord[nx]) + aabb->max()[nx] + half[nx] + E;
|
float newx = std::floor(coord[nx]) + aabb->max()[nx] + half[nx] + E;
|
||||||
if (std::abs(newx-pos[nx]) <= MAX_FIX) {
|
if (std::abs(newx-pos[nx]) <= MAX_FIX) {
|
||||||
@ -174,7 +174,7 @@ static bool calc_collision_neg(
|
|||||||
|
|
||||||
template <int nx, int ny, int nz>
|
template <int nx, int ny, int nz>
|
||||||
static void calc_collision_pos(
|
static void calc_collision_pos(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
glm::vec3& pos,
|
glm::vec3& pos,
|
||||||
glm::vec3& vel,
|
glm::vec3& vel,
|
||||||
const glm::vec3& half,
|
const glm::vec3& half,
|
||||||
@ -191,7 +191,7 @@ static void calc_collision_pos(
|
|||||||
for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) {
|
for (int iz = 0; iz <= (half[nz]-E)*2/s; iz++) {
|
||||||
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
coord[nz] = (pos[nz]-half[nz]+E) + iz * s;
|
||||||
coord[nx] = (pos[nx]+half[nx]+E);
|
coord[nx] = (pos[nx]+half[nx]+E);
|
||||||
if (const auto aabb = chunks->isObstacleAt(coord.x, coord.y, coord.z)) {
|
if (const auto aabb = chunks.isObstacleAt(coord.x, coord.y, coord.z)) {
|
||||||
vel[nx] = 0.0f;
|
vel[nx] = 0.0f;
|
||||||
float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E;
|
float newx = std::floor(coord[nx]) - half[nx] + aabb->min()[nx] - E;
|
||||||
if (std::abs(newx-pos[nx]) <= MAX_FIX) {
|
if (std::abs(newx-pos[nx]) <= MAX_FIX) {
|
||||||
@ -204,7 +204,7 @@ static void calc_collision_pos(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSolver::colisionCalc(
|
void PhysicsSolver::colisionCalc(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
Hitbox* hitbox,
|
Hitbox* hitbox,
|
||||||
glm::vec3& vel,
|
glm::vec3& vel,
|
||||||
glm::vec3& pos,
|
glm::vec3& pos,
|
||||||
@ -234,7 +234,7 @@ void PhysicsSolver::colisionCalc(
|
|||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
float z = (pos.z-half.z+E) + iz * s;
|
||||||
float y = (pos.y-half.y+E);
|
float y = (pos.y-half.y+E);
|
||||||
if ((aabb = chunks->isObstacleAt(x,y,z))){
|
if ((aabb = chunks.isObstacleAt(x,y,z))){
|
||||||
vel.y = 0.0f;
|
vel.y = 0.0f;
|
||||||
float newy = std::floor(y) + aabb->max().y + half.y;
|
float newy = std::floor(y) + aabb->max().y + half.y;
|
||||||
if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) {
|
if (std::abs(newy-pos.y) <= MAX_FIX+stepHeight) {
|
||||||
@ -251,7 +251,7 @@ void PhysicsSolver::colisionCalc(
|
|||||||
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
for (int iz = 0; iz <= (half.z-E)*2/s; iz++) {
|
||||||
float z = (pos.z-half.z+E) + iz * s;
|
float z = (pos.z-half.z+E) + iz * s;
|
||||||
float y = (pos.y+half.y+E);
|
float y = (pos.y+half.y+E);
|
||||||
if ((aabb = chunks->isObstacleAt(x,y,z))){
|
if ((aabb = chunks.isObstacleAt(x,y,z))){
|
||||||
vel.y = 0.0f;
|
vel.y = 0.0f;
|
||||||
float newy = std::floor(y) - half.y + aabb->min().y - E;
|
float newy = std::floor(y) - half.y + aabb->min().y - E;
|
||||||
if (std::abs(newy-pos.y) <= MAX_FIX) {
|
if (std::abs(newy-pos.y) <= MAX_FIX) {
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
class Block;
|
class Block;
|
||||||
class Chunks;
|
class GlobalChunks;
|
||||||
struct Sensor;
|
struct Sensor;
|
||||||
|
|
||||||
class PhysicsSolver {
|
class PhysicsSolver {
|
||||||
@ -18,14 +18,14 @@ class PhysicsSolver {
|
|||||||
public:
|
public:
|
||||||
PhysicsSolver(glm::vec3 gravity);
|
PhysicsSolver(glm::vec3 gravity);
|
||||||
void step(
|
void step(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
Hitbox* hitbox,
|
Hitbox* hitbox,
|
||||||
float delta,
|
float delta,
|
||||||
uint substeps,
|
uint substeps,
|
||||||
entityid_t entity
|
entityid_t entity
|
||||||
);
|
);
|
||||||
void colisionCalc(
|
void colisionCalc(
|
||||||
Chunks* chunks,
|
const GlobalChunks& chunks,
|
||||||
Hitbox* hitbox,
|
Hitbox* hitbox,
|
||||||
glm::vec3& vel,
|
glm::vec3& vel,
|
||||||
glm::vec3& pos,
|
glm::vec3& pos,
|
||||||
|
|||||||
@ -19,19 +19,30 @@ static bool perform_keyword(
|
|||||||
auto token = reader.next();
|
auto token = reader.next();
|
||||||
params.userFolder = fs::u8path(token);
|
params.userFolder = fs::u8path(token);
|
||||||
} else if (keyword == "--help" || keyword == "-h") {
|
} else if (keyword == "--help" || keyword == "-h") {
|
||||||
std::cout << "VoxelEngine command-line arguments:\n";
|
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||||
|
std::cout << "command-line arguments:\n";
|
||||||
std::cout << " --help - show help\n";
|
std::cout << " --help - show help\n";
|
||||||
|
std::cout << " --version - print engine version\n";
|
||||||
std::cout << " --res <path> - set resources directory\n";
|
std::cout << " --res <path> - set resources directory\n";
|
||||||
std::cout << " --dir <path> - set userfiles directory\n";
|
std::cout << " --dir <path> - set userfiles directory\n";
|
||||||
std::cout << " --headless - run in headless mode\n";
|
std::cout << " --headless - run in headless mode\n";
|
||||||
std::cout << " --test <path> - test script file\n";
|
std::cout << " --test <path> - test script file\n";
|
||||||
|
std::cout << " --script <path> - main script file\n";
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
} else if (keyword == "--version") {
|
||||||
|
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||||
|
return false;
|
||||||
} else if (keyword == "--headless") {
|
} else if (keyword == "--headless") {
|
||||||
params.headless = true;
|
params.headless = true;
|
||||||
} else if (keyword == "--test") {
|
} else if (keyword == "--test") {
|
||||||
auto token = reader.next();
|
auto token = reader.next();
|
||||||
params.testFile = fs::u8path(token);
|
params.testMode = true;
|
||||||
|
params.scriptFile = fs::u8path(token);
|
||||||
|
} else if (keyword == "--script") {
|
||||||
|
auto token = reader.next();
|
||||||
|
params.testMode = false;
|
||||||
|
params.scriptFile = fs::u8path(token);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("unknown argument " + keyword);
|
throw std::runtime_error("unknown argument " + keyword);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -181,7 +181,7 @@ public:
|
|||||||
bool translucent = false;
|
bool translucent = false;
|
||||||
|
|
||||||
/// @brief Set of block physical hitboxes
|
/// @brief Set of block physical hitboxes
|
||||||
std::vector<AABB> hitboxes;
|
std::vector<AABB> hitboxes {AABB()};
|
||||||
|
|
||||||
/// @brief Set of available block rotations (coord-systems)
|
/// @brief Set of available block rotations (coord-systems)
|
||||||
BlockRotProfile rotations = BlockRotProfile::NONE;
|
BlockRotProfile rotations = BlockRotProfile::NONE;
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
#include "Chunks.hpp"
|
#include "Chunks.hpp"
|
||||||
|
|
||||||
#include <limits.h>
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -20,53 +19,39 @@
|
|||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/LevelEvents.hpp"
|
#include "world/LevelEvents.hpp"
|
||||||
#include "VoxelsVolume.hpp"
|
#include "VoxelsVolume.hpp"
|
||||||
#include "Block.hpp"
|
#include "blocks_agent.hpp"
|
||||||
#include "Chunk.hpp"
|
|
||||||
#include "voxel.hpp"
|
|
||||||
|
|
||||||
Chunks::Chunks(
|
Chunks::Chunks(
|
||||||
int32_t w,
|
int32_t w,
|
||||||
int32_t d,
|
int32_t d,
|
||||||
int32_t ox,
|
int32_t ox,
|
||||||
int32_t oz,
|
int32_t oz,
|
||||||
WorldFiles* wfile,
|
LevelEvents* events,
|
||||||
Level* level
|
const ContentIndices* indices
|
||||||
)
|
)
|
||||||
: level(level),
|
: events(events),
|
||||||
indices(level->content->getIndices()),
|
indices(indices),
|
||||||
areaMap(w, d),
|
areaMap(w, d) {
|
||||||
worldFiles(wfile) {
|
|
||||||
areaMap.setCenter(ox-w/2, oz-d/2);
|
areaMap.setCenter(ox-w/2, oz-d/2);
|
||||||
areaMap.setOutCallback([this](int, int, const auto& chunk) {
|
areaMap.setOutCallback([this](int, int, const auto& chunk) {
|
||||||
this->level->events->trigger(EVT_CHUNK_HIDDEN, chunk.get());
|
this->events->trigger(EVT_CHUNK_HIDDEN, chunk.get());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Chunks::configure(int32_t x, int32_t z, uint32_t radius) {
|
||||||
|
setCenter(x, z);
|
||||||
|
uint32_t diameter = radius * 2LL;
|
||||||
|
if (getWidth() != diameter) {
|
||||||
|
resize(diameter, diameter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const {
|
voxel* Chunks::get(int32_t x, int32_t y, int32_t z) const {
|
||||||
if (y < 0 || y >= CHUNK_H) {
|
return blocks_agent::get(*this, x, y, z);
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
int cx = floordiv<CHUNK_W>(x);
|
|
||||||
int cz = floordiv<CHUNK_D>(z);
|
|
||||||
auto ptr = areaMap.getIf(cx, cz);
|
|
||||||
if (ptr == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
Chunk* chunk = ptr->get();
|
|
||||||
if (chunk == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
int lx = x - cx * CHUNK_W;
|
|
||||||
int lz = z - cz * CHUNK_D;
|
|
||||||
return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
voxel& Chunks::require(int32_t x, int32_t y, int32_t z) const {
|
voxel& Chunks::require(int32_t x, int32_t y, int32_t z) const {
|
||||||
auto voxel = get(x, y, z);
|
return blocks_agent::require(*this, x, y, z);
|
||||||
if (voxel == nullptr) {
|
|
||||||
throw std::runtime_error("voxel does not exist");
|
|
||||||
}
|
|
||||||
return *voxel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const AABB* Chunks::isObstacleAt(float x, float y, float z) const {
|
const AABB* Chunks::isObstacleAt(float x, float y, float z) const {
|
||||||
@ -103,15 +88,11 @@ const AABB* Chunks::isObstacleAt(float x, float y, float z) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) {
|
bool Chunks::isSolidBlock(int32_t x, int32_t y, int32_t z) {
|
||||||
voxel* v = get(x, y, z);
|
return blocks_agent::is_solid_at(*this, x, y, z);
|
||||||
if (v == nullptr) return false;
|
|
||||||
return indices->blocks.require(v->id).rt.solid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) {
|
bool Chunks::isReplaceableBlock(int32_t x, int32_t y, int32_t z) {
|
||||||
voxel* v = get(x, y, z);
|
return blocks_agent::is_replaceable_at(*this, x, y, z);
|
||||||
if (v == nullptr) return false;
|
|
||||||
return indices->blocks.require(v->id).replaceable;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) {
|
bool Chunks::isObstacleBlock(int32_t x, int32_t y, int32_t z) {
|
||||||
@ -182,72 +163,19 @@ Chunk* Chunks::getChunk(int x, int z) const {
|
|||||||
glm::ivec3 Chunks::seekOrigin(
|
glm::ivec3 Chunks::seekOrigin(
|
||||||
const glm::ivec3& srcpos, const Block& def, blockstate state
|
const glm::ivec3& srcpos, const Block& def, blockstate state
|
||||||
) const {
|
) const {
|
||||||
auto pos = srcpos;
|
return blocks_agent::seek_origin(*this, srcpos, def, state);
|
||||||
const auto& rotation = def.rotations.variants[state.rotation];
|
|
||||||
auto segment = state.segment;
|
|
||||||
while (true) {
|
|
||||||
if (!segment) {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
if (segment & 1) pos -= rotation.axisX;
|
|
||||||
if (segment & 2) pos -= rotation.axisY;
|
|
||||||
if (segment & 4) pos -= rotation.axisZ;
|
|
||||||
|
|
||||||
if (auto* voxel = get(pos.x, pos.y, pos.z)) {
|
|
||||||
segment = voxel->state.segment;
|
|
||||||
} else {
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunks::eraseSegments(
|
void Chunks::eraseSegments(
|
||||||
const Block& def, blockstate state, int x, int y, int z
|
const Block& def, blockstate state, int x, int y, int z
|
||||||
) {
|
) {
|
||||||
const auto& rotation = def.rotations.variants[state.rotation];
|
blocks_agent::erase_segments(*this, def, state, x, y, z);
|
||||||
for (int sy = 0; sy < def.size.y; sy++) {
|
|
||||||
for (int sz = 0; sz < def.size.z; sz++) {
|
|
||||||
for (int sx = 0; sx < def.size.x; sx++) {
|
|
||||||
if ((sx | sy | sz) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
glm::ivec3 pos(x, y, z);
|
|
||||||
pos += rotation.axisX * sx;
|
|
||||||
pos += rotation.axisY * sy;
|
|
||||||
pos += rotation.axisZ * sz;
|
|
||||||
set(pos.x, pos.y, pos.z, 0, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static constexpr uint8_t segment_to_int(int sx, int sy, int sz) {
|
|
||||||
return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunks::repairSegments(
|
void Chunks::repairSegments(
|
||||||
const Block& def, blockstate state, int x, int y, int z
|
const Block& def, blockstate state, int x, int y, int z
|
||||||
) {
|
) {
|
||||||
const auto& rotation = def.rotations.variants[state.rotation];
|
blocks_agent::repair_segments(*this, def, state, x, y, z);
|
||||||
const auto id = def.rt.id;
|
|
||||||
const auto size = def.size;
|
|
||||||
for (int sy = 0; sy < size.y; sy++) {
|
|
||||||
for (int sz = 0; sz < size.z; sz++) {
|
|
||||||
for (int sx = 0; sx < size.x; sx++) {
|
|
||||||
if ((sx | sy | sz) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
blockstate segState = state;
|
|
||||||
segState.segment = segment_to_int(sx, sy, sz);
|
|
||||||
|
|
||||||
glm::ivec3 pos(x, y, z);
|
|
||||||
pos += rotation.axisX * sx;
|
|
||||||
pos += rotation.axisY * sy;
|
|
||||||
pos += rotation.axisZ * sz;
|
|
||||||
set(pos.x, pos.y, pos.z, id, segState);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Chunks::checkReplaceability(
|
bool Chunks::checkReplaceability(
|
||||||
@ -256,176 +184,23 @@ bool Chunks::checkReplaceability(
|
|||||||
const glm::ivec3& origin,
|
const glm::ivec3& origin,
|
||||||
blockid_t ignore
|
blockid_t ignore
|
||||||
) {
|
) {
|
||||||
const auto& rotation = def.rotations.variants[state.rotation];
|
return blocks_agent::check_replaceability(*this, def, state, origin, ignore);
|
||||||
const auto size = def.size;
|
|
||||||
for (int sy = 0; sy < size.y; sy++) {
|
|
||||||
for (int sz = 0; sz < size.z; sz++) {
|
|
||||||
for (int sx = 0; sx < size.x; sx++) {
|
|
||||||
auto pos = origin;
|
|
||||||
pos += rotation.axisX * sx;
|
|
||||||
pos += rotation.axisY * sy;
|
|
||||||
pos += rotation.axisZ * sz;
|
|
||||||
if (auto vox = get(pos.x, pos.y, pos.z)) {
|
|
||||||
auto& target = indices->blocks.require(vox->id);
|
|
||||||
if (!target.replaceable && vox->id != ignore) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunks::setRotationExtended(
|
void Chunks::setRotationExtended(
|
||||||
const Block& def, blockstate state, const glm::ivec3& origin, uint8_t index
|
const Block& def, blockstate state, const glm::ivec3& origin, uint8_t index
|
||||||
) {
|
) {
|
||||||
auto newstate = state;
|
blocks_agent::set_rotation_extended(*this, def, state, origin, index);
|
||||||
newstate.rotation = index;
|
|
||||||
|
|
||||||
// unable to rotate block (cause: obstacles)
|
|
||||||
if (!checkReplaceability(def, newstate, origin, def.rt.id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& rotation = def.rotations.variants[index];
|
|
||||||
const auto size = def.size;
|
|
||||||
std::vector<glm::ivec3> segmentBlocks;
|
|
||||||
|
|
||||||
for (int sy = 0; sy < size.y; sy++) {
|
|
||||||
for (int sz = 0; sz < size.z; sz++) {
|
|
||||||
for (int sx = 0; sx < size.x; sx++) {
|
|
||||||
auto pos = origin;
|
|
||||||
pos += rotation.axisX * sx;
|
|
||||||
pos += rotation.axisY * sy;
|
|
||||||
pos += rotation.axisZ * sz;
|
|
||||||
|
|
||||||
blockstate segState = newstate;
|
|
||||||
segState.segment = segment_to_int(sx, sy, sz);
|
|
||||||
|
|
||||||
auto vox = get(pos);
|
|
||||||
// checked for nullptr by checkReplaceability
|
|
||||||
if (vox->id != def.rt.id) {
|
|
||||||
set(pos.x, pos.y, pos.z, def.rt.id, segState);
|
|
||||||
} else {
|
|
||||||
vox->state = segState;
|
|
||||||
auto chunk = getChunkByVoxel(pos.x, pos.y, pos.z);
|
|
||||||
assert(chunk != nullptr);
|
|
||||||
chunk->setModifiedAndUnsaved();
|
|
||||||
segmentBlocks.emplace_back(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto& prevRotation = def.rotations.variants[state.rotation];
|
|
||||||
for (int sy = 0; sy < size.y; sy++) {
|
|
||||||
for (int sz = 0; sz < size.z; sz++) {
|
|
||||||
for (int sx = 0; sx < size.x; sx++) {
|
|
||||||
auto pos = origin;
|
|
||||||
pos += prevRotation.axisX * sx;
|
|
||||||
pos += prevRotation.axisY * sy;
|
|
||||||
pos += prevRotation.axisZ * sz;
|
|
||||||
if (std::find(
|
|
||||||
segmentBlocks.begin(), segmentBlocks.end(), pos
|
|
||||||
) == segmentBlocks.end()) {
|
|
||||||
set(pos.x, pos.y, pos.z, 0, {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) {
|
void Chunks::setRotation(int32_t x, int32_t y, int32_t z, uint8_t index) {
|
||||||
if (index >= BlockRotProfile::MAX_COUNT) {
|
return blocks_agent::set_rotation(*this, x, y, z, index);
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto vox = get(x, y, z);
|
|
||||||
if (vox == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto& def = indices->blocks.require(vox->id);
|
|
||||||
if (!def.rotatable || vox->state.rotation == index) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (def.rt.extended) {
|
|
||||||
auto origin = seekOrigin({x, y, z}, def, vox->state);
|
|
||||||
vox = get(origin);
|
|
||||||
setRotationExtended(def, vox->state, origin, index);
|
|
||||||
} else {
|
|
||||||
vox->state.rotation = index;
|
|
||||||
auto chunk = getChunkByVoxel(x, y, z);
|
|
||||||
assert(chunk != nullptr);
|
|
||||||
chunk->setModifiedAndUnsaved();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Chunks::set(
|
void Chunks::set(
|
||||||
int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state
|
int32_t x, int32_t y, int32_t z, uint32_t id, blockstate state
|
||||||
) {
|
) {
|
||||||
if (y < 0 || y >= CHUNK_H) {
|
blocks_agent::set(*this, x, y, z, id, state);
|
||||||
return;
|
|
||||||
}
|
|
||||||
int cx = floordiv<CHUNK_W>(x);
|
|
||||||
int cz = floordiv<CHUNK_D>(z);
|
|
||||||
auto ptr = areaMap.getIf(cx, cz);
|
|
||||||
if (ptr == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Chunk* chunk = ptr->get();
|
|
||||||
if (chunk == nullptr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
int lx = x - cx * CHUNK_W;
|
|
||||||
int lz = z - cz * CHUNK_D;
|
|
||||||
size_t index = vox_index(lx, y, lz);
|
|
||||||
|
|
||||||
// block finalization
|
|
||||||
voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
|
||||||
const auto& prevdef = indices->blocks.require(vox.id);
|
|
||||||
if (prevdef.inventorySize != 0) {
|
|
||||||
chunk->removeBlockInventory(lx, y, lz);
|
|
||||||
}
|
|
||||||
if (prevdef.rt.extended && !vox.state.segment) {
|
|
||||||
eraseSegments(prevdef, vox.state, x, y, z);
|
|
||||||
}
|
|
||||||
if (prevdef.dataStruct) {
|
|
||||||
if (auto found = chunk->blocksMetadata.find(index)) {
|
|
||||||
chunk->blocksMetadata.free(found);
|
|
||||||
chunk->flags.unsaved = true;
|
|
||||||
chunk->flags.blocksData = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// block initialization
|
|
||||||
const auto& newdef = indices->blocks.require(id);
|
|
||||||
vox.id = id;
|
|
||||||
vox.state = state;
|
|
||||||
chunk->setModifiedAndUnsaved();
|
|
||||||
if (!state.segment && newdef.rt.extended) {
|
|
||||||
repairSegments(newdef, state, x, y, z);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y < chunk->bottom)
|
|
||||||
chunk->bottom = y;
|
|
||||||
else if (y + 1 > chunk->top)
|
|
||||||
chunk->top = y + 1;
|
|
||||||
else if (id == 0)
|
|
||||||
chunk->updateHeights();
|
|
||||||
|
|
||||||
if (lx == 0 && (chunk = getChunk(cx - 1, cz))) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
}
|
|
||||||
if (lz == 0 && (chunk = getChunk(cx, cz - 1))) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
}
|
|
||||||
if (lx == CHUNK_W - 1 && (chunk = getChunk(cx + 1, cz))) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
}
|
|
||||||
if (lz == CHUNK_D - 1 && (chunk = getChunk(cx, cz + 1))) {
|
|
||||||
chunk->flags.modified = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
voxel* Chunks::rayCast(
|
voxel* Chunks::rayCast(
|
||||||
@ -437,134 +212,9 @@ voxel* Chunks::rayCast(
|
|||||||
glm::ivec3& iend,
|
glm::ivec3& iend,
|
||||||
std::set<blockid_t> filter
|
std::set<blockid_t> filter
|
||||||
) const {
|
) const {
|
||||||
float px = start.x;
|
return blocks_agent::raycast(
|
||||||
float py = start.y;
|
*this, start, dir, maxDist, end, norm, iend, std::move(filter)
|
||||||
float pz = start.z;
|
);
|
||||||
|
|
||||||
float dx = dir.x;
|
|
||||||
float dy = dir.y;
|
|
||||||
float dz = dir.z;
|
|
||||||
|
|
||||||
float t = 0.0f;
|
|
||||||
int ix = std::floor(px);
|
|
||||||
int iy = std::floor(py);
|
|
||||||
int iz = std::floor(pz);
|
|
||||||
|
|
||||||
int stepx = (dx > 0.0f) ? 1 : -1;
|
|
||||||
int stepy = (dy > 0.0f) ? 1 : -1;
|
|
||||||
int stepz = (dz > 0.0f) ? 1 : -1;
|
|
||||||
|
|
||||||
constexpr float infinity = std::numeric_limits<float>::infinity();
|
|
||||||
constexpr float epsilon = 1e-6f; // 0.000001
|
|
||||||
float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx);
|
|
||||||
float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy);
|
|
||||||
float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz);
|
|
||||||
|
|
||||||
float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix);
|
|
||||||
float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy);
|
|
||||||
float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz);
|
|
||||||
|
|
||||||
float txMax = (txDelta < infinity) ? txDelta * xdist : infinity;
|
|
||||||
float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity;
|
|
||||||
float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity;
|
|
||||||
|
|
||||||
int steppedIndex = -1;
|
|
||||||
|
|
||||||
while (t <= maxDist) {
|
|
||||||
voxel* voxel = get(ix, iy, iz);
|
|
||||||
if (voxel == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto& def = indices->blocks.require(voxel->id);
|
|
||||||
if ((filter.empty() && def.selectable) ||
|
|
||||||
(!filter.empty() && filter.find(def.rt.id) == filter.end())) {
|
|
||||||
end.x = px + t * dx;
|
|
||||||
end.y = py + t * dy;
|
|
||||||
end.z = pz + t * dz;
|
|
||||||
iend.x = ix;
|
|
||||||
iend.y = iy;
|
|
||||||
iend.z = iz;
|
|
||||||
|
|
||||||
if (!def.rt.solid) {
|
|
||||||
const std::vector<AABB>& hitboxes =
|
|
||||||
def.rotatable ? def.rt.hitboxes[voxel->state.rotation]
|
|
||||||
: def.hitboxes;
|
|
||||||
|
|
||||||
scalar_t distance = maxDist;
|
|
||||||
Ray ray(start, dir);
|
|
||||||
|
|
||||||
bool hit = false;
|
|
||||||
|
|
||||||
glm::vec3 offset {};
|
|
||||||
if (voxel->state.segment) {
|
|
||||||
offset = seekOrigin(iend, def, voxel->state) - iend;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto box : hitboxes) {
|
|
||||||
box.a += offset;
|
|
||||||
box.b += offset;
|
|
||||||
scalar_t boxDistance;
|
|
||||||
glm::ivec3 boxNorm;
|
|
||||||
if (ray.intersectAABB(
|
|
||||||
iend, box, maxDist, boxNorm, boxDistance
|
|
||||||
) > RayRelation::None &&
|
|
||||||
boxDistance < distance) {
|
|
||||||
hit = true;
|
|
||||||
distance = boxDistance;
|
|
||||||
norm = boxNorm;
|
|
||||||
end = start + (dir * glm::vec3(distance));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hit) return voxel;
|
|
||||||
} else {
|
|
||||||
iend.x = ix;
|
|
||||||
iend.y = iy;
|
|
||||||
iend.z = iz;
|
|
||||||
|
|
||||||
norm.x = norm.y = norm.z = 0;
|
|
||||||
if (steppedIndex == 0) norm.x = -stepx;
|
|
||||||
if (steppedIndex == 1) norm.y = -stepy;
|
|
||||||
if (steppedIndex == 2) norm.z = -stepz;
|
|
||||||
return voxel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (txMax < tyMax) {
|
|
||||||
if (txMax < tzMax) {
|
|
||||||
ix += stepx;
|
|
||||||
t = txMax;
|
|
||||||
txMax += txDelta;
|
|
||||||
steppedIndex = 0;
|
|
||||||
} else {
|
|
||||||
iz += stepz;
|
|
||||||
t = tzMax;
|
|
||||||
tzMax += tzDelta;
|
|
||||||
steppedIndex = 2;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (tyMax < tzMax) {
|
|
||||||
iy += stepy;
|
|
||||||
t = tyMax;
|
|
||||||
tyMax += tyDelta;
|
|
||||||
steppedIndex = 1;
|
|
||||||
} else {
|
|
||||||
iz += stepz;
|
|
||||||
t = tzMax;
|
|
||||||
tzMax += tzDelta;
|
|
||||||
steppedIndex = 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
iend.x = ix;
|
|
||||||
iend.y = iy;
|
|
||||||
iend.z = iz;
|
|
||||||
|
|
||||||
end.x = px + t * dx;
|
|
||||||
end.y = py + t * dy;
|
|
||||||
end.z = pz + t * dz;
|
|
||||||
norm.x = norm.y = norm.z = 0;
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 Chunks::rayCastToObstacle(
|
glm::vec3 Chunks::rayCastToObstacle(
|
||||||
@ -672,7 +322,9 @@ void Chunks::resize(uint32_t newW, uint32_t newD) {
|
|||||||
|
|
||||||
bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
|
bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
|
||||||
if (areaMap.set(chunk->x, chunk->z, chunk)) {
|
if (areaMap.set(chunk->x, chunk->z, chunk)) {
|
||||||
level->events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get());
|
if (events) {
|
||||||
|
events->trigger(LevelEventType::EVT_CHUNK_SHOWN, chunk.get());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -682,8 +334,6 @@ bool Chunks::putChunk(const std::shared_ptr<Chunk>& chunk) {
|
|||||||
// 25.06.2024: not now
|
// 25.06.2024: not now
|
||||||
// 11.11.2024: not now
|
// 11.11.2024: not now
|
||||||
void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const {
|
void Chunks::getVoxels(VoxelsVolume* volume, bool backlight) const {
|
||||||
const Content* content = level->content;
|
|
||||||
auto indices = content->getIndices();
|
|
||||||
voxel* voxels = volume->getVoxels();
|
voxel* voxels = volume->getVoxels();
|
||||||
light_t* lights = volume->getLights();
|
light_t* lights = volume->getLights();
|
||||||
int x = volume->getX();
|
int x = volume->getX();
|
||||||
|
|||||||
@ -20,12 +20,11 @@ class Chunk;
|
|||||||
class WorldFiles;
|
class WorldFiles;
|
||||||
class LevelEvents;
|
class LevelEvents;
|
||||||
class Block;
|
class Block;
|
||||||
class Level;
|
|
||||||
class VoxelsVolume;
|
class VoxelsVolume;
|
||||||
|
|
||||||
/// Player-centred chunks matrix
|
/// Player-centred chunks matrix
|
||||||
class Chunks {
|
class Chunks {
|
||||||
Level* level;
|
LevelEvents* events;
|
||||||
const ContentIndices* const indices;
|
const ContentIndices* const indices;
|
||||||
|
|
||||||
void eraseSegments(const Block& def, blockstate state, int x, int y, int z);
|
void eraseSegments(const Block& def, blockstate state, int x, int y, int z);
|
||||||
@ -40,18 +39,19 @@ class Chunks {
|
|||||||
);
|
);
|
||||||
|
|
||||||
util::AreaMap2D<std::shared_ptr<Chunk>, int32_t> areaMap;
|
util::AreaMap2D<std::shared_ptr<Chunk>, int32_t> areaMap;
|
||||||
WorldFiles* worldFiles;
|
|
||||||
public:
|
public:
|
||||||
Chunks(
|
Chunks(
|
||||||
int32_t w,
|
int32_t w,
|
||||||
int32_t d,
|
int32_t d,
|
||||||
int32_t ox,
|
int32_t ox,
|
||||||
int32_t oz,
|
int32_t oz,
|
||||||
WorldFiles* worldFiles,
|
LevelEvents* events,
|
||||||
Level* level
|
const ContentIndices* indices
|
||||||
);
|
);
|
||||||
~Chunks() = default;
|
~Chunks() = default;
|
||||||
|
|
||||||
|
void configure(int32_t x, int32_t z, uint32_t radius);
|
||||||
|
|
||||||
bool putChunk(const std::shared_ptr<Chunk>& chunk);
|
bool putChunk(const std::shared_ptr<Chunk>& chunk);
|
||||||
|
|
||||||
Chunk* getChunk(int32_t x, int32_t z) const;
|
Chunk* getChunk(int32_t x, int32_t z) const;
|
||||||
@ -152,4 +152,12 @@ public:
|
|||||||
size_t getVolume() const {
|
size_t getVolume() const {
|
||||||
return areaMap.area();
|
return areaMap.area();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ContentIndices& getContentIndices() const {
|
||||||
|
return *indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline constexpr unsigned matrixSize(int loadDistance, int padding) {
|
||||||
|
return (loadDistance + padding) * 2;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,25 +10,18 @@
|
|||||||
#include "lighting/Lightmap.hpp"
|
#include "lighting/Lightmap.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/World.hpp"
|
#include "world/World.hpp"
|
||||||
#include "Block.hpp"
|
#include "Block.hpp"
|
||||||
#include "Chunk.hpp"
|
#include "Chunk.hpp"
|
||||||
|
|
||||||
inline long long keyfrom(int x, int z) {
|
|
||||||
union {
|
|
||||||
int pos[2];
|
|
||||||
long long key;
|
|
||||||
} ekey;
|
|
||||||
ekey.pos[0] = x;
|
|
||||||
ekey.pos[1] = z;
|
|
||||||
return ekey.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
static debug::Logger logger("chunks-storage");
|
static debug::Logger logger("chunks-storage");
|
||||||
|
|
||||||
GlobalChunks::GlobalChunks(Level* level) : level(level) {
|
GlobalChunks::GlobalChunks(Level* level)
|
||||||
|
: level(level), indices(level ? level->content->getIndices() : nullptr) {
|
||||||
|
chunksMap.max_load_factor(CHUNKS_MAP_MAX_LOAD_FACTOR);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Chunk> GlobalChunks::fetch(int x, int z) {
|
std::shared_ptr<Chunk> GlobalChunks::fetch(int x, int z) {
|
||||||
@ -67,6 +60,29 @@ void GlobalChunks::erase(int x, int z) {
|
|||||||
chunksMap.erase(keyfrom(x, z));
|
chunksMap.erase(keyfrom(x, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline auto load_inventories(
|
||||||
|
WorldRegions& regions,
|
||||||
|
const Chunk& chunk,
|
||||||
|
const ContentUnitIndices<Block>& defs
|
||||||
|
) {
|
||||||
|
auto invs = regions.fetchInventories(chunk.x, chunk.z);
|
||||||
|
auto iterator = invs.begin();
|
||||||
|
while (iterator != invs.end()) {
|
||||||
|
uint index = iterator->first;
|
||||||
|
const auto& def = defs.require(chunk.voxels[index].id);
|
||||||
|
if (def.inventorySize == 0) {
|
||||||
|
iterator = invs.erase(iterator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto& inventory = iterator->second;
|
||||||
|
if (def.inventorySize != inventory->size()) {
|
||||||
|
inventory->resize(def.inventorySize);
|
||||||
|
}
|
||||||
|
++iterator;
|
||||||
|
}
|
||||||
|
return invs;
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
|
std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
|
||||||
const auto& found = chunksMap.find(keyfrom(x, z));
|
const auto& found = chunksMap.find(keyfrom(x, z));
|
||||||
if (found != chunksMap.end()) {
|
if (found != chunksMap.end()) {
|
||||||
@ -84,22 +100,10 @@ std::shared_ptr<Chunk> GlobalChunks::create(int x, int z) {
|
|||||||
|
|
||||||
chunk->decode(data.get());
|
chunk->decode(data.get());
|
||||||
check_voxels(indices, *chunk);
|
check_voxels(indices, *chunk);
|
||||||
auto invs = regions.fetchInventories(chunk->x, chunk->z);
|
|
||||||
auto iterator = invs.begin();
|
chunk->setBlockInventories(
|
||||||
while (iterator != invs.end()) {
|
load_inventories(regions, *chunk, indices.blocks)
|
||||||
uint index = iterator->first;
|
);
|
||||||
const auto& def = indices.blocks.require(chunk->voxels[index].id);
|
|
||||||
if (def.inventorySize == 0) {
|
|
||||||
iterator = invs.erase(iterator);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto& inventory = iterator->second;
|
|
||||||
if (def.inventorySize != inventory->size()) {
|
|
||||||
inventory->resize(def.inventorySize);
|
|
||||||
}
|
|
||||||
++iterator;
|
|
||||||
}
|
|
||||||
chunk->setBlockInventories(std::move(invs));
|
|
||||||
|
|
||||||
auto entitiesData = regions.fetchEntities(chunk->x, chunk->z);
|
auto entitiesData = regions.fetchEntities(chunk->x, chunk->z);
|
||||||
if (entitiesData.getType() == dv::value_type::object) {
|
if (entitiesData.getType() == dv::value_type::object) {
|
||||||
@ -132,24 +136,6 @@ size_t GlobalChunks::size() const {
|
|||||||
return chunksMap.size();
|
return chunksMap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
voxel* GlobalChunks::get(int x, int y, int z) const {
|
|
||||||
if (y < 0 || y >= CHUNK_H) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int cx = floordiv<CHUNK_W>(x);
|
|
||||||
int cz = floordiv<CHUNK_D>(z);
|
|
||||||
|
|
||||||
const auto& found = chunksMap.find(keyfrom(cx, cz));
|
|
||||||
if (found == chunksMap.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
const auto& chunk = found->second;
|
|
||||||
int lx = x - cx * CHUNK_W;
|
|
||||||
int lz = z - cz * CHUNK_D;
|
|
||||||
return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
|
||||||
}
|
|
||||||
|
|
||||||
void GlobalChunks::incref(Chunk* chunk) {
|
void GlobalChunks::incref(Chunk* chunk) {
|
||||||
auto key = reinterpret_cast<ptrdiff_t>(chunk);
|
auto key = reinterpret_cast<ptrdiff_t>(chunk);
|
||||||
const auto& found = refCounters.find(key);
|
const auto& found = refCounters.find(key);
|
||||||
@ -209,3 +195,11 @@ void GlobalChunks::saveAll() {
|
|||||||
save(chunk.get());
|
save(chunk.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GlobalChunks::putChunk(std::shared_ptr<Chunk> chunk) {
|
||||||
|
chunksMap[keyfrom(chunk->x, chunk->z)] = std::move(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
const AABB* GlobalChunks::isObstacleAt(float x, float y, float z) const {
|
||||||
|
return blocks_agent::is_obstacle_at(*this, x, y, z);
|
||||||
|
}
|
||||||
|
|||||||
@ -11,10 +11,23 @@
|
|||||||
|
|
||||||
class Chunk;
|
class Chunk;
|
||||||
class Level;
|
class Level;
|
||||||
|
struct AABB;
|
||||||
|
class ContentIndices;
|
||||||
|
|
||||||
class GlobalChunks {
|
class GlobalChunks {
|
||||||
|
static inline uint64_t keyfrom(int32_t x, int32_t z) {
|
||||||
|
union {
|
||||||
|
int32_t pos[2];
|
||||||
|
uint64_t key;
|
||||||
|
} ekey;
|
||||||
|
ekey.pos[0] = x;
|
||||||
|
ekey.pos[1] = z;
|
||||||
|
return ekey.key;
|
||||||
|
}
|
||||||
|
|
||||||
Level* level;
|
Level* level;
|
||||||
std::unordered_map<long long, std::shared_ptr<Chunk>> chunksMap;
|
const ContentIndices* indices;
|
||||||
|
std::unordered_map<uint64_t, std::shared_ptr<Chunk>> chunksMap;
|
||||||
std::unordered_map<glm::ivec2, std::shared_ptr<Chunk>> pinnedChunks;
|
std::unordered_map<glm::ivec2, std::shared_ptr<Chunk>> pinnedChunks;
|
||||||
std::unordered_map<ptrdiff_t, int> refCounters;
|
std::unordered_map<ptrdiff_t, int> refCounters;
|
||||||
public:
|
public:
|
||||||
@ -27,8 +40,6 @@ public:
|
|||||||
void pinChunk(std::shared_ptr<Chunk> chunk);
|
void pinChunk(std::shared_ptr<Chunk> chunk);
|
||||||
void unpinChunk(int x, int z);
|
void unpinChunk(int x, int z);
|
||||||
|
|
||||||
voxel* get(int x, int y, int z) const;
|
|
||||||
|
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
|
|
||||||
void incref(Chunk* chunk);
|
void incref(Chunk* chunk);
|
||||||
@ -38,4 +49,20 @@ public:
|
|||||||
|
|
||||||
void save(Chunk* chunk);
|
void save(Chunk* chunk);
|
||||||
void saveAll();
|
void saveAll();
|
||||||
|
|
||||||
|
void putChunk(std::shared_ptr<Chunk> chunk);
|
||||||
|
|
||||||
|
const AABB* isObstacleAt(float x, float y, float z) const;
|
||||||
|
|
||||||
|
inline Chunk* getChunk(int cx, int cz) const {
|
||||||
|
const auto& found = chunksMap.find(keyfrom(cx, cz));
|
||||||
|
if (found == chunksMap.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return found->second.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const ContentIndices& getContentIndices() const {
|
||||||
|
return *indices;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
369
src/voxels/blocks_agent.cpp
Normal file
369
src/voxels/blocks_agent.cpp
Normal file
@ -0,0 +1,369 @@
|
|||||||
|
#include "blocks_agent.hpp"
|
||||||
|
|
||||||
|
#include "maths/rays.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
using namespace blocks_agent;
|
||||||
|
|
||||||
|
template <class Storage>
|
||||||
|
static inline void set_block(
|
||||||
|
Storage& chunks,
|
||||||
|
int32_t x,
|
||||||
|
int32_t y,
|
||||||
|
int32_t z,
|
||||||
|
uint32_t id,
|
||||||
|
blockstate state
|
||||||
|
) {
|
||||||
|
if (y < 0 || y >= CHUNK_H) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& indices = chunks.getContentIndices();
|
||||||
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
|
Chunk* chunk = get_chunk(chunks, cx, cz);
|
||||||
|
if (chunk == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int lx = x - cx * CHUNK_W;
|
||||||
|
int lz = z - cz * CHUNK_D;
|
||||||
|
size_t index = vox_index(lx, y, lz);
|
||||||
|
|
||||||
|
// block finalization
|
||||||
|
voxel& vox = chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
||||||
|
const auto& prevdef = indices.blocks.require(vox.id);
|
||||||
|
if (prevdef.inventorySize != 0) {
|
||||||
|
chunk->removeBlockInventory(lx, y, lz);
|
||||||
|
}
|
||||||
|
if (prevdef.rt.extended && !vox.state.segment) {
|
||||||
|
erase_segments(chunks, prevdef, vox.state, x, y, z);
|
||||||
|
}
|
||||||
|
if (prevdef.dataStruct) {
|
||||||
|
if (auto found = chunk->blocksMetadata.find(index)) {
|
||||||
|
chunk->blocksMetadata.free(found);
|
||||||
|
chunk->flags.unsaved = true;
|
||||||
|
chunk->flags.blocksData = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// block initialization
|
||||||
|
const auto& newdef = indices.blocks.require(id);
|
||||||
|
vox.id = id;
|
||||||
|
vox.state = state;
|
||||||
|
chunk->setModifiedAndUnsaved();
|
||||||
|
if (!state.segment && newdef.rt.extended) {
|
||||||
|
repair_segments(chunks, newdef, state, x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y < chunk->bottom)
|
||||||
|
chunk->bottom = y;
|
||||||
|
else if (y + 1 > chunk->top)
|
||||||
|
chunk->top = y + 1;
|
||||||
|
else if (id == 0)
|
||||||
|
chunk->updateHeights();
|
||||||
|
|
||||||
|
if (lx == 0 && (chunk = get_chunk(chunks, cx - 1, cz))) {
|
||||||
|
chunk->flags.modified = true;
|
||||||
|
}
|
||||||
|
if (lz == 0 && (chunk = get_chunk(chunks, cx, cz - 1))) {
|
||||||
|
chunk->flags.modified = true;
|
||||||
|
}
|
||||||
|
if (lx == CHUNK_W - 1 && (chunk = get_chunk(chunks, cx + 1, cz))) {
|
||||||
|
chunk->flags.modified = true;
|
||||||
|
}
|
||||||
|
if (lz == CHUNK_D - 1 && (chunk = get_chunk(chunks, cx, cz + 1))) {
|
||||||
|
chunk->flags.modified = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blocks_agent::set(
|
||||||
|
Chunks& chunks,
|
||||||
|
int32_t x,
|
||||||
|
int32_t y,
|
||||||
|
int32_t z,
|
||||||
|
uint32_t id,
|
||||||
|
blockstate state
|
||||||
|
) {
|
||||||
|
set_block(chunks, x, y, z, id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blocks_agent::set(
|
||||||
|
GlobalChunks& chunks,
|
||||||
|
int32_t x,
|
||||||
|
int32_t y,
|
||||||
|
int32_t z,
|
||||||
|
uint32_t id,
|
||||||
|
blockstate state
|
||||||
|
) {
|
||||||
|
set_block(chunks, x, y, z, id, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class Storage>
|
||||||
|
static inline voxel* raycast_blocks(
|
||||||
|
const Storage& chunks,
|
||||||
|
const glm::vec3& start,
|
||||||
|
const glm::vec3& dir,
|
||||||
|
float maxDist,
|
||||||
|
glm::vec3& end,
|
||||||
|
glm::ivec3& norm,
|
||||||
|
glm::ivec3& iend,
|
||||||
|
std::set<blockid_t> filter
|
||||||
|
) {
|
||||||
|
const auto& blocks = chunks.getContentIndices().blocks;
|
||||||
|
float px = start.x;
|
||||||
|
float py = start.y;
|
||||||
|
float pz = start.z;
|
||||||
|
|
||||||
|
float dx = dir.x;
|
||||||
|
float dy = dir.y;
|
||||||
|
float dz = dir.z;
|
||||||
|
|
||||||
|
float t = 0.0f;
|
||||||
|
int ix = std::floor(px);
|
||||||
|
int iy = std::floor(py);
|
||||||
|
int iz = std::floor(pz);
|
||||||
|
|
||||||
|
int stepx = (dx > 0.0f) ? 1 : -1;
|
||||||
|
int stepy = (dy > 0.0f) ? 1 : -1;
|
||||||
|
int stepz = (dz > 0.0f) ? 1 : -1;
|
||||||
|
|
||||||
|
constexpr float infinity = std::numeric_limits<float>::infinity();
|
||||||
|
constexpr float epsilon = 1e-6f; // 0.000001
|
||||||
|
float txDelta = (std::fabs(dx) < epsilon) ? infinity : std::fabs(1.0f / dx);
|
||||||
|
float tyDelta = (std::fabs(dy) < epsilon) ? infinity : std::fabs(1.0f / dy);
|
||||||
|
float tzDelta = (std::fabs(dz) < epsilon) ? infinity : std::fabs(1.0f / dz);
|
||||||
|
|
||||||
|
float xdist = (stepx > 0) ? (ix + 1 - px) : (px - ix);
|
||||||
|
float ydist = (stepy > 0) ? (iy + 1 - py) : (py - iy);
|
||||||
|
float zdist = (stepz > 0) ? (iz + 1 - pz) : (pz - iz);
|
||||||
|
|
||||||
|
float txMax = (txDelta < infinity) ? txDelta * xdist : infinity;
|
||||||
|
float tyMax = (tyDelta < infinity) ? tyDelta * ydist : infinity;
|
||||||
|
float tzMax = (tzDelta < infinity) ? tzDelta * zdist : infinity;
|
||||||
|
|
||||||
|
int steppedIndex = -1;
|
||||||
|
|
||||||
|
while (t <= maxDist) {
|
||||||
|
voxel* voxel = get(chunks, ix, iy, iz);
|
||||||
|
if (voxel == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& def = blocks.require(voxel->id);
|
||||||
|
if ((filter.empty() && def.selectable) ||
|
||||||
|
(!filter.empty() && filter.find(def.rt.id) == filter.end())) {
|
||||||
|
end.x = px + t * dx;
|
||||||
|
end.y = py + t * dy;
|
||||||
|
end.z = pz + t * dz;
|
||||||
|
iend.x = ix;
|
||||||
|
iend.y = iy;
|
||||||
|
iend.z = iz;
|
||||||
|
|
||||||
|
if (!def.rt.solid) {
|
||||||
|
const std::vector<AABB>& hitboxes =
|
||||||
|
def.rotatable ? def.rt.hitboxes[voxel->state.rotation]
|
||||||
|
: def.hitboxes;
|
||||||
|
|
||||||
|
scalar_t distance = maxDist;
|
||||||
|
Ray ray(start, dir);
|
||||||
|
|
||||||
|
bool hit = false;
|
||||||
|
|
||||||
|
glm::vec3 offset {};
|
||||||
|
if (voxel->state.segment) {
|
||||||
|
offset = seek_origin(chunks, iend, def, voxel->state) - iend;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto box : hitboxes) {
|
||||||
|
box.a += offset;
|
||||||
|
box.b += offset;
|
||||||
|
scalar_t boxDistance;
|
||||||
|
glm::ivec3 boxNorm;
|
||||||
|
if (ray.intersectAABB(
|
||||||
|
iend, box, maxDist, boxNorm, boxDistance
|
||||||
|
) > RayRelation::None &&
|
||||||
|
boxDistance < distance) {
|
||||||
|
hit = true;
|
||||||
|
distance = boxDistance;
|
||||||
|
norm = boxNorm;
|
||||||
|
end = start + (dir * glm::vec3(distance));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hit) return voxel;
|
||||||
|
} else {
|
||||||
|
iend.x = ix;
|
||||||
|
iend.y = iy;
|
||||||
|
iend.z = iz;
|
||||||
|
|
||||||
|
norm.x = norm.y = norm.z = 0;
|
||||||
|
if (steppedIndex == 0) norm.x = -stepx;
|
||||||
|
if (steppedIndex == 1) norm.y = -stepy;
|
||||||
|
if (steppedIndex == 2) norm.z = -stepz;
|
||||||
|
return voxel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (txMax < tyMax) {
|
||||||
|
if (txMax < tzMax) {
|
||||||
|
ix += stepx;
|
||||||
|
t = txMax;
|
||||||
|
txMax += txDelta;
|
||||||
|
steppedIndex = 0;
|
||||||
|
} else {
|
||||||
|
iz += stepz;
|
||||||
|
t = tzMax;
|
||||||
|
tzMax += tzDelta;
|
||||||
|
steppedIndex = 2;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tyMax < tzMax) {
|
||||||
|
iy += stepy;
|
||||||
|
t = tyMax;
|
||||||
|
tyMax += tyDelta;
|
||||||
|
steppedIndex = 1;
|
||||||
|
} else {
|
||||||
|
iz += stepz;
|
||||||
|
t = tzMax;
|
||||||
|
tzMax += tzDelta;
|
||||||
|
steppedIndex = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iend.x = ix;
|
||||||
|
iend.y = iy;
|
||||||
|
iend.z = iz;
|
||||||
|
|
||||||
|
end.x = px + t * dx;
|
||||||
|
end.y = py + t * dy;
|
||||||
|
end.z = pz + t * dz;
|
||||||
|
norm.x = norm.y = norm.z = 0;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
voxel* blocks_agent::raycast(
|
||||||
|
const Chunks& chunks,
|
||||||
|
const glm::vec3& start,
|
||||||
|
const glm::vec3& dir,
|
||||||
|
float maxDist,
|
||||||
|
glm::vec3& end,
|
||||||
|
glm::ivec3& norm,
|
||||||
|
glm::ivec3& iend,
|
||||||
|
std::set<blockid_t> filter
|
||||||
|
) {
|
||||||
|
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
voxel* blocks_agent::raycast(
|
||||||
|
const GlobalChunks& chunks,
|
||||||
|
const glm::vec3& start,
|
||||||
|
const glm::vec3& dir,
|
||||||
|
float maxDist,
|
||||||
|
glm::vec3& end,
|
||||||
|
glm::ivec3& norm,
|
||||||
|
glm::ivec3& iend,
|
||||||
|
std::set<blockid_t> filter
|
||||||
|
) {
|
||||||
|
return raycast_blocks(chunks, start, dir, maxDist, end, norm, iend, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
// reduce nesting on next modification
|
||||||
|
// 25.06.2024: not now
|
||||||
|
// 11.11.2024: not now
|
||||||
|
template <class Storage>
|
||||||
|
inline void get_voxels_impl(
|
||||||
|
const Storage& chunks, VoxelsVolume* volume, bool backlight
|
||||||
|
) {
|
||||||
|
const auto& blocks = chunks.getContentIndices().blocks;
|
||||||
|
voxel* voxels = volume->getVoxels();
|
||||||
|
light_t* lights = volume->getLights();
|
||||||
|
int x = volume->getX();
|
||||||
|
int y = volume->getY();
|
||||||
|
int z = volume->getZ();
|
||||||
|
|
||||||
|
int w = volume->getW();
|
||||||
|
int h = volume->getH();
|
||||||
|
int d = volume->getD();
|
||||||
|
|
||||||
|
int scx = floordiv<CHUNK_W>(x);
|
||||||
|
int scz = floordiv<CHUNK_D>(z);
|
||||||
|
|
||||||
|
int ecx = floordiv<CHUNK_W>(x + w);
|
||||||
|
int ecz = floordiv<CHUNK_D>(z + d);
|
||||||
|
|
||||||
|
int cw = ecx - scx + 1;
|
||||||
|
int cd = ecz - scz + 1;
|
||||||
|
|
||||||
|
// cw*cd chunks will be scanned
|
||||||
|
for (int cz = scz; cz < scz + cd; cz++) {
|
||||||
|
for (int cx = scx; cx < scx + cw; cx++) {
|
||||||
|
const auto chunk = get_chunk(chunks, cx, cz);
|
||||||
|
if (chunk == nullptr) {
|
||||||
|
// no chunk loaded -> filling with BLOCK_VOID
|
||||||
|
for (int ly = y; ly < y + h; ly++) {
|
||||||
|
for (int lz = std::max(z, cz * CHUNK_D);
|
||||||
|
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||||
|
lz++) {
|
||||||
|
for (int lx = std::max(x, cx * CHUNK_W);
|
||||||
|
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||||
|
lx++) {
|
||||||
|
uint idx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||||
|
voxels[idx].id = BLOCK_VOID;
|
||||||
|
lights[idx] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const voxel* cvoxels = chunk->voxels;
|
||||||
|
const light_t* clights = chunk->lightmap.getLights();
|
||||||
|
for (int ly = y; ly < y + h; ly++) {
|
||||||
|
for (int lz = std::max(z, cz * CHUNK_D);
|
||||||
|
lz < std::min(z + d, (cz + 1) * CHUNK_D);
|
||||||
|
lz++) {
|
||||||
|
for (int lx = std::max(x, cx * CHUNK_W);
|
||||||
|
lx < std::min(x + w, (cx + 1) * CHUNK_W);
|
||||||
|
lx++) {
|
||||||
|
uint vidx = vox_index(lx - x, ly - y, lz - z, w, d);
|
||||||
|
uint cidx = vox_index(
|
||||||
|
lx - cx * CHUNK_W,
|
||||||
|
ly,
|
||||||
|
lz - cz * CHUNK_D,
|
||||||
|
CHUNK_W,
|
||||||
|
CHUNK_D
|
||||||
|
);
|
||||||
|
voxels[vidx] = cvoxels[cidx];
|
||||||
|
light_t light = clights[cidx];
|
||||||
|
if (backlight) {
|
||||||
|
const auto block = blocks.get(voxels[vidx].id);
|
||||||
|
if (block && block->lightPassing) {
|
||||||
|
light = Lightmap::combine(
|
||||||
|
std::min(15,
|
||||||
|
Lightmap::extract(light, 0) + 1),
|
||||||
|
std::min(15,
|
||||||
|
Lightmap::extract(light, 1) + 1),
|
||||||
|
std::min(15,
|
||||||
|
Lightmap::extract(light, 2) + 1),
|
||||||
|
std::min(15,
|
||||||
|
static_cast<int>(Lightmap::extract(light, 3)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lights[vidx] = light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void blocks_agent::get_voxels(
|
||||||
|
const Chunks& chunks, VoxelsVolume* volume, bool backlight
|
||||||
|
) {
|
||||||
|
get_voxels_impl(chunks, volume, backlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
void blocks_agent::get_voxels(
|
||||||
|
const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight
|
||||||
|
) {
|
||||||
|
get_voxels_impl(chunks, volume, backlight);
|
||||||
|
}
|
||||||
476
src/voxels/blocks_agent.hpp
Normal file
476
src/voxels/blocks_agent.hpp
Normal file
@ -0,0 +1,476 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/// blocks_agent is set of templates but not a class to minimize OOP overhead.
|
||||||
|
|
||||||
|
#include "voxel.hpp"
|
||||||
|
#include "Block.hpp"
|
||||||
|
#include "Chunk.hpp"
|
||||||
|
#include "Chunks.hpp"
|
||||||
|
#include "VoxelsVolume.hpp"
|
||||||
|
#include "GlobalChunks.hpp"
|
||||||
|
#include "constants.hpp"
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
#include "content/Content.hpp"
|
||||||
|
#include "maths/voxmaths.hpp"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
struct AABB;
|
||||||
|
|
||||||
|
namespace blocks_agent {
|
||||||
|
|
||||||
|
/// @brief Get specified chunk.
|
||||||
|
/// @tparam Storage
|
||||||
|
/// @param chunks
|
||||||
|
/// @param cx chunk grid position X
|
||||||
|
/// @param cz chunk grid position Z
|
||||||
|
/// @return chunk or nullptr if does not exists
|
||||||
|
template<class Storage>
|
||||||
|
inline Chunk* get_chunk(const Storage& chunks, int cx, int cz) {
|
||||||
|
return chunks.getChunk(cx, cz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Get voxel at specified position.
|
||||||
|
/// Returns nullptr if voxel does not exists.
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param x position X
|
||||||
|
/// @param y position Y
|
||||||
|
/// @param z position Z
|
||||||
|
/// @return voxel pointer or nullptr
|
||||||
|
template<class Storage>
|
||||||
|
inline voxel* get(const Storage& chunks, int32_t x, int32_t y, int32_t z) {
|
||||||
|
if (y < 0 || y >= CHUNK_H) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
|
Chunk* chunk = get_chunk(chunks, cx, cz);
|
||||||
|
if (chunk == nullptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
int lx = x - cx * CHUNK_W;
|
||||||
|
int lz = z - cz * CHUNK_D;
|
||||||
|
return &chunk->voxels[(y * CHUNK_D + lz) * CHUNK_W + lx];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Get voxel at specified position.
|
||||||
|
/// @throws std::runtime_error if voxel does not exists
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param x position X
|
||||||
|
/// @param y position Y
|
||||||
|
/// @param z position Z
|
||||||
|
/// @return voxel reference
|
||||||
|
template<class Storage>
|
||||||
|
inline voxel& require(const Storage& chunks, int32_t x, int32_t y, int32_t z) {
|
||||||
|
auto vox = get(chunks, x, y, z);
|
||||||
|
if (vox == nullptr) {
|
||||||
|
throw std::runtime_error("voxel does not exist");
|
||||||
|
}
|
||||||
|
return *vox;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Storage>
|
||||||
|
inline const Block& get_block_def(const Storage& chunks, blockid_t id) {
|
||||||
|
return chunks.getContentIndices().blocks.require(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @brief Check if block at specified position is solid.
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param x position X
|
||||||
|
/// @param y position Y
|
||||||
|
/// @param z position Z
|
||||||
|
/// @return true if block exists and solid
|
||||||
|
template<class Storage>
|
||||||
|
inline bool is_solid_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) {
|
||||||
|
if (auto vox = get(chunks, x, y, z)) {
|
||||||
|
return get_block_def(chunks, vox->id).rt.solid;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check if block at specified position is replaceable.
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param x position X
|
||||||
|
/// @param y position Y
|
||||||
|
/// @param z position Z
|
||||||
|
/// @return true if block exists and replaceable
|
||||||
|
template<class Storage>
|
||||||
|
inline bool is_replaceable_at(const Storage& chunks, int32_t x, int32_t y, int32_t z) {
|
||||||
|
if (auto vox = get(chunks, x, y, z)) {
|
||||||
|
return get_block_def(chunks, vox->id).replaceable;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set block at specified position if voxel exists.
|
||||||
|
/// @param chunks chunks matrix
|
||||||
|
/// @param x block position X
|
||||||
|
/// @param y block position Y
|
||||||
|
/// @param z block position Z
|
||||||
|
/// @param id new block id
|
||||||
|
/// @param state new block state
|
||||||
|
void set(
|
||||||
|
Chunks& chunks,
|
||||||
|
int32_t x,
|
||||||
|
int32_t y,
|
||||||
|
int32_t z,
|
||||||
|
uint32_t id,
|
||||||
|
blockstate state
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @brief Set block at specified position if voxel exists.
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param x block position X
|
||||||
|
/// @param y block position Y
|
||||||
|
/// @param z block position Z
|
||||||
|
/// @param id new block id
|
||||||
|
/// @param state new block state
|
||||||
|
void set(
|
||||||
|
GlobalChunks& chunks,
|
||||||
|
int32_t x,
|
||||||
|
int32_t y,
|
||||||
|
int32_t z,
|
||||||
|
uint32_t id,
|
||||||
|
blockstate state
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @brief Erase extended block segments
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param def origin block definition
|
||||||
|
/// @param state origin block state
|
||||||
|
/// @param x origin position X
|
||||||
|
/// @param y origin position Y
|
||||||
|
/// @param z origin position Z
|
||||||
|
template<class Storage>
|
||||||
|
inline void erase_segments(
|
||||||
|
Storage& chunks, const Block& def, blockstate state, int x, int y, int z
|
||||||
|
) {
|
||||||
|
const auto& rotation = def.rotations.variants[state.rotation];
|
||||||
|
for (int sy = 0; sy < def.size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < def.size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < def.size.x; sx++) {
|
||||||
|
if ((sx | sy | sz) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
glm::ivec3 pos(x, y, z);
|
||||||
|
pos += rotation.axisX * sx;
|
||||||
|
pos += rotation.axisY * sy;
|
||||||
|
pos += rotation.axisZ * sz;
|
||||||
|
set(chunks, pos.x, pos.y, pos.z, 0, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Convert segment offset to segment bits
|
||||||
|
/// @param sx segment offset X
|
||||||
|
/// @param sy segment offset Y
|
||||||
|
/// @param sz segment offset Z
|
||||||
|
/// @return segment bits
|
||||||
|
static constexpr inline uint8_t segment_to_int(int sx, int sy, int sz) {
|
||||||
|
return ((sx > 0) | ((sy > 0) << 1) | ((sz > 0) << 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Restore invalid extended block segments
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param def origin block definition
|
||||||
|
/// @param state origin block state
|
||||||
|
/// @param x origin position X
|
||||||
|
/// @param y origin position Y
|
||||||
|
/// @param z origin position Z
|
||||||
|
template <class Storage>
|
||||||
|
inline void repair_segments(
|
||||||
|
Storage& chunks, const Block& def, blockstate state, int x, int y, int z
|
||||||
|
) {
|
||||||
|
const auto& rotation = def.rotations.variants[state.rotation];
|
||||||
|
const auto id = def.rt.id;
|
||||||
|
const auto size = def.size;
|
||||||
|
for (int sy = 0; sy < size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < size.x; sx++) {
|
||||||
|
if ((sx | sy | sz) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
blockstate segState = state;
|
||||||
|
segState.segment = segment_to_int(sx, sy, sz);
|
||||||
|
|
||||||
|
glm::ivec3 pos(x, y, z);
|
||||||
|
pos += rotation.axisX * sx;
|
||||||
|
pos += rotation.axisY * sy;
|
||||||
|
pos += rotation.axisZ * sz;
|
||||||
|
set(chunks, pos.x, pos.y, pos.z, id, segState);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Get origin position for specified extended block. Returns srcpos
|
||||||
|
/// if block is not extended.
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param srcpos block segment position
|
||||||
|
/// @param def definition of the block at srcpos
|
||||||
|
/// @param state state of the block at srcpos
|
||||||
|
/// @return origin block position
|
||||||
|
template <class Storage>
|
||||||
|
inline glm::ivec3 seek_origin(
|
||||||
|
Storage& chunks, const glm::ivec3& srcpos, const Block& def, blockstate state
|
||||||
|
) {
|
||||||
|
auto pos = srcpos;
|
||||||
|
const auto& rotation = def.rotations.variants[state.rotation];
|
||||||
|
auto segment = state.segment;
|
||||||
|
while (true) {
|
||||||
|
if (!segment) {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
if (segment & 1) pos -= rotation.axisX;
|
||||||
|
if (segment & 2) pos -= rotation.axisY;
|
||||||
|
if (segment & 4) pos -= rotation.axisZ;
|
||||||
|
|
||||||
|
if (auto* voxel = get(chunks, pos.x, pos.y, pos.z)) {
|
||||||
|
segment = voxel->state.segment;
|
||||||
|
} else {
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Check blocks replaceability with specified block
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param def block definition
|
||||||
|
/// @param state target block state
|
||||||
|
/// @param origin target block origin
|
||||||
|
/// @param ignore ignored block id
|
||||||
|
/// @return true if specified area may be replaced with the block/extended block
|
||||||
|
template <class Storage>
|
||||||
|
inline bool check_replaceability(
|
||||||
|
const Storage& chunks,
|
||||||
|
const Block& def,
|
||||||
|
blockstate state,
|
||||||
|
const glm::ivec3& origin,
|
||||||
|
blockid_t ignore
|
||||||
|
) {
|
||||||
|
const auto& blocks = chunks.getContentIndices().blocks;
|
||||||
|
const auto& rotation = def.rotations.variants[state.rotation];
|
||||||
|
const auto size = def.size;
|
||||||
|
for (int sy = 0; sy < size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < size.x; sx++) {
|
||||||
|
auto pos = origin;
|
||||||
|
pos += rotation.axisX * sx;
|
||||||
|
pos += rotation.axisY * sy;
|
||||||
|
pos += rotation.axisZ * sz;
|
||||||
|
if (auto vox = get(chunks, pos.x, pos.y, pos.z)) {
|
||||||
|
auto& target = blocks.require(vox->id);
|
||||||
|
if (!target.replaceable && vox->id != ignore) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set rotation to an extended block
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param def block definition
|
||||||
|
/// @param state current block state
|
||||||
|
/// @param origin extended block origin
|
||||||
|
/// @param index target rotation index
|
||||||
|
template <class Storage>
|
||||||
|
inline void set_rotation_extended(
|
||||||
|
Storage& chunks,
|
||||||
|
const Block& def,
|
||||||
|
blockstate state,
|
||||||
|
const glm::ivec3& origin,
|
||||||
|
uint8_t index
|
||||||
|
) {
|
||||||
|
auto newstate = state;
|
||||||
|
newstate.rotation = index;
|
||||||
|
|
||||||
|
// unable to rotate block (cause: obstacles)
|
||||||
|
if (!check_replaceability(chunks, def, newstate, origin, def.rt.id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& rotation = def.rotations.variants[index];
|
||||||
|
const auto size = def.size;
|
||||||
|
std::vector<glm::ivec3> segmentBlocks;
|
||||||
|
|
||||||
|
for (int sy = 0; sy < size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < size.x; sx++) {
|
||||||
|
auto pos = origin;
|
||||||
|
pos += rotation.axisX * sx;
|
||||||
|
pos += rotation.axisY * sy;
|
||||||
|
pos += rotation.axisZ * sz;
|
||||||
|
|
||||||
|
blockstate segState = newstate;
|
||||||
|
segState.segment = segment_to_int(sx, sy, sz);
|
||||||
|
|
||||||
|
auto vox = get(chunks, pos.x, pos.y, pos.z);
|
||||||
|
// checked for nullptr by checkReplaceability
|
||||||
|
if (vox->id != def.rt.id) {
|
||||||
|
set(chunks, pos.x, pos.y, pos.z, def.rt.id, segState);
|
||||||
|
} else {
|
||||||
|
vox->state = segState;
|
||||||
|
int cx = floordiv<CHUNK_W>(pos.x);
|
||||||
|
int cz = floordiv<CHUNK_D>(pos.z);
|
||||||
|
auto chunk = get_chunk(chunks, cx, cz);
|
||||||
|
assert(chunk != nullptr);
|
||||||
|
chunk->setModifiedAndUnsaved();
|
||||||
|
segmentBlocks.emplace_back(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto& prevRotation = def.rotations.variants[state.rotation];
|
||||||
|
for (int sy = 0; sy < size.y; sy++) {
|
||||||
|
for (int sz = 0; sz < size.z; sz++) {
|
||||||
|
for (int sx = 0; sx < size.x; sx++) {
|
||||||
|
auto pos = origin;
|
||||||
|
pos += prevRotation.axisX * sx;
|
||||||
|
pos += prevRotation.axisY * sy;
|
||||||
|
pos += prevRotation.axisZ * sz;
|
||||||
|
if (std::find(
|
||||||
|
segmentBlocks.begin(), segmentBlocks.end(), pos
|
||||||
|
) == segmentBlocks.end()) {
|
||||||
|
set(chunks, pos.x, pos.y, pos.z, 0, {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Set block rotation
|
||||||
|
/// @tparam Storage chunks storage class
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param x block X position
|
||||||
|
/// @param y block Y position
|
||||||
|
/// @param z block Z position
|
||||||
|
/// @param index target rotation index
|
||||||
|
template <class Storage>
|
||||||
|
inline void set_rotation(
|
||||||
|
Storage& chunks, int32_t x, int32_t y, int32_t z, uint8_t index
|
||||||
|
) {
|
||||||
|
if (index >= BlockRotProfile::MAX_COUNT) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto vox = get(chunks, x, y, z);
|
||||||
|
if (vox == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& def = chunks.getContentIndices().blocks.require(vox->id);
|
||||||
|
if (!def.rotatable || vox->state.rotation == index) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (def.rt.extended) {
|
||||||
|
auto origin = seek_origin(chunks, {x, y, z}, def, vox->state);
|
||||||
|
vox = get(chunks, origin.x, origin.y, origin.z);
|
||||||
|
set_rotation_extended(chunks, def, vox->state, origin, index);
|
||||||
|
} else {
|
||||||
|
vox->state.rotation = index;
|
||||||
|
int cx = floordiv<CHUNK_W>(x);
|
||||||
|
int cz = floordiv<CHUNK_D>(z);
|
||||||
|
auto chunk = get_chunk(chunks, cx, cz);
|
||||||
|
assert(chunk != nullptr);
|
||||||
|
chunk->setModifiedAndUnsaved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Cast ray to a selectable block with filter based on id.
|
||||||
|
/// @param chunks chunks matrix
|
||||||
|
/// @param start ray start position
|
||||||
|
/// @param dir normalized ray direction vector
|
||||||
|
/// @param maxDist maximum ray length
|
||||||
|
/// @param end [out] ray end position
|
||||||
|
/// @param norm [out] surface normal vector
|
||||||
|
/// @param iend [out] ray end integer position (voxel position + normal)
|
||||||
|
/// @param filter filtered ids
|
||||||
|
/// @return voxel pointer or nullptr
|
||||||
|
voxel* raycast(
|
||||||
|
const Chunks& chunks,
|
||||||
|
const glm::vec3& start,
|
||||||
|
const glm::vec3& dir,
|
||||||
|
float maxDist,
|
||||||
|
glm::vec3& end,
|
||||||
|
glm::ivec3& norm,
|
||||||
|
glm::ivec3& iend,
|
||||||
|
std::set<blockid_t> filter
|
||||||
|
);
|
||||||
|
|
||||||
|
/// @brief Cast ray to a selectable block with filter based on id.
|
||||||
|
/// @param chunks chunks storage
|
||||||
|
/// @param start ray start position
|
||||||
|
/// @param dir normalized ray direction vector
|
||||||
|
/// @param maxDist maximum ray length
|
||||||
|
/// @param end [out] ray end position
|
||||||
|
/// @param norm [out] surface normal vector
|
||||||
|
/// @param iend [out] ray end integer position (voxel position + normal)
|
||||||
|
/// @param filter filtered ids
|
||||||
|
/// @return voxel pointer or nullptr
|
||||||
|
voxel* raycast(
|
||||||
|
const GlobalChunks& chunks,
|
||||||
|
const glm::vec3& start,
|
||||||
|
const glm::vec3& dir,
|
||||||
|
float maxDist,
|
||||||
|
glm::vec3& end,
|
||||||
|
glm::ivec3& norm,
|
||||||
|
glm::ivec3& iend,
|
||||||
|
std::set<blockid_t> filter
|
||||||
|
);
|
||||||
|
|
||||||
|
void get_voxels(const Chunks& chunks, VoxelsVolume* volume, bool backlight=false);
|
||||||
|
|
||||||
|
void get_voxels(const GlobalChunks& chunks, VoxelsVolume* volume, bool backlight=false);
|
||||||
|
|
||||||
|
template <class Storage>
|
||||||
|
inline const AABB* is_obstacle_at(const Storage& chunks, float x, float y, float z) {
|
||||||
|
int ix = std::floor(x);
|
||||||
|
int iy = std::floor(y);
|
||||||
|
int iz = std::floor(z);
|
||||||
|
voxel* v = get(chunks, ix, iy, iz);
|
||||||
|
if (v == nullptr) {
|
||||||
|
if (iy >= CHUNK_H) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
static const AABB empty;
|
||||||
|
return ∅
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const auto& def = chunks.getContentIndices().blocks.require(v->id);
|
||||||
|
if (def.obstacle) {
|
||||||
|
glm::ivec3 offset {};
|
||||||
|
if (v->state.segment) {
|
||||||
|
glm::ivec3 point(ix, iy, iz);
|
||||||
|
offset = seek_origin(chunks, point, def, v->state) - point;
|
||||||
|
}
|
||||||
|
const auto& boxes =
|
||||||
|
def.rotatable ? def.rt.hitboxes[v->state.rotation] : def.hitboxes;
|
||||||
|
for (const auto& hitbox : boxes) {
|
||||||
|
if (hitbox.contains(
|
||||||
|
{x - ix - offset.x, y - iy - offset.y, z - iz - offset.z}
|
||||||
|
)) {
|
||||||
|
return &hitbox;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // blocks_agent
|
||||||
@ -199,7 +199,7 @@ int Window::initialize(DisplaySettings* settings) {
|
|||||||
|
|
||||||
observers_keeper = util::ObjectsKeeper();
|
observers_keeper = util::ObjectsKeeper();
|
||||||
observers_keeper.keepAlive(settings->fullscreen.observe(
|
observers_keeper.keepAlive(settings->fullscreen.observe(
|
||||||
[=](bool value) {
|
[](bool value) {
|
||||||
if (value != isFullscreen()) {
|
if (value != isFullscreen()) {
|
||||||
toggleFullscreen();
|
toggleFullscreen();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,6 @@
|
|||||||
#include "data/dv_util.hpp"
|
#include "data/dv_util.hpp"
|
||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
#include "lighting/Lighting.hpp"
|
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
@ -12,7 +11,6 @@
|
|||||||
#include "physics/PhysicsSolver.hpp"
|
#include "physics/PhysicsSolver.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "LevelEvents.hpp"
|
#include "LevelEvents.hpp"
|
||||||
@ -23,14 +21,14 @@ Level::Level(
|
|||||||
const Content* content,
|
const Content* content,
|
||||||
EngineSettings& settings
|
EngineSettings& settings
|
||||||
)
|
)
|
||||||
: world(std::move(worldPtr)),
|
: settings(settings),
|
||||||
|
world(std::move(worldPtr)),
|
||||||
content(content),
|
content(content),
|
||||||
chunksStorage(std::make_unique<GlobalChunks>(this)),
|
chunks(std::make_unique<GlobalChunks>(this)),
|
||||||
physics(std::make_unique<PhysicsSolver>(glm::vec3(0, -22.6f, 0))),
|
physics(std::make_unique<PhysicsSolver>(glm::vec3(0, -22.6f, 0))),
|
||||||
events(std::make_unique<LevelEvents>()),
|
events(std::make_unique<LevelEvents>()),
|
||||||
entities(std::make_unique<Entities>(this)),
|
entities(std::make_unique<Entities>(this)),
|
||||||
players(std::make_unique<Players>(this)),
|
players(std::make_unique<Players>(this)) {
|
||||||
settings(settings) {
|
|
||||||
const auto& worldInfo = world->getInfo();
|
const auto& worldInfo = world->getInfo();
|
||||||
auto& cameraIndices = content->getIndices(ResourceType::CAMERA);
|
auto& cameraIndices = content->getIndices(ResourceType::CAMERA);
|
||||||
for (size_t i = 0; i < cameraIndices.size(); i++) {
|
for (size_t i = 0; i < cameraIndices.size(); i++) {
|
||||||
@ -55,38 +53,16 @@ Level::Level(
|
|||||||
}
|
}
|
||||||
|
|
||||||
events->listen(LevelEventType::EVT_CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) {
|
events->listen(LevelEventType::EVT_CHUNK_SHOWN, [this](LevelEventType, Chunk* chunk) {
|
||||||
chunksStorage->incref(chunk);
|
chunks->incref(chunk);
|
||||||
});
|
});
|
||||||
events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) {
|
events->listen(LevelEventType::EVT_CHUNK_HIDDEN, [this](LevelEventType, Chunk* chunk) {
|
||||||
chunksStorage->decref(chunk);
|
chunks->decref(chunk);
|
||||||
});
|
});
|
||||||
|
|
||||||
uint matrixSize =
|
|
||||||
(settings.chunks.loadDistance.get() + settings.chunks.padding.get()) *
|
|
||||||
2;
|
|
||||||
chunks = std::make_unique<Chunks>(
|
|
||||||
matrixSize, matrixSize, 0, 0, world->wfile.get(), this
|
|
||||||
);
|
|
||||||
|
|
||||||
lighting = std::make_unique<Lighting>(content, chunks.get());
|
|
||||||
|
|
||||||
inventories = std::make_unique<Inventories>(*this);
|
inventories = std::make_unique<Inventories>(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Level::~Level() = default;
|
Level::~Level() = default;
|
||||||
|
|
||||||
void Level::loadMatrix(int32_t x, int32_t z, uint32_t radius) {
|
|
||||||
chunks->setCenter(x, z);
|
|
||||||
uint32_t diameter = std::min(
|
|
||||||
radius * 2LL,
|
|
||||||
(settings.chunks.loadDistance.get() + settings.chunks.padding.get()) *
|
|
||||||
2LL
|
|
||||||
);
|
|
||||||
if (chunks->getWidth() != diameter) {
|
|
||||||
chunks->resize(diameter, diameter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
World* Level::getWorld() {
|
World* Level::getWorld() {
|
||||||
return world.get();
|
return world.get();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,11 +9,9 @@
|
|||||||
|
|
||||||
class Content;
|
class Content;
|
||||||
class World;
|
class World;
|
||||||
class Chunks;
|
|
||||||
class Entities;
|
class Entities;
|
||||||
class Inventories;
|
class Inventories;
|
||||||
class LevelEvents;
|
class LevelEvents;
|
||||||
class Lighting;
|
|
||||||
class PhysicsSolver;
|
class PhysicsSolver;
|
||||||
class GlobalChunks;
|
class GlobalChunks;
|
||||||
class Camera;
|
class Camera;
|
||||||
@ -22,23 +20,19 @@ struct EngineSettings;
|
|||||||
|
|
||||||
/// @brief A level, contains chunks and objects
|
/// @brief A level, contains chunks and objects
|
||||||
class Level {
|
class Level {
|
||||||
|
const EngineSettings& settings;
|
||||||
std::unique_ptr<World> world;
|
std::unique_ptr<World> world;
|
||||||
public:
|
public:
|
||||||
const Content* const content;
|
const Content* const content;
|
||||||
|
|
||||||
std::unique_ptr<Chunks> chunks;
|
std::unique_ptr<GlobalChunks> chunks;
|
||||||
std::unique_ptr<GlobalChunks> chunksStorage;
|
|
||||||
std::unique_ptr<Inventories> inventories;
|
std::unique_ptr<Inventories> inventories;
|
||||||
|
|
||||||
std::unique_ptr<PhysicsSolver> physics;
|
std::unique_ptr<PhysicsSolver> physics;
|
||||||
std::unique_ptr<Lighting> lighting;
|
|
||||||
std::unique_ptr<LevelEvents> events;
|
std::unique_ptr<LevelEvents> events;
|
||||||
std::unique_ptr<Entities> entities;
|
std::unique_ptr<Entities> entities;
|
||||||
std::unique_ptr<Players> players;
|
std::unique_ptr<Players> players;
|
||||||
std::vector<std::shared_ptr<Camera>> cameras; // move somewhere?
|
std::vector<std::shared_ptr<Camera>> cameras; // move somewhere?
|
||||||
|
|
||||||
const EngineSettings& settings;
|
|
||||||
|
|
||||||
Level(
|
Level(
|
||||||
std::unique_ptr<World> world,
|
std::unique_ptr<World> world,
|
||||||
const Content* content,
|
const Content* content,
|
||||||
@ -46,8 +40,6 @@ public:
|
|||||||
);
|
);
|
||||||
~Level();
|
~Level();
|
||||||
|
|
||||||
void loadMatrix(int32_t x, int32_t z, uint32_t radius);
|
|
||||||
|
|
||||||
World* getWorld();
|
World* getWorld();
|
||||||
|
|
||||||
const World* getWorld() const;
|
const World* getWorld() const;
|
||||||
|
|||||||
@ -66,7 +66,7 @@ void World::writeResources(const Content* content) {
|
|||||||
|
|
||||||
void World::write(Level* level) {
|
void World::write(Level* level) {
|
||||||
const Content* content = level->content;
|
const Content* content = level->content;
|
||||||
level->chunksStorage->saveAll();
|
level->chunks->saveAll();
|
||||||
info.nextEntityId = level->entities->peekNextID();
|
info.nextEntityId = level->entities->peekNextID();
|
||||||
wfile->write(this, content);
|
wfile->write(this, content);
|
||||||
|
|
||||||
|
|||||||
@ -10,11 +10,12 @@
|
|||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/GlobalChunks.hpp"
|
#include "voxels/GlobalChunks.hpp"
|
||||||
#include "voxels/VoxelsVolume.hpp"
|
#include "voxels/VoxelsVolume.hpp"
|
||||||
|
#include "voxels/blocks_agent.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "core_defs.hpp"
|
#include "core_defs.hpp"
|
||||||
|
|
||||||
std::unique_ptr<VoxelFragment> VoxelFragment::create(
|
std::unique_ptr<VoxelFragment> VoxelFragment::create(
|
||||||
Level* level,
|
const Level& level,
|
||||||
const glm::ivec3& a,
|
const glm::ivec3& a,
|
||||||
const glm::ivec3& b,
|
const glm::ivec3& b,
|
||||||
bool crop,
|
bool crop,
|
||||||
@ -26,7 +27,7 @@ std::unique_ptr<VoxelFragment> VoxelFragment::create(
|
|||||||
if (crop) {
|
if (crop) {
|
||||||
VoxelsVolume volume(size.x, size.y, size.z);
|
VoxelsVolume volume(size.x, size.y, size.z);
|
||||||
volume.setPosition(start.x, start.y, start.z);
|
volume.setPosition(start.x, start.y, start.z);
|
||||||
level->chunks->getVoxels(&volume);
|
blocks_agent::get_voxels(*level.chunks, &volume);
|
||||||
|
|
||||||
auto end = start + size;
|
auto end = start + size;
|
||||||
|
|
||||||
@ -51,14 +52,14 @@ std::unique_ptr<VoxelFragment> VoxelFragment::create(
|
|||||||
|
|
||||||
VoxelsVolume volume(size.x, size.y, size.z);
|
VoxelsVolume volume(size.x, size.y, size.z);
|
||||||
volume.setPosition(start.x, start.y, start.z);
|
volume.setPosition(start.x, start.y, start.z);
|
||||||
level->chunks->getVoxels(&volume);
|
blocks_agent::get_voxels(*level.chunks, &volume);
|
||||||
|
|
||||||
auto volVoxels = volume.getVoxels();
|
auto volVoxels = volume.getVoxels();
|
||||||
std::vector<voxel> voxels(size.x * size.y * size.z);
|
std::vector<voxel> voxels(size.x * size.y * size.z);
|
||||||
|
|
||||||
std::vector<std::string> blockNames {CORE_AIR};
|
std::vector<std::string> blockNames {CORE_AIR};
|
||||||
std::unordered_map<blockid_t, blockid_t> blocksRegistered {{0, 0}};
|
std::unordered_map<blockid_t, blockid_t> blocksRegistered {{0, 0}};
|
||||||
auto contentIndices = level->content->getIndices();
|
auto contentIndices = level.content->getIndices();
|
||||||
for (size_t i = 0 ; i < voxels.size(); i++) {
|
for (size_t i = 0 ; i < voxels.size(); i++) {
|
||||||
blockid_t id = volVoxels[i].id;
|
blockid_t id = volVoxels[i].id;
|
||||||
blockid_t index;
|
blockid_t index;
|
||||||
@ -170,7 +171,7 @@ void VoxelFragment::prepare(const Content& content) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void VoxelFragment::place(
|
void VoxelFragment::place(
|
||||||
Chunks& chunks, const glm::ivec3& offset, ubyte rotation
|
GlobalChunks& chunks, const glm::ivec3& offset, ubyte rotation
|
||||||
) {
|
) {
|
||||||
auto& structVoxels = getRuntimeVoxels();
|
auto& structVoxels = getRuntimeVoxels();
|
||||||
for (int y = 0; y < size.y; y++) {
|
for (int y = 0; y < size.y; y++) {
|
||||||
@ -185,7 +186,9 @@ void VoxelFragment::place(
|
|||||||
const auto& structVoxel =
|
const auto& structVoxel =
|
||||||
structVoxels[vox_index(x, y, z, size.x, size.z)];
|
structVoxels[vox_index(x, y, z, size.x, size.z)];
|
||||||
if (structVoxel.id) {
|
if (structVoxel.id) {
|
||||||
chunks.set(sx, sy, sz, structVoxel.id, structVoxel.state);
|
blocks_agent::set(
|
||||||
|
chunks, sx, sy, sz, structVoxel.id, structVoxel.state
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ inline constexpr int STRUCTURE_FORMAT_VERSION = 1;
|
|||||||
|
|
||||||
class Level;
|
class Level;
|
||||||
class Content;
|
class Content;
|
||||||
class Chunks;
|
class GlobalChunks;
|
||||||
|
|
||||||
class VoxelFragment : public Serializable {
|
class VoxelFragment : public Serializable {
|
||||||
glm::ivec3 size;
|
glm::ivec3 size;
|
||||||
@ -45,13 +45,13 @@ public:
|
|||||||
/// @brief Place fragment to the world
|
/// @brief Place fragment to the world
|
||||||
/// @param offset target location
|
/// @param offset target location
|
||||||
/// @param rotation rotation index
|
/// @param rotation rotation index
|
||||||
void place(Chunks& chunks, const glm::ivec3& offset, ubyte rotation);
|
void place(GlobalChunks& chunks, const glm::ivec3& offset, ubyte rotation);
|
||||||
|
|
||||||
/// @brief Create structure copy rotated 90 deg. clockwise
|
/// @brief Create structure copy rotated 90 deg. clockwise
|
||||||
std::unique_ptr<VoxelFragment> rotated(const Content& content) const;
|
std::unique_ptr<VoxelFragment> rotated(const Content& content) const;
|
||||||
|
|
||||||
static std::unique_ptr<VoxelFragment> create(
|
static std::unique_ptr<VoxelFragment> create(
|
||||||
Level* level,
|
const Level& level,
|
||||||
const glm::ivec3& a,
|
const glm::ivec3& a,
|
||||||
const glm::ivec3& b,
|
const glm::ivec3& b,
|
||||||
bool crop,
|
bool crop,
|
||||||
|
|||||||
@ -189,10 +189,10 @@ void WorldGenerator::placeLine(const LinePlacement& line, int priority) {
|
|||||||
aabb.fix();
|
aabb.fix();
|
||||||
aabb.a -= line.radius;
|
aabb.a -= line.radius;
|
||||||
aabb.b += line.radius;
|
aabb.b += line.radius;
|
||||||
int cxa = floordiv(aabb.a.x, CHUNK_W);
|
int cxa = floordiv<CHUNK_W>(aabb.a.x);
|
||||||
int cza = floordiv(aabb.a.z, CHUNK_D);
|
int cza = floordiv<CHUNK_D>(aabb.a.z);
|
||||||
int cxb = floordiv(aabb.b.x, CHUNK_W);
|
int cxb = floordiv<CHUNK_W>(aabb.b.x);
|
||||||
int czb = floordiv(aabb.b.z, CHUNK_D);
|
int czb = floordiv<CHUNK_D>(aabb.b.z);
|
||||||
for (int cz = cza; cz <= czb; cz++) {
|
for (int cz = cza; cz <= czb; cz++) {
|
||||||
for (int cx = cxa; cx <= cxb; cx++) {
|
for (int cx = cxa; cx <= cxb; cx++) {
|
||||||
const auto& found = prototypes.find({cx, cz});
|
const auto& found = prototypes.find({cx, cz});
|
||||||
@ -354,8 +354,8 @@ void WorldGenerator::generateHeightmap(
|
|||||||
|
|
||||||
void WorldGenerator::update(int centerX, int centerY, int loadDistance) {
|
void WorldGenerator::update(int centerX, int centerY, int loadDistance) {
|
||||||
surroundMap.setCenter(centerX, centerY);
|
surroundMap.setCenter(centerX, centerY);
|
||||||
// 1 is safety padding preventing ChunksController rounding problem
|
// 2 is safety padding preventing ChunksController rounding problem
|
||||||
surroundMap.resize(loadDistance + 1);
|
surroundMap.resize(loadDistance + 2);
|
||||||
surroundMap.setCenter(centerX, centerY);
|
surroundMap.setCenter(centerX, centerY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -117,8 +117,10 @@ static void setup_working_dir(const fs::path& workingDir) {
|
|||||||
fs::create_directories(dir);
|
fs::create_directories(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void display_test_output(const fs::path& path, std::ostream& stream) {
|
static void display_test_output(
|
||||||
stream << "[OUTPUT]" << std::endl;
|
const fs::path& path, const fs::path& name, std::ostream& stream
|
||||||
|
) {
|
||||||
|
stream << "[OUTPUT] " << name << std::endl;
|
||||||
if (fs::exists(path)) {
|
if (fs::exists(path)) {
|
||||||
std::ifstream t(path);
|
std::ifstream t(path);
|
||||||
stream << t.rdbuf();
|
stream << t.rdbuf();
|
||||||
@ -160,13 +162,13 @@ static bool run_test(const Config& config, const fs::path& path) {
|
|||||||
.count();
|
.count();
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
display_test_output(outputFile, std::cerr);
|
display_test_output(outputFile, name, std::cerr);
|
||||||
std::cerr << "[FAILED] " << name << " in " << testTime << " ms" << std::endl;
|
std::cerr << "[FAILED] " << name << " in " << testTime << " ms" << std::endl;
|
||||||
fs::remove(outputFile);
|
fs::remove(outputFile);
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (config.outputAlways) {
|
if (config.outputAlways) {
|
||||||
display_test_output(outputFile, std::cout);
|
display_test_output(outputFile, name, std::cout);
|
||||||
}
|
}
|
||||||
std::cout << "[PASSED] " << name << " in " << testTime << " ms" << std::endl;
|
std::cout << "[PASSED] " << name << " in " << testTime << " ms" << std::endl;
|
||||||
fs::remove(outputFile);
|
fs::remove(outputFile);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user