mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-06 03:21:49 +00:00
Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
7536ac2736
130 changed files with 2932 additions and 1028 deletions
|
|
@ -173,6 +173,12 @@ void audio::initialize(
|
|||
logger.info() << "initializing NoAudio backend";
|
||||
backend = NoAudio::create().release();
|
||||
}
|
||||
if (settings.inputDevice.get() == "auto") {
|
||||
auto inputDevices = audio::get_input_devices_names();
|
||||
if (!inputDevices.empty()) {
|
||||
settings.inputDevice.set(inputDevices.at(0));
|
||||
}
|
||||
}
|
||||
struct {
|
||||
std::string name;
|
||||
NumberSetting* setting;
|
||||
|
|
@ -210,7 +216,9 @@ void audio::initialize(
|
|||
::input_device = backend->openInputDevice(
|
||||
settings.inputDevice.get(), 44100, 1, 16
|
||||
);
|
||||
::input_device->startCapture();
|
||||
if (::input_device != nullptr) {
|
||||
::input_device->startCapture();
|
||||
}
|
||||
} else {
|
||||
if (::input_device) {
|
||||
::input_device->stopCapture();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ namespace {
|
|||
public:
|
||||
FTFontFile(FT_Face face, util::Buffer<ubyte> buffer)
|
||||
: face(std::move(face)), buffer(std::move(buffer)) {
|
||||
monospace = this->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH;
|
||||
}
|
||||
|
||||
~FTFontFile() {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,12 @@ namespace vector_fonts {
|
|||
virtual std::unique_ptr<Texture> renderPage(
|
||||
int pageid, std::vector<Glyph>& glyphs, int size
|
||||
) = 0;
|
||||
|
||||
bool isMonospace() const {
|
||||
return monospace;
|
||||
}
|
||||
protected:
|
||||
bool monospace = true;
|
||||
};
|
||||
|
||||
void initialize();
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ inline constexpr bool ENGINE_DEBUG_BUILD = true;
|
|||
|
||||
inline const std::string ENGINE_VERSION_STRING = "0.32";
|
||||
|
||||
inline constexpr uint MAX_SUBPROCESSES = 1;
|
||||
inline constexpr uint MAX_SUBPROCESS_DEPTH = 2;
|
||||
|
||||
/// @brief world regions format version
|
||||
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
||||
|
||||
|
|
|
|||
|
|
@ -188,6 +188,7 @@ public:
|
|||
ContentUnitDefs<GeneratorDef> generators;
|
||||
std::unique_ptr<DrawGroups> const drawGroups;
|
||||
ResourceIndicesSet resourceIndices {};
|
||||
std::set<std::string> components {};
|
||||
|
||||
Content(
|
||||
std::unique_ptr<ContentIndices> indices,
|
||||
|
|
|
|||
|
|
@ -26,17 +26,21 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
|||
def.rt.tags.insert(tags.add(tag));
|
||||
}
|
||||
|
||||
auto isSolid = [](const Variant& variant) {
|
||||
return variant.model.type == BlockModelType::BLOCK ||
|
||||
variant.culling == CullingMode::OPTIONAL;
|
||||
};
|
||||
|
||||
if (def.variants) {
|
||||
for (auto& variant : def.variants->variants) {
|
||||
variant.rt.solid =
|
||||
variant.model.type == BlockModelType::BLOCK ||
|
||||
def.explictlySolid;
|
||||
variant.rt.solid = isSolid(variant) || def.explictlySolid;
|
||||
}
|
||||
def.defaults = def.variants->variants.at(0);
|
||||
} else {
|
||||
def.defaults.rt.solid =
|
||||
def.defaults.model.type == BlockModelType::BLOCK ||
|
||||
def.explictlySolid;
|
||||
def.defaults.rt.solid = isSolid(def.defaults) || def.explictlySolid;
|
||||
}
|
||||
if (def.material.empty()) {
|
||||
defaults.at("block-material").get(def.material);
|
||||
}
|
||||
|
||||
const float EPSILON = 0.01f;
|
||||
|
|
|
|||
|
|
@ -506,6 +506,7 @@ void ContentLoader::loadScripts(Content& content) {
|
|||
scripting::on_scripts_loading();
|
||||
load_scripts(content, content.blocks);
|
||||
load_scripts(content, content.items);
|
||||
auto& tmpContent = content;
|
||||
|
||||
for (const auto& [packid, runtime] : content.getPacks()) {
|
||||
auto env = runtime->getEnvironment();
|
||||
|
|
@ -516,7 +517,7 @@ void ContentLoader::loadScripts(Content& content) {
|
|||
|
||||
// Load entity components
|
||||
io::path componentsDir = folder / "scripts/components";
|
||||
foreach_file(componentsDir, [&pack, env](const io::path& file) {
|
||||
foreach_file(componentsDir, [&pack, env, &tmpContent](const io::path& file) {
|
||||
auto name = pack.id + ":" + file.stem();
|
||||
scripting::load_entity_component(
|
||||
env,
|
||||
|
|
@ -524,9 +525,21 @@ void ContentLoader::loadScripts(Content& content) {
|
|||
file,
|
||||
pack.id + ":scripts/components/" + file.name()
|
||||
);
|
||||
tmpContent.components.insert(name);
|
||||
});
|
||||
}
|
||||
|
||||
for (const auto& [eid, def] : content.entities.getDefs()) {
|
||||
for (const auto& instance : def->components) {
|
||||
if (content.components.find(instance.component) == content.components.end()) {
|
||||
throw std::runtime_error(
|
||||
"component " + instance.component +
|
||||
" is not available (required by entity " + eid + ")"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scripting::on_content_loaded();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
|
|||
root.at("skeleton-name").get(def.skeletonName);
|
||||
root.at("material").get(def.material);
|
||||
root.at("blocking").get(def.blocking);
|
||||
root.at("selectable").get(def.selectable);
|
||||
root.at("solid").get(def.solid);
|
||||
root.at("mass").get(def.mass);
|
||||
root.at("elasticity").get(def.elasticity);
|
||||
|
|
|
|||
|
|
@ -9,6 +9,10 @@
|
|||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef VC_ENABLE_REFLECTION
|
||||
#include "util/EnumMetadata.hpp"
|
||||
#endif
|
||||
|
||||
namespace util {
|
||||
template<class T> class Buffer;
|
||||
}
|
||||
|
|
@ -70,6 +74,14 @@ namespace dv {
|
|||
return ptr != nullptr;
|
||||
}
|
||||
|
||||
inline value* operator->() noexcept {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
inline const value* operator->() const noexcept {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
inline value& operator*() noexcept {
|
||||
return *ptr;
|
||||
}
|
||||
|
|
@ -78,6 +90,11 @@ namespace dv {
|
|||
return *ptr;
|
||||
}
|
||||
|
||||
#ifdef VC_ENABLE_REFLECTION
|
||||
template <typename T>
|
||||
bool get(T& dst, const util::EnumMetadata<T>& mt) const;
|
||||
#endif
|
||||
|
||||
bool get(std::string& dst) const;
|
||||
bool get(bool& dst) const;
|
||||
bool get(char& dst) const;
|
||||
|
|
@ -552,6 +569,17 @@ namespace dv {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef VC_ENABLE_REFLECTION
|
||||
template <typename T>
|
||||
inline bool optionalvalue::get(T& dst, const util::EnumMetadata<T>& mt) const {
|
||||
if (ptr) {
|
||||
return mt.getItem(ptr->asString(), dst);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool optionalvalue::get(std::string& dst) const {
|
||||
if (ptr) {
|
||||
dst = ptr->asString();
|
||||
|
|
|
|||
59
src/devtools/AppScriptsControl.cpp
Normal file
59
src/devtools/AppScriptsControl.cpp
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
#include "AppScriptsControl.hpp"
|
||||
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/CoreParameters.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "Project.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
|
||||
static debug::Logger logger("app-scripts");
|
||||
|
||||
AppScriptsControl::AppScriptsControl(const CoreParameters& params) {
|
||||
io::path scriptFile =
|
||||
params.scriptFile.empty()
|
||||
? "project:start.lua"
|
||||
: std::string("script:") + params.scriptFile.filename().u8string();
|
||||
if (io::exists(scriptFile)) {
|
||||
logger.info() << "starting script: " << params.scriptFile.u8string();
|
||||
scriptCoroutine = scripting::start_app_script(scriptFile);
|
||||
} else {
|
||||
logger.warning() << "script does not exists: " << params.scriptFile.u8string();
|
||||
}
|
||||
|
||||
if (!params.headless) {
|
||||
loadProjectClientScript();
|
||||
}
|
||||
}
|
||||
|
||||
void AppScriptsControl::loadProjectClientScript() {
|
||||
io::path scriptFile = "project:project_client.lua";
|
||||
if (io::exists(scriptFile)) {
|
||||
logger.info() << "starting project client script: " << scriptFile.string();
|
||||
clientScript = scripting::load_client_project_script(scriptFile);
|
||||
} else {
|
||||
logger.warning() << "project client script does not exists: " << scriptFile.string();
|
||||
}
|
||||
}
|
||||
|
||||
void AppScriptsControl::onScreenChange(const std::string& name, bool show) {
|
||||
if (clientScript) {
|
||||
clientScript->onScreenChange(name, show);
|
||||
}
|
||||
}
|
||||
|
||||
void AppScriptsControl::tick() {
|
||||
if (scriptCoroutine && scriptCoroutine->isActive()) {
|
||||
scriptCoroutine->update();
|
||||
}
|
||||
}
|
||||
|
||||
void AppScriptsControl::terminate(std::string_view reason) {
|
||||
if (scriptCoroutine->isActive()) {
|
||||
scriptCoroutine->terminate();
|
||||
logger.info() << "script has been terminated due to " << reason;
|
||||
}
|
||||
}
|
||||
|
||||
bool AppScriptsControl::isFinished() const {
|
||||
return scriptCoroutine == nullptr || !scriptCoroutine->isActive();
|
||||
}
|
||||
29
src/devtools/AppScriptsControl.hpp
Normal file
29
src/devtools/AppScriptsControl.hpp
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
#pragma once
|
||||
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace scripting {
|
||||
class IClientProjectScript;
|
||||
}
|
||||
|
||||
struct CoreParameters;
|
||||
struct Project;
|
||||
|
||||
class AppScriptsControl {
|
||||
public:
|
||||
AppScriptsControl(const CoreParameters& params);
|
||||
|
||||
void tick();
|
||||
void loadProjectClientScript();
|
||||
void terminate(std::string_view reason);
|
||||
|
||||
void onScreenChange(const std::string& name, bool show);
|
||||
|
||||
bool isFinished() const;
|
||||
private:
|
||||
std::unique_ptr<scripting::IClientProjectScript> clientScript;
|
||||
std::unique_ptr<Process> scriptCoroutine;
|
||||
};
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "data/dv_util.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/CoreParameters.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "io/path.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
|
|
@ -40,26 +41,6 @@ void Project::deserialize(const dv::value& src) {
|
|||
}
|
||||
}
|
||||
|
||||
void Project::loadProjectClientScript() {
|
||||
io::path scriptFile = "project:project_client.lua";
|
||||
if (io::exists(scriptFile)) {
|
||||
logger.info() << "starting project client script";
|
||||
clientScript = scripting::load_client_project_script(scriptFile);
|
||||
} else {
|
||||
logger.warning() << "project client script does not exists";
|
||||
}
|
||||
}
|
||||
|
||||
void Project::loadProjectStartScript() {
|
||||
io::path scriptFile = "project:start.lua";
|
||||
if (io::exists(scriptFile)) {
|
||||
logger.info() << "starting project start script";
|
||||
setupCoroutine = scripting::start_app_script(scriptFile);
|
||||
} else {
|
||||
logger.warning() << "project start script does not exists";
|
||||
}
|
||||
}
|
||||
|
||||
bool Permissions::has(const std::string& name) const {
|
||||
return permissions.find(name) != permissions.end();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,19 @@
|
|||
#pragma once
|
||||
|
||||
#include "interfaces/Serializable.hpp"
|
||||
#include "io/path.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "interfaces/Process.hpp"
|
||||
#include "interfaces/Serializable.hpp"
|
||||
|
||||
namespace scripting {
|
||||
class IClientProjectScript;
|
||||
}
|
||||
|
||||
struct Permissions {
|
||||
static inline std::string DEBUGGING = "debugging";
|
||||
static inline std::string NETWORK = "network";
|
||||
static inline std::string RECORD_AUDIO = "record-audio";
|
||||
static inline std::string WRITE_TO_USER = "write-to-user";
|
||||
static inline std::string SUB_INSTANCES = "sub-instances";
|
||||
|
||||
std::set<std::string> permissions;
|
||||
|
||||
|
|
@ -26,16 +23,12 @@ struct Permissions {
|
|||
struct Project : Serializable {
|
||||
std::string name;
|
||||
std::string title;
|
||||
io::path path;
|
||||
std::vector<std::string> basePacks;
|
||||
std::unique_ptr<scripting::IClientProjectScript> clientScript;
|
||||
std::unique_ptr<Process> setupCoroutine;
|
||||
Permissions permissions;
|
||||
|
||||
~Project();
|
||||
|
||||
dv::value serialize() const override;
|
||||
void deserialize(const dv::value& src) override;
|
||||
|
||||
void loadProjectClientScript();
|
||||
void loadProjectStartScript();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,9 @@ struct CoreParameters {
|
|||
std::filesystem::path userFolder = ".";
|
||||
std::filesystem::path scriptFile;
|
||||
std::filesystem::path projectFolder;
|
||||
std::filesystem::path logFile;
|
||||
std::string debugServerString;
|
||||
int tps = 20;
|
||||
int subProcessDepth = 0;
|
||||
std::unordered_map<std::string, std::string> projectArgs;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include "content/ContentControl.hpp"
|
||||
#include "core_defs.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "devtools/AppScriptsControl.hpp"
|
||||
#include "devtools/DebuggingServer.hpp"
|
||||
#include "devtools/Editor.hpp"
|
||||
#include "devtools/Project.hpp"
|
||||
|
|
@ -191,10 +192,8 @@ void Engine::initialize(CoreParameters coreParameters) {
|
|||
audio::set_input_device(name == "auto" ? "" : name);
|
||||
}));
|
||||
|
||||
project->loadProjectStartScript();
|
||||
if (!params.headless) {
|
||||
project->loadProjectClientScript();
|
||||
}
|
||||
appScripts = std::make_unique<AppScriptsControl>(params);
|
||||
|
||||
if (params.stdinCommands) {
|
||||
cmd::start_stdin_cmd_reader(*this);
|
||||
}
|
||||
|
|
@ -263,9 +262,7 @@ void Engine::detachDebugger() {
|
|||
}
|
||||
|
||||
void Engine::applicationTick() {
|
||||
if (project->setupCoroutine && project->setupCoroutine->isActive()) {
|
||||
project->setupCoroutine->update();
|
||||
}
|
||||
appScripts->tick();
|
||||
}
|
||||
|
||||
void Engine::updateFrontend() {
|
||||
|
|
@ -353,6 +350,7 @@ void Engine::close() {
|
|||
network.reset();
|
||||
clearKeepedObjects();
|
||||
project.reset();
|
||||
appScripts.reset();
|
||||
scripting::close();
|
||||
logger.info() << "scripting finished";
|
||||
if (!params.headless) {
|
||||
|
|
@ -382,13 +380,14 @@ void Engine::loadAssets() {
|
|||
void Engine::loadProject() {
|
||||
io::path projectFile = "project:project.toml";
|
||||
project = std::make_unique<Project>();
|
||||
project->path = "project:";
|
||||
project->deserialize(io::read_object(projectFile));
|
||||
logger.info() << "loaded project " << util::quote(project->name);
|
||||
}
|
||||
|
||||
void Engine::setScreen(std::shared_ptr<Screen> screen) {
|
||||
if (project->clientScript && this->screen) {
|
||||
project->clientScript->onScreenChange(this->screen->getName(), false);
|
||||
if (this->screen) {
|
||||
appScripts->onScreenChange(this->screen->getName(), false);
|
||||
}
|
||||
// reset audio channels (stop all sources)
|
||||
audio::reset_channel(audio::get_channel_index("regular"));
|
||||
|
|
@ -397,8 +396,8 @@ void Engine::setScreen(std::shared_ptr<Screen> screen) {
|
|||
if (this->screen) {
|
||||
this->screen->onOpen();
|
||||
}
|
||||
if (project->clientScript && this->screen) {
|
||||
project->clientScript->onScreenChange(this->screen->getName(), true);
|
||||
if (this->screen) {
|
||||
appScripts->onScreenChange(this->screen->getName(), true);
|
||||
window->setShouldRefresh();
|
||||
}
|
||||
}
|
||||
|
|
@ -414,6 +413,7 @@ void Engine::onWorldClosed() {
|
|||
}
|
||||
|
||||
void Engine::quit() {
|
||||
logger.info() << "quitSignal set to true";
|
||||
quitSignal = true;
|
||||
if (!isHeadless()) {
|
||||
window->setShouldClose(true);
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
class Assets;
|
||||
class AssetsLoader;
|
||||
class AssetsManagement;
|
||||
class AppScriptsControl;
|
||||
class ContentControl;
|
||||
class EngineController;
|
||||
class EnginePaths;
|
||||
|
|
@ -55,6 +56,7 @@ class Engine : public util::ObjectsKeeper {
|
|||
std::unique_ptr<Project> project;
|
||||
std::unique_ptr<SettingsHandler> settingsHandler;
|
||||
std::unique_ptr<AssetsManagement> assets;
|
||||
std::unique_ptr<AppScriptsControl> appScripts;
|
||||
std::shared_ptr<Screen> screen;
|
||||
std::unique_ptr<ContentControl> content;
|
||||
std::unique_ptr<EngineController> controller;
|
||||
|
|
@ -184,5 +186,9 @@ public:
|
|||
return debuggingServer.get();
|
||||
}
|
||||
|
||||
AppScriptsControl& getAppScripts() {
|
||||
return *appScripts;
|
||||
}
|
||||
|
||||
void detachDebugger();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#include "Mainloop.hpp"
|
||||
|
||||
#include "Engine.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "devtools/AppScriptsControl.hpp"
|
||||
#include "devtools/Project.hpp"
|
||||
#include "frontend/screens/MenuScreen.hpp"
|
||||
#include "Engine.hpp"
|
||||
#include "frontend/screens/LevelScreen.hpp"
|
||||
#include "frontend/screens/MenuScreen.hpp"
|
||||
#include "graphics/ui/elements/Container.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "io/path.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "graphics/ui/GUI.hpp"
|
||||
#include "graphics/ui/elements/Container.hpp"
|
||||
|
||||
static debug::Logger logger("mainloop");
|
||||
|
||||
|
|
@ -19,6 +22,7 @@ void Mainloop::run() {
|
|||
auto& time = engine.getTime();
|
||||
auto& window = engine.getWindow();
|
||||
auto& settings = engine.getSettings();
|
||||
const auto& coreParams = engine.getCoreParameters();
|
||||
|
||||
engine.setLevelConsumer([this](auto level, int64_t localPlayer) {
|
||||
if (level == nullptr) {
|
||||
|
|
@ -35,9 +39,11 @@ void Mainloop::run() {
|
|||
|
||||
logger.info() << "starting menu screen";
|
||||
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||
|
||||
auto& appScripts = engine.getAppScripts();
|
||||
|
||||
logger.info() << "main loop started";
|
||||
while (!window.isShouldClose()){
|
||||
while (!window.isShouldClose() && !engine.isQuitSignal()) {
|
||||
time.update(window.time());
|
||||
engine.applicationTick();
|
||||
engine.updateFrontend();
|
||||
|
|
@ -50,6 +56,10 @@ void Mainloop::run() {
|
|||
settings.display.adaptiveFpsInMenu.get() &&
|
||||
dynamic_cast<const MenuScreen*>(engine.getScreen().get()) != nullptr
|
||||
);
|
||||
if (coreParams.testMode && appScripts.isFinished()) {
|
||||
logger.info() << "test finished";
|
||||
engine.quit();
|
||||
}
|
||||
}
|
||||
logger.info() << "main loop stopped";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
#include "ServerMainloop.hpp"
|
||||
|
||||
#include "Engine.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "devtools/AppScriptsControl.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 "devtools/Project.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
|
|
@ -32,21 +33,12 @@ void ServerMainloop::run() {
|
|||
setLevel(std::move(level));
|
||||
});
|
||||
|
||||
auto process = scripting::start_app_script(
|
||||
"script:" + coreParams.scriptFile.filename().u8string()
|
||||
);
|
||||
|
||||
double targetDelta = 1.0 / static_cast<double>(coreParams.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;
|
||||
}
|
||||
while (!engine.isQuitSignal() && !engine.getAppScripts().isFinished()) {
|
||||
if (coreParams.testMode) {
|
||||
time.step(delta);
|
||||
} else {
|
||||
|
|
@ -55,7 +47,6 @@ void ServerMainloop::run() {
|
|||
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);
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ void ContentGfxCache::refreshVariant(
|
|||
uint8_t variantIndex,
|
||||
const Atlas& atlas
|
||||
) {
|
||||
std::unordered_map<std::string, std::string> optionalTextures;
|
||||
bool denseRender = settings.denseRender.get();
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
std::string tex = variant.textureFaces[side];
|
||||
|
|
@ -43,6 +44,7 @@ void ContentGfxCache::refreshVariant(
|
|||
texOpaque = tex;
|
||||
} else if (variant.culling == CullingMode::OPTIONAL && !denseRender) {
|
||||
tex = texOpaque;
|
||||
optionalTextures[variant.textureFaces[side]] = texOpaque;
|
||||
}
|
||||
size_t index = getRegionIndex(def.rt.id, variantIndex, side, false);
|
||||
sideregions[index] = atlas.get(tex);
|
||||
|
|
@ -56,7 +58,12 @@ void ContentGfxCache::refreshVariant(
|
|||
if (pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
if (auto region = atlas.getIf(mesh.texture.substr(pos+1))) {
|
||||
std::string texture = mesh.texture.substr(pos+1);
|
||||
const auto& found = optionalTextures.find(texture);
|
||||
if (found != optionalTextures.end()) {
|
||||
texture = found->second;
|
||||
}
|
||||
if (auto region = atlas.getIf(texture)) {
|
||||
for (auto& vertex : mesh.vertices) {
|
||||
vertex.uv = region->apply(vertex.uv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@
|
|||
#include "graphics/ui/elements/InventoryView.hpp"
|
||||
#include "graphics/ui/gui_xml.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
static debug::Logger logger("ui-document");
|
||||
|
||||
UiDocument::UiDocument(
|
||||
std::string id,
|
||||
|
|
@ -17,6 +20,19 @@ UiDocument::UiDocument(
|
|||
rebuildIndices();
|
||||
}
|
||||
|
||||
UiDocument::~UiDocument() {
|
||||
try {
|
||||
scripting::on_ui_destroy(*this);
|
||||
} catch (const std::exception& err) {
|
||||
logger.error() << "an error occurred on calling on_destroy event for document '"
|
||||
<< id << "': " << err.what();
|
||||
} catch (...) {
|
||||
logger.error() << "unknown exception caught on calling on_destroy "
|
||||
"event for document '"
|
||||
<< id << "'";
|
||||
}
|
||||
}
|
||||
|
||||
void UiDocument::rebuildIndices() {
|
||||
map.clear();
|
||||
gui::UINode::getIndices(root, map);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ struct UiDocScript {
|
|||
bool onopen : 1;
|
||||
bool onprogress : 1;
|
||||
bool onclose : 1;
|
||||
bool ondestroy : 1;
|
||||
};
|
||||
|
||||
using UINodesMap = std::unordered_map<std::string, std::weak_ptr<gui::UINode>>;
|
||||
|
|
@ -35,6 +36,8 @@ public:
|
|||
scriptenv env
|
||||
);
|
||||
|
||||
~UiDocument();
|
||||
|
||||
void rebuildIndices();
|
||||
void pushIndices(const std::shared_ptr<gui::UINode>& node);
|
||||
|
||||
|
|
|
|||
|
|
@ -563,7 +563,7 @@ void Hud::add(const HudElement& element, const dv::value& argsArray) {
|
|||
args.emplace_back(static_cast<integer_t>(blockPos[i]));
|
||||
}
|
||||
scripting::on_ui_open(
|
||||
element.getDocument(),
|
||||
*element.getDocument(),
|
||||
std::move(args)
|
||||
);
|
||||
}
|
||||
|
|
@ -578,7 +578,7 @@ void Hud::onRemove(const HudElement& element) {
|
|||
if (invview) {
|
||||
inventory = invview->getInventory().get();
|
||||
}
|
||||
scripting::on_ui_close(document, inventory);
|
||||
scripting::on_ui_close(*document, inventory);
|
||||
if (invview) {
|
||||
invview->unbind();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ UiDocument* menus::show(
|
|||
);
|
||||
auto document = documentPtr.get();
|
||||
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->setPage(name);
|
||||
return document;
|
||||
|
|
@ -99,13 +99,13 @@ void menus::show_process_panel(
|
|||
|
||||
auto menu = engine.getGUI().getMenu();
|
||||
menu->reset();
|
||||
auto doc =
|
||||
auto document =
|
||||
menus::show(engine, "process", {util::wstr2str_utf8(langs::get(text))});
|
||||
std::dynamic_pointer_cast<Container>(doc->getRoot())
|
||||
std::dynamic_pointer_cast<Container>(document->getRoot())
|
||||
->listenInterval(0.01f, [=]() {
|
||||
task->update();
|
||||
|
||||
uint tasksDone = task->getWorkDone();
|
||||
scripting::on_ui_progress(doc, tasksDone, initialWork);
|
||||
scripting::on_ui_progress(*document, tasksDone, initialWork);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
#include <GL/glew.h>
|
||||
|
||||
Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat)
|
||||
: Texture(0, width, height)
|
||||
: Texture(0U, width, height, imageFormat)
|
||||
{
|
||||
glGenTextures(1, &id);
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, id);
|
||||
|
|
@ -14,7 +14,6 @@ Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat)
|
|||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexParameterf(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
||||
|
||||
uint format = gl::to_glenum(imageFormat);
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
||||
|
|
@ -37,3 +36,23 @@ void Cubemap::bind() const {
|
|||
void Cubemap::unbind() const {
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
||||
void Cubemap::reload(const ubyte* data, uint width, uint height) {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, id);
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
||||
0,
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
format,
|
||||
GL_UNSIGNED_BYTE,
|
||||
data
|
||||
);
|
||||
}
|
||||
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,5 @@ public:
|
|||
|
||||
virtual void bind() const override;
|
||||
virtual void unbind() const override;
|
||||
virtual void reload(const ubyte* data, uint w, uint h) override;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -74,6 +74,18 @@ DrawContext::~DrawContext() {
|
|||
if (lineWidth != parent->lineWidth) {
|
||||
glLineWidth(parent->lineWidth);
|
||||
}
|
||||
|
||||
for (int i = 0; i < textures.size(); i++) {
|
||||
auto texture = parent->textures[i];
|
||||
if (textures[i] != texture) {
|
||||
glActiveTexture(GL_TEXTURE0 + i);
|
||||
if (texture == nullptr) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
} else {
|
||||
texture->bind();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const glm::uvec2& DrawContext::getViewport() const {
|
||||
|
|
@ -154,3 +166,13 @@ void DrawContext::setLineWidth(float width) {
|
|||
lineWidth = width;
|
||||
glLineWidth(width);
|
||||
}
|
||||
|
||||
void DrawContext::useTexture(int target, const Bindable* texture) {
|
||||
glActiveTexture(GL_TEXTURE0 + target);
|
||||
if (texture == nullptr) {
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
} else {
|
||||
texture->bind();
|
||||
}
|
||||
textures.at(target) = texture;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec4.hpp>
|
||||
|
||||
|
|
@ -23,6 +24,7 @@ class DrawContext {
|
|||
BlendMode blendMode = BlendMode::normal;
|
||||
int scissorsCount = 0;
|
||||
float lineWidth = 1.0f;
|
||||
std::array<const Bindable*, advanced_pipeline::TARGETS_COUNT> textures {};
|
||||
public:
|
||||
DrawContext(
|
||||
const DrawContext* parent,
|
||||
|
|
@ -44,4 +46,5 @@ public:
|
|||
void setBlendMode(BlendMode mode);
|
||||
void setScissors(const glm::vec4& area);
|
||||
void setLineWidth(float width);
|
||||
void useTexture(int target, const Bindable* texture);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@ Font::Font(
|
|||
pages(std::move(pages)),
|
||||
glyphs(std::move(glyphs)),
|
||||
fontFile(std::move(fontFile)) {
|
||||
if (this->fontFile.has_value()) {
|
||||
if (auto fontPtr = this->fontFile->lock()) {
|
||||
monospace = fontPtr->isMonospace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Font::~Font() = default;
|
||||
|
|
@ -34,6 +39,10 @@ int Font::getYOffset() const {
|
|||
return yoffset;
|
||||
}
|
||||
|
||||
bool Font::isMonospace() const {
|
||||
return monospace;
|
||||
}
|
||||
|
||||
int Font::getLineHeight() const {
|
||||
return lineHeight;
|
||||
}
|
||||
|
|
@ -53,7 +62,7 @@ bool Font::isPrintableChar(uint codepoint) const {
|
|||
|
||||
int FontMetrics::calcWidth(std::wstring_view text, size_t offset, size_t length) const {
|
||||
auto font = this->font.has_value() ? this->font->lock() : nullptr;
|
||||
if (font == nullptr) {
|
||||
if (font == nullptr || font->isMonospace()) {
|
||||
return std::min(text.length() - offset, length) * _glyphInterval;
|
||||
}
|
||||
int totalWidth = 0;
|
||||
|
|
@ -153,6 +162,8 @@ static inline void draw_text(
|
|||
const FontStylesScheme* styles,
|
||||
size_t styleMapOffset
|
||||
) {
|
||||
bool is3d = std::is_same<Batch, Batch3D>();
|
||||
|
||||
static FontStylesScheme defStyles {{{}}, {0}};
|
||||
|
||||
if (styles == nullptr) {
|
||||
|
|
@ -185,13 +196,26 @@ static inline void draw_text(
|
|||
float advance = baseAdvance;
|
||||
if (auto glyph = font.getGlyph(c)) {
|
||||
yOffset = glyph->yOffset;
|
||||
advance = glyph->xAdvance;
|
||||
advance = glyph->xAdvance /
|
||||
static_cast<float>(font.getLineHeight()) * 2.0f *
|
||||
baseAdvance;
|
||||
}
|
||||
uint charpage = c >> 8;
|
||||
if (charpage == page){
|
||||
batch.texture(font.getPage(charpage));
|
||||
draw_glyph(
|
||||
batch, pos, glm::vec2(x, y - yOffset / static_cast<float>(font.getLineHeight())), c, right, up, interval, style
|
||||
batch,
|
||||
pos,
|
||||
glm::vec2(
|
||||
x,
|
||||
y - yOffset * (is3d ? -1 : 1) /
|
||||
static_cast<float>(font.getLineHeight())
|
||||
),
|
||||
c,
|
||||
right,
|
||||
up,
|
||||
interval,
|
||||
style
|
||||
);
|
||||
}
|
||||
else if (charpage > page && charpage < next){
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ public:
|
|||
);
|
||||
~Font();
|
||||
|
||||
bool isMonospace() const;
|
||||
int getLineHeight() const;
|
||||
int getYOffset() const;
|
||||
|
||||
|
|
@ -94,6 +95,7 @@ private:
|
|||
int lineHeight;
|
||||
int yoffset;
|
||||
int glyphInterval;
|
||||
bool monospace = true;
|
||||
std::vector<std::unique_ptr<Texture>> pages;
|
||||
std::vector<Glyph> glyphs;
|
||||
std::optional<std::weak_ptr<vector_fonts::FontFile>> fontFile;
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
#include "Framebuffer.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include "Texture.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
static debug::Logger logger("gl-framebuffer");
|
||||
|
||||
Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture)
|
||||
: fbo(fbo), depth(depth), texture(std::move(texture))
|
||||
{
|
||||
: fbo(fbo), depth(depth), texture(std::move(texture)) {
|
||||
if (this->texture) {
|
||||
width = this->texture->getWidth();
|
||||
height = this->texture->getHeight();
|
||||
|
|
@ -18,35 +18,55 @@ Framebuffer::Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture)
|
|||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<Texture> create_texture(int width, int height, int format) {
|
||||
GLuint tex;
|
||||
glGenTextures(1, &tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, nullptr);
|
||||
static std::unique_ptr<Texture> create_texture(
|
||||
int width, int height, bool alpha
|
||||
) {
|
||||
GLenum glformat = alpha ? GL_RGBA : GL_RGB;
|
||||
|
||||
GLuint textureid;
|
||||
glGenTextures(1, &textureid);
|
||||
glBindTexture(GL_TEXTURE_2D, textureid);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D,
|
||||
0,
|
||||
glformat,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
glformat,
|
||||
GL_UNSIGNED_BYTE,
|
||||
nullptr
|
||||
);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
|
||||
return std::make_unique<Texture>(tex, width, height);
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureid, 0
|
||||
);
|
||||
return std::make_unique<Texture>(
|
||||
textureid,
|
||||
width,
|
||||
height,
|
||||
alpha ? ImageFormat::RGBA8888 : ImageFormat::RGB888
|
||||
);
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(uint width, uint height, bool alpha)
|
||||
: width(width), height(height)
|
||||
{
|
||||
Framebuffer::Framebuffer(uint width, uint height, bool alpha)
|
||||
: width(width), height(height) {
|
||||
glGenFramebuffers(1, &fbo);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
format = alpha ? GL_RGBA : GL_RGB;
|
||||
|
||||
// Setup color attachment (texture)
|
||||
texture = create_texture(width, height, format);
|
||||
texture = create_texture(width, height, alpha);
|
||||
|
||||
// Setup depth attachment
|
||||
glGenRenderbuffers(1, &depth);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, depth);
|
||||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
|
||||
glFramebufferRenderbuffer(
|
||||
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth
|
||||
);
|
||||
|
||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||
logger.error() << "framebuffer is not complete!";
|
||||
|
|
@ -60,11 +80,15 @@ Framebuffer::~Framebuffer() {
|
|||
glDeleteRenderbuffers(1, &depth);
|
||||
}
|
||||
|
||||
void Framebuffer::bind() {
|
||||
void Framebuffer::setTexture(std::unique_ptr<Texture> texture) {
|
||||
this->texture = std::move(texture);
|
||||
}
|
||||
|
||||
void Framebuffer::bind() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
}
|
||||
|
||||
void Framebuffer::unbind() {
|
||||
void Framebuffer::unbind() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
|
|
@ -81,7 +105,7 @@ void Framebuffer::resize(uint width, uint height) {
|
|||
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
||||
|
||||
texture = create_texture(width, height, format);
|
||||
texture->resize(width, height);
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
|
|
@ -102,5 +126,5 @@ uint Framebuffer::getHeight() const {
|
|||
}
|
||||
|
||||
uint Framebuffer::getFBO() const {
|
||||
return fbo;
|
||||
return fbo;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,18 +12,19 @@ class Framebuffer : public Bindable {
|
|||
uint depth;
|
||||
uint width;
|
||||
uint height;
|
||||
uint format;
|
||||
std::shared_ptr<Texture> texture;
|
||||
public:
|
||||
Framebuffer(uint fbo, uint depth, std::unique_ptr<Texture> texture);
|
||||
Framebuffer(uint width, uint height, bool alpha=false);
|
||||
~Framebuffer();
|
||||
|
||||
void setTexture(std::unique_ptr<Texture> texture);
|
||||
|
||||
/// @brief Use framebuffer
|
||||
void bind() override;
|
||||
void bind() const override;
|
||||
|
||||
/// @brief Stop using framebuffer
|
||||
void unbind() override;
|
||||
void unbind() const override;
|
||||
|
||||
/// @brief Update framebuffer texture size
|
||||
/// @param width new width
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ GBuffer::~GBuffer() {
|
|||
glDeleteFramebuffers(1, &ssaoFbo);
|
||||
}
|
||||
|
||||
void GBuffer::bind() {
|
||||
void GBuffer::bind() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ void GBuffer::bindSSAO() const {
|
|||
glBindFramebuffer(GL_FRAMEBUFFER, ssaoFbo);
|
||||
}
|
||||
|
||||
void GBuffer::unbind() {
|
||||
void GBuffer::unbind() const {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,9 +9,9 @@ public:
|
|||
GBuffer(uint width, uint height);
|
||||
~GBuffer() override;
|
||||
|
||||
void bind() override;
|
||||
void bind() const override;
|
||||
void bindSSAO() const;
|
||||
void unbind() override;
|
||||
void unbind() const override;
|
||||
|
||||
void bindBuffers() const;
|
||||
void bindSSAOBuffer() const;
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
|
||||
uint Texture::MAX_RESOLUTION = 1024; // Window.initialize overrides it
|
||||
|
||||
Texture::Texture(uint id, uint width, uint height)
|
||||
: id(id), width(width), height(height) {
|
||||
Texture::Texture(uint id, uint width, uint height, ImageFormat imageFormat)
|
||||
: id(id), width(width), height(height), format(gl::to_glenum(imageFormat)) {
|
||||
}
|
||||
|
||||
Texture::Texture(const ubyte* data, uint width, uint height, ImageFormat imageFormat)
|
||||
|
|
@ -17,7 +17,7 @@ Texture::Texture(const ubyte* data, uint width, uint height, ImageFormat imageFo
|
|||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||
|
||||
GLenum format = gl::to_glenum(imageFormat);
|
||||
format = gl::to_glenum(imageFormat);
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_2D, 0, format, width, height, 0,
|
||||
format, GL_UNSIGNED_BYTE, static_cast<const GLvoid*>(data)
|
||||
|
|
@ -41,23 +41,27 @@ void Texture::unbind() const {
|
|||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture::resize(uint width, uint height) {
|
||||
reload(nullptr, width, height);
|
||||
}
|
||||
|
||||
void Texture::reload(const ImageData& image) {
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
reload(image.getData(), width, height);
|
||||
}
|
||||
|
||||
void Texture::reload(const ubyte* data, uint width, uint height) {
|
||||
this->width = width;
|
||||
this->height = height;
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, static_cast<const GLvoid*>(data));
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0,
|
||||
format, GL_UNSIGNED_BYTE, static_cast<const GLvoid*>(data));
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
void Texture::reloadPartial(const ImageData& image, uint x, uint y, uint w, uint h) {
|
||||
glBindTexture(GL_TEXTURE_2D, id);
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, image.getData());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, format, GL_UNSIGNED_BYTE, image.getData());
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "maths/UVRegion.hpp"
|
||||
#include "ImageData.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Texture {
|
||||
#include "ImageData.hpp"
|
||||
#include "commons.hpp"
|
||||
#include "maths/UVRegion.hpp"
|
||||
#include "typedefs.hpp"
|
||||
|
||||
class Texture : public Bindable {
|
||||
protected:
|
||||
uint id;
|
||||
uint width;
|
||||
uint height;
|
||||
uint format;
|
||||
public:
|
||||
Texture(uint id, uint width, uint height);
|
||||
Texture(uint id, uint width, uint height, ImageFormat imageFormat);
|
||||
Texture(const ubyte* data, uint width, uint height, ImageFormat format);
|
||||
virtual ~Texture();
|
||||
|
||||
virtual void bind() const;
|
||||
virtual void unbind() const;
|
||||
void reload(const ubyte* data, uint w, uint h);
|
||||
virtual void bind() const override;
|
||||
virtual void unbind() const override;
|
||||
virtual void reload(const ubyte* data, uint w, uint h);
|
||||
void reloadPartial(const ImageData& image, uint x, uint y, uint w, uint h);
|
||||
|
||||
void setNearestFilter();
|
||||
|
||||
void reload(const ImageData& image);
|
||||
virtual void resize(uint w, uint h);
|
||||
|
||||
void setMipMapping(bool flag, bool pixelated);
|
||||
|
||||
|
|
|
|||
|
|
@ -59,6 +59,8 @@ namespace advanced_pipeline {
|
|||
inline constexpr int TARGET_SSAO = 5;
|
||||
inline constexpr int TARGET_SHADOWS0 = 6;
|
||||
inline constexpr int TARGET_SHADOWS1 = 7;
|
||||
|
||||
inline constexpr int TARGETS_COUNT = 8;
|
||||
}
|
||||
|
||||
VC_ENUM_METADATA(CursorShape)
|
||||
|
|
@ -85,6 +87,6 @@ class Bindable {
|
|||
public:
|
||||
virtual ~Bindable() = default;
|
||||
|
||||
virtual void bind() = 0;
|
||||
virtual void unbind() = 0;
|
||||
virtual void bind() const = 0;
|
||||
virtual void unbind() const = 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -538,7 +538,6 @@ void BlocksRenderer::render(
|
|||
break;
|
||||
}
|
||||
case BlockModelType::CUSTOM: {
|
||||
if (!denseRender)
|
||||
blockCustomModel(
|
||||
{x, y, z},
|
||||
def,
|
||||
|
|
|
|||
|
|
@ -34,4 +34,7 @@ void HandsRenderer::render(const Camera& camera) {
|
|||
glm::vec3(),
|
||||
glm::vec3(1.0f)
|
||||
);
|
||||
|
||||
modelBatch.render();
|
||||
modelBatch.setLightsOffset(glm::vec3());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,91 +1,64 @@
|
|||
#include "Skybox.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Mesh.hpp"
|
||||
#include "graphics/core/Batch3D.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/Cubemap.hpp"
|
||||
#include "graphics/core/Framebuffer.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "window/Window.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "maths/UVRegion.hpp"
|
||||
|
||||
#include <GL/glew.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <iostream>
|
||||
#include <GL/glew.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <limits>
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "graphics/core/Batch3D.hpp"
|
||||
#include "graphics/core/Cubemap.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/Framebuffer.hpp"
|
||||
#include "graphics/core/Mesh.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "maths/UVRegion.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "window/Window.hpp"
|
||||
|
||||
using namespace advanced_pipeline;
|
||||
|
||||
const int STARS_COUNT = 3000;
|
||||
const int STARS_SEED = 632;
|
||||
|
||||
Skybox::Skybox(uint size, Shader& shader)
|
||||
: size(size),
|
||||
shader(shader),
|
||||
batch3d(std::make_unique<Batch3D>(4096))
|
||||
{
|
||||
auto cubemap = std::make_unique<Cubemap>(size, size, ImageFormat::RGB888);
|
||||
Skybox::Skybox(uint size, const Assets& assets)
|
||||
: size(size),
|
||||
assets(assets),
|
||||
shader(assets.require<Shader>("skybox_gen")),
|
||||
batch3d(std::make_unique<Batch3D>(4096)) {
|
||||
|
||||
uint fboid;
|
||||
glGenFramebuffers(1, &fboid);
|
||||
fbo = std::make_unique<Framebuffer>(fboid, 0, std::move(cubemap));
|
||||
|
||||
SkyboxVertex vertices[]{
|
||||
SkyboxVertex vertices[] {
|
||||
{{-1.0f, -1.0f}},
|
||||
{{-1.0f, 1.0f}},
|
||||
{{1.0f, 1.0f}},
|
||||
{{-1.0f, -1.0f}},
|
||||
{{1.0f, 1.0f}},
|
||||
{{1.0f, -1.0f}}
|
||||
};
|
||||
{{1.0f, -1.0f}}};
|
||||
|
||||
mesh = std::make_unique<Mesh<SkyboxVertex>>(vertices, 6);
|
||||
|
||||
sprites.push_back(SkySprite {
|
||||
"misc/moon",
|
||||
glm::pi<float>() * 0.5f,
|
||||
4.0f,
|
||||
false,
|
||||
glm::pi<float>() * 0.25f,
|
||||
});
|
||||
|
||||
sprites.push_back(SkySprite {
|
||||
"misc/moon_flare",
|
||||
glm::pi<float>() * 0.5f,
|
||||
0.5f,
|
||||
false,
|
||||
glm::pi<float>() * 0.25f,
|
||||
});
|
||||
|
||||
sprites.push_back(SkySprite {
|
||||
"misc/sun",
|
||||
glm::pi<float>() * 1.5f,
|
||||
4.0f,
|
||||
true,
|
||||
glm::pi<float>() * 0.25f,
|
||||
});
|
||||
setMode(mode);
|
||||
}
|
||||
|
||||
Skybox::~Skybox() = default;
|
||||
|
||||
void Skybox::drawBackground(
|
||||
const Camera& camera, const Assets& assets, int width, int height
|
||||
) {
|
||||
void Skybox::drawBackground(const Camera& camera, int width, int height) {
|
||||
auto backShader = assets.get<Shader>("background");
|
||||
backShader->use();
|
||||
backShader->uniformMatrix("u_view", camera.getView(false));
|
||||
backShader->uniform1f(
|
||||
"u_zoom", camera.zoom * camera.getFov() / glm::half_pi<float>()
|
||||
);
|
||||
backShader->uniform1f("u_ar", float(width)/float(height));
|
||||
backShader->uniform1f("u_ar", float(width) / float(height));
|
||||
backShader->uniform1i("u_skybox", 1);
|
||||
bind();
|
||||
|
||||
auto texture = fbo->getTexture();
|
||||
texture->bind();
|
||||
mesh->draw();
|
||||
unbind();
|
||||
texture->unbind();
|
||||
}
|
||||
|
||||
void Skybox::drawStars(float angle, float opacity) {
|
||||
|
|
@ -93,9 +66,7 @@ void Skybox::drawStars(float angle, float opacity) {
|
|||
random.setSeed(STARS_SEED);
|
||||
|
||||
glm::mat4 rotation = glm::rotate(
|
||||
glm::mat4(1.0f),
|
||||
-angle + glm::pi<float>() * 0.5f,
|
||||
glm::vec3(0, 0, -1)
|
||||
glm::mat4(1.0f), -angle + glm::pi<float>() * 0.5f, glm::vec3(0, 0, -1)
|
||||
);
|
||||
rotation = glm::rotate(rotation, sunAltitude, glm::vec3(1, 0, 0));
|
||||
|
||||
|
|
@ -108,41 +79,21 @@ void Skybox::drawStars(float angle, float opacity) {
|
|||
glm::vec3 pos = glm::vec4(rx, ry, rz, 1) * rotation;
|
||||
|
||||
float sopacity = random.randFloat();
|
||||
if (pos.y < 0.0f)
|
||||
continue;
|
||||
if (pos.y < 0.0f) continue;
|
||||
|
||||
sopacity *= (0.2f + std::sqrt(std::cos(angle)) * 0.5f) - 0.05f;
|
||||
glm::vec4 tint (1,1,1, sopacity * opacity);
|
||||
glm::vec4 tint(1, 1, 1, sopacity * opacity);
|
||||
batch3d->point(pos * depth, tint);
|
||||
}
|
||||
batch3d->flushPoints();
|
||||
}
|
||||
|
||||
void Skybox::draw(
|
||||
const DrawContext& pctx,
|
||||
const Camera& camera,
|
||||
const Assets& assets,
|
||||
void Skybox::drawSkySprites(
|
||||
float daytime,
|
||||
float fog)
|
||||
{
|
||||
const glm::uvec2& viewport = pctx.getViewport();
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
drawBackground(camera, assets, viewport.x, viewport.y);
|
||||
|
||||
DrawContext ctx = pctx.sub();
|
||||
ctx.setBlendMode(BlendMode::addition);
|
||||
|
||||
auto shader = assets.get<Shader>("ui3d");
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_projview", camera.getProjView(false));
|
||||
shader->uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
batch3d->begin();
|
||||
|
||||
float angle = daytime * glm::pi<float>() * 2.0f;
|
||||
float opacity = glm::pow(1.0f - fog, 7.0f);
|
||||
|
||||
float angle,
|
||||
float opacity,
|
||||
const std::vector<SkySprite>& sprites
|
||||
) {
|
||||
float depthScale = 2e3;
|
||||
for (auto& sprite : sprites) {
|
||||
batch3d->texture(assets.get<Texture>(sprite.texture));
|
||||
|
|
@ -159,39 +110,124 @@ void Skybox::draw(
|
|||
glm::vec3 pos = glm::vec4(0, distance, 0, 1) * rotation;
|
||||
glm::vec3 up = glm::vec4(depthScale, 0, 0, 1) * rotation;
|
||||
glm::vec3 right = glm::vec4(0, 0, depthScale, 1) * rotation;
|
||||
glm::vec4 tint (1,1,1, opacity);
|
||||
glm::vec4 tint(1, 1, 1, opacity);
|
||||
if (!sprite.emissive) {
|
||||
tint *= 0.6f + std::cos(angle)*0.4;
|
||||
tint *= 0.6f + std::cos(angle) * 0.4;
|
||||
}
|
||||
batch3d->sprite(pos, right,
|
||||
up, 1, 1, UVRegion(), tint);
|
||||
batch3d->sprite(pos, right, up, 1, 1, UVRegion(), tint);
|
||||
}
|
||||
batch3d->flush();
|
||||
drawStars(angle, opacity);
|
||||
}
|
||||
|
||||
void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality) {
|
||||
void Skybox::draw(
|
||||
const Environment& environment,
|
||||
const DrawContext& pctx,
|
||||
const Camera& camera,
|
||||
float daytime,
|
||||
float fog
|
||||
) {
|
||||
const glm::uvec2& viewport = pctx.getViewport();
|
||||
drawBackground(camera, viewport.x, viewport.y);
|
||||
|
||||
float angle = daytime * glm::pi<float>() * 2.0f;
|
||||
float opacity = glm::pow(1.0f - fog, 7.0f);
|
||||
|
||||
DrawContext ctx = pctx.sub();
|
||||
ctx.setBlendMode(BlendMode::addition);
|
||||
|
||||
if (environment.sky.sprites.empty() && !environment.sky.stars) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto shader = assets.get<Shader>("ui3d");
|
||||
shader->use();
|
||||
shader->uniformMatrix("u_projview", camera.getProjView(false));
|
||||
shader->uniformMatrix("u_apply", glm::mat4(1.0f));
|
||||
batch3d->begin();
|
||||
|
||||
if (!environment.sky.sprites.empty()) {
|
||||
drawSkySprites(daytime, angle, opacity, environment.sky.sprites);
|
||||
}
|
||||
if (environment.sky.stars) {
|
||||
drawStars(angle, opacity);
|
||||
}
|
||||
}
|
||||
|
||||
void Skybox::setMode(SkyMode mode) {
|
||||
if (this->mode == mode && fbo) {
|
||||
return;
|
||||
}
|
||||
this->mode = mode;
|
||||
|
||||
uint size = mode == SkyMode::BOX ? this->size : 1U;
|
||||
prevT = std::numeric_limits<float>().min();
|
||||
|
||||
uint fboid;
|
||||
glGenFramebuffers(1, &fboid);
|
||||
fbo = std::make_unique<Framebuffer>(
|
||||
fboid,
|
||||
false,
|
||||
std::make_unique<Cubemap>(size, size, ImageFormat::RGB888)
|
||||
);
|
||||
}
|
||||
|
||||
void Skybox::refresh(
|
||||
const Environment& environment,
|
||||
const DrawContext& pctx,
|
||||
float dayTime,
|
||||
float mie,
|
||||
const glm::vec3& tint,
|
||||
const glm::vec3& hightlight,
|
||||
uint quality
|
||||
) {
|
||||
setMode(environment.sky.mode);
|
||||
|
||||
if (mode == SkyMode::NONE) {
|
||||
return;
|
||||
} else if (mode == SkyMode::SOLID) {
|
||||
DrawContext ctx = pctx.sub();
|
||||
ctx.setDepthMask(false);
|
||||
ctx.setDepthTest(false);
|
||||
ctx.setFramebuffer(fbo.get());
|
||||
ctx.setViewport({fbo->getWidth(), fbo->getHeight()});
|
||||
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, fbo->getTexture());
|
||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||
|
||||
auto cubemap = dynamic_cast<Cubemap*>(fbo->getTexture());
|
||||
assert(cubemap != nullptr);
|
||||
|
||||
for (int face = 0; face < 6; face++) {
|
||||
glFramebufferTexture2D(
|
||||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
||||
cubemap->getId(),
|
||||
0
|
||||
);
|
||||
glClearColor(tint.r, tint.g, tint.b, 1);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
frameid++;
|
||||
float dayTime = t;
|
||||
DrawContext ctx = pctx.sub();
|
||||
ctx.setDepthMask(false);
|
||||
ctx.setDepthTest(false);
|
||||
ctx.setFramebuffer(fbo.get());
|
||||
ctx.setViewport({size, size});
|
||||
ctx.setViewport({fbo->getWidth(), fbo->getHeight()});
|
||||
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, fbo->getTexture());
|
||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||
|
||||
auto cubemap = dynamic_cast<Cubemap*>(fbo->getTexture());
|
||||
assert(cubemap != nullptr);
|
||||
|
||||
ready = true;
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX);
|
||||
cubemap->bind();
|
||||
shader.use();
|
||||
t *= glm::two_pi<float>();
|
||||
|
||||
float t = dayTime * glm::two_pi<float>();
|
||||
lightDir = glm::normalize(glm::vec3(sin(t), -cos(t), 0.0f));
|
||||
|
||||
float sunAngle = glm::radians((t / glm::two_pi<float>() - 0.25f) * 360.0f);
|
||||
float x = -glm::cos(sunAngle + glm::pi<float>() * 0.5f) * glm::radians(sunAltitude);
|
||||
float sunAngle = glm::radians((dayTime - 0.25f) * 360.0f);
|
||||
float x = -glm::cos(sunAngle + glm::pi<float>() * 0.5f) *
|
||||
glm::radians(sunAltitude);
|
||||
float y = sunAngle - glm::pi<float>() * 0.5f;
|
||||
float z = glm::radians(0.0f);
|
||||
rotation = glm::rotate(glm::mat4(1.0f), y, glm::vec3(0, 1, 0));
|
||||
|
|
@ -199,6 +235,7 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec
|
|||
rotation = glm::rotate(rotation, z, glm::vec3(0, 0, 1));
|
||||
lightDir = glm::vec3(rotation * glm::vec4(0, 0, -1, 1));
|
||||
|
||||
shader.use();
|
||||
shader.uniform1i("u_quality", quality);
|
||||
shader.uniform1f("u_mie", mie);
|
||||
shader.uniform3f("u_tint", tint);
|
||||
|
|
@ -207,23 +244,22 @@ void Skybox::refresh(const DrawContext& pctx, float t, float mie, const glm::vec
|
|||
shader.uniform3f("u_lightDir", lightDir);
|
||||
shader.uniform1f("u_dayTime", dayTime);
|
||||
|
||||
if (glm::abs(mie-prevMie) + glm::abs(t-prevT) + glm::abs(prevHighlight.r - hightlight.r) >= 0.01) {
|
||||
if (glm::abs(mie - prevMie) + glm::abs(t - prevT) +
|
||||
glm::abs(prevHighlight.r - hightlight.r) >=
|
||||
0.01) {
|
||||
for (uint face = 0; face < 6; face++) {
|
||||
refreshFace(face, cubemap);
|
||||
refreshFace(face, *cubemap);
|
||||
}
|
||||
} else {
|
||||
uint face = frameid % 6;
|
||||
refreshFace(face, cubemap);
|
||||
refreshFace(face, *cubemap);
|
||||
}
|
||||
prevMie = mie;
|
||||
prevT = t;
|
||||
prevHighlight = hightlight;
|
||||
|
||||
cubemap->unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void Skybox::refreshFace(uint face, Cubemap* cubemap) {
|
||||
void Skybox::refreshFace(uint face, Cubemap& cubemap) {
|
||||
const glm::vec3 xaxs[] = {
|
||||
{0.0f, 0.0f, -1.0f},
|
||||
{0.0f, 0.0f, 1.0f},
|
||||
|
|
@ -256,7 +292,7 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) {
|
|||
GL_FRAMEBUFFER,
|
||||
GL_COLOR_ATTACHMENT0,
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + face,
|
||||
cubemap->getId(),
|
||||
cubemap.getId(),
|
||||
0
|
||||
);
|
||||
shader.uniform3f("u_xaxis", xaxs[face]);
|
||||
|
|
@ -265,14 +301,6 @@ void Skybox::refreshFace(uint face, Cubemap* cubemap) {
|
|||
mesh->draw();
|
||||
}
|
||||
|
||||
void Skybox::bind() const {
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX);
|
||||
fbo->getTexture()->bind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
void Skybox::unbind() const {
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SKYBOX);
|
||||
fbo->getTexture()->unbind();
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
const Cubemap* Skybox::getCubemap() const {
|
||||
return dynamic_cast<const Cubemap*>(fbo->getTexture());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <glm/glm.hpp>
|
||||
#include "typedefs.hpp"
|
||||
#include "maths/fastmaths.hpp"
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
|
||||
template<typename VertexStructure> class Mesh;
|
||||
#include "graphics/core/MeshData.hpp"
|
||||
#include "maths/fastmaths.hpp"
|
||||
#include "typedefs.hpp"
|
||||
#include "world/Environment.hpp"
|
||||
|
||||
template <typename VertexStructure>
|
||||
class Mesh;
|
||||
class Shader;
|
||||
class Assets;
|
||||
class Camera;
|
||||
|
|
@ -21,29 +24,20 @@ struct SkyboxVertex {
|
|||
glm::vec2 position;
|
||||
|
||||
static constexpr VertexAttribute ATTRIBUTES[] {
|
||||
{VertexAttribute::Type::FLOAT, false, 2},
|
||||
{{}, 0}};
|
||||
};
|
||||
|
||||
struct SkySprite {
|
||||
std::string texture;
|
||||
float phase;
|
||||
float distance;
|
||||
bool emissive;
|
||||
float altitude;
|
||||
{VertexAttribute::Type::FLOAT, false, 2}, {{}, 0}};
|
||||
};
|
||||
|
||||
class Skybox {
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
SkyMode mode = SkyMode::SOLID;
|
||||
uint size;
|
||||
std::unique_ptr<Framebuffer> fbo;
|
||||
const Assets& assets;
|
||||
Shader& shader;
|
||||
bool ready = false;
|
||||
FastRandom random;
|
||||
glm::vec3 lightDir;
|
||||
|
||||
std::unique_ptr<Mesh<SkyboxVertex>> mesh;
|
||||
std::unique_ptr<Batch3D> batch3d;
|
||||
std::vector<SkySprite> sprites;
|
||||
int frameid = 0;
|
||||
|
||||
float prevMie = -1.0f;
|
||||
|
|
@ -53,30 +47,41 @@ class Skybox {
|
|||
glm::mat4 rotation;
|
||||
|
||||
void drawStars(float angle, float opacity);
|
||||
void drawBackground(
|
||||
const Camera& camera, const Assets& assets, int width, int height
|
||||
void drawBackground(const Camera& camera, int width, int height);
|
||||
void drawSkySprites(
|
||||
float daytime,
|
||||
float angle,
|
||||
float opacity,
|
||||
const std::vector<SkySprite>& sprites
|
||||
);
|
||||
void refreshFace(uint face, Cubemap* cubemap);
|
||||
void refreshFace(uint face, Cubemap& cubemap);
|
||||
public:
|
||||
Skybox(uint size, Shader& shader);
|
||||
Skybox(uint size, const Assets& assets);
|
||||
~Skybox();
|
||||
|
||||
void setMode(SkyMode mode);
|
||||
|
||||
void draw(
|
||||
const DrawContext& pctx,
|
||||
const Camera& camera,
|
||||
const Assets& assets,
|
||||
const Environment& environment,
|
||||
const DrawContext& pctx,
|
||||
const Camera& camera,
|
||||
float daytime,
|
||||
float fog
|
||||
);
|
||||
|
||||
void refresh(const DrawContext& pctx, float t, float mie, const glm::vec3& tint, const glm::vec3& hightlight, uint quality);
|
||||
void bind() const;
|
||||
void unbind() const;
|
||||
bool isReady() const {
|
||||
return ready;
|
||||
}
|
||||
void refresh(
|
||||
const Environment& environment,
|
||||
const DrawContext& pctx,
|
||||
float t,
|
||||
float mie,
|
||||
const glm::vec3& tint,
|
||||
const glm::vec3& hightlight,
|
||||
uint quality
|
||||
);
|
||||
|
||||
const glm::vec3 getLightDir() const {
|
||||
const Cubemap* getCubemap() const;
|
||||
|
||||
const glm::vec3& getLightDir() const {
|
||||
return lightDir;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -58,7 +58,10 @@ void TextsRenderer::renderNote(
|
|||
}
|
||||
opacity = preset.xrayOpacity;
|
||||
}
|
||||
auto& font = assets.require<Font>(FONT_DEFAULT);
|
||||
|
||||
auto specifiedFont = assets.get<Font>(preset.font);
|
||||
auto& font =
|
||||
specifiedFont ? *specifiedFont : assets.require<Font>(FONT_DEFAULT);
|
||||
|
||||
glm::vec3 xvec = note.getAxisX();
|
||||
glm::vec3 yvec = note.getAxisY();
|
||||
|
|
@ -76,7 +79,10 @@ void TextsRenderer::renderNote(
|
|||
yvec = camera.up;
|
||||
}
|
||||
float scale =
|
||||
(1.0f - preset.perspective) * glm::pow(glm::distance(camera.position, pos), 1.0f-preset.perspective);
|
||||
(1.0f - preset.perspective) *
|
||||
glm::pow(
|
||||
glm::distance(camera.position, pos), 1.0f - preset.perspective
|
||||
);
|
||||
xvec *= 1.0f + scale;
|
||||
yvec *= 1.0f + scale;
|
||||
}
|
||||
|
|
@ -118,6 +124,7 @@ void TextsRenderer::renderNote(
|
|||
}
|
||||
auto color = preset.color;
|
||||
batch.setColor(glm::vec4(color.r, color.g, color.b, color.a * opacity));
|
||||
|
||||
font.draw(
|
||||
batch,
|
||||
text,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,39 @@
|
|||
#include "WorldRenderer.hpp"
|
||||
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
#include "CloudsRenderer.hpp"
|
||||
#include "DebugLinesRenderer.hpp"
|
||||
#include "Emitter.hpp"
|
||||
#include "HandsRenderer.hpp"
|
||||
#include "ModelBatch.hpp"
|
||||
#include "NamedSkeletons.hpp"
|
||||
#include "ParticlesRenderer.hpp"
|
||||
#include "PrecipitationRenderer.hpp"
|
||||
#include "Skybox.hpp"
|
||||
#include "TextNote.hpp"
|
||||
#include "TextsRenderer.hpp"
|
||||
#include "assets/Assets.hpp"
|
||||
#include "assets/assets_util.hpp"
|
||||
#include "coders/GLSLExtension.hpp"
|
||||
#include "content/Content.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "coders/GLSLExtension.hpp"
|
||||
#include "frontend/LevelFrontend.hpp"
|
||||
#include "frontend/ContentGfxCache.hpp"
|
||||
#include "frontend/LevelFrontend.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Batch3D.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "graphics/core/Framebuffer.hpp"
|
||||
#include "graphics/core/GBuffer.hpp"
|
||||
#include "graphics/core/LineBatch.hpp"
|
||||
#include "graphics/core/Mesh.hpp"
|
||||
#include "graphics/core/PostProcessing.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Shadows.hpp"
|
||||
#include "graphics/core/Cubemap.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "items/Inventory.hpp"
|
||||
#include "items/ItemDef.hpp"
|
||||
#include "items/ItemStack.hpp"
|
||||
|
|
@ -25,32 +52,6 @@
|
|||
#include "world/Level.hpp"
|
||||
#include "world/LevelEvents.hpp"
|
||||
#include "world/World.hpp"
|
||||
#include "graphics/commons/Model.hpp"
|
||||
#include "graphics/core/Atlas.hpp"
|
||||
#include "graphics/core/Batch3D.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
#include "graphics/core/LineBatch.hpp"
|
||||
#include "graphics/core/Mesh.hpp"
|
||||
#include "graphics/core/PostProcessing.hpp"
|
||||
#include "graphics/core/Framebuffer.hpp"
|
||||
#include "graphics/core/Shader.hpp"
|
||||
#include "graphics/core/Texture.hpp"
|
||||
#include "graphics/core/Font.hpp"
|
||||
#include "graphics/core/Shadows.hpp"
|
||||
#include "graphics/core/GBuffer.hpp"
|
||||
#include "BlockWrapsRenderer.hpp"
|
||||
#include "ParticlesRenderer.hpp"
|
||||
#include "PrecipitationRenderer.hpp"
|
||||
#include "HandsRenderer.hpp"
|
||||
#include "NamedSkeletons.hpp"
|
||||
#include "TextsRenderer.hpp"
|
||||
#include "ChunksRenderer.hpp"
|
||||
#include "DebugLinesRenderer.hpp"
|
||||
#include "ModelBatch.hpp"
|
||||
#include "Skybox.hpp"
|
||||
#include "Emitter.hpp"
|
||||
#include "TextNote.hpp"
|
||||
#include "CloudsRenderer.hpp"
|
||||
|
||||
#include <assert.h>
|
||||
#include <algorithm>
|
||||
|
|
@ -58,8 +59,6 @@
|
|||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <memory>
|
||||
|
||||
using namespace advanced_pipeline;
|
||||
|
||||
inline constexpr size_t BATCH3D_CAPACITY = 4096;
|
||||
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
|
||||
|
||||
|
|
@ -105,8 +104,7 @@ WorldRenderer::WorldRenderer(
|
|||
[this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); }
|
||||
);
|
||||
skybox = std::make_unique<Skybox>(
|
||||
settings.graphics.skyboxResolution.get(),
|
||||
assets.require<Shader>("skybox_gen")
|
||||
settings.graphics.skyboxResolution.get(), assets
|
||||
);
|
||||
|
||||
const auto& content = level.content;
|
||||
|
|
@ -160,7 +158,7 @@ void WorldRenderer::setupWorldShader(
|
|||
shader.uniform1i("u_debugNormals", false);
|
||||
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
|
||||
shader.uniform2f("u_lightDir", skybox->getLightDir());
|
||||
shader.uniform1i("u_skybox", TARGET_SKYBOX);
|
||||
shader.uniform1i("u_skybox", advanced_pipeline::TARGET_SKYBOX);
|
||||
|
||||
auto indices = level.content.getIndices();
|
||||
// Light emission when an emissive item is chosen
|
||||
|
|
@ -179,6 +177,11 @@ void WorldRenderer::setupWorldShader(
|
|||
}
|
||||
}
|
||||
|
||||
float WorldRenderer::calcFogFactor() const {
|
||||
const auto& settings = engine.getSettings();
|
||||
return 15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
|
||||
}
|
||||
|
||||
void WorldRenderer::renderOpaque(
|
||||
const DrawContext& ctx,
|
||||
const Camera& camera,
|
||||
|
|
@ -187,14 +190,12 @@ void WorldRenderer::renderOpaque(
|
|||
) {
|
||||
texts->render(ctx, camera, settings, hudVisible, false);
|
||||
|
||||
bool culling = engine.getSettings().graphics.frustumCulling.get();
|
||||
float fogFactor =
|
||||
15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
|
||||
float fogFactor = calcFogFactor();
|
||||
|
||||
auto& entityShader = assets.require<Shader>("entity");
|
||||
setupWorldShader(entityShader, camera, settings, fogFactor);
|
||||
skybox->bind();
|
||||
|
||||
bool culling = engine.getSettings().graphics.frustumCulling.get();
|
||||
if (culling) {
|
||||
frustumCulling->update(camera.getProjView());
|
||||
}
|
||||
|
|
@ -213,25 +214,26 @@ void WorldRenderer::renderOpaque(
|
|||
|
||||
auto& shader = assets.require<Shader>("main");
|
||||
auto& cloudsShader = assets.require<Shader>("clouds");
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
|
||||
setupWorldShader(shader, camera, settings, fogFactor);
|
||||
|
||||
chunksRenderer->drawChunks(camera, shader);
|
||||
blockWraps->draw(ctx);
|
||||
|
||||
int cloudsQuality = settings.graphics.cloudsQuality.get();
|
||||
if (cloudsQuality > 0) {
|
||||
setupWorldShader(cloudsShader, camera, settings, fogFactor);
|
||||
cloudsRenderer->draw(
|
||||
cloudsShader, weather, timer, fogFactor, camera, cloudsQuality
|
||||
);
|
||||
if (level.environment.sky.clouds) {
|
||||
int cloudsQuality = settings.graphics.cloudsQuality.get();
|
||||
if (cloudsQuality > 0) {
|
||||
setupWorldShader(cloudsShader, camera, settings, fogFactor);
|
||||
cloudsRenderer->draw(
|
||||
cloudsShader, weather, timer, fogFactor, camera, cloudsQuality
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (hudVisible) {
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
renderLines(camera, linesShader, ctx);
|
||||
}
|
||||
skybox->unbind();
|
||||
}
|
||||
|
||||
void WorldRenderer::renderBlockSelection() {
|
||||
|
|
@ -287,21 +289,19 @@ void WorldRenderer::refreshSettings(Shader** shaders) {
|
|||
|
||||
int shadowsQuality = graphics.shadowsQuality.get() * gbufferPipeline;
|
||||
shadowMapping->setQuality(shadowsQuality);
|
||||
|
||||
|
||||
CompileTimeShaderSettings currentSettings {
|
||||
gbufferPipeline,
|
||||
shadowsQuality != 0,
|
||||
graphics.ssao.get() && gbufferPipeline
|
||||
};
|
||||
if (
|
||||
prevCTShaderSettings.advancedRender != currentSettings.advancedRender ||
|
||||
graphics.ssao.get() && gbufferPipeline};
|
||||
if (prevCTShaderSettings.advancedRender != currentSettings.advancedRender ||
|
||||
prevCTShaderSettings.shadows != currentSettings.shadows ||
|
||||
prevCTShaderSettings.ssao != currentSettings.ssao
|
||||
) {
|
||||
prevCTShaderSettings.ssao != currentSettings.ssao) {
|
||||
std::vector<std::string> defines;
|
||||
if (currentSettings.shadows) defines.emplace_back("ENABLE_SHADOWS");
|
||||
if (currentSettings.ssao) defines.emplace_back("ENABLE_SSAO");
|
||||
if (currentSettings.advancedRender) defines.emplace_back("ADVANCED_RENDER");
|
||||
if (currentSettings.advancedRender)
|
||||
defines.emplace_back("ADVANCED_RENDER");
|
||||
|
||||
for (size_t i = 0; shaders[i]; i++) {
|
||||
shaders[i]->recompile(defines);
|
||||
|
|
@ -333,7 +333,8 @@ void WorldRenderer::renderFrame(
|
|||
auto& entityShader = assets.require<Shader>("entity");
|
||||
auto& cloudsShader = assets.require<Shader>("clouds");
|
||||
auto& translucentShader = assets.require<Shader>("translucent");
|
||||
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
|
||||
auto& deferredShader =
|
||||
assets.require<PostEffect>("deferred_lighting").getShader();
|
||||
|
||||
const auto& settings = engine.getSettings();
|
||||
|
||||
|
|
@ -343,19 +344,19 @@ void WorldRenderer::renderFrame(
|
|||
&cloudsShader,
|
||||
&translucentShader,
|
||||
&deferredShader,
|
||||
nullptr
|
||||
};
|
||||
nullptr};
|
||||
|
||||
refreshSettings(affectedShaders);
|
||||
|
||||
const auto& worldInfo = world->getInfo();
|
||||
|
||||
|
||||
float clouds = weather.clouds();
|
||||
clouds = glm::max(worldInfo.fog, clouds);
|
||||
float mie = 1.0f + glm::max(worldInfo.fog, clouds * 0.5f) * 2.0f;
|
||||
|
||||
float random = rand() / static_cast<float>(RAND_MAX);
|
||||
skybox->refresh(
|
||||
level.environment,
|
||||
pctx,
|
||||
worldInfo.daytime,
|
||||
mie,
|
||||
|
|
@ -374,20 +375,19 @@ void WorldRenderer::renderFrame(
|
|||
{
|
||||
DrawContext wctx = pctx.sub();
|
||||
postProcessing.use(wctx, gbufferPipeline);
|
||||
|
||||
display::clearDepth();
|
||||
|
||||
/* Main opaque pass (GBuffer pass) */ {
|
||||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.setCullFace(true);
|
||||
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||
renderOpaque(ctx, camera, settings, hudVisible);
|
||||
}
|
||||
texts->render(pctx, camera, settings, hudVisible, true);
|
||||
}
|
||||
skybox->bind();
|
||||
float fogFactor =
|
||||
15.0f / static_cast<float>(settings.chunks.loadDistance.get() - 2);
|
||||
float fogFactor = calcFogFactor();
|
||||
if (gbufferPipeline) {
|
||||
deferredShader.use();
|
||||
setupWorldShader(deferredShader, camera, settings, fogFactor);
|
||||
|
|
@ -396,6 +396,7 @@ void WorldRenderer::renderFrame(
|
|||
{
|
||||
DrawContext ctx = pctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||
|
||||
if (gbufferPipeline) {
|
||||
postProcessing.bindDepthBuffer();
|
||||
|
|
@ -404,7 +405,9 @@ void WorldRenderer::renderFrame(
|
|||
}
|
||||
|
||||
// Background sky plane
|
||||
skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds);
|
||||
skybox->draw(
|
||||
level.environment, ctx, camera, worldInfo.daytime, clouds
|
||||
);
|
||||
|
||||
auto& linesShader = assets.require<Shader>("lines");
|
||||
linesShader.use();
|
||||
|
|
@ -416,10 +419,12 @@ void WorldRenderer::renderFrame(
|
|||
linesShader.uniformMatrix("u_projview", projView);
|
||||
lineBatch->flush();
|
||||
|
||||
skybox->bind();
|
||||
DrawContext wctx = ctx.sub();
|
||||
wctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||
wctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||
// Translucent blocks
|
||||
{
|
||||
auto sctx = ctx.sub();
|
||||
auto sctx = wctx.sub();
|
||||
sctx.setCullFace(true);
|
||||
translucentShader.use();
|
||||
setupWorldShader(translucentShader, camera, settings, fogFactor);
|
||||
|
|
@ -430,44 +435,41 @@ void WorldRenderer::renderFrame(
|
|||
entityShader.use();
|
||||
setupWorldShader(entityShader, camera, settings, fogFactor);
|
||||
|
||||
std::array<const WeatherPreset*, 2> weatherInstances {&weather.a, &weather.b};
|
||||
std::array<const WeatherPreset*, 2> weatherInstances {
|
||||
&weather.a, &weather.b};
|
||||
for (const auto& weather : weatherInstances) {
|
||||
float maxIntensity = weather->fall.maxIntensity;
|
||||
float zero = weather->fall.minOpacity;
|
||||
float one = weather->fall.maxOpacity;
|
||||
float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
|
||||
entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
|
||||
entityShader.uniform1f("u_opacity", weather->fall.opaque ? t * t : t);
|
||||
entityShader.uniform1f(
|
||||
"u_opacity", weather->fall.opaque ? t * t : t
|
||||
);
|
||||
if (weather->intensity > 1.e-3f && !weather->fall.texture.empty()) {
|
||||
precipitation->render(camera, *weather);
|
||||
}
|
||||
}
|
||||
skybox->unbind();
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
postProcessing.render(pctx, assets, timer, camera);
|
||||
|
||||
|
||||
if (player.currentCamera == player.fpCamera) {
|
||||
DrawContext ctx = pctx.sub();
|
||||
ctx.setDepthTest(true);
|
||||
ctx.setCullFace(true);
|
||||
ctx.useTexture(advanced_pipeline::TARGET_SKYBOX, skybox->getCubemap());
|
||||
ctx.useTexture(advanced_pipeline::TARGET_COLOR, nullptr);
|
||||
display::clearDepth();
|
||||
|
||||
// prepare modified HUD camera
|
||||
Camera hudcam = camera;
|
||||
hudcam.far = 10.0f;
|
||||
hudcam.setFov(0.9f);
|
||||
hudcam.position = {};
|
||||
|
||||
hands->render(camera);
|
||||
|
||||
display::clearDepth();
|
||||
setupWorldShader(entityShader, hudcam, engine.getSettings(), 0.0f);
|
||||
|
||||
skybox->bind();
|
||||
modelBatch->render();
|
||||
modelBatch->setLightsOffset(glm::vec3());
|
||||
skybox->unbind();
|
||||
hands->render(camera);
|
||||
}
|
||||
renderBlockOverlay(pctx);
|
||||
|
||||
|
|
@ -487,13 +489,12 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
|
|||
if (def.overlayTexture.empty()) {
|
||||
return;
|
||||
}
|
||||
auto textureRegion = util::get_texture_region(
|
||||
assets, def.overlayTexture, "blocks:notfound"
|
||||
);
|
||||
auto textureRegion =
|
||||
util::get_texture_region(assets, def.overlayTexture, "blocks:notfound");
|
||||
DrawContext ctx = wctx.sub();
|
||||
ctx.setDepthTest(false);
|
||||
ctx.setCullFace(false);
|
||||
|
||||
|
||||
auto& shader = assets.require<Shader>("ui3d");
|
||||
shader.use();
|
||||
batch3d->begin();
|
||||
|
|
|
|||
|
|
@ -99,6 +99,8 @@ class WorldRenderer {
|
|||
);
|
||||
|
||||
void refreshSettings(Shader** shaders);
|
||||
|
||||
float calcFogFactor() const;
|
||||
public:
|
||||
std::unique_ptr<ParticlesRenderer> particles;
|
||||
std::unique_ptr<TextsRenderer> texts;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ InlineFrame::~InlineFrame() = default;
|
|||
void InlineFrame::setSrc(const std::string& src) {
|
||||
this->src = src;
|
||||
if (document) {
|
||||
scripting::on_ui_close(document.get(), nullptr);
|
||||
scripting::on_ui_close(*document, nullptr);
|
||||
document = nullptr;
|
||||
root = nullptr;
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ void InlineFrame::setDocument(const std::shared_ptr<UiDocument>& document) {
|
|||
root->setSize(size);
|
||||
|
||||
gui.postRunnable([this]() {
|
||||
scripting::on_ui_open(this->document.get(), {});
|
||||
scripting::on_ui_open(*this->document, {});
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,8 +15,33 @@ public:
|
|||
memory_streambuf(const memory_streambuf&) = delete;
|
||||
memory_streambuf& operator=(const memory_streambuf&) = delete;
|
||||
|
||||
pos_type seekoff(off_type off, std::ios_base::seekdir way,
|
||||
std::ios_base::openmode mode = std::ios_base::in) override {
|
||||
if (!(mode & std::ios_base::in)) return pos_type(off_type(-1));
|
||||
char* base = eback();
|
||||
char* end = egptr();
|
||||
if (!base || !end) return pos_type(off_type(-1));
|
||||
|
||||
std::ptrdiff_t current = gptr() - base;
|
||||
std::ptrdiff_t newpos;
|
||||
if (way == std::ios_base::beg) newpos = off;
|
||||
else if (way == std::ios_base::cur) newpos = current + off;
|
||||
else if (way == std::ios_base::end) newpos = (end - base) + off;
|
||||
else return pos_type(off_type(-1));
|
||||
|
||||
if (newpos < 0 || base + newpos > end) return pos_type(off_type(-1));
|
||||
setg(base, base + newpos, end);
|
||||
return pos_type(off_type(newpos));
|
||||
}
|
||||
|
||||
pos_type seekpos(pos_type sp, std::ios_base::openmode mode = std::ios_base::in) override {
|
||||
return seekoff(off_type(sp), std::ios_base::beg, mode);
|
||||
}
|
||||
protected:
|
||||
int_type underflow() override {
|
||||
if (gptr() < egptr()) {
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +61,7 @@ private:
|
|||
class memory_view_streambuf : public std::streambuf {
|
||||
public:
|
||||
explicit memory_view_streambuf(const util::Buffer<char>& buffer)
|
||||
: buffer(std::move(buffer)) {
|
||||
: buffer(buffer) {
|
||||
char* base = const_cast<char*>(this->buffer.data());
|
||||
char* end = base + this->buffer.size();
|
||||
setg(base, base, end);
|
||||
|
|
@ -45,8 +70,33 @@ public:
|
|||
memory_view_streambuf(const memory_view_streambuf&) = delete;
|
||||
memory_view_streambuf& operator=(const memory_view_streambuf&) = delete;
|
||||
|
||||
pos_type seekoff(off_type off, std::ios_base::seekdir way,
|
||||
std::ios_base::openmode mode = std::ios_base::in) override {
|
||||
if (!(mode & std::ios_base::in)) return pos_type(off_type(-1));
|
||||
char* base = eback();
|
||||
char* end = egptr();
|
||||
if (!base || !end) return pos_type(off_type(-1));
|
||||
|
||||
std::ptrdiff_t current = gptr() - base;
|
||||
std::ptrdiff_t newpos;
|
||||
if (way == std::ios_base::beg) newpos = off;
|
||||
else if (way == std::ios_base::cur) newpos = current + off;
|
||||
else if (way == std::ios_base::end) newpos = (end - base) + off;
|
||||
else return pos_type(off_type(-1));
|
||||
|
||||
if (newpos < 0 || base + newpos > end) return pos_type(off_type(-1));
|
||||
setg(base, base + newpos, end);
|
||||
return pos_type(off_type(newpos));
|
||||
}
|
||||
|
||||
pos_type seekpos(pos_type sp, std::ios_base::openmode mode = std::ios_base::in) override {
|
||||
return seekoff(off_type(sp), std::ios_base::beg, mode);
|
||||
}
|
||||
protected:
|
||||
int_type underflow() override {
|
||||
if (gptr() < egptr()) {
|
||||
return traits_type::to_int_type(*gptr());
|
||||
}
|
||||
return traits_type::eof();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ namespace io {
|
|||
}
|
||||
}
|
||||
|
||||
path(std::string_view str) : path(std::string(str)) {}
|
||||
|
||||
path(const char* str) : path(std::string(str)) {}
|
||||
|
||||
bool operator==(const std::string& other) const {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const uint MIN_SURROUNDING = 9;
|
|||
ChunksController::ChunksController(Level& level)
|
||||
: level(level),
|
||||
generator(std::make_unique<WorldGenerator>(
|
||||
level.content.generators.require(level.getWorld()->getGenerator()),
|
||||
level.content.generators.require(level.environment.generator),
|
||||
level.content,
|
||||
level.getWorld()->getSeed()
|
||||
)) {}
|
||||
|
|
|
|||
|
|
@ -179,7 +179,7 @@ void EngineController::onMissingContent(
|
|||
if (engine.isHeadless()) {
|
||||
throw std::runtime_error(
|
||||
"missing content: " +
|
||||
json::stringify(create_missing_content_report(report), true, " ")
|
||||
util::quote(json::stringify(create_missing_content_report(report), true, " "))
|
||||
);
|
||||
} else {
|
||||
engine.setScreen(std::make_shared<MenuScreen>(engine));
|
||||
|
|
@ -268,7 +268,7 @@ inline uint64_t str2seed(const std::string& seedstr) {
|
|||
void EngineController::createWorld(
|
||||
const std::string& name,
|
||||
const std::string& seedstr,
|
||||
const std::string& generatorID
|
||||
const std::string& environment
|
||||
) {
|
||||
uint64_t seed = str2seed(seedstr);
|
||||
|
||||
|
|
@ -279,17 +279,31 @@ void EngineController::createWorld(
|
|||
paths.setCurrentWorldFolder(folder);
|
||||
engine.getContentControl().loadContent();
|
||||
});
|
||||
const auto& generators = engine.getContentControl().get()->generators;
|
||||
|
||||
// generator-based environments must die in 1.0
|
||||
bool genBasedEnv = false;
|
||||
if (auto genDef = generators.find(environment)) {
|
||||
genBasedEnv = true;
|
||||
}
|
||||
|
||||
auto& contentControl = engine.getContentControl();
|
||||
auto level = World::create(
|
||||
name,
|
||||
generatorID,
|
||||
genBasedEnv ? "" : environment,
|
||||
genBasedEnv ? environment : "",
|
||||
folder,
|
||||
seed,
|
||||
engine.getSettings(),
|
||||
*contentControl.get(),
|
||||
contentControl.getContentPacks()
|
||||
);
|
||||
|
||||
// generator-based environment initialization
|
||||
if (genBasedEnv) {
|
||||
level->environment.generator = environment;
|
||||
}
|
||||
|
||||
if (!engine.isHeadless()) {
|
||||
level->players->create(localPlayer);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ void LevelController::saveWorld() {
|
|||
world->wfile->createDirectories();
|
||||
scripting::on_world_save();
|
||||
level->onSave();
|
||||
level->getWorld()->write(level.get());
|
||||
level->getWorld()->write(*level);
|
||||
}
|
||||
|
||||
void LevelController::onWorldQuit() {
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
#include "logic/scripting/descriptors_manager.hpp"
|
||||
|
||||
#include "debug/Logger.hpp"
|
||||
|
||||
static debug::Logger logger("descriptors-manager");
|
||||
|
||||
namespace scripting {
|
||||
|
||||
std::vector<std::optional<StreamDescriptor>> descriptors_manager::descriptors;
|
||||
|
||||
std::istream* descriptors_manager::get_input(int descriptor) {
|
||||
if (!is_readable(descriptor))
|
||||
return nullptr;
|
||||
|
||||
return descriptors[descriptor]->in.get();
|
||||
}
|
||||
|
||||
std::ostream* descriptors_manager::get_output(int descriptor) {
|
||||
if (!is_writeable(descriptor))
|
||||
return nullptr;
|
||||
|
||||
return descriptors[descriptor]->out.get();
|
||||
}
|
||||
|
||||
void descriptors_manager::flush(int descriptor) {
|
||||
if (is_writeable(descriptor)) {
|
||||
descriptors[descriptor]->out->flush();
|
||||
}
|
||||
}
|
||||
|
||||
bool descriptors_manager::has_descriptor(int descriptor) {
|
||||
return is_readable(descriptor) || is_writeable(descriptor);
|
||||
}
|
||||
|
||||
bool descriptors_manager::is_readable(int descriptor) {
|
||||
return descriptor >= 0 && descriptor < static_cast<int>(descriptors.size())
|
||||
&& descriptors[descriptor].has_value()
|
||||
&& descriptors[descriptor]->in != nullptr;
|
||||
}
|
||||
|
||||
bool descriptors_manager::is_writeable(int descriptor) {
|
||||
return descriptor >= 0 && descriptor < static_cast<int>(descriptors.size())
|
||||
&& descriptors[descriptor].has_value()
|
||||
&& descriptors[descriptor]->out != nullptr;
|
||||
}
|
||||
|
||||
void descriptors_manager::close(int descriptor) {
|
||||
if (descriptor >= 0 && descriptor < static_cast<int>(descriptors.size())) {
|
||||
if (descriptors[descriptor].has_value()) {
|
||||
auto& desc = descriptors[descriptor].value();
|
||||
|
||||
if (desc.out)
|
||||
desc.out->flush();
|
||||
|
||||
desc.in.reset();
|
||||
desc.out.reset();
|
||||
}
|
||||
|
||||
descriptors[descriptor].reset();
|
||||
|
||||
descriptors[descriptor] = std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
int descriptors_manager::open_descriptor(const io::path& path, bool write, bool read) {
|
||||
std::unique_ptr<std::istream> in;
|
||||
std::unique_ptr<std::ostream> out;
|
||||
|
||||
try {
|
||||
if (read)
|
||||
in = io::read(path);
|
||||
|
||||
if (write)
|
||||
out = io::write(path);
|
||||
} catch (const std::exception& e) {
|
||||
logger.error() << "failed to open descriptor for " << path.string()
|
||||
<< ": " << e.what();
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(descriptors.size()); ++i) {
|
||||
if (!descriptors[i].has_value()) {
|
||||
descriptors[i] = StreamDescriptor{ std::move(in), std::move(out) };
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
descriptors.emplace_back(StreamDescriptor{ std::move(in), std::move(out) });
|
||||
|
||||
return static_cast<int>(descriptors.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
void descriptors_manager::close_all_descriptors() {
|
||||
for (int i = 0; i < static_cast<int>(descriptors.size()); ++i) {
|
||||
if (descriptors[i].has_value()) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
|
||||
descriptors.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
#include "io/io.hpp"
|
||||
|
||||
namespace scripting {
|
||||
|
||||
struct StreamDescriptor {
|
||||
std::unique_ptr<std::istream> in;
|
||||
std::unique_ptr<std::ostream> out;
|
||||
};
|
||||
|
||||
class descriptors_manager {
|
||||
private:
|
||||
static std::vector<std::optional<StreamDescriptor>> descriptors;
|
||||
|
||||
public:
|
||||
static std::istream* get_input(int descriptor);
|
||||
static std::ostream* get_output(int descriptor);
|
||||
|
||||
static void flush(int descriptor);
|
||||
|
||||
static bool has_descriptor(int descriptor);
|
||||
|
||||
static bool is_readable(int descriptor);
|
||||
static bool is_writeable(int descriptor);
|
||||
|
||||
static void close(int descriptor);
|
||||
static int open_descriptor(const io::path& path, bool write, bool read);
|
||||
|
||||
static void close_all_descriptors();
|
||||
};
|
||||
}
|
||||
160
src/logic/scripting/io_descriptors.cpp
Normal file
160
src/logic/scripting/io_descriptors.cpp
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#include "logic/scripting/io_descriptors.hpp"
|
||||
|
||||
#include "debug/Logger.hpp"
|
||||
#include "io/io.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
static debug::Logger logger("descriptors-manager");
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
namespace {
|
||||
struct StreamDescriptor {
|
||||
// TODO: std::iostream?
|
||||
std::unique_ptr<std::istream> in;
|
||||
std::unique_ptr<std::ostream> out;
|
||||
};
|
||||
std::vector<std::optional<StreamDescriptor>> descriptors;
|
||||
}
|
||||
|
||||
std::istream* io_descriptors::get_input(int id) {
|
||||
if (!is_readable(id)) {
|
||||
return nullptr;
|
||||
}
|
||||
return ::descriptors[id]->in.get();
|
||||
}
|
||||
|
||||
std::ostream* io_descriptors::get_output(int id) {
|
||||
if (!is_writeable(id)) {
|
||||
return nullptr;
|
||||
}
|
||||
return ::descriptors[id]->out.get();
|
||||
}
|
||||
|
||||
static StreamDescriptor& require_descriptor(int id) {
|
||||
if (!io_descriptors::has_descriptor(id)) {
|
||||
throw std::runtime_error(
|
||||
"io-descriptor with id " + std::to_string(id) + " does not exists"
|
||||
);
|
||||
}
|
||||
return *::descriptors[id];
|
||||
}
|
||||
|
||||
std::istream& io_descriptors::require_input(int id) {
|
||||
const auto& descriptor = require_descriptor(id);
|
||||
if (descriptor.in) {
|
||||
return *descriptor.in;
|
||||
}
|
||||
throw std::runtime_error("io-descriptor is not readable");
|
||||
}
|
||||
|
||||
std::ostream& io_descriptors::require_output(int id) {
|
||||
const auto& descriptor = require_descriptor(id);
|
||||
if (descriptor.out) {
|
||||
return *descriptor.out;
|
||||
}
|
||||
throw std::runtime_error("io-descriptor is not writeable");
|
||||
}
|
||||
|
||||
void io_descriptors::flush(int id) {
|
||||
if (is_writeable(id)) {
|
||||
::descriptors[id]->out->flush();
|
||||
}
|
||||
}
|
||||
|
||||
int io_descriptors::available(int id) {
|
||||
if (!is_readable(id))
|
||||
return 0;
|
||||
|
||||
auto* stream = descriptors[id]->in.get();
|
||||
|
||||
auto current_pos = stream->tellg();
|
||||
|
||||
if (current_pos == -1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->seekg(0, std::ios::end);
|
||||
|
||||
auto end_pos = stream->tellg();
|
||||
|
||||
stream->seekg(current_pos, std::ios::beg);
|
||||
|
||||
auto remaining = end_pos - current_pos;
|
||||
|
||||
if (remaining > std::numeric_limits<int>::max()) {
|
||||
return std::numeric_limits<int>::max();
|
||||
}
|
||||
|
||||
return static_cast<int>(remaining);
|
||||
}
|
||||
|
||||
bool io_descriptors::has_descriptor(int id) {
|
||||
return id >= 0 && id < static_cast<int>(::descriptors.size()) &&
|
||||
::descriptors[id].has_value();
|
||||
}
|
||||
|
||||
bool io_descriptors::is_readable(int id) {
|
||||
return has_descriptor(id) && ::descriptors[id]->in != nullptr;
|
||||
}
|
||||
|
||||
bool io_descriptors::is_writeable(int id) {
|
||||
return id >= 0 && id < static_cast<int>(::descriptors.size())
|
||||
&& ::descriptors[id].has_value()
|
||||
&& ::descriptors[id]->out != nullptr;
|
||||
}
|
||||
|
||||
void io_descriptors::close(int id) {
|
||||
if (!has_descriptor(id)) {
|
||||
return;
|
||||
}
|
||||
auto& desc = ::descriptors[id].value();
|
||||
if (desc.out) {
|
||||
desc.out->flush();
|
||||
}
|
||||
desc.in.reset();
|
||||
desc.out.reset();
|
||||
::descriptors[id] = std::nullopt;
|
||||
}
|
||||
|
||||
int io_descriptors::open_descriptor(const io::path& path, bool write, bool read) {
|
||||
std::unique_ptr<std::istream> in;
|
||||
std::unique_ptr<std::ostream> out;
|
||||
|
||||
try {
|
||||
if (read) {
|
||||
in = io::read(path);
|
||||
}
|
||||
if (write) {
|
||||
out = io::write(path);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
logger.error() << "failed to open descriptor for " << path.string()
|
||||
<< ": " << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < static_cast<int>(descriptors.size()); ++i) {
|
||||
if (!descriptors[i].has_value()) {
|
||||
descriptors[i] = StreamDescriptor{ std::move(in), std::move(out) };
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
::descriptors.emplace_back(StreamDescriptor{ std::move(in), std::move(out) });
|
||||
return static_cast<int>(::descriptors.size() - 1);
|
||||
}
|
||||
|
||||
|
||||
void io_descriptors::close_all_descriptors() {
|
||||
for (int i = 0; i < static_cast<int>(::descriptors.size()); ++i) {
|
||||
if (::descriptors[i].has_value()) {
|
||||
close(i);
|
||||
}
|
||||
}
|
||||
::descriptors.clear();
|
||||
}
|
||||
27
src/logic/scripting/io_descriptors.hpp
Normal file
27
src/logic/scripting/io_descriptors.hpp
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#pragma once
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
|
||||
#include "io/fwd.hpp"
|
||||
|
||||
namespace scripting::io_descriptors {
|
||||
std::istream* get_input(int id);
|
||||
std::ostream* get_output(int id);
|
||||
|
||||
std::istream& require_input(int id);
|
||||
std::ostream& require_output(int id);
|
||||
|
||||
void flush(int id);
|
||||
int available(int id);
|
||||
|
||||
bool has_descriptor(int id);
|
||||
|
||||
bool is_readable(int id);
|
||||
bool is_writeable(int id);
|
||||
|
||||
void close(int id);
|
||||
int open_descriptor(const io::path& path, bool write, bool read);
|
||||
|
||||
void close_all_descriptors();
|
||||
}
|
||||
|
|
@ -44,6 +44,7 @@ extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
|||
extern const luaL_Reg quatlib[];
|
||||
extern const luaL_Reg randomlib[];
|
||||
extern const luaL_Reg compressionlib[];
|
||||
extern const luaL_Reg testlib[];
|
||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||
extern const luaL_Reg timelib[];
|
||||
extern const luaL_Reg tomllib[];
|
||||
|
|
|
|||
|
|
@ -20,8 +20,14 @@
|
|||
#include "window/Window.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
namespace {
|
||||
static std::array<std::unique_ptr<Process>, MAX_SUBPROCESSES> processes;
|
||||
}
|
||||
|
||||
/// @brief Check if content is loaded
|
||||
static int l_is_content_loaded(lua::State* L) {
|
||||
return lua::pushboolean(L, content != nullptr);
|
||||
|
|
@ -337,6 +343,10 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
if (!engine->getProject().permissions.has(Permissions::DEBUGGING)) {
|
||||
throw std::runtime_error("project has no debugging permission");
|
||||
}
|
||||
const auto& params = engine->getCoreParameters();
|
||||
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
|
||||
throw std::runtime_error("max subprocess depth exceeded");
|
||||
}
|
||||
|
||||
int port = lua::tointeger(L, 1);
|
||||
if (port == 0) {
|
||||
|
|
@ -357,6 +367,8 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
"--res", paths.getResourcesFolder().u8string(),
|
||||
"--dir", paths.getUserFilesFolder().u8string(),
|
||||
"--dbg-server", "tcp:" + std::to_string(port),
|
||||
"--sub-depth", std::to_string(engine->getCoreParameters()
|
||||
.subProcessDepth + 1),
|
||||
};
|
||||
if (!projectPath.empty()) {
|
||||
args.emplace_back("--project");
|
||||
|
|
@ -365,11 +377,76 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
|
||||
platform::new_engine_instance(
|
||||
std::move(args),
|
||||
outputPath.empty() ? "" : io::resolve(std::string(outputPath))
|
||||
outputPath.empty() ? "" : io::resolve(std::string(outputPath)),
|
||||
false
|
||||
);
|
||||
return lua::pushinteger(L, port);
|
||||
}
|
||||
|
||||
static int l_start_background_instance(lua::State* L) {
|
||||
if (!engine->getProject().permissions.has(Permissions::SUB_INSTANCES)) {
|
||||
throw std::runtime_error("project has no sub-instances permission");
|
||||
}
|
||||
const auto& params = engine->getCoreParameters();
|
||||
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
|
||||
throw std::runtime_error("max subprocess depth exceeded");
|
||||
}
|
||||
|
||||
auto scriptPath = lua::require_lstring(L, 1);
|
||||
io::path outputPath = lua::isstring(L, 2) ? lua::require_lstring(L, 2) : "";
|
||||
const auto& paths = engine->getPaths();
|
||||
|
||||
std::vector<std::string> args {
|
||||
"--headless",
|
||||
"--res", paths.getResourcesFolder().u8string(),
|
||||
"--dir", paths.getUserFilesFolder().u8string(),
|
||||
"--script", io::resolve(scriptPath).u8string(),
|
||||
"--sub-depth", std::to_string(engine->getCoreParameters()
|
||||
.subProcessDepth + 1),
|
||||
"--log", io::resolve(outputPath).u8string(),
|
||||
};
|
||||
args.emplace_back("--project");
|
||||
args.emplace_back(io::resolve(engine->getProject().path).u8string());
|
||||
|
||||
int handle = -1;
|
||||
for (int i = 0; i < ::processes.size(); i++) {
|
||||
if (!::processes[i] || !::processes[i]->isActive()) {
|
||||
handle = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle == -1) {
|
||||
throw std::runtime_error("sub-processes limit exceeded");
|
||||
}
|
||||
::processes[handle] =
|
||||
platform::new_engine_instance(std::move(args), "", true);
|
||||
|
||||
return lua::pushinteger(L, handle);
|
||||
}
|
||||
|
||||
static int l_is_instance_alive(lua::State* L) {
|
||||
int handle = lua::tointeger(L, 1);
|
||||
if (handle < 0 || handle >= ::processes.size()) {
|
||||
throw std::runtime_error("invalid process handle");
|
||||
}
|
||||
auto& process = ::processes[handle];
|
||||
return lua::pushboolean(L, process && process->isActive());
|
||||
}
|
||||
|
||||
static int l_terminate_instance(lua::State* L) {
|
||||
int handle = lua::tointeger(L, 1);
|
||||
if (handle < 0 || handle >= ::processes.size()) {
|
||||
throw std::runtime_error("invalid process handle");
|
||||
}
|
||||
auto& process = ::processes[handle];
|
||||
if (process == nullptr) {
|
||||
return lua::pushboolean(L, false);
|
||||
}
|
||||
bool active = process->isActive();
|
||||
process.reset();
|
||||
return lua::pushboolean(L, active);
|
||||
}
|
||||
|
||||
const luaL_Reg applib[] = {
|
||||
/// content
|
||||
{"is_content_loaded", lua::wrap<l_is_content_loaded>},
|
||||
|
|
@ -402,5 +479,8 @@ const luaL_Reg applib[] = {
|
|||
{"get_version", lua::wrap<l_get_version>},
|
||||
{"create_memory_device", lua::wrap<l_create_memory_device>},
|
||||
{"start_debug_instance", lua::wrap<l_start_debug_instance>},
|
||||
{"start_background_instance", lua::wrap<l_start_background_instance>},
|
||||
{"is_instance_alive", lua::wrap<l_is_instance_alive>},
|
||||
{"terminate_instance", lua::wrap<l_terminate_instance>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -314,6 +314,10 @@ static int l_world_raycast(lua::State* L) {
|
|||
entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "nonselect_entities", 1)) {
|
||||
entityRaycast.includeNonSelectable = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "filter_blocks", 1)) {
|
||||
load_blocks_filter(L, -1, filteredBlocks);
|
||||
blocksRaycast.filter = &filteredBlocks;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
#include "util/stringutil.hpp"
|
||||
#include "api_lua.hpp"
|
||||
#include "../lua_engine.hpp"
|
||||
#include "logic/scripting/descriptors_manager.hpp"
|
||||
#include "logic/scripting/io_descriptors.hpp"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace scripting;
|
||||
|
|
@ -263,13 +263,13 @@ static int l_open_descriptor(lua::State* L) {
|
|||
std::vector<char> buffer;
|
||||
|
||||
if(wplusMode) {
|
||||
int temp_descriptor = scripting::descriptors_manager::open_descriptor(path, false, true);
|
||||
int temp_descriptor = io_descriptors::open_descriptor(path, false, true);
|
||||
|
||||
if (temp_descriptor == -1) {
|
||||
throw std::runtime_error("failed to open descriptor for initial reading");
|
||||
}
|
||||
|
||||
auto* in_stream = scripting::descriptors_manager::get_input(temp_descriptor);
|
||||
auto* in_stream = io_descriptors::get_input(temp_descriptor);
|
||||
|
||||
in_stream->seekg(0, std::ios::end);
|
||||
std::streamsize size = in_stream->tellg();
|
||||
|
|
@ -278,17 +278,17 @@ static int l_open_descriptor(lua::State* L) {
|
|||
buffer.resize(size);
|
||||
in_stream->read(buffer.data(), size);
|
||||
|
||||
scripting::descriptors_manager::close(temp_descriptor);
|
||||
io_descriptors::close(temp_descriptor);
|
||||
}
|
||||
|
||||
int descriptor = scripting::descriptors_manager::open_descriptor(path, write, read);
|
||||
int descriptor = io_descriptors::open_descriptor(path, write, read);
|
||||
|
||||
if(descriptor == -1) {
|
||||
throw std::runtime_error("failed to open descriptor");
|
||||
}
|
||||
|
||||
if(wplusMode) {
|
||||
auto* out_stream = scripting::descriptors_manager::get_output(descriptor);
|
||||
auto* out_stream = io_descriptors::get_output(descriptor);
|
||||
out_stream->write(buffer.data(), buffer.size());
|
||||
out_stream->flush();
|
||||
}
|
||||
|
|
@ -297,51 +297,31 @@ static int l_open_descriptor(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_has_descriptor(lua::State* L) {
|
||||
return lua::pushboolean(L, scripting::descriptors_manager::has_descriptor(lua::tointeger(L, 1)));
|
||||
return lua::pushboolean(L, io_descriptors::has_descriptor(lua::tointeger(L, 1)));
|
||||
}
|
||||
|
||||
static int l_read_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!scripting::descriptors_manager::is_readable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not readable");
|
||||
}
|
||||
|
||||
int maxlen = lua::tointeger(L, 2);
|
||||
|
||||
auto* stream = scripting::descriptors_manager::get_input(descriptor);
|
||||
auto& stream = io_descriptors::require_input(descriptor);
|
||||
if (stream.eof()) {
|
||||
stream.clear();
|
||||
}
|
||||
|
||||
util::Buffer<char> buffer(maxlen);
|
||||
|
||||
stream->read(buffer.data(), maxlen);
|
||||
|
||||
std::streamsize read_len = stream->gcount();
|
||||
|
||||
stream.read(buffer.data(), maxlen);
|
||||
std::streamsize read_len = stream.gcount();
|
||||
return lua::create_bytearray(L, buffer.data(), read_len);
|
||||
}
|
||||
|
||||
static int l_write_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!scripting::descriptors_manager::is_writeable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not writeable");
|
||||
}
|
||||
|
||||
auto data = lua::bytearray_as_string(L, 2);
|
||||
|
||||
auto* stream = scripting::descriptors_manager::get_output(descriptor);
|
||||
|
||||
stream->write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
|
||||
if (!stream->good()) {
|
||||
auto& stream = io_descriptors::require_output(descriptor);
|
||||
stream.write(data.data(), static_cast<std::streamsize>(data.size()));
|
||||
if (!stream.good()) {
|
||||
throw std::runtime_error("failed to write to stream");
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -350,12 +330,13 @@ static int l_write_descriptor(lua::State* L) {
|
|||
static int l_seek_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
if (!io_descriptors::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
std::string mode = lua::require_string(L, 2);
|
||||
auto mode = lua::require_string(L, 2);
|
||||
std::ios_base::seekdir dir;
|
||||
auto position = lua::tointeger(L, 3);
|
||||
|
||||
switch (mode[0]) {
|
||||
case 'b':
|
||||
|
|
@ -371,45 +352,81 @@ static int l_seek_descriptor(lua::State* L) {
|
|||
throw std::runtime_error("invalid seek mode");
|
||||
}
|
||||
|
||||
auto* stream = scripting::descriptors_manager::get_output(descriptor);
|
||||
if (io_descriptors::is_writeable(descriptor)) {
|
||||
auto& stream = io_descriptors::require_output(descriptor);
|
||||
stream.seekp(position, dir);
|
||||
if (!stream.good()) {
|
||||
throw std::runtime_error("failed to seek stream");
|
||||
}
|
||||
}
|
||||
if (io_descriptors::is_readable(descriptor)) {
|
||||
auto& stream = io_descriptors::require_input(descriptor);
|
||||
stream.seekg(position, dir);
|
||||
if (!stream.good()) {
|
||||
throw std::runtime_error("failed to seek stream");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream->seekp(lua::tointeger(L, 3), dir);
|
||||
static int l_tell_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!stream->good()) {
|
||||
throw std::runtime_error("failed to seek stream");
|
||||
if (!io_descriptors::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (io_descriptors::is_writeable(descriptor)) {
|
||||
auto& stream = io_descriptors::require_output(descriptor);
|
||||
return lua::pushinteger(L, stream.tellp());
|
||||
} else {
|
||||
auto& stream = io_descriptors::require_input(descriptor);
|
||||
return lua::pushinteger(L, stream.tellg());
|
||||
}
|
||||
}
|
||||
|
||||
static int l_flush_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
if (!io_descriptors::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!scripting::descriptors_manager::is_writeable(descriptor)) {
|
||||
if (!io_descriptors::is_writeable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not writeable");
|
||||
}
|
||||
|
||||
scripting::descriptors_manager::flush(descriptor);
|
||||
io_descriptors::flush(descriptor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_available_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!io_descriptors::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
if (!io_descriptors::is_readable(descriptor)) {
|
||||
throw std::runtime_error("descriptor is not readable");
|
||||
}
|
||||
|
||||
return lua::pushinteger(L, io_descriptors::available(descriptor));
|
||||
}
|
||||
|
||||
static int l_close_descriptor(lua::State* L) {
|
||||
int descriptor = lua::tointeger(L, 1);
|
||||
|
||||
if (!scripting::descriptors_manager::has_descriptor(descriptor)) {
|
||||
if (!io_descriptors::has_descriptor(descriptor)) {
|
||||
throw std::runtime_error("unknown descriptor");
|
||||
}
|
||||
|
||||
scripting::descriptors_manager::close(descriptor);
|
||||
io_descriptors::close(descriptor);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_close_all_descriptors(lua::State* L) {
|
||||
scripting::descriptors_manager::close_all_descriptors();
|
||||
io_descriptors::close_all_descriptors();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -442,7 +459,9 @@ const luaL_Reg filelib[] = {
|
|||
{"__read_descriptor", lua::wrap<l_read_descriptor>},
|
||||
{"__write_descriptor", lua::wrap<l_write_descriptor>},
|
||||
{"__seek_descriptor", lua::wrap<l_seek_descriptor>},
|
||||
{"__tell_descriptor", lua::wrap<l_tell_descriptor>},
|
||||
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
||||
{"__available_descriptor", lua::wrap<l_available_descriptor>},
|
||||
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
||||
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
||||
{nullptr, nullptr}
|
||||
|
|
|
|||
|
|
@ -420,15 +420,19 @@ static int p_get_data(UINode* node, lua::State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const std::string& request_node_id(const DocumentNode& docnode) {
|
||||
std::string id = docnode.node->getId();
|
||||
static const std::string& request_node_id(UiDocument& document, UINode& node) {
|
||||
const std::string& id = node.getId();
|
||||
if (id.empty()) {
|
||||
id = "#" + std::to_string(
|
||||
reinterpret_cast<std::ptrdiff_t>(docnode.node.get()));
|
||||
node.setId( "#" + std::to_string(
|
||||
reinterpret_cast<std::ptrdiff_t>(&node)));
|
||||
document.pushIndices(node.shared_from_this());
|
||||
return node.getId();
|
||||
}
|
||||
docnode.node->setId(std::move(id));
|
||||
docnode.document->pushIndices(docnode.node);
|
||||
return docnode.node->getId();
|
||||
return id;
|
||||
}
|
||||
|
||||
static const std::string& request_node_id(const DocumentNode& docnode) {
|
||||
return request_node_id(*docnode.document, *docnode.node);
|
||||
}
|
||||
|
||||
/// @brief Push UI-document node object to stack
|
||||
|
|
@ -443,15 +447,14 @@ static int push_document_node(lua::State* L, const std::string& id) {
|
|||
|
||||
static int p_get_parent(UINode* node, lua::State* L) {
|
||||
auto parent = node->getParent();
|
||||
if (!parent) {
|
||||
if (parent == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
auto docname = lua::require_string(L, 1);
|
||||
auto element = lua::require_string(L, 2);
|
||||
auto docnode = get_document_node_impl(L, docname, element);
|
||||
|
||||
const auto& id = request_node_id(docnode);
|
||||
|
||||
const auto& id = request_node_id(*docnode.document, *parent);
|
||||
return push_document_node(L, id);
|
||||
}
|
||||
|
||||
|
|
@ -1144,7 +1147,7 @@ static int l_gui_load_document(lua::State* L) {
|
|||
auto document = documentPtr.get();
|
||||
engine->requireAssets().store(std::move(documentPtr), alias);
|
||||
|
||||
scripting::on_ui_open(document, {args});
|
||||
scripting::on_ui_open(*document, {args});
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
45
src/logic/scripting/lua/libs/libtest.cpp
Normal file
45
src/logic/scripting/lua/libs/libtest.cpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
#include "api_lua.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "window/input.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static int l_set_button_pressed(lua::State* L) {
|
||||
int button = lua::touinteger(L, 1);
|
||||
int x = lua::tointeger(L, 2);
|
||||
int y = lua::tointeger(L, 3);
|
||||
bool pressed = lua::toboolean(L, 4);
|
||||
|
||||
auto& input = engine->getInput();
|
||||
input.simulateCursorPos(x, y);
|
||||
input.simulateClick(button, pressed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_set_key_pressed(lua::State* L) {
|
||||
auto key = lua::require_string(L, 1);
|
||||
bool pressed = lua::toboolean(L, 2);
|
||||
|
||||
auto& input = engine->getInput();
|
||||
input.simulateKey(input_util::keycode_from(key), pressed);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_enter_text(lua::State* L) {
|
||||
auto string = lua::require_wstring(L, 1);
|
||||
|
||||
auto& input = engine->getInput();
|
||||
for (auto chr : string) {
|
||||
input.simulateCodepoint(chr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg testlib[] = {
|
||||
{"set_button_pressed", lua::wrap<l_set_button_pressed>},
|
||||
{"set_key_pressed", lua::wrap<l_set_key_pressed>},
|
||||
{"enter_text", lua::wrap<l_enter_text>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
@ -125,10 +125,13 @@ static int l_is_night(lua::State* L) {
|
|||
}
|
||||
|
||||
static int l_get_generator(lua::State* L) {
|
||||
return lua::pushstring(L, require_world_info().generator);
|
||||
return lua::pushstring(L, require_level().environment.generator);
|
||||
}
|
||||
|
||||
static int l_get_chunk_data(lua::State* L) {
|
||||
if (level == nullptr) {
|
||||
throw std::runtime_error("world is not open");
|
||||
}
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
int z = static_cast<int>(lua::tointeger(L, 2));
|
||||
const auto& chunk = level->chunks->getChunk(x, z);
|
||||
|
|
@ -175,7 +178,7 @@ static void integrate_chunk_client(Chunk& chunk) {
|
|||
|
||||
static int l_set_chunk_data(lua::State* L) {
|
||||
if (level == nullptr) {
|
||||
throw std::runtime_error("no open world");
|
||||
throw std::runtime_error("world is not open");
|
||||
}
|
||||
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
|
|
@ -201,7 +204,7 @@ static int l_set_chunk_data(lua::State* L) {
|
|||
|
||||
static int l_save_chunk_data(lua::State* L) {
|
||||
if (level == nullptr) {
|
||||
throw std::runtime_error("no open world");
|
||||
throw std::runtime_error("world is not open");
|
||||
}
|
||||
|
||||
int x = static_cast<int>(lua::tointeger(L, 1));
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ namespace {
|
|||
debug::Logger logger("lua-state");
|
||||
lua::State* main_thread = nullptr;
|
||||
bool headless_mode = false;
|
||||
bool test_mode = false;
|
||||
const std::unordered_map<std::string, std::string>* project_args;
|
||||
}
|
||||
|
||||
|
|
@ -70,12 +71,12 @@ static void create_libs(State* L, StateType stateType) {
|
|||
openlib(L, "yaml", yamllib);
|
||||
|
||||
openlib(L, "__vc_app", applib);
|
||||
lua::getglobal(L, "__vc_app");
|
||||
lua::setregistry(L, "app");
|
||||
getglobal(L, "__vc_app");
|
||||
setregistry(L, "app");
|
||||
|
||||
if (stateType == StateType::SCRIPT) {
|
||||
lua::getregistry(L, "app");
|
||||
lua::setglobal(L, "app");
|
||||
getregistry(L, "app");
|
||||
setglobal(L, "app");
|
||||
}
|
||||
if (stateType == StateType::BASE || stateType == StateType::SCRIPT) {
|
||||
openlib(L, "assets", assetslib);
|
||||
|
|
@ -100,6 +101,10 @@ static void create_libs(State* L, StateType stateType) {
|
|||
openlib(L, "__transform", transformlib);
|
||||
}
|
||||
|
||||
if (::test_mode) {
|
||||
openlib(L, "test", testlib);
|
||||
}
|
||||
|
||||
addfunc(L, "print", lua::wrap<l_print>);
|
||||
addfunc(L, "crc32", lua::wrap<l_crc32>);
|
||||
}
|
||||
|
|
@ -192,6 +197,7 @@ void lua::initialize(const EnginePaths& paths, const CoreParameters& params) {
|
|||
logger.info() << LUAJIT_VERSION;
|
||||
|
||||
headless_mode = params.headless;
|
||||
test_mode = params.testMode;
|
||||
project_args = ¶ms.projectArgs;
|
||||
main_thread = create_state(
|
||||
paths, params.headless ? StateType::SCRIPT : StateType::BASE
|
||||
|
|
|
|||
|
|
@ -591,36 +591,46 @@ bool scripting::on_item_break_block(
|
|||
);
|
||||
}
|
||||
|
||||
void scripting::on_ui_open(
|
||||
UiDocument* layout, std::vector<dv::value> args
|
||||
) {
|
||||
auto argsptr =
|
||||
std::make_shared<std::vector<dv::value>>(std::move(args));
|
||||
std::string name = layout->getId() + ".open";
|
||||
lua::emit_event(lua::get_main_state(), name, [=](auto L) {
|
||||
for (const auto& value : *argsptr) {
|
||||
lua::pushvalue(L, value);
|
||||
static void call_layout_event(const UiDocument& layout, const std::vector<dv::value>& args, const std::string& eventName) {
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *layout.getEnvironment());
|
||||
if (lua::getfield(L, eventName)) {
|
||||
for (const auto& arg : args) {
|
||||
lua::pushvalue(L, arg);
|
||||
}
|
||||
return argsptr->size();
|
||||
});
|
||||
lua::call_nothrow(L, args.size(), 0);
|
||||
}
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::on_ui_open(
|
||||
const UiDocument& layout, std::vector<dv::value> args
|
||||
) {
|
||||
if (layout.getScript().onopen) {
|
||||
call_layout_event(layout, args, "on_open");
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_ui_progress(
|
||||
UiDocument* layout, int workDone, int workTotal
|
||||
const UiDocument& layout, int workDone, int workTotal
|
||||
) {
|
||||
std::string name = layout->getId() + ".progress";
|
||||
lua::emit_event(lua::get_main_state(), name, [=](auto L) {
|
||||
lua::pushinteger(L, workDone);
|
||||
lua::pushinteger(L, workTotal);
|
||||
return 2;
|
||||
});
|
||||
if (layout.getScript().onprogress){
|
||||
call_layout_event(layout, {workDone, workTotal}, "on_progress");
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_ui_close(UiDocument* layout, Inventory* inventory) {
|
||||
std::string name = layout->getId() + ".close";
|
||||
lua::emit_event(lua::get_main_state(), name, [inventory](auto L) {
|
||||
return lua::pushinteger(L, inventory ? inventory->getId() : 0);
|
||||
});
|
||||
void scripting::on_ui_close(const UiDocument& layout, Inventory* inventory) {
|
||||
if (layout.getScript().onclose) {
|
||||
call_layout_event(
|
||||
layout, {inventory ? inventory->getId() : 0}, "on_close"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_ui_destroy(const UiDocument& layout) {
|
||||
if (layout.getScript().ondestroy) {
|
||||
call_layout_event(layout, {}, "on_destroy");
|
||||
}
|
||||
}
|
||||
|
||||
void scripting::on_scripts_loading() {
|
||||
|
|
@ -734,9 +744,10 @@ void scripting::load_entity_component(
|
|||
const io::path& file,
|
||||
const std::string& fileName
|
||||
) {
|
||||
logger.info() << "script (component) " << file.string();
|
||||
|
||||
auto L = lua::get_main_state();
|
||||
std::string src = io::read_string(file);
|
||||
logger.info() << "script (component) " << file.string();
|
||||
lua::loadbuffer(L, *env, src, fileName);
|
||||
lua::store_in_registry(L, lua::CHUNKS_TABLE, name);
|
||||
}
|
||||
|
|
@ -805,11 +816,14 @@ void scripting::load_layout_script(
|
|||
) {
|
||||
int env = *senv;
|
||||
|
||||
lua::pop(lua::get_main_state(), load_script(env, "layout", file, fileName));
|
||||
script.onopen = register_event(env, "on_open", prefix + ".open");
|
||||
script.onprogress =
|
||||
register_event(env, "on_progress", prefix + ".progress");
|
||||
script.onclose = register_event(env, "on_close", prefix + ".close");
|
||||
auto L = lua::get_main_state();
|
||||
lua::pop(L, load_script(env, "layout", file, fileName));
|
||||
lua::pushenv(L, env);
|
||||
script.onopen = lua::hasfield(L, "on_open");
|
||||
script.onprogress = lua::hasfield(L, "on_progress");
|
||||
script.onclose = lua::hasfield(L, "on_close");
|
||||
script.ondestroy = lua::hasfield(L, "on_destroy");
|
||||
lua::pop(L);
|
||||
}
|
||||
|
||||
void scripting::close() {
|
||||
|
|
|
|||
|
|
@ -153,13 +153,16 @@ namespace scripting {
|
|||
void on_attacked(const Entity& entity, Player* player, entityid_t attacker);
|
||||
void on_entity_used(const Entity& entity, Player* player);
|
||||
|
||||
/// @brief Called on UI view show
|
||||
void on_ui_open(UiDocument* layout, std::vector<dv::value> args);
|
||||
/// @brief Called on UI document show
|
||||
void on_ui_open(const UiDocument& layout, std::vector<dv::value> args);
|
||||
|
||||
void on_ui_progress(UiDocument* layout, int workDone, int totalWork);
|
||||
void on_ui_progress(const UiDocument& layout, int workDone, int totalWork);
|
||||
|
||||
/// @brief Called on UI view close
|
||||
void on_ui_close(UiDocument* layout, Inventory* inventory);
|
||||
/// @brief Called on UI document close
|
||||
void on_ui_close(const UiDocument& layout, Inventory* inventory);
|
||||
|
||||
/// @brief Called on UI document destroy
|
||||
void on_ui_destroy(const UiDocument& layout);
|
||||
|
||||
/// @brief Called on Content loading
|
||||
void on_scripts_loading();
|
||||
|
|
|
|||
|
|
@ -148,7 +148,12 @@ void scripting::on_entity_spawn(
|
|||
lua::call(L, 1);
|
||||
}
|
||||
for (auto& component : components) {
|
||||
create_component(L, -1, *component, args, saved);
|
||||
try {
|
||||
create_component(L, -1, *component, args, saved);
|
||||
} catch (const std::runtime_error& err) {
|
||||
logger.error() << "could not to initialize user component "
|
||||
<< component->name << ": " << err.what();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& [packid, pack] : content->getPacks()) {
|
||||
|
|
@ -166,6 +171,9 @@ static void process_entity_callback(
|
|||
const std::string& name,
|
||||
std::function<int(lua::State*)> args
|
||||
) {
|
||||
if (env == nullptr) {
|
||||
return;
|
||||
}
|
||||
auto L = lua::get_main_state();
|
||||
lua::pushenv(L, *env);
|
||||
if (lua::hasfield(L, "__disabled")) {
|
||||
|
|
|
|||
17
src/main.cpp
17
src/main.cpp
|
|
@ -7,9 +7,11 @@
|
|||
#include <iostream>
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace std::literals;
|
||||
static debug::Logger logger("main");
|
||||
|
||||
static void sigterm_handler(int signum) {
|
||||
logger.info() << (signum == SIGTERM ? "SIGTERM" : "SIGINT") << " received";
|
||||
Engine::getInstance().quit();
|
||||
}
|
||||
|
||||
|
|
@ -25,13 +27,24 @@ int main(int argc, char** argv) {
|
|||
if (!parse_cmdline(argc, argv, coreParameters)) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
logger.debug() << "sub-process depth: "
|
||||
<< coreParameters.subProcessDepth;
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::signal(SIGTERM, sigterm_handler);
|
||||
|
||||
debug::Logger::init(coreParameters.userFolder.string() + "/latest.log");
|
||||
#ifdef NDEBUG
|
||||
std::signal(SIGINT, sigterm_handler);
|
||||
#endif
|
||||
std::filesystem::path logFile = coreParameters.logFile;
|
||||
if (logFile.empty()) {
|
||||
logFile = coreParameters.userFolder.string() + "/latest"s +
|
||||
(coreParameters.subProcessDepth > 0
|
||||
? ".sub" + std::to_string(coreParameters.subProcessDepth)
|
||||
: "") + ".log"s;
|
||||
}
|
||||
debug::Logger::init(logFile.u8string());
|
||||
platform::configure_encoding();
|
||||
|
||||
auto& engine = Engine::getInstance();
|
||||
|
|
|
|||
|
|
@ -176,7 +176,8 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
|||
|
||||
for (auto [entity, eid, transform, body] : view.each()) {
|
||||
if (eid.uid == settings.ignoredUid || !body.enabled ||
|
||||
(settings.solidEntitiesOnly && !eid.def.solid)) {
|
||||
(settings.solidEntitiesOnly && !eid.def.solid) ||
|
||||
(!eid.def.selectable && !settings.includeNonSelectable)) {
|
||||
continue;
|
||||
}
|
||||
if (settings.entitiesFilter) {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ public:
|
|||
bool solidEntitiesOnly = false;
|
||||
bool entityFilterExcludeMode = false;
|
||||
const std::set<entitydefid_t>* entitiesFilter = nullptr;
|
||||
bool includeNonSelectable = false;
|
||||
};
|
||||
|
||||
struct RaycastResult {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,9 @@ dv::value Entity::serialize() const {
|
|||
if (!scripts.components.empty()) {
|
||||
auto& compsMap = root.object("comps");
|
||||
for (auto& comp : scripts.components) {
|
||||
if (comp->env == nullptr) {
|
||||
continue;
|
||||
}
|
||||
auto data =
|
||||
scripting::get_component_value(comp->env, SAVED_DATA_VARNAME);
|
||||
compsMap[comp->name] = data;
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ struct EntityDef {
|
|||
/// @brief Solid entity material material
|
||||
std::string material;
|
||||
|
||||
/// @brief Can the entity be interacted with cursor (on_attack, on_use)
|
||||
bool selectable = true;
|
||||
|
||||
/// @brief Does entity prevent blocks setup
|
||||
bool blocking = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ void Player::teleport(glm::vec3 position) {
|
|||
void Player::attemptToChooseSpawnpoint() {
|
||||
// looks bad to be here tbh
|
||||
const auto& generatorDef =
|
||||
level.content.generators.require(level.getWorld()->getGenerator());
|
||||
level.content.generators.require(level.environment.generator);
|
||||
|
||||
int minHeight = generatorDef.playerMinSpawnHeight;
|
||||
int maxHeight = generatorDef.playerMaxSpawnHeight;
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ dv::value NotePreset::serialize() const {
|
|||
{"render_distance", renderDistance},
|
||||
{"xray_opacity", xrayOpacity},
|
||||
{"perspective", perspective},
|
||||
{"font", font},
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -27,4 +28,5 @@ void NotePreset::deserialize(const dv::value& src) {
|
|||
src.at("render_distance").get(renderDistance);
|
||||
src.at("xray_opacity").get(xrayOpacity);
|
||||
src.at("perspective").get(perspective);
|
||||
src.at("font").get(font);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ struct NotePreset : public Serializable {
|
|||
float renderDistance = 32.0f;
|
||||
float xrayOpacity = 0.0f;
|
||||
float perspective = 1.0f;
|
||||
std::string font {};
|
||||
|
||||
dv::value serialize() const override;
|
||||
void deserialize(const dv::value& src) override;
|
||||
|
|
|
|||
|
|
@ -13,22 +13,22 @@
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
class ArgC {
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool()> execute;
|
||||
std::string args;
|
||||
std::string help;
|
||||
ArgC(
|
||||
const std::string& keyword,
|
||||
std::function<bool()> execute,
|
||||
const std::string& args,
|
||||
const std::string& help
|
||||
) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool(CoreParameters&, util::ArgsReader&)> execute;
|
||||
std::string args;
|
||||
std::string help;
|
||||
ArgC(
|
||||
const std::string& keyword,
|
||||
std::function<bool(CoreParameters&, util::ArgsReader&)> execute,
|
||||
const std::string& args,
|
||||
const std::string& help
|
||||
) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -36,54 +36,66 @@ static bool perform_keyword(
|
|||
util::ArgsReader& reader, const std::string& keyword, CoreParameters& params
|
||||
) {
|
||||
static const std::vector<ArgC> argumentsCommandline = {
|
||||
ArgC("--res", [¶ms, &reader]() -> bool {
|
||||
ArgC("--res", [](auto& params, auto& reader) -> bool {
|
||||
params.resFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set resources directory."),
|
||||
ArgC("--dir", [¶ms, &reader]() -> bool {
|
||||
ArgC("--dir", [](auto& params, auto& reader) -> bool {
|
||||
params.userFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set userfiles directory."),
|
||||
ArgC("--project", [¶ms, &reader]() -> bool {
|
||||
ArgC("--project", [](auto& params, auto& reader) -> bool {
|
||||
params.projectFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set project directory."),
|
||||
ArgC("--test", [¶ms, &reader]() -> bool {
|
||||
ArgC("--test", [](auto& params, auto& reader) -> bool {
|
||||
params.testMode = true;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path>", "test script file."),
|
||||
ArgC("--script", [¶ms, &reader]() -> bool {
|
||||
ArgC("--script", [](auto& params, auto& reader) -> bool {
|
||||
params.testMode = false;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path>", "main script file."),
|
||||
ArgC("--headless", [¶ms]() -> bool {
|
||||
ArgC("--headless", [](auto& params, auto& reader) -> bool {
|
||||
params.headless = true;
|
||||
return true;
|
||||
}, "", "run in headless mode."),
|
||||
ArgC("--stdin-cmd", [¶ms]() -> bool {
|
||||
ArgC("--stdin-cmd", [](auto& params, auto& reader) -> bool {
|
||||
params.stdinCommands = true;
|
||||
return true;
|
||||
}, "", "run commands from stdin."),
|
||||
ArgC("--tps", [¶ms, &reader]() -> bool {
|
||||
ArgC("--tps", [](auto& params, auto& reader) -> bool {
|
||||
params.tps = reader.nextInt();
|
||||
return true;
|
||||
}, "<tps>", "headless mode tick-rate (default - 20)."),
|
||||
ArgC("--version", []() -> bool {
|
||||
ArgC("--version", [](auto&, auto&) -> bool {
|
||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||
return false;
|
||||
}, "", "display the engine version."),
|
||||
ArgC("--dbg-server", [¶ms, &reader]() -> bool {
|
||||
ArgC("--dbg-server", [](auto& params, auto& reader) -> bool {
|
||||
params.debugServerString = reader.next();
|
||||
return true;
|
||||
}, "<serv>", "open debugging server where <serv> is {transport}:{port}"),
|
||||
ArgC("--help", []() -> bool {
|
||||
ArgC("--sub-depth", [](auto& params, auto& reader) -> bool {
|
||||
params.subProcessDepth = reader.nextInt();
|
||||
return true;
|
||||
}, "<depth>", "sub-process depth"),
|
||||
ArgC("--log", [](auto& params, auto& reader) -> bool {
|
||||
params.logFile = reader.next();
|
||||
return true;
|
||||
}, "<file>", "target log file"),
|
||||
ArgC("--help", [](auto&, auto&) -> bool {
|
||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||
std::cout << "Command-line arguments:\n";
|
||||
for (auto& a : argumentsCommandline) {
|
||||
std::cout << std::setw(24) << std::left << (a.keyword + " " + a.args);
|
||||
std::cout << "- " << a.help << std::endl;
|
||||
for (auto& arg : argumentsCommandline) {
|
||||
if (arg.help.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::cout << std::setw(24) << std::left
|
||||
<< (arg.keyword + " " + arg.args);
|
||||
std::cout << "- " << arg.help << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
|
|
@ -91,7 +103,7 @@ static bool perform_keyword(
|
|||
};
|
||||
for (auto& a : argumentsCommandline) {
|
||||
if (a.keyword == keyword) {
|
||||
return a.execute();
|
||||
return a.execute(params, reader);
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("unknown argument " + keyword);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,16 @@
|
|||
#else
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
using namespace std::literals;
|
||||
|
||||
namespace platform::internal {
|
||||
std::filesystem::path get_executable_path();
|
||||
|
|
@ -215,8 +224,102 @@ std::filesystem::path platform::get_executable_path() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void platform::new_engine_instance(
|
||||
const std::vector<std::string>& args, std::filesystem::path outputFile
|
||||
class SystemProcess final : public Process {
|
||||
public:
|
||||
#ifdef __linux__
|
||||
SystemProcess(pid_t pid) : pid(pid) {
|
||||
}
|
||||
|
||||
~SystemProcess() {
|
||||
terminate();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
SystemProcess(const PROCESS_INFORMATION& pi)
|
||||
: processHandle(pi.hProcess), pid(pi.dwProcessId) {
|
||||
if (pi.hThread != nullptr) {
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
}
|
||||
|
||||
~SystemProcess() {
|
||||
terminate();
|
||||
}
|
||||
#endif
|
||||
SystemProcess(const SystemProcess&) = delete;
|
||||
SystemProcess(SystemProcess&&) noexcept = default;
|
||||
SystemProcess& operator=(const SystemProcess&) = delete;
|
||||
|
||||
SystemProcess& operator=(SystemProcess&& other) noexcept {
|
||||
if (this != &other) {
|
||||
#ifdef __linux__
|
||||
pid = other.pid;
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
CloseHandle(processHandle);
|
||||
}
|
||||
processHandle = other.processHandle;
|
||||
pid = other.pid;
|
||||
other.processHandle = nullptr;
|
||||
other.pid = 0;
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isActive() const override {
|
||||
#ifdef __linux__
|
||||
if (kill(pid, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
return errno != ESRCH;
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DWORD exitCode;
|
||||
if (GetExitCodeProcess(processHandle, &exitCode)) {
|
||||
return exitCode == STILL_ACTIVE;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void update() override {}
|
||||
|
||||
void waitForEnd() override {
|
||||
#ifdef __linux__
|
||||
waitpid(pid, nullptr, 0);
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
WaitForSingleObject(processHandle, INFINITE);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void terminate() final override {
|
||||
#ifdef __linux__
|
||||
logger.info() << "terminating " << pid;
|
||||
kill(pid, SIGTERM);
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
TerminateProcess(processHandle, 1);
|
||||
WaitForSingleObject(processHandle, INFINITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
#ifdef __linux__
|
||||
pid_t pid;
|
||||
#elif defined(_WIN32)
|
||||
HANDLE processHandle = nullptr;
|
||||
DWORD pid = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
std::unique_ptr<Process> platform::new_engine_instance(
|
||||
const std::vector<std::string>& args,
|
||||
std::filesystem::path outputFile,
|
||||
bool subProcess
|
||||
) {
|
||||
auto executable = get_executable_path();
|
||||
|
||||
|
|
@ -255,7 +358,7 @@ void platform::new_engine_instance(
|
|||
if (!outputFile.empty()) {
|
||||
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||
si.hStdOutput = CreateFileW(
|
||||
toWString(outputFile.u8string()).data(),
|
||||
toWString(outputFile.empty() ? outputFile.u8string() : "NUL"s).data(),
|
||||
GENERIC_WRITE,
|
||||
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
||||
nullptr,
|
||||
|
|
@ -285,6 +388,38 @@ void platform::new_engine_instance(
|
|||
&si,
|
||||
&pi
|
||||
);
|
||||
if (subProcess) {
|
||||
HANDLE job = CreateJobObjectW(nullptr, nullptr);
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info{};
|
||||
info.BasicLimitInformation.LimitFlags =
|
||||
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
|
||||
try {
|
||||
if (!SetInformationJobObject(
|
||||
job,
|
||||
JobObjectExtendedLimitInformation,
|
||||
&info,
|
||||
sizeof(info))) {
|
||||
throw std::runtime_error(
|
||||
"SetInformationJobObject failed with code: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
if (!AssignProcessToJobObject(job, pi.hProcess)) {
|
||||
throw std::runtime_error(
|
||||
"SetInformationJobObject failed with code: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
return std::make_unique<SystemProcess>(pi);
|
||||
} catch (const std::exception& err) {
|
||||
CloseHandle(job);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
|
@ -295,6 +430,49 @@ void platform::new_engine_instance(
|
|||
);
|
||||
}
|
||||
#else
|
||||
|
||||
// TODO: implement
|
||||
#ifndef __APPLE__
|
||||
if (subProcess) {
|
||||
pid_t pid = fork();
|
||||
|
||||
if (outputFile.empty()) {
|
||||
outputFile = "/dev/null";
|
||||
}
|
||||
int fd = open(
|
||||
outputFile.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644
|
||||
);
|
||||
|
||||
if (pid == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
|
||||
if (getppid() == 1) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
std::vector<const char*> argv(args.size() + 2);
|
||||
argv[0] = const_cast<char*>(executable.c_str());
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
argv[i + 1] = args[i].c_str();
|
||||
}
|
||||
argv[args.size() + 1] = nullptr;
|
||||
|
||||
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
execvp(executable.c_str(), const_cast<char**>(argv.data()));
|
||||
|
||||
_exit(127);
|
||||
} else {
|
||||
close(fd);
|
||||
usleep(1000); // waiting for execvp call
|
||||
return std::make_unique<SystemProcess>(pid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::stringstream ss;
|
||||
ss << executable;
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
|
|
@ -315,6 +493,7 @@ void platform::new_engine_instance(
|
|||
);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool platform::stdin_has_data() {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
namespace platform {
|
||||
void configure_encoding();
|
||||
/// @brief Get Environment locale in ISO format ll_CC
|
||||
|
|
@ -18,8 +21,10 @@ namespace platform {
|
|||
/// @brief Get current process running executable path
|
||||
std::filesystem::path get_executable_path();
|
||||
/// @brief Run a separate engine instance with specified arguments
|
||||
void new_engine_instance(
|
||||
const std::vector<std::string>& args, std::filesystem::path outputFile
|
||||
std::unique_ptr<Process> new_engine_instance(
|
||||
const std::vector<std::string>& args,
|
||||
std::filesystem::path outputFile,
|
||||
bool subProcess
|
||||
);
|
||||
/// @brief Open URL in web browser
|
||||
bool open_url(const std::string& url);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ inline constexpr size_t MAX_USER_BLOCK_FIELDS_SIZE = 240;
|
|||
|
||||
inline constexpr int BLOCK_MAX_VARIANTS = 16;
|
||||
|
||||
inline std::string DEFAULT_MATERIAL = "base:stone";
|
||||
|
||||
struct BlockFuncsSet {
|
||||
bool init : 1;
|
||||
bool update : 1;
|
||||
|
|
@ -206,7 +204,7 @@ public:
|
|||
dv::value properties = nullptr;
|
||||
|
||||
/// @brief id of used BlockMaterial, may specify non-existing material
|
||||
std::string material = DEFAULT_MATERIAL;
|
||||
std::string material;
|
||||
|
||||
/// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive
|
||||
/// clouds)
|
||||
|
|
|
|||
|
|
@ -168,17 +168,127 @@ inline constexpr short MOUSE_KEYS_OFFSET = 1024;
|
|||
|
||||
static GLFWcursor* standard_cursors[static_cast<int>(CursorShape::LAST) + 1] = {};
|
||||
|
||||
class GLFWInput : public Input {
|
||||
class BaseInput : public Input {
|
||||
public:
|
||||
std::vector<uint> codepoints;
|
||||
int scroll = 0;
|
||||
|
||||
void onKeyCallback(int key, bool pressed) {
|
||||
bool prevPressed = keys[key];
|
||||
keys[key] = pressed;
|
||||
frames[key] = currentFrame;
|
||||
if (pressed && !prevPressed) {
|
||||
const auto& callbacks = keyCallbacks.find(static_cast<Keycode>(key));
|
||||
if (callbacks != keyCallbacks.end()) {
|
||||
callbacks->second.notify();
|
||||
}
|
||||
}
|
||||
if (pressed && key < MOUSE_KEYS_OFFSET) {
|
||||
pressedKeys.push_back(static_cast<Keycode>(key));
|
||||
}
|
||||
}
|
||||
|
||||
void onMouseCallback(int button, bool pressed) {
|
||||
int key = button + MOUSE_KEYS_OFFSET;
|
||||
onKeyCallback(key, pressed);
|
||||
}
|
||||
|
||||
bool isCursorLocked() const override {
|
||||
return cursorLocked;
|
||||
}
|
||||
|
||||
void setCursorPosition(double xpos, double ypos) {
|
||||
if (cursorDrag) {
|
||||
delta.x += xpos - cursor.x;
|
||||
delta.y += ypos - cursor.y;
|
||||
} else {
|
||||
cursorDrag = true;
|
||||
}
|
||||
cursor.x = xpos;
|
||||
cursor.y = ypos;
|
||||
}
|
||||
|
||||
Bindings& getBindings() override {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
const Bindings& getBindings() const override {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
ObserverHandler addKeyCallback(Keycode key, KeyCallback callback) override {
|
||||
return keyCallbacks[key].add(std::move(callback));
|
||||
}
|
||||
|
||||
const std::vector<Keycode>& getPressedKeys() const override {
|
||||
return pressedKeys;
|
||||
}
|
||||
|
||||
const std::vector<uint>& getCodepoints() const override {
|
||||
return codepoints;
|
||||
}
|
||||
|
||||
CursorState getCursor() const override {
|
||||
return {isCursorLocked(), cursor, delta};
|
||||
}
|
||||
|
||||
int getScroll() override {
|
||||
return scroll;
|
||||
}
|
||||
|
||||
bool pressed(Keycode key) const override {
|
||||
int keycode = static_cast<int>(key);
|
||||
if (keycode < 0 || keycode >= KEYS_BUFFER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return keys[keycode];
|
||||
}
|
||||
bool jpressed(Keycode keycode) const override {
|
||||
return pressed(keycode) &&
|
||||
frames[static_cast<int>(keycode)] == currentFrame;
|
||||
}
|
||||
|
||||
bool clicked(Mousecode code) const override {
|
||||
return pressed(
|
||||
static_cast<Keycode>(MOUSE_KEYS_OFFSET + static_cast<int>(code))
|
||||
);
|
||||
}
|
||||
bool jclicked(Mousecode code) const override {
|
||||
return clicked(code) &&
|
||||
frames[static_cast<int>(code) + MOUSE_KEYS_OFFSET] ==
|
||||
currentFrame;
|
||||
}
|
||||
|
||||
void simulateKey(Keycode key, bool pressed) override {
|
||||
onKeyCallback(static_cast<int>(key), pressed);
|
||||
}
|
||||
|
||||
void simulateClick(int button, bool pressed) override {
|
||||
onMouseCallback(static_cast<int>(button), pressed);
|
||||
}
|
||||
|
||||
void simulateCursorPos(double xpos, double ypos) override {
|
||||
setCursorPosition(xpos, ypos);
|
||||
}
|
||||
|
||||
void simulateCodepoint(uint codepoint) override {
|
||||
codepoints.push_back(codepoint);
|
||||
}
|
||||
protected:
|
||||
uint currentFrame = 0;
|
||||
uint frames[KEYS_BUFFER_SIZE] {};
|
||||
std::vector<uint> codepoints;
|
||||
std::vector<Keycode> pressedKeys;
|
||||
Bindings bindings;
|
||||
bool keys[KEYS_BUFFER_SIZE] {};
|
||||
std::unordered_map<Keycode, util::HandlersList<>> keyCallbacks;
|
||||
bool cursorLocked = false;
|
||||
bool cursorDrag = false;
|
||||
glm::vec2 delta {};
|
||||
glm::vec2 cursor {};
|
||||
};
|
||||
|
||||
class GLFWInput : public BaseInput {
|
||||
public:
|
||||
GLFWInput(GLFWwindow* window)
|
||||
: window(window) {
|
||||
}
|
||||
|
|
@ -228,26 +338,6 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void onKeyCallback(int key, bool pressed) {
|
||||
bool prevPressed = keys[key];
|
||||
keys[key] = pressed;
|
||||
frames[key] = currentFrame;
|
||||
if (pressed && !prevPressed) {
|
||||
const auto& callbacks = keyCallbacks.find(static_cast<Keycode>(key));
|
||||
if (callbacks != keyCallbacks.end()) {
|
||||
callbacks->second.notify();
|
||||
}
|
||||
}
|
||||
if (pressed && key < MOUSE_KEYS_OFFSET) {
|
||||
pressedKeys.push_back(static_cast<Keycode>(key));
|
||||
}
|
||||
}
|
||||
|
||||
void onMouseCallback(int button, bool pressed) {
|
||||
int key = button + MOUSE_KEYS_OFFSET;
|
||||
onKeyCallback(key, pressed);
|
||||
}
|
||||
|
||||
const char* getClipboardText() const override {
|
||||
return glfwGetClipboardString(window);
|
||||
}
|
||||
|
|
@ -256,41 +346,6 @@ public:
|
|||
glfwSetClipboardString(window, text);
|
||||
}
|
||||
|
||||
int getScroll() override {
|
||||
return scroll;
|
||||
}
|
||||
|
||||
bool pressed(Keycode key) const override {
|
||||
int keycode = static_cast<int>(key);
|
||||
if (keycode < 0 || keycode >= KEYS_BUFFER_SIZE) {
|
||||
return false;
|
||||
}
|
||||
return keys[keycode];
|
||||
}
|
||||
bool jpressed(Keycode keycode) const override {
|
||||
return pressed(keycode) &&
|
||||
frames[static_cast<int>(keycode)] == currentFrame;
|
||||
}
|
||||
|
||||
bool clicked(Mousecode code) const override {
|
||||
return pressed(
|
||||
static_cast<Keycode>(MOUSE_KEYS_OFFSET + static_cast<int>(code))
|
||||
);
|
||||
}
|
||||
bool jclicked(Mousecode code) const override {
|
||||
return clicked(code) &&
|
||||
frames[static_cast<int>(code) + MOUSE_KEYS_OFFSET] ==
|
||||
currentFrame;
|
||||
}
|
||||
|
||||
CursorState getCursor() const override {
|
||||
return {isCursorLocked(), cursor, delta};
|
||||
}
|
||||
|
||||
bool isCursorLocked() const override {
|
||||
return cursorLocked;
|
||||
}
|
||||
|
||||
void toggleCursor() override {
|
||||
cursorDrag = false;
|
||||
if (cursorLocked) {
|
||||
|
|
@ -301,43 +356,8 @@ public:
|
|||
}
|
||||
cursorLocked = !cursorLocked;
|
||||
}
|
||||
|
||||
void setCursorPosition(double xpos, double ypos) {
|
||||
if (cursorDrag) {
|
||||
delta.x += xpos - cursor.x;
|
||||
delta.y += ypos - cursor.y;
|
||||
} else {
|
||||
cursorDrag = true;
|
||||
}
|
||||
cursor.x = xpos;
|
||||
cursor.y = ypos;
|
||||
}
|
||||
|
||||
Bindings& getBindings() override {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
const Bindings& getBindings() const override {
|
||||
return bindings;
|
||||
}
|
||||
|
||||
ObserverHandler addKeyCallback(Keycode key, KeyCallback callback) override {
|
||||
return keyCallbacks[key].add(std::move(callback));
|
||||
}
|
||||
|
||||
const std::vector<Keycode>& getPressedKeys() const override {
|
||||
return pressedKeys;
|
||||
}
|
||||
|
||||
const std::vector<uint>& getCodepoints() const override {
|
||||
return codepoints;
|
||||
}
|
||||
private:
|
||||
GLFWwindow* window;
|
||||
bool cursorLocked = false;
|
||||
bool cursorDrag = false;
|
||||
glm::vec2 delta {};
|
||||
glm::vec2 cursor {};
|
||||
};
|
||||
static_assert(!std::is_abstract<GLFWInput>());
|
||||
|
||||
|
|
|
|||
|
|
@ -274,6 +274,11 @@ public:
|
|||
virtual bool clicked(Mousecode mousecode) const = 0;
|
||||
virtual bool jclicked(Mousecode mousecode) const = 0;
|
||||
|
||||
virtual void simulateKey(Keycode key, bool pressed) = 0;
|
||||
virtual void simulateClick(int button, bool pressed) = 0;
|
||||
virtual void simulateCursorPos(double xpos, double ypos) = 0;
|
||||
virtual void simulateCodepoint(uint codepoint) = 0;
|
||||
|
||||
virtual CursorState getCursor() const = 0;
|
||||
|
||||
virtual bool isCursorLocked() const = 0;
|
||||
|
|
|
|||
49
src/world/Environment.cpp
Normal file
49
src/world/Environment.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#define VC_ENABLE_REFLECTION
|
||||
#include "Environment.hpp"
|
||||
|
||||
#include "data/dv.hpp"
|
||||
#include "data/dv_util.hpp"
|
||||
|
||||
dv::value Environment::serialize() const {
|
||||
auto skySpritesList = dv::list();
|
||||
for (const auto& sprite : sky.sprites) {
|
||||
skySpritesList.add(dv::object({
|
||||
{"texture", sprite.texture},
|
||||
{"phase", sprite.phase},
|
||||
{"distance", sprite.distance},
|
||||
{"emissive", sprite.emissive},
|
||||
{"altitude", sprite.altitude},
|
||||
}));
|
||||
}
|
||||
return dv::object({
|
||||
{"sky", dv::object({
|
||||
{"mode", SkyModeMeta.getNameString(sky.mode)},
|
||||
{"stars", sky.stars},
|
||||
{"clouds", sky.clouds},
|
||||
{"sprites", std::move(skySpritesList)},
|
||||
})},
|
||||
{"generator", generator}
|
||||
});
|
||||
}
|
||||
|
||||
void Environment::deserialize(const dv::value& src) {
|
||||
if (auto skyItem = src.at("sky")) {
|
||||
skyItem->at("mode").get(sky.mode, SkyModeMeta);
|
||||
skyItem->at("stars").get(sky.stars);
|
||||
skyItem->at("clouds").get(sky.clouds);
|
||||
|
||||
if (auto skySpritesList = skyItem->at("sprites")) {
|
||||
for (int i = 0; i < skySpritesList->size(); i++) {
|
||||
SkySprite sprite;
|
||||
const auto& item = (*skySpritesList.ptr)[i];
|
||||
item.at("texture").get(sprite.texture);
|
||||
item.at("phase").get(sprite.phase);
|
||||
item.at("distance").get(sprite.distance);
|
||||
item.at("emissive").get(sprite.emissive);
|
||||
item.at("altitude").get(sprite.altitude);
|
||||
sky.sprites.push_back(std::move(sprite));
|
||||
}
|
||||
}
|
||||
}
|
||||
src.at("generator").get(generator);
|
||||
}
|
||||
66
src/world/Environment.hpp
Normal file
66
src/world/Environment.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#pragma once
|
||||
|
||||
#include "interfaces/Serializable.hpp"
|
||||
#include "util/EnumMetadata.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
|
||||
enum class SkyMode {
|
||||
NONE, SOLID, BOX
|
||||
};
|
||||
|
||||
VC_ENUM_METADATA(SkyMode)
|
||||
{"none", SkyMode::NONE},
|
||||
{"solid", SkyMode::SOLID},
|
||||
{"box", SkyMode::BOX},
|
||||
VC_ENUM_END
|
||||
|
||||
struct SkySprite {
|
||||
std::string texture;
|
||||
float phase;
|
||||
float distance;
|
||||
bool emissive;
|
||||
float altitude;
|
||||
};
|
||||
|
||||
class Environment : public Serializable {
|
||||
public:
|
||||
struct {
|
||||
SkyMode mode = SkyMode::BOX;
|
||||
bool stars = true;
|
||||
bool clouds = true;
|
||||
// TODO: move defaults to config file
|
||||
std::vector<SkySprite> sprites {
|
||||
SkySprite {
|
||||
"misc/moon",
|
||||
glm::pi<float>() * 0.5f,
|
||||
4.0f,
|
||||
false,
|
||||
glm::pi<float>() * 0.25f,
|
||||
},
|
||||
SkySprite {
|
||||
"misc/moon_flare",
|
||||
glm::pi<float>() * 0.5f,
|
||||
0.5f,
|
||||
false,
|
||||
glm::pi<float>() * 0.25f,
|
||||
},
|
||||
SkySprite {
|
||||
"misc/sun",
|
||||
glm::pi<float>() * 1.5f,
|
||||
4.0f,
|
||||
true,
|
||||
glm::pi<float>() * 0.25f,
|
||||
},
|
||||
};
|
||||
} sky;
|
||||
|
||||
std::string generator;
|
||||
|
||||
Environment() = default;
|
||||
|
||||
dv::value serialize() const override;
|
||||
void deserialize(const dv::value& src) override;
|
||||
private:
|
||||
};
|
||||
|
|
@ -31,6 +31,7 @@ Level::Level(
|
|||
entities(std::make_unique<Entities>(*this)),
|
||||
players(std::make_unique<Players>(*this)),
|
||||
pathfinding(std::make_unique<voxels::Pathfinding>(*this)) {
|
||||
|
||||
const auto& worldInfo = world->getInfo();
|
||||
auto& cameraIndices = content.getIndices(ResourceType::CAMERA);
|
||||
for (size_t i = 0; i < cameraIndices.size(); i++) {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <unordered_map>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "Environment.hpp"
|
||||
|
||||
class Content;
|
||||
class World;
|
||||
|
|
@ -36,6 +37,7 @@ public:
|
|||
std::unique_ptr<Players> players;
|
||||
std::unique_ptr<voxels::Pathfinding> pathfinding;
|
||||
std::vector<std::shared_ptr<Camera>> cameras; // move somewhere?
|
||||
Environment environment {};
|
||||
|
||||
Level(
|
||||
std::unique_ptr<World> world,
|
||||
|
|
|
|||
|
|
@ -65,12 +65,12 @@ void World::writeResources(const Content& content) {
|
|||
io::write_json(wfile->getResourcesFile(), root);
|
||||
}
|
||||
|
||||
void World::write(Level* level) {
|
||||
level->chunks->saveAll();
|
||||
info.nextEntityId = level->entities->peekNextID();
|
||||
void World::write(Level& level) {
|
||||
level.chunks->saveAll();
|
||||
info.nextEntityId = level.entities->peekNextID();
|
||||
wfile->write(this, &content);
|
||||
|
||||
auto playerFile = level->players->serialize();
|
||||
auto playerFile = level.players->serialize();
|
||||
io::write_json(wfile->getPlayerFile(), playerFile);
|
||||
|
||||
writeResources(content);
|
||||
|
|
@ -78,7 +78,8 @@ void World::write(Level* level) {
|
|||
|
||||
std::unique_ptr<Level> World::create(
|
||||
const std::string& name,
|
||||
const std::string& generator,
|
||||
const std::string& environment,
|
||||
const std::string& generatorOverride,
|
||||
const io::path& directory,
|
||||
uint64_t seed,
|
||||
EngineSettings& settings,
|
||||
|
|
@ -87,7 +88,8 @@ std::unique_ptr<Level> World::create(
|
|||
) {
|
||||
WorldInfo info {};
|
||||
info.name = name;
|
||||
info.generator = generator;
|
||||
info.environment = environment;
|
||||
info.explicitGenerator = generatorOverride;
|
||||
info.seed = seed;
|
||||
auto world = std::make_unique<World>(
|
||||
info,
|
||||
|
|
@ -101,7 +103,7 @@ std::unique_ptr<Level> World::create(
|
|||
logger.info() << "created world '" << name << "' ("
|
||||
<< directory.string() << ")";
|
||||
}
|
||||
logger.info() << "world seed: " << seed << " generator: " << generator;
|
||||
logger.info() << "world seed: " << seed << " environment: " << environment;
|
||||
return std::make_unique<Level>(std::move(world), content, settings);
|
||||
}
|
||||
|
||||
|
|
@ -122,16 +124,19 @@ std::unique_ptr<Level> World::load(
|
|||
<< worldFilesPtr->getFolder().string() << ")";
|
||||
logger.info() << "world version: " << info->major << "." << info->minor
|
||||
<< " seed: " << info->seed
|
||||
<< " generator: " << info->generator;
|
||||
<< " environment: " << info->environment;
|
||||
|
||||
auto world = std::make_unique<World>(
|
||||
auto worldPtr = std::make_unique<World>(
|
||||
info.value(), std::move(worldFilesPtr), content, packs
|
||||
);
|
||||
auto world = worldPtr.get();
|
||||
auto& wfile = world->wfile;
|
||||
wfile->readResourcesData(content);
|
||||
|
||||
auto level = std::make_unique<Level>(std::move(world), content, settings);
|
||||
|
||||
auto level = std::make_unique<Level>(std::move(worldPtr), content, settings);
|
||||
if (world->getEnvironment().empty()) {
|
||||
level->environment.generator = world->getInfo().explicitGenerator;
|
||||
}
|
||||
io::path file = wfile->getPlayerFile();
|
||||
if (!io::is_regular_file(file)) {
|
||||
logger.warning() << "player.json does not exists";
|
||||
|
|
@ -163,8 +168,8 @@ void World::setName(const std::string& name) {
|
|||
this->info.name = name;
|
||||
}
|
||||
|
||||
void World::setGenerator(const std::string& generator) {
|
||||
this->info.generator = generator;
|
||||
void World::setEnvironment(const std::string& environment) {
|
||||
this->info.environment = environment;
|
||||
}
|
||||
|
||||
bool World::hasPack(const std::string& id) const {
|
||||
|
|
@ -186,8 +191,8 @@ uint64_t World::getSeed() const {
|
|||
return info.seed;
|
||||
}
|
||||
|
||||
std::string World::getGenerator() const {
|
||||
return info.generator;
|
||||
std::string World::getEnvironment() const {
|
||||
return info.environment;
|
||||
}
|
||||
|
||||
const std::vector<ContentPack>& World::getPacks() const {
|
||||
|
|
@ -196,7 +201,8 @@ const std::vector<ContentPack>& World::getPacks() const {
|
|||
|
||||
void WorldInfo::deserialize(const dv::value& root) {
|
||||
name = root["name"].asString();
|
||||
generator = root["generator"].asString(generator);
|
||||
root.at("environment").get(environment);
|
||||
root.at("generator").get(explicitGenerator);
|
||||
seed = root["seed"].asInteger(seed);
|
||||
|
||||
if (root.has("version")) {
|
||||
|
|
@ -226,7 +232,11 @@ dv::value WorldInfo::serialize() const {
|
|||
versionobj["minor"] = ENGINE_VERSION_MINOR;
|
||||
|
||||
root["name"] = name;
|
||||
root["generator"] = generator;
|
||||
if (environment.empty()) {
|
||||
root["generator"] = explicitGenerator;
|
||||
} else {
|
||||
root["environment"] = environment;
|
||||
}
|
||||
root["seed"] = seed;
|
||||
|
||||
auto& timeobj = root.object("time");
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ public:
|
|||
|
||||
struct WorldInfo : public Serializable {
|
||||
std::string name;
|
||||
std::string generator;
|
||||
std::string environment;
|
||||
std::string explicitGenerator;
|
||||
uint64_t seed;
|
||||
int64_t nextInventoryId = 1;
|
||||
int64_t nextPlayerId = 0;
|
||||
|
|
@ -76,7 +77,7 @@ public:
|
|||
void updateTimers(float delta);
|
||||
|
||||
/// @brief Write all unsaved level data to the world directory
|
||||
void write(Level* level);
|
||||
void write(Level& level);
|
||||
|
||||
/// @brief Check world indices and generate ContentReport if convert required
|
||||
/// @param directory world directory
|
||||
|
|
@ -98,7 +99,8 @@ public:
|
|||
/// @return Level instance containing World instance
|
||||
static std::unique_ptr<Level> create(
|
||||
const std::string& name,
|
||||
const std::string& generator,
|
||||
const std::string& environment,
|
||||
const std::string& generatorOverride,
|
||||
const io::path& directory,
|
||||
uint64_t seed,
|
||||
EngineSettings& settings,
|
||||
|
|
@ -123,7 +125,7 @@ public:
|
|||
|
||||
void setName(const std::string& name);
|
||||
void setSeed(uint64_t seed);
|
||||
void setGenerator(const std::string& generator);
|
||||
void setEnvironment(const std::string& environment);
|
||||
|
||||
/// @brief Check if world has content-pack installed
|
||||
/// @param id content-pack id
|
||||
|
|
@ -137,7 +139,7 @@ public:
|
|||
uint64_t getSeed() const;
|
||||
|
||||
/// @brief Get world generator id
|
||||
std::string getGenerator() const;
|
||||
std::string getEnvironment() const;
|
||||
|
||||
bool isNameless() const {
|
||||
return info.name.empty();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue