This commit is contained in:
Xertis
2025-09-30 23:45:25 +03:00
181 changed files with 4659 additions and 1546 deletions
+3 -3
View File
@@ -71,8 +71,8 @@ static auto process_program(const ResPaths& paths, const std::string& filename)
auto& preprocessor = *Shader::preprocessor;
auto vertex = preprocessor.process(vertexFile, vertexSource);
auto fragment = preprocessor.process(fragmentFile, fragmentSource);
auto vertex = preprocessor.process(vertexFile, vertexSource, false, {});
auto fragment = preprocessor.process(fragmentFile, fragmentSource, false, {});
return std::make_pair(vertex, fragment);
}
@@ -121,7 +121,7 @@ assetload::postfunc assetload::posteffect(
auto& preprocessor = *Shader::preprocessor;
preprocessor.addHeader(
"__effect__", preprocessor.process(effectFile, effectSource, true)
"__effect__", preprocessor.process(effectFile, effectSource, true, {})
);
auto [vertex, fragment] = process_program(paths, SHADERS_FOLDER + "/effect");
+45 -8
View File
@@ -22,6 +22,10 @@ void GLSLExtension::setPaths(const ResPaths* paths) {
this->paths = paths;
}
void GLSLExtension::setTraceOutput(bool enabled) {
this->traceOutput = enabled;
}
void GLSLExtension::loadHeader(const std::string& name) {
if (paths == nullptr) {
return;
@@ -29,7 +33,7 @@ void GLSLExtension::loadHeader(const std::string& name) {
io::path file = paths->find("shaders/lib/" + name + ".glsl");
std::string source = io::read_string(file);
addHeader(name, {});
addHeader(name, process(file, source, true));
addHeader(name, process(file, source, true, {}));
}
void GLSLExtension::addHeader(const std::string& name, ProcessingResult header) {
@@ -123,13 +127,22 @@ static Value default_value_for(Type type) {
class GLSLParser : public BasicParser<char> {
public:
GLSLParser(GLSLExtension& glsl, std::string_view file, std::string_view source, bool header)
GLSLParser(
GLSLExtension& glsl,
std::string_view file,
std::string_view source,
bool header,
const std::vector<std::string>& defines
)
: BasicParser(file, source), glsl(glsl) {
if (!header) {
ss << "#version " << GLSLExtension::VERSION << '\n';
}
for (auto& entry : glsl.getDefines()) {
ss << "#define " << entry.first << " " << entry.second << '\n';
for (auto& entry : defines) {
ss << "#define " << entry << '\n';
}
for (auto& entry : defines) {
ss << "#define " << entry << '\n';
}
}
uint linenum = 1;
source_line(ss, linenum);
@@ -289,10 +302,34 @@ private:
std::stringstream ss;
};
static void trace_output(
const io::path& file,
const std::string& source,
const GLSLExtension::ProcessingResult& result
) {
std::stringstream ss;
ss << "export:trace/" << file.name();
io::path outfile = ss.str();
try {
io::create_directories(outfile.parent());
io::write_string(outfile, result.code);
} catch (const std::runtime_error& err) {
logger.error() << "error on saving GLSLExtension::preprocess output ("
<< outfile.string() << "): " << err.what();
}
}
GLSLExtension::ProcessingResult GLSLExtension::process(
const io::path& file, const std::string& source, bool header
const io::path& file,
const std::string& source,
bool header,
const std::vector<std::string>& defines
) {
std::string filename = file.string();
GLSLParser parser(*this, filename, source, header);
return parser.process();
GLSLParser parser(*this, filename, source, header, defines);
auto result = parser.process();
if (traceOutput) {
trace_output(file, source, result);
}
return result;
}
+5 -1
View File
@@ -5,6 +5,7 @@
#include <vector>
#include "io/io.hpp"
#include "data/setting.hpp"
#include "graphics/core/PostEffect.hpp"
class ResPaths;
@@ -19,6 +20,7 @@ public:
};
void setPaths(const ResPaths* paths);
void setTraceOutput(bool enabled);
void define(const std::string& name, std::string value);
void undefine(const std::string& name);
@@ -37,7 +39,8 @@ public:
ProcessingResult process(
const io::path& file,
const std::string& source,
bool header = false
bool header,
const std::vector<std::string>& defines
);
static inline std::string VERSION = "330 core";
@@ -46,4 +49,5 @@ private:
std::unordered_map<std::string, std::string> defines;
const ResPaths* paths = nullptr;
bool traceOutput = false;
};
+2 -2
View File
@@ -6,7 +6,7 @@
#include <string>
inline constexpr int ENGINE_VERSION_MAJOR = 0;
inline constexpr int ENGINE_VERSION_MINOR = 29;
inline constexpr int ENGINE_VERSION_MINOR = 30;
#ifdef NDEBUG
inline constexpr bool ENGINE_DEBUG_BUILD = false;
@@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false;
inline constexpr bool ENGINE_DEBUG_BUILD = true;
#endif // NDEBUG
inline const std::string ENGINE_VERSION_STRING = "0.29";
inline const std::string ENGINE_VERSION_STRING = "0.30";
/// @brief world regions format version
inline constexpr uint REGION_FORMAT_VERSION = 3;
+3 -1
View File
@@ -35,13 +35,15 @@ Content::Content(
UptrsMap<std::string, BlockMaterial> blockMaterials,
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
ResourceIndicesSet resourceIndices,
dv::value defaults
dv::value defaults,
std::unordered_map<std::string, int> tags
)
: indices(std::move(indices)),
packs(std::move(packs)),
blockMaterials(std::move(blockMaterials)),
skeletons(std::move(skeletons)),
defaults(std::move(defaults)),
tags(std::move(tags)),
blocks(std::move(blocks)),
items(std::move(items)),
entities(std::move(entities)),
+11 -1
View File
@@ -176,6 +176,7 @@ class Content {
UptrsMap<std::string, BlockMaterial> blockMaterials;
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
dv::value defaults = nullptr;
std::unordered_map<std::string, int> tags;
public:
ContentUnitDefs<Block> blocks;
ContentUnitDefs<ItemDef> items;
@@ -195,7 +196,8 @@ public:
UptrsMap<std::string, BlockMaterial> blockMaterials,
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
ResourceIndicesSet resourceIndices,
dv::value defaults
dv::value defaults,
std::unordered_map<std::string, int> tags
);
~Content();
@@ -211,6 +213,14 @@ public:
return defaults;
}
int getTagIndex(const std::string& tag) const {
const auto& found = tags.find(tag);
if (found == tags.end()) {
return -1;
}
return found->second;
}
const rigging::SkeletonConfig* getSkeleton(const std::string& id) const;
const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const;
const BlockMaterial* findBlockMaterial(const std::string& id) const;
+6 -2
View File
@@ -28,6 +28,9 @@ std::unique_ptr<Content> ContentBuilder::build() {
// Generating runtime info
def.rt.id = blockDefsIndices.size();
def.rt.emissive = *reinterpret_cast<uint32_t*>(def.emission);
for (const auto& tag : def.tags) {
def.rt.tags.insert(tags.add(tag));
}
if (def.variants) {
for (auto& variant : def.variants->variants) {
@@ -58,7 +61,7 @@ std::unique_ptr<Content> ContentBuilder::build() {
}
blockDefsIndices.push_back(&def);
groups->insert(def.defaults.drawGroup); // FIXME
groups->insert(def.defaults.drawGroup); // FIXME: variants
}
std::vector<ItemDef*> itemDefsIndices;
@@ -93,7 +96,8 @@ std::unique_ptr<Content> ContentBuilder::build() {
std::move(blockMaterials),
std::move(skeletons),
std::move(resourceIndices),
std::move(defaults)
std::move(defaults),
std::move(tags.map)
);
// Now, it's time to resolve foreign keys
+22
View File
@@ -62,6 +62,27 @@ public:
}
};
struct TagsIndices {
int nextIndex = 1;
std::unordered_map<std::string, int> map;
int add(const std::string& tag) {
const auto& found = map.find(tag);
if (found != map.end()) {
return found->second;
}
return map[tag] = nextIndex++;
}
int indexOf(const std::string& tag) {
const auto& found = map.find(tag);
if (found == map.end()) {
return -1;
}
return found->second;
}
};
class ContentBuilder {
UptrsMap<std::string, BlockMaterial> blockMaterials;
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
@@ -74,6 +95,7 @@ public:
ContentUnitBuilder<GeneratorDef> generators {allNames, ContentType::GENERATOR};
ResourceIndicesSet resourceIndices {};
dv::value defaults = nullptr;
TagsIndices tags {};
~ContentBuilder();
+20
View File
@@ -288,6 +288,7 @@ void ContentLoader::loadContent(const dv::value& root) {
item.iconType = ItemIconType::BLOCK;
item.icon = def.name;
item.placingBlock = def.name;
item.tags = def.tags;
for (uint j = 0; j < 4; j++) {
item.emission[j] = def.emission[j];
@@ -412,6 +413,25 @@ void ContentLoader::load() {
if (io::exists(contentFile)) {
loadContent(io::read_json(contentFile));
}
// Load attached tags
io::path tagsFile = folder / "tags.toml";
if (io::exists(tagsFile)) {
auto tagsMap = io::read_object(tagsFile);
for (const auto& [key, list] : tagsMap.asObject()) {
for (const auto& id : list) {
const auto& stringId = id.asString();
if (auto block = builder.blocks.get(stringId)) {
block->tags.push_back(key);
if (auto item = builder.items.get(stringId + BLOCK_ITEM_SUFFIX)) {
item->tags.push_back(key);
}
} else if (auto item = builder.items.get(stringId)) {
item->tags.push_back(key);
}
}
}
}
}
template <class T>
+41 -4
View File
@@ -118,21 +118,58 @@ ContentPack ContentPack::read(const io::path& folder) {
const auto& dependencies = *found;
for (const auto& elem : dependencies) {
std::string depName = elem.asString();
auto level = DependencyLevel::required;
auto level = DependencyLevel::REQUIRED;
switch (depName.at(0)) {
case '!':
depName = depName.substr(1);
break;
case '?':
depName = depName.substr(1);
level = DependencyLevel::optional;
level = DependencyLevel::OPTIONAL;
break;
case '~':
depName = depName.substr(1);
level = DependencyLevel::weak;
level = DependencyLevel::WEAK;
break;
}
pack.dependencies.push_back({level, depName});
std::string depVer = "*";
std::string depVerOperator = "=";
size_t versionPos = depName.rfind("@");
if (versionPos != std::string::npos) {
depVer = depName.substr(versionPos + 1);
depName = depName.substr(0, versionPos);
if (depVer.size() >= 2) {
std::string op = depVer.substr(0, 2);
std::uint8_t op_size = 0;
// Two symbol operators
if (op == ">=" || op == "=>" || op == "<=" || op == "=<") {
op_size = 2;
depVerOperator = op;
}
// One symbol operators
else {
op = depVer.substr(0, 1);
if (op == ">" || op == "<") {
op_size = 1;
depVerOperator = op;
}
}
depVer = depVer.substr(op_size);
} else {
if (depVer == ">" || depVer == "<"){
depVer = "*";
}
}
}
pack.dependencies.push_back({level, depName, depVer, depVerOperator});
}
}
+11 -4
View File
@@ -20,21 +20,28 @@ public:
io::path folder,
const std::string& message
);
std::string getPackId() const;
io::path getFolder() const;
};
enum class DependencyVersionOperator {
EQUAL, GREATHER, LESS,
GREATHER_OR_EQUAL, LESS_OR_EQUAL
};
enum class DependencyLevel {
required, // dependency must be installed
optional, // dependency will be installed if found
weak, // only affects packs order
REQUIRED, // dependency must be installed
OPTIONAL, // dependency will be installed if found
WEAK, // only affects packs order
};
/// @brief Content-pack that should be installed earlier the dependent
struct DependencyPack {
DependencyLevel level;
std::string id;
std::string version;
std::string op;
};
struct ContentPackStats {
+67
View File
@@ -0,0 +1,67 @@
#include "ContentPackVersion.hpp"
#include <algorithm>
#include <iostream>
#include <sstream>
#include "coders/commons.hpp"
Version::Version(const std::string& version) {
major = 0;
minor = 0;
patch = 0;
std::vector<int> parts;
std::stringstream ss(version);
std::string part;
while (std::getline(ss, part, '.')) {
if (!part.empty()) {
parts.push_back(std::stoi(part));
}
}
if (parts.size() > 0) major = parts[0];
if (parts.size() > 1) minor = parts[1];
if (parts.size() > 2) patch = parts[2];
}
DependencyVersionOperator Version::string_to_operator(const std::string& op) {
if (op == "=")
return DependencyVersionOperator::EQUAL;
else if (op == ">")
return DependencyVersionOperator::GREATHER;
else if (op == "<")
return DependencyVersionOperator::LESS;
else if (op == ">=" || op == "=>")
return DependencyVersionOperator::GREATHER_OR_EQUAL;
else if (op == "<=" || op == "=<")
return DependencyVersionOperator::LESS_OR_EQUAL;
else
return DependencyVersionOperator::EQUAL;
}
bool isNumber(const std::string& s) {
return !s.empty() && std::all_of(s.begin(), s.end(), ::is_digit);
}
bool Version::matches_pattern(const std::string& version) {
for (char c : version) {
if (!isdigit(c) && c != '.') {
return false;
}
}
std::stringstream ss(version);
std::vector<std::string> parts;
std::string part;
while (std::getline(ss, part, '.')) {
if (part.empty()) return false;
if (!isNumber(part)) return false;
parts.push_back(part);
}
return parts.size() == 2 || parts.size() == 3;
}
+57
View File
@@ -0,0 +1,57 @@
#include <string>
#include "content/ContentPack.hpp"
class Version {
public:
int major;
int minor;
int patch;
Version(const std::string& version);
bool operator==(const Version& other) const {
return major == other.major && minor == other.minor &&
patch == other.patch;
}
bool operator<(const Version& other) const {
if (major != other.major) return major < other.major;
if (minor != other.minor) return minor < other.minor;
return patch < other.patch;
}
bool operator>(const Version& other) const {
return other < *this;
}
bool operator>=(const Version& other) const {
return !(*this < other);
}
bool operator<=(const Version& other) const {
return !(*this > other);
}
bool process_operator(const std::string& op, const Version& other) const {
auto dep_op = Version::string_to_operator(op);
switch (dep_op) {
case DependencyVersionOperator::EQUAL:
return *this == other;
case DependencyVersionOperator::GREATHER:
return *this > other;
case DependencyVersionOperator::LESS:
return *this < other;
case DependencyVersionOperator::LESS_OR_EQUAL:
return *this <= other;
case DependencyVersionOperator::GREATHER_OR_EQUAL:
return *this >= other;
default:
return false;
}
}
static DependencyVersionOperator string_to_operator(const std::string& op);
static bool matches_pattern(const std::string& version);
};
+21 -3
View File
@@ -3,6 +3,7 @@
#include <queue>
#include <sstream>
#include "ContentPackVersion.hpp"
#include "util/listutil.hpp"
PacksManager::PacksManager() = default;
@@ -90,7 +91,7 @@ static bool resolve_dependencies(
}
auto found = packs.find(dep.id);
bool exists = found != packs.end();
if (!exists && dep.level == DependencyLevel::required) {
if (!exists && dep.level == DependencyLevel::REQUIRED) {
throw contentpack_error(
dep.id, io::path(), "dependency of '" + pack->id + "'"
);
@@ -99,15 +100,32 @@ static bool resolve_dependencies(
// ignored for optional or weak dependencies
continue;
}
if (resolveWeaks && dep.level == DependencyLevel::weak) {
if (resolveWeaks && dep.level == DependencyLevel::WEAK) {
// dependency pack is found but not added yet
// resolveWeaks is used on second iteration, so it's will not be
// added
continue;
}
auto dep_pack = found -> second;
if (Version::matches_pattern(dep.version) && Version::matches_pattern(dep_pack.version)
&& Version(dep_pack.version)
.process_operator(dep.op, Version(dep.version))
) {
// dependency pack version meets the required one
continue;
} else if (dep.version == "*" || dep.version == dep_pack.version){
// fallback: dependency pack version also meets required one
continue;
} else {
throw contentpack_error(
dep.id, io::path(), "does not meet required version '" + dep.op + dep.version +"' of '" + pack->id + "'"
);
}
if (!util::contains(allNames, dep.id) &&
dep.level != DependencyLevel::weak) {
dep.level != DependencyLevel::WEAK) {
allNames.push_back(dep.id);
queue.push(&found->second);
}
+3 -14
View File
@@ -1,5 +1,6 @@
#define VC_ENABLE_REFLECTION
#include "ContentUnitLoader.hpp"
#include "ContentLoadingCommons.hpp"
#include "../ContentBuilder.hpp"
#include "coders/json.hpp"
@@ -87,20 +88,8 @@ template<> void ContentUnitLoader<Block>::loadUnit(
Block& def, const std::string& name, const io::path& file
) {
auto root = io::read_json(file);
if (def.properties == nullptr) {
def.properties = dv::object();
def.properties["name"] = name;
}
for (auto& [key, value] : root.asObject()) {
auto pos = key.rfind('@');
if (pos == std::string::npos) {
def.properties[key] = value;
continue;
}
auto field = key.substr(0, pos);
auto suffix = key.substr(pos + 1);
process_method(def.properties, suffix, field, value);
}
process_properties(def, name, root);
process_tags(def, root);
if (root.has("parent")) {
const auto& parentName = root["parent"].asString();
@@ -0,0 +1,37 @@
#pragma once
#include "data/dv.hpp"
#include <string>
template <typename T>
inline void process_properties(T& def, const std::string& name, const dv::value& root) {
if (def.properties == nullptr) {
def.properties = dv::object();
def.properties["name"] = name;
}
for (auto& [key, value] : root.asObject()) {
auto pos = key.rfind('@');
if (pos == std::string::npos) {
def.properties[key] = value;
continue;
}
auto field = key.substr(0, pos);
auto suffix = key.substr(pos + 1);
process_method(def.properties, suffix, field, value);
}
}
template <typename T>
inline void process_tags(T& def, const dv::value& root) {
if (!root.has("tags")) {
return;
}
const auto& tags = root["tags"];
for (const auto& tagValue : tags) {
if (!tagValue.isString()) {
continue;
}
def.tags.push_back(tagValue.asString());
}
}
+12 -1
View File
@@ -30,7 +30,18 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
if (auto found = root.at("components")) {
for (const auto& elem : *found) {
def.components.emplace_back(elem.asString());
std::string name;
dv::value params;
if (elem.isObject()) {
name = elem["name"].asString();
if (elem.has("args")) {
params = elem["args"];
}
} else {
name = elem.asString();
}
def.components.push_back(ComponentInstance {
std::move(name), std::move(params)});
}
}
if (auto found = root.at("hitbox")) {
+4 -1
View File
@@ -1,5 +1,6 @@
#define VC_ENABLE_REFLECTION
#include "ContentUnitLoader.hpp"
#include "ContentLoadingCommons.hpp"
#include "../ContentBuilder.hpp"
#include "coders/json.hpp"
@@ -12,11 +13,13 @@
static debug::Logger logger("item-content-loader");
template<> void ContentUnitLoader<ItemDef>::loadUnit(
ItemDef& def, const std::string& name, const io::path& file
) {
auto root = io::read_json(file);
def.properties = root;
process_properties(def, name, root);
process_tags(def, root);
if (root.has("parent")) {
const auto& parentName = root["parent"].asString();
+3 -3
View File
@@ -46,12 +46,12 @@ namespace dv {
if (!map.has(key)) {
return;
}
auto& list = map[key];
const auto& srcList = map[key];
for (size_t i = 0; i < n; i++) {
if constexpr (std::is_floating_point<T>()) {
vec[i] = list[i].asNumber();
vec[i] = srcList[i].asNumber();
} else {
vec[i] = list[i].asInteger();
vec[i] = srcList[i].asInteger();
}
}
}
+6
View File
@@ -143,6 +143,12 @@ void Engine::initializeClient() {
},
true
));
keepAlive(settings.debug.doTraceShaders.observe(
[](bool value) {
Shader::preprocessor->setTraceOutput(value);
},
true
));
}
void Engine::initialize(CoreParameters coreParameters) {
+15
View File
@@ -12,12 +12,14 @@
#include "graphics/render/WorldRenderer.hpp"
#include "graphics/render/ParticlesRenderer.hpp"
#include "graphics/render/ChunksRenderer.hpp"
#include "graphics/render/DebugLinesRenderer.hpp"
#include "logic/scripting/scripting.hpp"
#include "network/Network.hpp"
#include "objects/Player.hpp"
#include "objects/Players.hpp"
#include "objects/Entities.hpp"
#include "objects/EntityDef.hpp"
#include "objects/Entity.hpp"
#include "physics/Hitbox.hpp"
#include "util/stringutil.hpp"
#include "voxels/Block.hpp"
@@ -44,6 +46,7 @@ static std::shared_ptr<Label> create_label(GUI& gui, wstringsupplier supplier) {
// TODO: move to xml
// TODO: move to xml finally
// TODO: move to xml finally
// TODO: move to xml finally
std::shared_ptr<UINode> create_debug_panel(
Engine& engine,
Level& level,
@@ -260,6 +263,18 @@ std::shared_ptr<UINode> create_debug_panel(
});
panel->add(checkbox);
}
{
auto checkbox = std::make_shared<FullCheckBox>(
gui, L"Show Paths", glm::vec2(400, 24)
);
checkbox->setSupplier([=]() {
return DebugLinesRenderer::showPaths;
});
checkbox->setConsumer([=](bool checked) {
DebugLinesRenderer::showPaths = checked;
});
panel->add(checkbox);
}
{
auto checkbox = std::make_shared<FullCheckBox>(
gui, L"Show Generator Minimap", glm::vec2(400, 24)
+4 -1
View File
@@ -207,6 +207,9 @@ Hud::Hud(Engine& engine, LevelFrontend& frontend, Player& player)
}
Hud::~Hud() {
if (input.isCursorLocked()) {
input.toggleCursor();
}
// removing all controlled ui
for (auto& element : elements) {
onRemove(element);
@@ -339,7 +342,7 @@ void Hud::update(bool visible) {
if (!gui.isFocusCaught()) {
processInput(visible);
}
if ((isMenuOpen || inventoryOpen) == input.getCursor().locked) {
if ((isMenuOpen || inventoryOpen) == input.isCursorLocked()) {
input.toggleCursor();
}
+2 -2
View File
@@ -176,7 +176,7 @@ void LevelScreen::saveWorldPreview() {
static_cast<uint>(previewSize)}
);
renderer->draw(ctx, camera, false, true, 0.0f, *postProcessing);
renderer->renderFrame(ctx, camera, false, true, 0.0f, *postProcessing);
auto image = postProcessing->toImage();
image->flipY();
imageio::write("world:preview.png", image.get());
@@ -263,7 +263,7 @@ void LevelScreen::draw(float delta) {
if (!hud->isPause()) {
scripting::on_entities_render(engine.getTime().getDelta());
}
renderer->draw(
renderer->renderFrame(
ctx, *camera, hudVisible, hud->isPause(), delta, *postProcessing
);
+36
View File
@@ -1,5 +1,6 @@
#include "ImageData.hpp"
#include <glm/glm.hpp>
#include <assert.h>
#include <stdexcept>
#include <cstring>
@@ -187,6 +188,41 @@ void ImageData::drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color
}
}
template<uint channels>
static void draw_rect(ImageData& image, int dstX, int dstY, int width, int height, const glm::ivec4& color) {
ubyte* data = image.getData();
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
int x1 = glm::min(glm::max(dstX, 0), imageWidth - 1);
int y1 = glm::min(glm::max(dstY, 0), imageHeight - 1);
int x2 = glm::min(glm::max(dstX + width, 0), imageWidth - 1);
int y2 = glm::min(glm::max(dstY + height, 0), imageHeight - 1);
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
int index = (y * imageWidth + x) * channels;
for (int i = 0; i < channels; i++) {
data[index + i] = color[i];
}
}
}
}
void ImageData::drawRect(int x, int y, int width, int height, const glm::ivec4& color) {
switch (format) {
case ImageFormat::rgb888:
draw_rect<3>(*this, x, y, width, height, color);
break;
case ImageFormat::rgba8888:
draw_rect<4>(*this, x, y, width, height, color);
break;
default:
break;
}
}
void ImageData::blitRGB_on_RGBA(const ImageData& image, int x, int y) {
ubyte* source = image.getData();
uint srcwidth = image.getWidth();
+1
View File
@@ -28,6 +28,7 @@ public:
void flipY();
void drawLine(int x1, int y1, int x2, int y2, const glm::ivec4& color);
void drawRect(int x, int y, int width, int height, const glm::ivec4& color);
void blit(const ImageData& image, int x, int y);
void extrude(int x, int y, int w, int h);
void fixAlphaColor();
+12 -7
View File
@@ -2,7 +2,6 @@
#include <exception>
#include <fstream>
#include <iostream>
#include <sstream>
#include <filesystem>
@@ -138,15 +137,21 @@ glshader compile_shader(GLenum type, const GLchar* source, const std::string& fi
}
static GLuint compile_program(
const Shader::Source& vertexSource, const Shader::Source& fragmentSource
const Shader::Source& vertexSource,
const Shader::Source& fragmentSource,
const std::vector<std::string>& defines
) {
auto& preprocessor = *Shader::preprocessor;
auto vertexCode = std::move(
preprocessor.process(vertexSource.file, vertexSource.code).code
preprocessor
.process(vertexSource.file, vertexSource.code, false, defines)
.code
);
auto fragmentCode = std::move(
preprocessor.process(fragmentSource.file, fragmentSource.code).code
preprocessor
.process(fragmentSource.file, fragmentSource.code, false, defines)
.code
);
const GLchar* vCode = vertexCode.c_str();
@@ -176,8 +181,8 @@ static GLuint compile_program(
return program;
}
void Shader::recompile() {
GLuint newProgram = compile_program(vertexSource, fragmentSource);
void Shader::recompile(const std::vector<std::string>& defines) {
GLuint newProgram = compile_program(vertexSource, fragmentSource, defines);
glDeleteProgram(id);
id = newProgram;
uniformLocations.clear();
@@ -188,7 +193,7 @@ std::unique_ptr<Shader> Shader::create(
Source&& vertexSource, Source&& fragmentSource
) {
return std::make_unique<Shader>(
compile_program(vertexSource, fragmentSource),
compile_program(vertexSource, fragmentSource, {}),
std::move(vertexSource),
std::move(fragmentSource)
);
+2 -1
View File
@@ -4,6 +4,7 @@
#include <string>
#include <memory>
#include <vector>
#include <unordered_map>
#include <glm/glm.hpp>
@@ -50,7 +51,7 @@ public:
void uniform4v(const std::string& name, int length, const float* v);
/// @brief Re-preprocess source code and re-compile shader program
void recompile();
void recompile(const std::vector<std::string>& defines);
/// @brief Create shader program using vertex and fragment shaders source.
/// @return linked shader program containing vertex and fragment shaders
-47
View File
@@ -1,47 +0,0 @@
#include "ShadowMap.hpp"
#include <GL/glew.h>
ShadowMap::ShadowMap(int resolution) : resolution(resolution) {
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR, border);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
ShadowMap::~ShadowMap() {
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &depthMap);
}
void ShadowMap::bind() {
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_DEPTH_BUFFER_BIT);
}
void ShadowMap::unbind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
uint ShadowMap::getDepthMap() const {
return depthMap;
}
int ShadowMap::getResolution() const {
return resolution;
}
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include "typedefs.hpp"
class ShadowMap {
public:
ShadowMap(int resolution);
~ShadowMap();
void bind();
void unbind();
uint getDepthMap() const;
int getResolution() const;
private:
uint fbo;
uint depthMap;
int resolution;
};
+199
View File
@@ -0,0 +1,199 @@
#include "Shadows.hpp"
#include <GL/glew.h>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/norm.hpp>
#include "assets/Assets.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/commons.hpp"
#include "world/Level.hpp"
#include "world/Weather.hpp"
#include "world/World.hpp"
using namespace advanced_pipeline;
inline constexpr int MIN_SHADOW_MAP_RES = 512;
inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0;
class ShadowMap {
public:
ShadowMap(int resolution) : resolution(resolution) {
glGenTextures(1, &depthMap);
glBindTexture(GL_TEXTURE_2D, depthMap);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexParameteri(
GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE
);
float border[4] {1.0f, 1.0f, 1.0f, 1.0f};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, border);
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture2D(
GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0
);
glDrawBuffer(GL_NONE);
glReadBuffer(GL_NONE);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
~ShadowMap() {
glDeleteFramebuffers(1, &fbo);
glDeleteTextures(1, &depthMap);
}
void bind(){
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_DEPTH_BUFFER_BIT);
}
void unbind() {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
uint getDepthMap() const {
return depthMap;
}
int getResolution() const {
return resolution;
}
private:
uint fbo;
uint depthMap;
int resolution;
};
Shadows::Shadows(const Level& level) : level(level) {}
Shadows::~Shadows() = default;
void Shadows::setQuality(int quality) {
int resolution = MIN_SHADOW_MAP_RES << quality;
if (quality > 0 && !shadows) {
shadowMap = std::make_unique<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
shadows = true;
} else if (quality == 0 && shadows) {
shadowMap.reset();
wideShadowMap.reset();
shadows = false;
}
if (shadows && shadowMap->getResolution() != resolution) {
shadowMap = std::make_unique<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
}
this->quality = quality;
}
void Shadows::setup(Shader& shader, const Weather& weather) {
if (shadows) {
const auto& worldInfo = level.getWorld()->getInfo();
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
shader.uniform1i("u_screen", 0);
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
shader.uniform3f("u_sunDir", shadowCamera.front);
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
glActiveTexture(TEXTURE_MAIN);
}
}
void Shadows::refresh(const Camera& camera, const DrawContext& pctx, std::function<void(Camera&)> renderShadowPass) {
static int frameid = 0;
if (shadows) {
if (frameid % 2 == 0) {
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f, renderShadowPass);
} else {
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f, renderShadowPass);
}
}
frameid++;
}
void Shadows::generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale,
std::function<void(Camera&)> renderShadowPass
) {
auto world = level.getWorld();
const auto& worldInfo = world->getInfo();
int resolution = shadowMap.getResolution();
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
float shadowMapSize = resolution * shadowMapScale;
glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f;
glm::vec3 prevPos = shadowCamera.position;
shadowCamera = Camera(
glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos,
shadowMapSize
);
shadowCamera.near = 0.1f;
shadowCamera.far = 1000.0f;
shadowCamera.perspective = false;
shadowCamera.setAspectRatio(1.0f);
float t = worldInfo.daytime - 0.25f;
if (t < 0.0f) {
t += 1.0f;
}
t = fmod(t, 0.5f);
float sunCycleStep = 1.0f / 500.0f;
float sunAngle = glm::radians(
90.0f -
((static_cast<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
);
float sunAltitude = glm::pi<float>() * 0.25f;
shadowCamera.rotate(
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
sunAngle - glm::pi<float>() * 0.5f,
glm::radians(0.0f)
);
shadowCamera.position -= shadowCamera.front * 500.0f;
shadowCamera.position += shadowCamera.up * 0.0f;
shadowCamera.position += camera.front * 0.0f;
auto view = shadowCamera.getView();
auto currentPos = shadowCamera.position;
auto topRight = shadowCamera.right + shadowCamera.up;
auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f);
auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f);
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
{
auto sctx = pctx.sub();
sctx.setDepthTest(true);
sctx.setCullFace(true);
sctx.setViewport({resolution, resolution});
shadowMap.bind();
if (renderShadowPass) {
renderShadowPass(shadowCamera);
}
shadowMap.unbind();
}
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include <memory>
#include <functional>
#include "typedefs.hpp"
#include "window/Camera.hpp"
class Shader;
class Level;
class Assets;
struct Weather;
class DrawContext;
struct EngineSettings;
class ShadowMap;
class Shadows {
public:
Shadows(const Level& level);
~Shadows();
void setup(Shader& shader, const Weather& weather);
void setQuality(int quality);
void refresh(
const Camera& camera,
const DrawContext& pctx,
std::function<void(Camera&)> renderShadowPass
);
private:
const Level& level;
bool shadows = false;
Camera shadowCamera;
Camera wideShadowCamera;
std::unique_ptr<ShadowMap> shadowMap;
std::unique_ptr<ShadowMap> wideShadowMap;
int quality = 0;
void generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale,
std::function<void(Camera&)> renderShadowPass
);
};
+1 -1
View File
@@ -187,7 +187,7 @@ const Mesh<ChunkVertex>* ChunksRenderer::retrieveChunk(
return mesh;
}
void ChunksRenderer::drawChunksShadowsPass(
void ChunksRenderer::drawShadowsPass(
const Camera& camera, Shader& shader, const Camera& playerCamera
) {
Frustum frustum;
+1 -1
View File
@@ -73,7 +73,7 @@ public:
const std::shared_ptr<Chunk>& chunk, bool important
);
void drawChunksShadowsPass(
void drawShadowsPass(
const Camera& camera, Shader& shader, const Camera& playerCamera
);
@@ -1,15 +1,51 @@
#include "GuidesRenderer.hpp"
#include <glm/gtc/matrix_transform.hpp>
#include "DebugLinesRenderer.hpp"
#include "graphics/core/Shader.hpp"
#include "window/Camera.hpp"
#include "graphics/core/LineBatch.hpp"
#include "graphics/core/DrawContext.hpp"
#include "graphics/render/LinesRenderer.hpp"
#include "world/Level.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/Pathfinding.hpp"
#include "maths/voxmaths.hpp"
#include "window/Camera.hpp"
#include "constants.hpp"
void GuidesRenderer::drawBorders(
bool DebugLinesRenderer::showPaths = false;
static void draw_route(
LinesRenderer& lines, const voxels::Agent& agent
) {
const auto& route = agent.route;
if (!route.found)
return;
for (int i = 1; i < route.nodes.size(); i++) {
const auto& a = route.nodes.at(i - 1);
const auto& b = route.nodes.at(i);
if (i == 1) {
lines.pushLine(
glm::vec3(a.pos) + glm::vec3(0.5f),
glm::vec3(a.pos) + glm::vec3(0.5f, 1.0f, 0.5f),
glm::vec4(1, 1, 1, 1)
);
}
lines.pushLine(
glm::vec3(a.pos) + glm::vec3(0.5f),
glm::vec3(b.pos) + glm::vec3(0.5f),
glm::vec4(1, 0, 1, 1)
);
lines.pushLine(
glm::vec3(b.pos) + glm::vec3(0.5f),
glm::vec3(b.pos) + glm::vec3(0.5f, 1.0f, 0.5f),
glm::vec4(1, 1, 1, 1)
);
}
}
void DebugLinesRenderer::drawBorders(
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
) {
int ww = ex - sx;
@@ -37,7 +73,7 @@ void GuidesRenderer::drawBorders(
batch.flush();
}
void GuidesRenderer::drawCoordSystem(
void DebugLinesRenderer::drawCoordSystem(
LineBatch& batch, const DrawContext& pctx, float length
) {
auto ctx = pctx.sub();
@@ -55,14 +91,22 @@ void GuidesRenderer::drawCoordSystem(
batch.line(0.f, 0.f, 0.f, 0.f, 0.f, length, 0.f, 0.f, 1.f, 1.f);
}
void GuidesRenderer::renderDebugLines(
const DrawContext& pctx,
void DebugLinesRenderer::render(
DrawContext& pctx,
const Camera& camera,
LineBatch& batch,
LinesRenderer& renderer,
LineBatch& linesBatch,
Shader& linesShader,
bool showChunkBorders
) {
DrawContext ctx = pctx.sub(&batch);
// In-world lines
if (showPaths) {
for (const auto& [_, agent] : level.pathfinding->getAgents()) {
draw_route(renderer, agent);
}
}
DrawContext ctx = pctx.sub(&linesBatch);
const auto& viewport = ctx.getViewport();
ctx.setDepthTest(true);
@@ -78,7 +122,7 @@ void GuidesRenderer::renderDebugLines(
int cz = floordiv(static_cast<int>(coord.z), CHUNK_D);
drawBorders(
batch,
linesBatch,
cx * CHUNK_W,
0,
cz * CHUNK_D,
@@ -103,5 +147,5 @@ void GuidesRenderer::renderDebugLines(
) * model *
glm::inverse(camera.rotation)
);
drawCoordSystem(batch, ctx, length);
drawCoordSystem(linesBatch, ctx, length);
}
@@ -0,0 +1,41 @@
#pragma once
class DrawContext;
class Camera;
class LineBatch;
class LinesRenderer;
class Shader;
class Level;
class DebugLinesRenderer {
public:
static bool showPaths;
DebugLinesRenderer(const Level& level)
: level(level) {};
/// @brief Render debug lines in the world
/// @param ctx Draw context
/// @param camera Camera used for rendering
/// @param renderer Lines renderer used for rendering lines
/// @param linesShader Shader used for rendering lines
/// @param showChunkBorders Whether to show chunk borders
void render(
DrawContext& ctx,
const Camera& camera,
LinesRenderer& renderer,
LineBatch& linesBatch,
Shader& linesShader,
bool showChunkBorders
);
private:
const Level& level;
void drawBorders(
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
);
void drawCoordSystem(
LineBatch& batch, const DrawContext& pctx, float length
);
};
+1
View File
@@ -15,6 +15,7 @@
#include "objects/Player.hpp"
#include "objects/Players.hpp"
#include "objects/Entities.hpp"
#include "objects/Entity.hpp"
#include "logic/LevelController.hpp"
#include "util/stringutil.hpp"
#include "engine/Engine.hpp"
+1
View File
@@ -7,6 +7,7 @@
#include "window/Camera.hpp"
#include "graphics/core/Texture.hpp"
#include "objects/Entities.hpp"
#include "objects/Entity.hpp"
#include "world/Level.hpp"
Emitter::Emitter(
-28
View File
@@ -1,28 +0,0 @@
#pragma once
class LineBatch;
class DrawContext;
class Camera;
class Shader;
class GuidesRenderer {
public:
void drawBorders(
LineBatch& batch, int sx, int sy, int sz, int ex, int ey, int ez
);
void drawCoordSystem(
LineBatch& batch, const DrawContext& pctx, float length
);
/// @brief Render all debug lines (chunks borders, coord system guides)
/// @param context graphics context
/// @param camera active camera
/// @param linesShader shader used
void renderDebugLines(
const DrawContext& context,
const Camera& camera,
LineBatch& batch,
Shader& linesShader,
bool showChunkBorders
);
};
+14
View File
@@ -0,0 +1,14 @@
#include "LinesRenderer.hpp"
#include "graphics/core/LineBatch.hpp"
void LinesRenderer::draw(LineBatch& batch) {
for (const auto& line : queue) {
batch.line(line.a, line.b, line.color);
}
queue.clear();
}
void LinesRenderer::pushLine(const glm::vec3& a, const glm::vec3& b, const glm::vec4& color) {
queue.push_back({a, b, color});
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <vector>
class LineBatch;
class LinesRenderer {
public:
struct Line {
glm::vec3 a;
glm::vec3 b;
glm::vec4 color;
};
void draw(LineBatch& batch);
void pushLine(const glm::vec3& a, const glm::vec3& b, const glm::vec4& color);
private:
std::vector<Line> queue;
};
+77 -163
View File
@@ -27,6 +27,7 @@
#include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/Chunks.hpp"
#include "voxels/Pathfinding.hpp"
#include "window/Window.hpp"
#include "world/Level.hpp"
#include "world/LevelEvents.hpp"
@@ -42,7 +43,7 @@
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/ShadowMap.hpp"
#include "graphics/core/Shadows.hpp"
#include "graphics/core/GBuffer.hpp"
#include "BlockWrapsRenderer.hpp"
#include "ParticlesRenderer.hpp"
@@ -51,7 +52,8 @@
#include "NamedSkeletons.hpp"
#include "TextsRenderer.hpp"
#include "ChunksRenderer.hpp"
#include "GuidesRenderer.hpp"
#include "LinesRenderer.hpp"
#include "DebugLinesRenderer.hpp"
#include "ModelBatch.hpp"
#include "Skybox.hpp"
#include "Emitter.hpp"
@@ -61,8 +63,6 @@ using namespace advanced_pipeline;
inline constexpr size_t BATCH3D_CAPACITY = 4096;
inline constexpr size_t MODEL_BATCH_CAPACITY = 20'000;
inline constexpr GLenum TEXTURE_MAIN = GL_TEXTURE0;
inline constexpr int MIN_SHADOW_MAP_RES = 512;
bool WorldRenderer::showChunkBorders = false;
bool WorldRenderer::showEntitiesDebug = false;
@@ -80,8 +80,7 @@ WorldRenderer::WorldRenderer(
modelBatch(std::make_unique<ModelBatch>(
MODEL_BATCH_CAPACITY, assets, *player.chunks, engine.getSettings()
)),
guides(std::make_unique<GuidesRenderer>()),
chunks(std::make_unique<ChunksRenderer>(
chunksRenderer(std::make_unique<ChunksRenderer>(
&level,
*player.chunks,
assets,
@@ -102,7 +101,7 @@ WorldRenderer::WorldRenderer(
auto& settings = engine.getSettings();
level.events->listen(
LevelEventType::CHUNK_HIDDEN,
[this](LevelEventType, Chunk* chunk) { chunks->unload(chunk); }
[this](LevelEventType, Chunk* chunk) { chunksRenderer->unload(chunk); }
);
auto assets = engine.getAssets();
skybox = std::make_unique<Skybox>(
@@ -118,10 +117,26 @@ WorldRenderer::WorldRenderer(
hands = std::make_unique<HandsRenderer>(
*assets, *modelBatch, skeletons->createSkeleton("hand", &skeletonConfig)
);
lines = std::make_unique<LinesRenderer>();
shadowMapping = std::make_unique<Shadows>(level);
debugLines = std::make_unique<DebugLinesRenderer>(level);
}
WorldRenderer::~WorldRenderer() = default;
static void setup_weather(Shader& shader, const Weather& weather) {
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
}
static void setup_camera(Shader& shader, const Camera& camera) {
shader.uniformMatrix("u_model", glm::mat4(1.0f));
shader.uniformMatrix("u_proj", camera.getProjection());
shader.uniformMatrix("u_view", camera.getView());
shader.uniform3f("u_cameraPos", camera.position);
}
void WorldRenderer::setupWorldShader(
Shader& shader,
const Camera& camera,
@@ -129,45 +144,20 @@ void WorldRenderer::setupWorldShader(
float fogFactor
) {
shader.use();
shader.uniformMatrix("u_model", glm::mat4(1.0f));
shader.uniformMatrix("u_proj", camera.getProjection());
shader.uniformMatrix("u_view", camera.getView());
setup_camera(shader, camera);
setup_weather(shader, weather);
shadowMapping->setup(shader, weather);
shader.uniform1f("u_timer", timer);
shader.uniform1f("u_gamma", settings.graphics.gamma.get());
shader.uniform1f("u_fogFactor", fogFactor);
shader.uniform1f("u_fogCurve", settings.graphics.fogCurve.get());
shader.uniform1i("u_debugLights", lightsDebug);
shader.uniform1i("u_debugNormals", false);
shader.uniform1f("u_weatherFogOpacity", weather.fogOpacity());
shader.uniform1f("u_weatherFogDencity", weather.fogDencity());
shader.uniform1f("u_weatherFogCurve", weather.fogCurve());
shader.uniform1f("u_dayTime", level.getWorld()->getInfo().daytime);
shader.uniform2f("u_lightDir", skybox->getLightDir());
shader.uniform3f("u_cameraPos", camera.position);
shader.uniform1i("u_skybox", 1);
shader.uniform1i("u_enableShadows", shadows);
if (shadows) {
const auto& worldInfo = level.getWorld()->getInfo();
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
shader.uniform1i("u_screen", 0);
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
shader.uniform3f("u_sunDir", shadowCamera.front);
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
shader.uniform1i("u_shadows[0]", TARGET_SHADOWS0);
glBindTexture(GL_TEXTURE_2D, shadowMap->getDepthMap());
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS1);
shader.uniform1i("u_shadows[1]", TARGET_SHADOWS1);
glBindTexture(GL_TEXTURE_2D, wideShadowMap->getDepthMap());
glActiveTexture(TEXTURE_MAIN);
}
shader.uniform1i("u_skybox", TARGET_SKYBOX);
auto indices = level.content.getIndices();
// Light emission when an emissive item is chosen
@@ -186,7 +176,7 @@ void WorldRenderer::setupWorldShader(
}
}
void WorldRenderer::renderLevel(
void WorldRenderer::renderOpaque(
const DrawContext& ctx,
const Camera& camera,
const EngineSettings& settings,
@@ -215,7 +205,9 @@ void WorldRenderer::renderLevel(
*modelBatch,
culling ? frustumCulling.get() : nullptr,
delta,
pause
pause,
player.currentCamera.get() == player.fpCamera.get() ? player.getEntity()
: 0
);
modelBatch->render();
particles->render(camera, delta * !pause);
@@ -225,7 +217,7 @@ void WorldRenderer::renderLevel(
setupWorldShader(shader, camera, settings, fogFactor);
chunks->drawChunks(camera, shader);
chunksRenderer->drawChunks(camera, shader);
blockWraps->draw(ctx, player);
if (hudVisible) {
@@ -284,79 +276,7 @@ void WorldRenderer::renderLines(
}
}
void WorldRenderer::generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale
) {
auto& shadowsShader = assets.require<Shader>("shadows");
auto world = level.getWorld();
const auto& worldInfo = world->getInfo();
const auto& settings = engine.getSettings();
int resolution = shadowMap.getResolution();
int quality = settings.graphics.shadowsQuality.get();
float shadowMapScale = 0.32f / (1 << glm::max(0, quality)) * scale;
float shadowMapSize = resolution * shadowMapScale;
glm::vec3 basePos = glm::floor(camera.position / 4.0f) * 4.0f;
glm::vec3 prevPos = shadowCamera.position;
shadowCamera = Camera(
glm::distance2(prevPos, basePos) > 25.0f ? basePos : prevPos,
shadowMapSize
);
shadowCamera.near = 0.1f;
shadowCamera.far = 1000.0f;
shadowCamera.perspective = false;
shadowCamera.setAspectRatio(1.0f);
float t = worldInfo.daytime - 0.25f;
if (t < 0.0f) {
t += 1.0f;
}
t = fmod(t, 0.5f);
float sunCycleStep = 1.0f / 500.0f;
float sunAngle = glm::radians(
90.0f -
((static_cast<int>(t / sunCycleStep)) * sunCycleStep + 0.25f) * 360.0f
);
float sunAltitude = glm::pi<float>() * 0.25f;
shadowCamera.rotate(
-glm::cos(sunAngle + glm::pi<float>() * 0.5f) * sunAltitude,
sunAngle - glm::pi<float>() * 0.5f,
glm::radians(0.0f)
);
shadowCamera.position -= shadowCamera.front * 500.0f;
shadowCamera.position += shadowCamera.up * 0.0f;
shadowCamera.position += camera.front * 0.0f;
auto view = shadowCamera.getView();
auto currentPos = shadowCamera.position;
auto topRight = shadowCamera.right + shadowCamera.up;
auto min = view * glm::vec4(currentPos - topRight * shadowMapSize * 0.5f, 1.0f);
auto max = view * glm::vec4(currentPos + topRight * shadowMapSize * 0.5f, 1.0f);
shadowCamera.setProjection(glm::ortho(min.x, max.x, min.y, max.y, 0.1f, 1000.0f));
{
auto sctx = pctx.sub();
sctx.setDepthTest(true);
sctx.setCullFace(true);
sctx.setViewport({resolution, resolution});
shadowMap.bind();
setupWorldShader(shadowsShader, shadowCamera, settings, 0.0f);
chunks->drawChunksShadowsPass(shadowCamera, shadowsShader, camera);
shadowMap.unbind();
}
}
void WorldRenderer::draw(
void WorldRenderer::renderFrame(
const DrawContext& pctx,
Camera& camera,
bool hudVisible,
@@ -365,6 +285,9 @@ void WorldRenderer::draw(
PostProcessing& postProcessing
) {
// TODO: REFACTOR WHOLE RENDER ENGINE
auto projView = camera.getProjView();
float delta = uiDelta * !pause;
timer += delta;
weather.update(delta);
@@ -380,22 +303,17 @@ void WorldRenderer::draw(
auto& deferredShader = assets.require<PostEffect>("deferred_lighting").getShader();
const auto& settings = engine.getSettings();
Shader* affectedShaders[] {
&mainShader, &entityShader, &translucentShader, &deferredShader
};
gbufferPipeline = settings.graphics.advancedRender.get();
int shadowsQuality = settings.graphics.shadowsQuality.get() * gbufferPipeline;
int resolution = MIN_SHADOW_MAP_RES << shadowsQuality;
if (shadowsQuality > 0 && !shadows) {
shadowMap = std::make_unique<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
shadows = true;
} else if (shadowsQuality == 0 && shadows) {
shadowMap.reset();
wideShadowMap.reset();
shadows = false;
}
shadowMapping->setQuality(shadowsQuality);
CompileTimeShaderSettings currentSettings {
gbufferPipeline,
shadows,
shadowsQuality != 0,
settings.graphics.ssao.get() && gbufferPipeline
};
if (
@@ -403,19 +321,15 @@ void WorldRenderer::draw(
prevCTShaderSettings.shadows != currentSettings.shadows ||
prevCTShaderSettings.ssao != currentSettings.ssao
) {
Shader::preprocessor->setDefined("ENABLE_SHADOWS", currentSettings.shadows);
Shader::preprocessor->setDefined("ENABLE_SSAO", currentSettings.ssao);
Shader::preprocessor->setDefined("ADVANCED_RENDER", currentSettings.advancedRender);
mainShader.recompile();
entityShader.recompile();
deferredShader.recompile();
translucentShader.recompile();
prevCTShaderSettings = currentSettings;
}
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 (shadows && shadowMap->getResolution() != resolution) {
shadowMap = std::make_unique<ShadowMap>(resolution);
wideShadowMap = std::make_unique<ShadowMap>(resolution);
for (auto shader : affectedShaders) {
shader->recompile(defines);
}
prevCTShaderSettings = currentSettings;
}
const auto& worldInfo = world->getInfo();
@@ -426,38 +340,24 @@ void WorldRenderer::draw(
skybox->refresh(pctx, worldInfo.daytime, mie, 4);
chunks->update();
chunksRenderer->update();
static int frameid = 0;
if (shadows) {
if (frameid % 2 == 0) {
generateShadowsMap(camera, pctx, *shadowMap, shadowCamera, 1.0f);
} else {
generateShadowsMap(camera, pctx, *wideShadowMap, wideShadowCamera, 3.0f);
}
}
frameid++;
auto& linesShader = assets.require<Shader>("lines");
/* World render scope with diegetic HUD included */ {
shadowMapping->refresh(camera, pctx, [this, &camera](Camera& shadowCamera) {
auto& shader = assets.require<Shader>("shadows");
setupWorldShader(shader, shadowCamera, engine.getSettings(), 0.0f);
chunksRenderer->drawShadowsPass(shadowCamera, shader, camera);
});
{
DrawContext wctx = pctx.sub();
postProcessing.use(wctx, gbufferPipeline);
display::clearDepth();
/* Actually world render with depth buffer on */ {
/* Main opaque pass (GBuffer pass) */ {
DrawContext ctx = wctx.sub();
ctx.setDepthTest(true);
ctx.setCullFace(true);
renderLevel(ctx, camera, settings, uiDelta, pause, hudVisible);
// Debug lines
if (hudVisible) {
if (debug) {
guides->renderDebugLines(
ctx, camera, *lineBatch, linesShader, showChunkBorders
);
}
}
renderOpaque(ctx, camera, settings, uiDelta, pause, hudVisible);
}
texts->render(pctx, camera, settings, hudVisible, true);
}
@@ -478,19 +378,33 @@ void WorldRenderer::draw(
} else {
postProcessing.getFramebuffer()->bind();
}
// Drawing background sky plane
// Background sky plane
skybox->draw(ctx, camera, assets, worldInfo.daytime, clouds);
auto& linesShader = assets.require<Shader>("lines");
linesShader.use();
if (debug && hudVisible) {
debugLines->render(
ctx, camera, *lines, *lineBatch, linesShader, showChunkBorders
);
}
linesShader.uniformMatrix("u_projview", projView);
lines->draw(*lineBatch);
lineBatch->flush();
// Translucent blocks
{
auto sctx = ctx.sub();
sctx.setCullFace(true);
skybox->bind();
translucentShader.use();
setupWorldShader(translucentShader, camera, settings, fogFactor);
chunks->drawSortedMeshes(camera, translucentShader);
chunksRenderer->drawSortedMeshes(camera, translucentShader);
skybox->unbind();
}
// Weather effects
entityShader.use();
setupWorldShader(entityShader, camera, settings, fogFactor);
@@ -582,7 +496,7 @@ void WorldRenderer::renderBlockOverlay(const DrawContext& wctx) {
}
void WorldRenderer::clear() {
chunks->clear();
chunksRenderer->clear();
}
void WorldRenderer::setDebug(bool flag) {
+19 -29
View File
@@ -22,7 +22,7 @@ class BlockWrapsRenderer;
class PrecipitationRenderer;
class HandsRenderer;
class NamedSkeletons;
class GuidesRenderer;
class LinesRenderer;
class TextsRenderer;
class Shader;
class Frustum;
@@ -33,8 +33,9 @@ class PostProcessing;
class DrawContext;
class ModelBatch;
class Assets;
class ShadowMap;
class Shadows;
class GBuffer;
class DebugLinesRenderer;
struct EngineSettings;
struct CompileTimeShaderSettings {
@@ -52,21 +53,17 @@ class WorldRenderer {
std::unique_ptr<LineBatch> lineBatch;
std::unique_ptr<Batch3D> batch3d;
std::unique_ptr<ModelBatch> modelBatch;
std::unique_ptr<GuidesRenderer> guides;
std::unique_ptr<ChunksRenderer> chunks;
std::unique_ptr<ChunksRenderer> chunksRenderer;
std::unique_ptr<HandsRenderer> hands;
std::unique_ptr<Skybox> skybox;
std::unique_ptr<ShadowMap> shadowMap;
std::unique_ptr<ShadowMap> wideShadowMap;
std::unique_ptr<Shadows> shadowMapping;
std::unique_ptr<DebugLinesRenderer> debugLines;
Weather weather {};
Camera shadowCamera;
Camera wideShadowCamera;
float timer = 0.0f;
bool debug = false;
bool lightsDebug = false;
bool gbufferPipeline = false;
bool shadows = false;
CompileTimeShaderSettings prevCTShaderSettings {};
@@ -89,12 +86,17 @@ class WorldRenderer {
float fogFactor
);
void generateShadowsMap(
const Camera& camera,
const DrawContext& pctx,
ShadowMap& shadowMap,
Camera& shadowCamera,
float scale
/// @brief Render opaque pass
/// @param context graphics context
/// @param camera active camera
/// @param settings engine settings
void renderOpaque(
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause,
bool hudVisible
);
public:
std::unique_ptr<ParticlesRenderer> particles;
@@ -102,6 +104,7 @@ public:
std::unique_ptr<BlockWrapsRenderer> blockWraps;
std::unique_ptr<PrecipitationRenderer> precipitation;
std::unique_ptr<NamedSkeletons> skeletons;
std::unique_ptr<LinesRenderer> lines;
static bool showChunkBorders;
static bool showEntitiesDebug;
@@ -109,7 +112,7 @@ public:
WorldRenderer(Engine& engine, LevelFrontend& frontend, Player& player);
~WorldRenderer();
void draw(
void renderFrame(
const DrawContext& context,
Camera& camera,
bool hudVisible,
@@ -118,19 +121,6 @@ public:
PostProcessing& postProcessing
);
/// @brief Render level without diegetic interface
/// @param context graphics context
/// @param camera active camera
/// @param settings engine settings
void renderLevel(
const DrawContext& context,
const Camera& camera,
const EngineSettings& settings,
float delta,
bool pause,
bool hudVisible
);
void clear();
void setDebug(bool flag);
+7 -2
View File
@@ -103,9 +103,14 @@ void guiutil::confirm(
panel->setGravity(Gravity::center_center);
container->add(panel);
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
panel->add(std::make_shared<Label>(gui, text));
auto label = std::make_shared<Label>(gui, text);
label->setSize(glm::vec2(600, 50));
label->setMultiline(true);
label->setTextWrapping(true);
panel->add(label);
auto subpanel = std::make_shared<Panel>(gui, glm::vec2(600, 53));
subpanel->setColor(glm::vec4(0));
+4
View File
@@ -83,9 +83,13 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
builder.add("language", &settings.ui.language);
builder.add("world-preview-size", &settings.ui.worldPreviewSize);
builder.section("pathfinding");
builder.add("steps-per-async-agent", &settings.pathfinding.stepsPerAsyncAgent);
builder.section("debug");
builder.add("generator-test-mode", &settings.debug.generatorTestMode);
builder.add("do-write-lights", &settings.debug.doWriteLights);
builder.add("do-trace-shaders", &settings.debug.doTraceShaders);
builder.add("enable-experimental", &settings.debug.enableExperimental);
}
+1
View File
@@ -19,4 +19,5 @@ void ItemDef::cloneTo(ItemDef& dst) {
dst.modelName = modelName;
dst.uses = uses;
dst.usesDisplay = usesDisplay;
dst.tags = tags;
}
+6
View File
@@ -2,6 +2,8 @@
#include <glm/glm.hpp>
#include <string>
#include <vector>
#include <set>
#include "data/dv.hpp"
#include "typedefs.hpp"
@@ -64,11 +66,15 @@ struct ItemDef {
std::string scriptFile;
std::vector<std::string> tags;
struct {
itemid_t id;
blockid_t placingBlock;
ItemFuncsSet funcsset {};
bool emissive = false;
std::set<int> tags;
} rt {};
ItemDef(const std::string& name);
+4 -1
View File
@@ -11,6 +11,7 @@
#include "objects/Player.hpp"
#include "physics/Hitbox.hpp"
#include "voxels/Chunks.hpp"
#include "voxels/Pathfinding.hpp"
#include "scripting/scripting.hpp"
#include "lighting/Lighting.hpp"
#include "settings.hpp"
@@ -69,6 +70,9 @@ LevelController::LevelController(
}
void LevelController::update(float delta, bool pause) {
level->pathfinding->performAllAsync(
settings.pathfinding.stepsPerAsyncAgent.get()
);
for (const auto& [_, player] : *level->players) {
if (player->isSuspended()) {
continue;
@@ -91,7 +95,6 @@ void LevelController::update(float delta, bool pause) {
if (!pause) {
// update all objects that needed
blocks->update(delta, settings.chunks.padding.get());
level->entities->updatePhysics(delta);
level->entities->update(delta);
for (const auto& [_, player] : *level->players) {
if (player->isSuspended()) {
+8 -17
View File
@@ -13,6 +13,7 @@
#include "items/ItemStack.hpp"
#include "lighting/Lighting.hpp"
#include "objects/Entities.hpp"
#include "objects/Entity.hpp"
#include "objects/Player.hpp"
#include "objects/Players.hpp"
#include "physics/Hitbox.hpp"
@@ -270,7 +271,6 @@ void PlayerController::update(float delta, const Input* inputEvents) {
} else {
resetKeyboard();
}
updatePlayer(delta);
}
void PlayerController::postUpdate(
@@ -309,20 +309,7 @@ void PlayerController::updateKeyboard(const Input& inputEvents) {
}
void PlayerController::resetKeyboard() {
input.zoom = false;
input.moveForward = false;
input.moveBack = false;
input.moveLeft = false;
input.moveRight = false;
input.sprint = false;
input.shift = false;
input.cheat = false;
input.jump = false;
input.delta = {};
}
void PlayerController::updatePlayer(float delta) {
player.updateInput(input, delta);
input = {};
}
static int determine_rotation(
@@ -338,7 +325,8 @@ static int determine_rotation(
if (norm.z > 0.0f) return BLOCK_DIR_NORTH;
if (norm.z < 0.0f) return BLOCK_DIR_SOUTH;
} else if (name == "pane" || name == "stairs") {
int verticalBit = (name == "stairs" && (norm.y - camDir.y * 0.5f) < 0.0) ? 4 : 0;
int verticalBit =
(name == "stairs" && (norm.y - camDir.y * 0.5f) < 0.0) ? 4 : 0;
if (abs(camDir.x) > abs(camDir.z)) {
if (camDir.x > 0.0f) return BLOCK_DIR_EAST | verticalBit;
if (camDir.x < 0.0f) return BLOCK_DIR_WEST | verticalBit;
@@ -500,7 +488,10 @@ void PlayerController::updateInteraction(const Input& inputEvents, float delta)
}
const auto& bindings = inputEvents.getBindings();
bool xkey = bindings.active(BIND_PLAYER_FAST_INTERACTOIN);
float maxDistance = xkey ? 200.0f : 10.0f;
float maxDistance = player.getMaxInteractionDistance();
if (xkey) {
maxDistance *= 100.0;
}
bool longInteraction = interactionTimer <= 0 || xkey;
bool lclick = bindings.jactive(BIND_PLAYER_DESTROY) ||
(longInteraction && bindings.active(BIND_PLAYER_DESTROY));
-1
View File
@@ -60,7 +60,6 @@ class PlayerController {
void updateKeyboard(const Input& inputEvents);
void resetKeyboard();
void updatePlayer(float delta);
void updateEntityInteraction(entityid_t eid, bool lclick, bool rclick);
void updateInteraction(const Input& inputEvents, float delta);
+2
View File
@@ -38,9 +38,11 @@ extern const luaL_Reg mat4lib[];
extern const luaL_Reg networklib[];
extern const luaL_Reg packlib[];
extern const luaL_Reg particleslib[]; // gfx.particles
extern const luaL_Reg pathfindinglib[];
extern const luaL_Reg playerlib[];
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
extern const luaL_Reg quatlib[];
extern const luaL_Reg randomlib[];
extern const luaL_Reg text3dlib[]; // gfx.text3d
extern const luaL_Reg timelib[];
extern const luaL_Reg tomllib[];
@@ -62,6 +62,15 @@ static int l_set_gravity_scale(lua::State* L) {
static int l_is_vdamping(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
return lua::pushboolean(
L, entity->getRigidbody().hitbox.verticalDamping > 0.0
);
}
return 0;
}
static int l_get_vdamping(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
return lua::pushnumber(
L, entity->getRigidbody().hitbox.verticalDamping
);
}
@@ -70,7 +79,11 @@ static int l_is_vdamping(lua::State* L) {
static int l_set_vdamping(lua::State* L) {
if (auto entity = get_entity(L, 1)) {
entity->getRigidbody().hitbox.verticalDamping = lua::toboolean(L, 2);
if (lua::isboolean(L, 2)) {
entity->getRigidbody().hitbox.verticalDamping = lua::toboolean(L, 2);
} else {
entity->getRigidbody().hitbox.verticalDamping = lua::tonumber(L, 2);
}
}
return 0;
}
@@ -144,6 +157,7 @@ const luaL_Reg rigidbodylib[] = {
{"get_linear_damping", lua::wrap<l_get_linear_damping>},
{"set_linear_damping", lua::wrap<l_set_linear_damping>},
{"is_vdamping", lua::wrap<l_is_vdamping>},
{"get_vdamping", lua::wrap<l_get_vdamping>},
{"set_vdamping", lua::wrap<l_set_vdamping>},
{"is_grounded", lua::wrap<l_is_grounded>},
{"is_crouching", lua::wrap<l_is_crouching>},
@@ -91,7 +91,7 @@ static int l_set_texture(lua::State* L) {
}
static int l_index(lua::State* L) {
if (auto skeleton= get_skeleton(L)) {
if (auto skeleton = get_skeleton(L)) {
if (auto bone = skeleton->config->find(lua::require_string(L, 2))) {
return lua::pushinteger(L, bone->getIndex());
}
+9 -5
View File
@@ -2,6 +2,7 @@
#include "util/stringutil.hpp"
template<std::string(*encode_func)(const ubyte*, size_t)>
static int l_encode(lua::State* L) {
if (lua::istable(L, 1)) {
lua::pushvalue(L, 1);
@@ -13,12 +14,12 @@ static int l_encode(lua::State* L) {
lua::pop(L);
}
lua::pop(L);
return lua::pushstring(L, util::base64_encode(
return lua::pushstring(L, encode_func(
reinterpret_cast<const ubyte*>(buffer.data()), buffer.size()
));
} else {
auto string = lua::bytearray_as_string(L, 1);
auto out = util::base64_encode(
auto out = encode_func(
reinterpret_cast<const ubyte*>(string.data()),
string.size()
);
@@ -28,8 +29,9 @@ static int l_encode(lua::State* L) {
throw std::runtime_error("array or ByteArray expected");
}
template<util::Buffer<ubyte>(*decode_func)(std::string_view)>
static int l_decode(lua::State* L) {
auto buffer = util::base64_decode(lua::require_lstring(L, 1));
auto buffer = decode_func(lua::require_lstring(L, 1));
if (lua::toboolean(L, 2)) {
lua::createtable(L, buffer.size(), 0);
for (size_t i = 0; i < buffer.size(); i++) {
@@ -43,7 +45,9 @@ static int l_decode(lua::State* L) {
}
const luaL_Reg base64lib[] = {
{"encode", lua::wrap<l_encode>},
{"decode", lua::wrap<l_decode>},
{"encode", lua::wrap<l_encode<util::base64_encode>>},
{"decode", lua::wrap<l_decode<util::base64_decode>>},
{"encode_urlsafe", lua::wrap<l_encode<util::base64_urlsafe_encode>>},
{"decode_urlsafe", lua::wrap<l_decode<util::base64_urlsafe_decode>>},
{NULL, NULL}
};
+58 -12
View File
@@ -20,21 +20,21 @@
using namespace scripting;
static inline const Block* require_block(lua::State* L) {
static inline const Block* get_block_def(lua::State* L) {
auto indices = content->getIndices();
auto id = lua::tointeger(L, 1);
return indices->blocks.get(id);
}
static inline int l_get_def(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushstring(L, def->name);
}
return 0;
}
static int l_material(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushstring(L, def->material);
}
return 0;
@@ -59,14 +59,14 @@ static int l_index(lua::State* L) {
}
static int l_is_extended(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushboolean(L, def->rt.extended);
}
return 0;
}
static int l_get_size(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushivec_stack(L, glm::ivec3(def->size));
}
return 0;
@@ -343,14 +343,14 @@ static int l_is_replaceable_at(lua::State* L) {
}
static int l_caption(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushstring(L, def->caption);
}
return 0;
}
static int l_get_textures(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
lua::createtable(L, 6, 0);
for (size_t i = 0; i < 6; i++) {
lua::pushstring(L, def->defaults.textureFaces[i]); // TODO: variant argument
@@ -363,7 +363,7 @@ static int l_get_textures(lua::State* L) {
static int l_model_name(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
// TODO: variant argument
const auto& modelName = def->defaults.model.name;
if (modelName.empty()) {
@@ -375,7 +375,7 @@ static int l_model_name(lua::State* L) {
}
static int l_get_model(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
// TODO: variant argument
return lua::pushlstring(L, BlockModelTypeMeta.getName(def->defaults.model.type));
}
@@ -383,7 +383,7 @@ static int l_get_model(lua::State* L) {
}
static int l_get_hitbox(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
size_t rotation = lua::tointeger(L, 2);
if (def->rotatable) {
rotation %= def->rotations.MAX_COUNT;
@@ -404,14 +404,14 @@ static int l_get_hitbox(lua::State* L) {
}
static int l_get_rotation_profile(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushstring(L, def->rotations.name);
}
return 0;
}
static int l_get_picking_item(lua::State* L) {
if (auto def = require_block(L)) {
if (auto def = get_block_def(L)) {
return lua::pushinteger(L, def->rt.pickingItem);
}
return 0;
@@ -698,6 +698,49 @@ static int l_reload_script(lua::State* L) {
return 0;
}
static int l_has_tag(lua::State* L) {
if (auto def = get_block_def(L)) {
auto tag = lua::require_string(L, 2);
const auto& tags = def->rt.tags;
return lua::pushboolean(L, tags.find(content->getTagIndex(tag)) != tags.end());
}
return 0;
}
static int l_get_tags(lua::State* L) {
if (auto def = get_block_def(L)) {
if (def->tags.empty()) {
return 0;
}
lua::createtable(L, 0, def->tags.size());
for (const auto& tag : def->tags) {
lua::pushboolean(L, true);
lua::setfield(L, tag);
}
return 1;
}
return 0;
}
static int l_pull_register_events(lua::State* L) {
auto events = blocks_agent::pull_register_events();
if (events.empty())
return 0;
lua::createtable(L, events.size() * 4, 0);
for (int i = 0; i < events.size(); i++) {
const auto& event = events[i];
lua::pushinteger(L, static_cast<int>(event.type) | event.id << 16);
lua::rawseti(L, i * 4 + 1);
for (int j = 0; j < 3; j++) {
lua::pushinteger(L, event.coord[j]);
lua::rawseti(L, i * 4 + j + 2);
}
}
return 1;
}
const luaL_Reg blocklib[] = {
{"index", lua::wrap<l_index>},
{"name", lua::wrap<l_get_def>},
@@ -737,5 +780,8 @@ const luaL_Reg blocklib[] = {
{"get_field", lua::wrap<l_get_field>},
{"set_field", lua::wrap<l_set_field>},
{"reload_script", lua::wrap<l_reload_script>},
{"has_tag", lua::wrap<l_has_tag>},
{"__get_tags", lua::wrap<l_get_tags>},
{"__pull_register_events", lua::wrap<l_pull_register_events>},
{NULL, NULL}
};
+24
View File
@@ -20,6 +20,11 @@
#include "util/platform.hpp"
#include "world/Level.hpp"
#include "world/generator/WorldGenerator.hpp"
#include "util/platform.hpp"
#include "frontend/locale.hpp"
#include "graphics/ui/gui_util.hpp"
#include "graphics/ui/GUI.hpp"
#include "graphics/ui/elements/Menu.hpp"
using namespace scripting;
@@ -229,6 +234,24 @@ static int l_open_folder(lua::State* L) {
return 0;
}
static int l_open_url(lua::State* L) {
auto url = lua::require_string(L, 1);
std::wstring msg = langs::get(L"Are you sure you want to open the link:") +
L"\n" + util::str2wstr_utf8(url) +
std::wstring(L"?");
auto menu = engine->getGUI().getMenu();
guiutil::confirm(*engine, msg, [url, menu]() {
platform::open_url(url);
if (!menu->back()) {
menu->reset();
}
});
return 0;
}
/// @brief Quit the game
static int l_quit(lua::State*) {
engine->quit();
@@ -284,6 +307,7 @@ const luaL_Reg corelib[] = {
{"str_setting", lua::wrap<l_str_setting>},
{"get_setting_info", lua::wrap<l_get_setting_info>},
{"open_folder", lua::wrap<l_open_folder>},
{"open_url", lua::wrap<l_open_url>},
{"quit", lua::wrap<l_quit>},
{"capture_output", lua::wrap<l_capture_output>},
{NULL, NULL}
@@ -4,6 +4,8 @@
#include "engine/Engine.hpp"
#include "objects/Entities.hpp"
#include "objects/EntityDef.hpp"
#include "objects/Entity.hpp"
#include "objects/Rigidbody.hpp"
#include "objects/Player.hpp"
#include "objects/rigging.hpp"
#include "physics/Hitbox.hpp"
@@ -4,6 +4,7 @@
#include "frontend/hud.hpp"
#include "objects/Entities.hpp"
#include "objects/Entity.hpp"
#include "world/Level.hpp"
#include "logic/LevelController.hpp"
#include "api_lua.hpp"
+18 -1
View File
@@ -31,6 +31,8 @@ static int l_mousecode(lua::State* L) {
}
static int l_add_callback(lua::State* L) {
if (engine->isHeadless())
return 0;
std::string bindname = lua::require_string(L, 1);
size_t pos = bindname.find(':');
@@ -75,10 +77,14 @@ static int l_add_callback(lua::State* L) {
}
static int l_get_mouse_pos(lua::State* L) {
if (engine->isHeadless())
return 0;
return lua::pushvec2(L, engine->getInput().getCursor().pos);
}
static int l_get_bindings(lua::State* L) {
if (engine->isHeadless())
return 0;
const auto& bindings = engine->getInput().getBindings().getAll();
lua::createtable(L, bindings.size(), 0);
@@ -92,18 +98,24 @@ static int l_get_bindings(lua::State* L) {
}
static int l_get_binding_text(lua::State* L) {
if (engine->isHeadless())
return 0;
auto bindname = lua::require_string(L, 1);
const auto& bind = engine->getInput().getBindings().require(bindname);
return lua::pushstring(L, bind.text());
}
static int l_is_active(lua::State* L) {
if (engine->isHeadless())
return 0;
auto bindname = lua::require_string(L, 1);
auto& bind = engine->getInput().getBindings().require(bindname);
return lua::pushboolean(L, bind.active());
}
static int l_is_pressed(lua::State* L) {
if (engine->isHeadless())
return 0;
std::string code = lua::require_string(L, 1);
size_t sep = code.find(':');
if (sep == std::string::npos) {
@@ -136,6 +148,8 @@ static void reset_pack_bindings(const io::path& packFolder) {
}
static int l_reset_bindings(lua::State*) {
if (engine->isHeadless())
return 0;
reset_pack_bindings("res:");
for (const auto& pack : content_control->getContentPacks()) {
reset_pack_bindings(pack.folder);
@@ -144,6 +158,8 @@ static int l_reset_bindings(lua::State*) {
}
static int l_set_enabled(lua::State* L) {
if (engine->isHeadless())
return 0;
std::string bindname = lua::require_string(L, 1);
bool enabled = lua::toboolean(L, 2);
engine->getInput().getBindings().require(bindname).enabled = enabled;
@@ -161,4 +177,5 @@ const luaL_Reg inputlib[] = {
{"is_pressed", lua::wrap<l_is_pressed>},
{"reset_bindings", lua::wrap<l_reset_bindings>},
{"set_enabled", lua::wrap<l_set_enabled>},
{NULL, NULL}};
{NULL, NULL}
};
+26
View File
@@ -108,6 +108,30 @@ static int l_reload_script(lua::State* L) {
return 0;
}
static int l_has_tag(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
auto tag = lua::require_string(L, 2);
const auto& tags = def->rt.tags;
return lua::pushboolean(L, tags.find(content->getTagIndex(tag)) != tags.end());
}
return 0;
}
static int l_get_tags(lua::State* L) {
if (auto def = get_item_def(L, 1)) {
if (def->tags.empty()) {
return 0;
}
lua::createtable(L, 0, def->tags.size());
for (const auto& tag : def->tags) {
lua::pushboolean(L, true);
lua::setfield(L, tag);
}
return 1;
}
return 0;
}
const luaL_Reg itemlib[] = {
{"index", lua::wrap<l_index>},
{"name", lua::wrap<l_name>},
@@ -121,5 +145,7 @@ const luaL_Reg itemlib[] = {
{"emission", lua::wrap<l_emission>},
{"uses", lua::wrap<l_uses>},
{"reload_script", lua::wrap<l_reload_script>},
{"has_tag", lua::wrap<l_has_tag>},
{"__get_tags", lua::wrap<l_get_tags>},
{NULL, NULL}
};
+215 -156
View File
@@ -3,73 +3,124 @@
#include "engine/Engine.hpp"
#include "network/Network.hpp"
#include <variant>
#include <utility>
using namespace scripting;
static int l_get(lua::State* L, network::Network& network) {
std::string url(lua::require_lstring(L, 1));
enum NetworkEventType {
CLIENT_CONNECTED = 1,
CONNECTED_TO_SERVER,
DATAGRAM,
RESPONSE,
};
lua::pushvalue(L, 2);
auto onResponse = lua::create_lambda_nothrow(L);
struct ConnectionEventDto {
u64id_t server;
u64id_t client;
};
network::OnReject onReject = nullptr;
if (lua::gettop(L) >= 3) {
lua::pushvalue(L, 3);
auto callback = lua::create_lambda_nothrow(L);
onReject = [callback](int code) {
callback({code});
};
struct ResponseEventDto {
int status;
bool binary;
int requestId;
std::vector<char> bytes;
};
enum NetworkDatagramSide {
ON_SERVER = 1,
ON_CLIENT
};
struct NetworkDatagramEventDto {
NetworkDatagramSide side;
u64id_t server;
u64id_t client;
std::string addr;
int port;
std::vector<char> buffer;
};
struct NetworkEvent {
using Payload = std::variant<
ConnectionEventDto,
ResponseEventDto,
NetworkDatagramEventDto
>;
NetworkEventType type;
Payload payload;
NetworkEvent(
NetworkEventType type,
Payload payload
) : type(type), payload(std::move(payload)) {}
virtual ~NetworkEvent() = default;
};
static std::vector<NetworkEvent> events_queue {};
static std::mutex events_queue_mutex;
static void push_event(NetworkEvent&& event) {
std::lock_guard lock(events_queue_mutex);
events_queue.push_back(std::move(event));
}
static std::vector<std::string> read_headers(lua::State* L, int index) {
std::vector<std::string> headers;
if (lua::istable(L, index)) {
int len = lua::objlen(L, index);
for (int i = 1; i <= len; i++) {
lua::rawgeti(L, i, index);
headers.push_back(lua::tostring(L, -1));
lua::pop(L);
}
}
return headers;
}
network.get(url, [onResponse](std::vector<char> bytes) {
engine->postRunnable([=]() {
onResponse({std::string(bytes.data(), bytes.size())});
});
}, std::move(onReject));
return 0;
static int request_id = 1;
static int perform_get(lua::State* L, network::Network& network, bool binary) {
std::string url(lua::require_lstring(L, 1));
auto headers = read_headers(L, 2);
int currentRequestId = request_id++;
network.get(
url,
[currentRequestId, binary](std::vector<char> bytes) {
push_event(NetworkEvent(
RESPONSE,
ResponseEventDto {
200, binary, currentRequestId, std::move(bytes)}
));
},
[currentRequestId, binary](int code, std::vector<char> bytes) {
push_event(NetworkEvent(
RESPONSE,
ResponseEventDto {
code, binary, currentRequestId, std::move(bytes)}
));
},
std::move(headers)
);
return lua::pushinteger(L, currentRequestId);
}
static int l_get(lua::State* L, network::Network& network) {
return perform_get(L, network, false);
}
static int l_get_binary(lua::State* L, network::Network& network) {
std::string url(lua::require_lstring(L, 1));
lua::pushvalue(L, 2);
auto onResponse = lua::create_lambda_nothrow(L);
network::OnReject onReject = nullptr;
if (lua::gettop(L) >= 3) {
lua::pushvalue(L, 3);
auto callback = lua::create_lambda_nothrow(L);
onReject = [callback](int code) {
callback({code});
};
}
network.get(url, [onResponse](std::vector<char> bytes) {
auto buffer = std::make_shared<util::Buffer<ubyte>>(
reinterpret_cast<const ubyte*>(bytes.data()), bytes.size()
);
engine->postRunnable([=]() {
onResponse({buffer});
});
}, std::move(onReject));
return 0;
return perform_get(L, network, true);
}
static int l_post(lua::State* L, network::Network& network) {
std::string url(lua::require_lstring(L, 1));
auto data = lua::tovalue(L, 2);
lua::pushvalue(L, 3);
auto onResponse = lua::create_lambda_nothrow(L);
network::OnReject onReject = nullptr;
if (lua::gettop(L) >= 4) {
lua::pushvalue(L, 4);
auto callback = lua::create_lambda_nothrow(L);
onReject = [callback](int code) {
callback({code});
};
}
std::string string;
if (data.isString()) {
string = data.asString();
@@ -77,15 +128,29 @@ static int l_post(lua::State* L, network::Network& network) {
string = json::stringify(data, false);
}
engine->getNetwork().post(url, string, [onResponse](std::vector<char> bytes) {
auto buffer = std::make_shared<util::Buffer<ubyte>>(
reinterpret_cast<const ubyte*>(bytes.data()), bytes.size()
);
engine->postRunnable([=]() {
onResponse({std::string(bytes.data(), bytes.size())});
});
}, std::move(onReject));
return 0;
auto headers = read_headers(L, 3);
int currentRequestId = request_id++;
engine->getNetwork().post(
url,
string,
[currentRequestId](std::vector<char> bytes) {
push_event(NetworkEvent(
RESPONSE,
ResponseEventDto {
200, false, currentRequestId, std::move(bytes)}
));
},
[currentRequestId](int code, std::vector<char> bytes) {
push_event(NetworkEvent(
RESPONSE,
ResponseEventDto {
code, false, currentRequestId, std::move(bytes)}
));
},
std::move(headers)
);
return lua::pushinteger(L, currentRequestId);
}
static int l_close(lua::State* L, network::Network& network) {
@@ -210,69 +275,14 @@ static int l_available(lua::State* L, network::Network& network) {
return 0;
}
enum NetworkEventType {
CLIENT_CONNECTED = 1,
CONNECTED_TO_SERVER,
DATAGRAM
};
struct NetworkEvent {
NetworkEventType type;
u64id_t server;
u64id_t client;
NetworkEvent(
NetworkEventType type,
u64id_t server,
u64id_t client
) {
this->type = type;
this->server = server;
this->client = client;
}
virtual ~NetworkEvent() = default;
};
enum NetworkDatagramSide {
ON_SERVER = 1,
ON_CLIENT
};
struct NetworkDatagramEvent : NetworkEvent {
NetworkDatagramSide side;
std::string addr;
int port;
const char* buffer;
size_t length;
NetworkDatagramEvent(
NetworkEventType datagram,
u64id_t sid,
u64id_t cid,
NetworkDatagramSide side,
const std::string& addr,
int port,
const char* data,
size_t length
) : NetworkEvent(DATAGRAM, sid, cid) {
this->side = side;
this->addr = addr;
this->port = port;
buffer = data;
this->length = length;
}
};
static std::vector<std::unique_ptr<NetworkEvent>> events_queue {};
static int l_connect_tcp(lua::State* L, network::Network& network) {
std::string address = lua::require_string(L, 1);
int port = lua::tointeger(L, 2);
u64id_t id = network.connectTcp(address, port, [](u64id_t cid) {
events_queue.push_back(std::make_unique<NetworkEvent>(CONNECTED_TO_SERVER, 0, cid));
push_event(NetworkEvent(
CONNECTED_TO_SERVER,
ConnectionEventDto {0, cid}
));
});
return lua::pushinteger(L, id);
}
@@ -280,7 +290,10 @@ static int l_connect_tcp(lua::State* L, network::Network& network) {
static int l_open_tcp(lua::State* L, network::Network& network) {
int port = lua::tointeger(L, 1);
u64id_t id = network.openTcpServer(port, [](u64id_t sid, u64id_t id) {
events_queue.push_back(std::make_unique<NetworkEvent>(CLIENT_CONNECTED, sid, id));
push_event(NetworkEvent(
CLIENT_CONNECTED,
ConnectionEventDto {sid, id}
));
});
return lua::pushinteger(L, id);
}
@@ -289,18 +302,22 @@ static int l_connect_udp(lua::State* L, network::Network& network) {
std::string address = lua::require_string(L, 1);
int port = lua::tointeger(L, 2);
u64id_t id = network.connectUdp(address, port, [](u64id_t cid) {
events_queue.push_back(std::make_unique<NetworkEvent>(CONNECTED_TO_SERVER, 0, cid));
push_event(NetworkEvent(
CONNECTED_TO_SERVER,
ConnectionEventDto {0, cid}
));
}, [address, port](
u64id_t cid,
const char* buffer,
size_t length
) {
events_queue.push_back(
std::make_unique<NetworkDatagramEvent>(
DATAGRAM, 0, cid, ON_CLIENT,
address, port, buffer, length
)
);
push_event(NetworkEvent(
DATAGRAM,
NetworkDatagramEventDto {
ON_CLIENT, 0, cid,
address, port, std::vector<char>(buffer, buffer + length)
}
));
});
return lua::pushinteger(L, id);
}
@@ -313,10 +330,13 @@ static int l_open_udp(lua::State* L, network::Network& network) {
int port,
const char* buffer,
size_t length) {
events_queue.push_back(
std::make_unique<NetworkDatagramEvent>(
DATAGRAM, sid, 0, ON_SERVER,
addr, port, buffer, length
push_event(
NetworkEvent(
DATAGRAM,
NetworkDatagramEventDto {
ON_SERVER, sid, 0,
addr, port, std::vector<char>(buffer, buffer + length)
}
)
);
});
@@ -405,39 +425,78 @@ static int l_get_nodelay(lua::State* L, network::Network& network) {
}
static int l_pull_events(lua::State* L, network::Network& network) {
lua::createtable(L, events_queue.size(), 0);
std::vector<NetworkEvent> local_queue;
{
std::lock_guard lock(events_queue_mutex);
local_queue.swap(events_queue);
}
for (size_t i = 0; i < events_queue.size(); i++) {
const auto* datagramEvent = dynamic_cast<NetworkDatagramEvent*>(events_queue[i].get());
lua::createtable(L, local_queue.size(), 0);
lua::createtable(L, datagramEvent ? 7 : 3, 0);
for (size_t i = 0; i < local_queue.size(); i++) {
lua::createtable(L, 7, 0);
lua::pushinteger(L, events_queue[i]->type);
lua::rawseti(L, 1);
const auto& event = local_queue[i];
switch (event.type) {
case CLIENT_CONNECTED:
case CONNECTED_TO_SERVER: {
const auto& dto = std::get<ConnectionEventDto>(event.payload);
lua::pushinteger(L, event.type);
lua::rawseti(L, 1);
lua::pushinteger(L, events_queue[i]->server);
lua::rawseti(L, 2);
lua::pushinteger(L, dto.server);
lua::rawseti(L, 2);
lua::pushinteger(L, events_queue[i]->client);
lua::rawseti(L, 3);
lua::pushinteger(L, dto.client);
lua::rawseti(L, 3);
break;
}
case DATAGRAM: {
const auto& dto = std::get<NetworkDatagramEventDto>(event.payload);
lua::pushinteger(L, event.type);
lua::rawseti(L, 1);
if (datagramEvent) {
lua::pushstring(L, datagramEvent->addr);
lua::rawseti(L, 4);
lua::pushinteger(L, dto.server);
lua::rawseti(L, 2);
lua::pushinteger(L, datagramEvent->port);
lua::rawseti(L, 5);
lua::pushinteger(L, dto.client);
lua::rawseti(L, 3);
lua::pushinteger(L, datagramEvent->side);
lua::rawseti(L, 6);
lua::pushstring(L, dto.addr);
lua::rawseti(L, 4);
lua::create_bytearray(L, datagramEvent->buffer, datagramEvent->length);
lua::rawseti(L, 7);
lua::pushinteger(L, dto.port);
lua::rawseti(L, 5);
lua::pushinteger(L, dto.side);
lua::rawseti(L, 6);
lua::create_bytearray(L, dto.buffer.data(), dto.buffer.size());
lua::rawseti(L, 7);
break;
}
case RESPONSE: {
const auto& dto = std::get<ResponseEventDto>(event.payload);
lua::pushinteger(L, event.type);
lua::rawseti(L, 1);
lua::pushinteger(L, dto.status);
lua::rawseti(L, 2);
lua::pushinteger(L, dto.requestId);
lua::rawseti(L, 3);
if (dto.binary) {
lua::create_bytearray(L, dto.bytes.data(), dto.bytes.size());
} else {
lua::pushlstring(L, std::string_view(dto.bytes.data(), dto.bytes.size()));
}
lua::rawseti(L, 4);
break;
}
}
lua::rawseti(L, i + 1);
}
events_queue.clear();
return 1;
}
@@ -459,9 +518,9 @@ int wrap(lua_State* L) {
}
const luaL_Reg networklib[] = {
{"get", wrap<l_get>},
{"get_binary", wrap<l_get_binary>},
{"post", wrap<l_post>},
{"__get", wrap<l_get>},
{"__get_binary", wrap<l_get_binary>},
{"__post", wrap<l_post>},
{"get_total_upload", wrap<l_get_total_upload>},
{"get_total_download", wrap<l_get_total_download>},
{"__pull_events", wrap<l_pull_events>},
+5 -4
View File
@@ -102,19 +102,20 @@ static int l_pack_get_info(
auto& dpack = pack.dependencies[i];
std::string prefix;
switch (dpack.level) {
case DependencyLevel::required:
case DependencyLevel::REQUIRED:
prefix = "!";
break;
case DependencyLevel::optional:
case DependencyLevel::OPTIONAL:
prefix = "?";
break;
case DependencyLevel::weak:
case DependencyLevel::WEAK:
prefix = "~";
break;
default:
throw std::runtime_error("");
}
lua::pushfstring(L, "%s%s", prefix.c_str(), dpack.id.c_str());
lua::pushfstring(L, "%s%s@%s%s", prefix.c_str(), dpack.id.c_str(), dpack.op.c_str(), dpack.version.c_str());
lua::rawseti(L, i + 1);
}
lua::setfield(L, "dependencies");
@@ -0,0 +1,130 @@
#include "api_lua.hpp"
#include "content/Content.hpp"
#include "voxels/Pathfinding.hpp"
#include "world/Level.hpp"
using namespace scripting;
static voxels::Agent* get_agent(lua::State* L) {
return level->pathfinding->getAgent(lua::tointeger(L, 1));
}
static int l_create_agent(lua::State* L) {
return lua::pushinteger(L, level->pathfinding->createAgent());
}
static int l_remove_agent(lua::State* L) {
int id = lua::tointeger(L, 1);
return lua::pushboolean(L, level->pathfinding->removeAgent(id));
}
static int l_set_enabled(lua::State* L) {
if (auto agent = get_agent(L)) {
agent->enabled = lua::toboolean(L, 2);
}
return 0;
}
static int l_is_enabled(lua::State* L) {
if (auto agent = get_agent(L)) {
return lua::pushboolean(L, agent->enabled);
}
return lua::pushboolean(L, false);
}
static int push_route(lua::State* L, const voxels::Route& route) {
lua::createtable(L, route.nodes.size(), 1);
for (int i = 0; i < route.nodes.size(); i++) {
lua::pushvec3(L, route.nodes[i].pos);
lua::rawseti(L, i + 1);
}
lua::pushinteger(L, route.totalVisited);
lua::setfield(L, "total_visited");
return 1;
}
static int l_make_route(lua::State* L) {
if (auto agent = get_agent(L)) {
auto start = lua::tovec3(L, 2);
auto target = lua::tovec3(L, 3);
agent->state = {};
agent->start = glm::floor(start);
agent->target = target;
auto route = level->pathfinding->perform(*agent);
if (!route.found) {
return 0;
}
return push_route(L, route);
}
return 0;
}
static int l_make_route_async(lua::State* L) {
if (auto agent = get_agent(L)) {
auto start = lua::tovec3(L, 2);
auto target = lua::tovec3(L, 3);
agent->state = {};
agent->start = glm::floor(start);
agent->target = target;
level->pathfinding->perform(*agent, 0);
}
return 0;
}
static int l_pull_route(lua::State* L) {
if (auto agent = get_agent(L)) {
auto& route = agent->route;
if (!agent->state.finished) {
return 0;
}
if (!route.found && !agent->mayBeIncomplete) {
return lua::createtable(L, 0, 0);
}
return push_route(L, route);
}
return 0;
}
static int l_set_max_visited_blocks(lua::State* L) {
if (auto agent = get_agent(L)) {
agent->maxVisitedBlocks = lua::tointeger(L, 2);
}
return 0;
}
static int l_set_jump_height(lua::State* L) {
if (auto agent = get_agent(L)) {
agent->jumpHeight = lua::tointeger(L, 2);
}
return 0;
}
static int l_avoid_tag(lua::State* L) {
if (auto agent = get_agent(L)) {
int index =
content->getTagIndex(std::string(lua::require_lstring(L, 2)));
if (index != -1) {
int cost = lua::tonumber(L, 3);
if (cost == 0) {
cost = 10;
}
agent->avoidTags.insert({index, cost});
}
}
return 0;
}
const luaL_Reg pathfindinglib[] = {
{"create_agent", lua::wrap<l_create_agent>},
{"remove_agent", lua::wrap<l_remove_agent>},
{"set_enabled", lua::wrap<l_set_enabled>},
{"is_enabled", lua::wrap<l_is_enabled>},
{"make_route", lua::wrap<l_make_route>},
{"make_route_async", lua::wrap<l_make_route_async>},
{"pull_route", lua::wrap<l_pull_route>},
{"set_max_visited", lua::wrap<l_set_max_visited_blocks>},
{"set_jump_height", lua::wrap<l_set_jump_height>},
{"avoid_tag", lua::wrap<l_avoid_tag>},
{NULL, NULL}
};
+20 -2
View File
@@ -2,13 +2,14 @@
#include <glm/glm.hpp>
#include "items/Inventory.hpp"
#include "libentity.hpp"
#include "objects/Entities.hpp"
#include "objects/Entity.hpp"
#include "objects/Player.hpp"
#include "objects/Players.hpp"
#include "physics/Hitbox.hpp"
#include "window/Camera.hpp"
#include "world/Level.hpp"
#include "libentity.hpp"
using namespace scripting;
@@ -52,7 +53,7 @@ static int l_set_vel(lua::State* L) {
auto x = lua::tonumber(L, 2);
auto y = lua::tonumber(L, 3);
auto z = lua::tonumber(L, 4);
if (auto hitbox = player->getHitbox()) {
hitbox->velocity = glm::vec3(x, y, z);
}
@@ -180,6 +181,21 @@ static int l_set_loading_chunks(lua::State* L) {
return 0;
}
static int l_get_interaction_distance(lua::State* L) {
if (auto player = get_player(L, 1)) {
return lua::pushnumber(L, player->getMaxInteractionDistance());
}
return 0;
}
static int l_set_interaction_distance(lua::State* L) {
if (auto player = get_player(L, 1)) {
player->setMaxInteractionDistance(
static_cast<float>(lua::tonumber(L, 2)));
}
return 0;
}
static int l_get_selected_block(lua::State* L) {
if (auto player = get_player(L, 1)) {
if (player->selection.vox.id == BLOCK_VOID) {
@@ -327,6 +343,8 @@ const luaL_Reg playerlib[] = {
{"set_instant_destruction", lua::wrap<l_set_instant_destruction>},
{"is_loading_chunks", lua::wrap<l_is_loading_chunks>},
{"set_loading_chunks", lua::wrap<l_set_loading_chunks>},
{"get_interaction_distance", lua::wrap<l_get_interaction_distance>},
{"set_interaction_distance", lua::wrap<l_set_interaction_distance>},
{"set_selected_slot", lua::wrap<l_set_selected_slot>},
{"get_selected_block", lua::wrap<l_get_selected_block>},
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
@@ -0,0 +1,46 @@
#include "api_lua.hpp"
#include "util/random.hpp"
static std::random_device random_device;
static int l_random(lua::State* L) {
int argc = lua::gettop(L);
auto randomEngine = util::seeded_random_engine(random_device);
if (argc == 0) {
std::uniform_real_distribution<> dist(0.0, 1.0);
return lua::pushnumber(L, dist(randomEngine));
} else if (argc == 1) {
std::uniform_int_distribution<integer_t> dist(1, lua::tointeger(L, 1));
return lua::pushinteger(L, dist(randomEngine));
} else {
std::uniform_int_distribution<integer_t> dist(
lua::tointeger(L, 1), lua::tointeger(L, 2)
);
return lua::pushinteger(L, dist(randomEngine));
}
}
static int l_bytes(lua::State* L) {
size_t size = lua::tointeger(L, 1);
auto randomEngine = util::seeded_random_engine(random_device);
static std::uniform_int_distribution<integer_t> dist(0, 0xFF);
std::vector<ubyte> bytes (size);
for (size_t i = 0; i < bytes.size(); i++) {
bytes[i] = dist(randomEngine);
}
return lua::create_bytearray(L, bytes);
}
static int l_uuid(lua::State* L) {
return lua::pushlstring(L, util::generate_uuid());
}
const luaL_Reg randomlib[] = {
{"random", lua::wrap<l_random>},
{"bytes", lua::wrap<l_bytes>},
{"uuid", lua::wrap<l_uuid>},
{NULL, NULL}
};
+56 -15
View File
@@ -15,10 +15,24 @@ inline T angle(glm::vec<2, T> vec) {
return val;
}
template <int n>
static int l_mix(lua::State* L) {
uint argc = lua::check_argc(L, 3, 4);
auto a = lua::tovec<n, number_t>(L, 1);
auto b = lua::tovec<n, number_t>(L, 2);
auto t = lua::tonumber(L, 3);
if (argc == 3) {
return lua::pushvec(L, a * (1.0 - t) + b * t);
} else {
return lua::setvec(L, 4, a * (1.0 - t) + b * t);
}
}
template <int n, template <class> class Op>
static int l_binop(lua::State* L) {
uint argc = lua::check_argc(L, 2, 3);
auto a = lua::tovec<n>(L, 1);
auto a = lua::tovec<n, number_t>(L, 1);
if (lua::isnumber(L, 2)) { // scalar second operand overload
auto b = lua::tonumber(L, 2);
@@ -31,10 +45,10 @@ static int l_binop(lua::State* L) {
}
return 1;
} else {
return lua::setvec(L, 3, op(a, glm::vec<n, float>(b)));
return lua::setvec(L, 3, op(a, glm::vec<n, number_t>(b)));
}
} else {
auto b = lua::tovec<n>(L, 2);
auto b = lua::tovec<n, number_t>(L, 2);
Op op;
if (argc == 2) {
lua::createtable(L, n, 0);
@@ -49,10 +63,10 @@ static int l_binop(lua::State* L) {
}
}
template <int n, glm::vec<n, float> (*func)(const glm::vec<n, float>&)>
template <int n, glm::vec<n, number_t> (*func)(const glm::vec<n, number_t>&)>
static int l_unaryop(lua::State* L) {
uint argc = lua::check_argc(L, 1, 2);
auto vec = func(lua::tovec<n>(L, 1));
auto vec = func(lua::tovec<n, number_t>(L, 1));
switch (argc) {
case 1:
lua::createtable(L, n, 0);
@@ -67,17 +81,25 @@ static int l_unaryop(lua::State* L) {
return 0;
}
template <int n, float (*func)(const glm::vec<n, float>&)>
template <int n, number_t (*func)(const glm::vec<n, number_t>&)>
static int l_scalar_op(lua::State* L) {
lua::check_argc(L, 1);
auto vec = lua::tovec<n>(L, 1);
auto vec = lua::tovec<n, number_t>(L, 1);
return lua::pushnumber(L, func(vec));
}
template <int n>
static int l_distance(lua::State* L) {
lua::check_argc(L, 2);
auto a = lua::tovec<n, number_t>(L, 1);
auto b = lua::tovec<n, number_t>(L, 2);
return lua::pushnumber(L,glm::distance(a, b));
}
template <int n>
static int l_pow(lua::State* L) {
uint argc = lua::check_argc(L, 2, 3);
auto a = lua::tovec<n>(L, 1);
auto a = lua::tovec<n, number_t>(L, 1);
if (lua::isnumber(L, 2)) {
auto b = lua::tonumber(L, 2);
@@ -89,10 +111,10 @@ static int l_pow(lua::State* L) {
}
return 1;
} else {
return lua::setvec(L, 3, pow(a, glm::vec<n, float>(b)));
return lua::setvec(L, 3, pow(a, glm::vec<n, number_t>(b)));
}
} else {
auto b = lua::tovec<n>(L, 2);
auto b = lua::tovec<n, number_t>(L, 2);
if (argc == 2) {
lua::createtable(L, n, 0);
for (uint i = 0; i < n; i++) {
@@ -109,15 +131,15 @@ static int l_pow(lua::State* L) {
template <int n>
static int l_dot(lua::State* L) {
lua::check_argc(L, 2);
const auto& a = lua::tovec<n>(L, 1);
const auto& b = lua::tovec<n>(L, 2);
auto a = lua::tovec<n, number_t>(L, 1);
auto b = lua::tovec<n, number_t>(L, 2);
return lua::pushnumber(L, glm::dot(a, b));
}
template <int n>
static int l_inverse(lua::State* L) {
uint argc = lua::check_argc(L, 1, 2);
auto vec = lua::tovec<n>(L, 1);
auto vec = lua::tovec<n, number_t>(L, 1);
switch (argc) {
case 1:
lua::createtable(L, n, 0);
@@ -141,7 +163,7 @@ static int l_spherical_rand(lua::State* L) {
return lua::setvec(
L,
2,
glm::sphericalRand(static_cast<float>(lua::tonumber(L, 1)))
glm::sphericalRand(lua::tonumber(L, 1))
);
}
return 0;
@@ -161,10 +183,22 @@ static int l_vec2_angle(lua::State* L) {
}
}
static int l_vec2_rotate(lua::State* L) {
uint argc = lua::check_argc(L, 2, 3);
auto vec = lua::tovec<2, number_t>(L, 1);
auto angle = glm::radians(lua::tonumber(L, 2));
if (argc == 2) {
return lua::pushvec(L, glm::rotate(vec, angle));
} else {
return lua::setvec(L, 3, glm::rotate(vec, angle));
}
}
template <int n>
static int l_tostring(lua::State* L) {
lua::check_argc(L, 1);
auto vec = lua::tovec<n>(L, 1);
auto vec = lua::tovec<n, number_t>(L, 1);
std::stringstream ss;
ss << "vec" << std::to_string(n) << "{";
for (int i = 0; i < n; i++) {
@@ -182,6 +216,7 @@ const luaL_Reg vec2lib[] = {
{"sub", lua::wrap<l_binop<2, std::minus>>},
{"mul", lua::wrap<l_binop<2, std::multiplies>>},
{"div", lua::wrap<l_binop<2, std::divides>>},
{"distance", lua::wrap<l_distance<2>>},
{"normalize", lua::wrap<l_unaryop<2, glm::normalize>>},
{"length", lua::wrap<l_scalar_op<2, glm::length>>},
{"tostring", lua::wrap<l_tostring<2>>},
@@ -191,6 +226,8 @@ const luaL_Reg vec2lib[] = {
{"pow", lua::wrap<l_pow<2>>},
{"dot", lua::wrap<l_dot<2>>},
{"angle", lua::wrap<l_vec2_angle>},
{"mix", lua::wrap<l_mix<2>>},
{"rotate", lua::wrap<l_vec2_rotate>},
{NULL, NULL}};
const luaL_Reg vec3lib[] = {
@@ -198,6 +235,7 @@ const luaL_Reg vec3lib[] = {
{"sub", lua::wrap<l_binop<3, std::minus>>},
{"mul", lua::wrap<l_binop<3, std::multiplies>>},
{"div", lua::wrap<l_binop<3, std::divides>>},
{"distance", lua::wrap<l_distance<3>>},
{"normalize", lua::wrap<l_unaryop<3, glm::normalize>>},
{"length", lua::wrap<l_scalar_op<3, glm::length>>},
{"tostring", lua::wrap<l_tostring<3>>},
@@ -207,6 +245,7 @@ const luaL_Reg vec3lib[] = {
{"pow", lua::wrap<l_pow<3>>},
{"dot", lua::wrap<l_dot<3>>},
{"spherical_rand", lua::wrap<l_spherical_rand>},
{"mix", lua::wrap<l_mix<3>>},
{NULL, NULL}};
const luaL_Reg vec4lib[] = {
@@ -214,6 +253,7 @@ const luaL_Reg vec4lib[] = {
{"sub", lua::wrap<l_binop<4, std::minus>>},
{"mul", lua::wrap<l_binop<4, std::multiplies>>},
{"div", lua::wrap<l_binop<4, std::divides>>},
{"distance", lua::wrap<l_distance<4>>},
{"normalize", lua::wrap<l_unaryop<4, glm::normalize>>},
{"length", lua::wrap<l_scalar_op<4, glm::length>>},
{"tostring", lua::wrap<l_tostring<4>>},
@@ -222,4 +262,5 @@ const luaL_Reg vec4lib[] = {
{"inverse", lua::wrap<l_inverse<4>>},
{"pow", lua::wrap<l_pow<4>>},
{"dot", lua::wrap<l_dot<4>>},
{"mix", lua::wrap<l_mix<4>>},
{NULL, NULL}};
+2
View File
@@ -51,6 +51,7 @@ static void create_libs(State* L, StateType stateType) {
openlib(L, "mat4", mat4lib);
openlib(L, "pack", packlib);
openlib(L, "quat", quatlib);
openlib(L, "random", randomlib);
openlib(L, "toml", tomllib);
openlib(L, "utf8", utf8lib);
openlib(L, "vec2", vec2lib);
@@ -72,6 +73,7 @@ static void create_libs(State* L, StateType stateType) {
openlib(L, "input", inputlib);
openlib(L, "inventory", inventorylib);
openlib(L, "network", networklib);
openlib(L, "pathfinding", pathfindinglib);
openlib(L, "player", playerlib);
openlib(L, "time", timelib);
openlib(L, "world", worldlib);
+8 -8
View File
@@ -48,8 +48,8 @@ namespace lua {
return true;
}
template <int n>
inline int pushvec(lua::State* L, const glm::vec<n, float>& vec) {
template <int n, typename T = float>
inline int pushvec(lua::State* L, const glm::vec<n, T>& vec) {
createtable(L, n, 0);
for (int i = 0; i < n; i++) {
pushnumber(L, vec[i]);
@@ -161,8 +161,8 @@ namespace lua {
}
return 1;
}
template <int n>
inline int setvec(lua::State* L, int idx, glm::vec<n, float> vec) {
template <int n, typename T = float>
inline int setvec(lua::State* L, int idx, glm::vec<n, T> vec) {
pushvalue(L, idx);
for (int i = 0; i < n; i++) {
pushnumber(L, vec[i]);
@@ -305,15 +305,15 @@ namespace lua {
setglobal(L, name);
}
template <int n>
inline glm::vec<n, float> tovec(lua::State* L, int idx) {
template <int n, typename T = float>
inline glm::vec<n, T> tovec(lua::State* L, int idx) {
pushvalue(L, idx);
if (!istable(L, idx) || objlen(L, idx) < n) {
throw std::runtime_error(
"value must be an array of " + std::to_string(n) + " numbers"
);
}
glm::vec<n, float> vec;
glm::vec<n, T> vec;
for (int i = 0; i < n; i++) {
rawgeti(L, i + 1);
vec[i] = tonumber(L, -1);
@@ -455,7 +455,7 @@ namespace lua {
inline bool getfield(lua::State* L, const std::string& name, int idx = -1) {
lua_getfield(L, idx, name.c_str());
if (isnil(L, idx)) {
if (isnoneornil(L, -1)) {
pop(L);
return false;
}
+5 -272
View File
@@ -19,13 +19,12 @@
#include "lua/lua_engine.hpp"
#include "lua/lua_custom_types.hpp"
#include "maths/Heightmap.hpp"
#include "objects/Entities.hpp"
#include "objects/EntityDef.hpp"
#include "objects/Player.hpp"
#include "util/stringutil.hpp"
#include "util/timeutil.hpp"
#include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
#include "voxels/blocks_agent.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
#include "interfaces/Process.hpp"
@@ -34,8 +33,6 @@ using namespace scripting;
static debug::Logger logger("scripting");
static inline const std::string STDCOMP = "stdcomp";
std::ostream* scripting::output_stream = &std::cout;
std::ostream* scripting::error_stream = &std::cerr;
Engine* scripting::engine = nullptr;
@@ -234,36 +231,6 @@ std::unique_ptr<Process> scripting::start_coroutine(const io::path& script) {
});
}
[[nodiscard]] static scriptenv create_component_environment(
const scriptenv& parent, int entityIdx, const std::string& name
) {
auto L = lua::get_main_state();
int id = lua::create_environment(L, *parent);
lua::pushvalue(L, entityIdx);
lua::pushenv(L, id);
lua::pushvalue(L, -1);
lua::setfield(L, "this");
lua::pushvalue(L, -2);
lua::setfield(L, "entity");
lua::pop(L);
if (lua::getfield(L, "components")) {
lua::pushenv(L, id);
lua::setfield(L, name);
lua::pop(L);
}
lua::pop(L);
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
lua::remove_environment(L, *id);
delete id;
});
}
void scripting::process_post_runnables() {
auto L = lua::get_main_state();
if (lua::getglobal(L, "__process_post_runnables")) {
@@ -498,6 +465,7 @@ void scripting::on_chunk_present(const Chunk& chunk, bool loaded) {
);
}
}
blocks_agent::on_chunk_present(*content->getIndices(), chunk);
}
void scripting::on_chunk_remove(const Chunk& chunk) {
@@ -512,6 +480,7 @@ void scripting::on_chunk_remove(const Chunk& chunk) {
);
}
}
blocks_agent::on_chunk_remove(*content->getIndices(), chunk);
}
void scripting::on_inventory_open(const Player* player, const Inventory& inventory) {
@@ -599,244 +568,6 @@ bool scripting::on_item_break_block(
);
}
dv::value scripting::get_component_value(
const scriptenv& env, const std::string& name
) {
auto L = lua::get_main_state();
lua::pushenv(L, *env);
if (lua::getfield(L, name)) {
return lua::tovalue(L, -1);
}
return nullptr;
}
void scripting::on_entity_spawn(
const EntityDef&,
entityid_t eid,
const std::vector<std::unique_ptr<UserComponent>>& components,
const dv::value& args,
const dv::value& saved
) {
auto L = lua::get_main_state();
lua::stackguard guard(L);
lua::requireglobal(L, STDCOMP);
if (lua::getfield(L, "new_Entity")) {
lua::pushinteger(L, eid);
lua::call(L, 1);
}
if (components.size() > 1) {
for (size_t i = 0; i < components.size() - 1; i++) {
lua::pushvalue(L, -1);
}
}
for (auto& component : components) {
auto compenv = create_component_environment(
get_root_environment(), -1, component->name
);
lua::get_from(L, lua::CHUNKS_TABLE, component->name, true);
lua::pushenv(L, *compenv);
if (args != nullptr) {
std::string compfieldname = component->name;
util::replaceAll(compfieldname, ":", "__");
if (args.has(compfieldname)) {
lua::pushvalue(L, args[compfieldname]);
} else {
lua::createtable(L, 0, 0);
}
} else {
lua::createtable(L, 0, 0);
}
lua::setfield(L, "ARGS");
if (saved == nullptr) {
lua::createtable(L, 0, 0);
} else {
if (saved.has(component->name)) {
lua::pushvalue(L, saved[component->name]);
} else {
lua::createtable(L, 0, 0);
}
}
lua::setfield(L, "SAVED_DATA");
lua::setfenv(L);
lua::call_nothrow(L, 0, 0);
lua::pushenv(L, *compenv);
auto& funcsset = component->funcsset;
funcsset.on_grounded = lua::hasfield(L, "on_grounded");
funcsset.on_fall = lua::hasfield(L, "on_fall");
funcsset.on_despawn = lua::hasfield(L, "on_despawn");
funcsset.on_sensor_enter = lua::hasfield(L, "on_sensor_enter");
funcsset.on_sensor_exit = lua::hasfield(L, "on_sensor_exit");
funcsset.on_save = lua::hasfield(L, "on_save");
funcsset.on_aim_on = lua::hasfield(L, "on_aim_on");
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
funcsset.on_used = lua::hasfield(L, "on_used");
lua::pop(L, 2);
component->env = compenv;
}
}
static void process_entity_callback(
const scriptenv& env,
const std::string& name,
std::function<int(lua::State*)> args
) {
auto L = lua::get_main_state();
lua::pushenv(L, *env);
if (lua::hasfield(L, "__disabled")) {
lua::pop(L);
return;
}
if (lua::getfield(L, name)) {
if (args) {
lua::call_nothrow(L, args(L), 0);
} else {
lua::call_nothrow(L, 0, 0);
}
}
lua::pop(L);
}
static void process_entity_callback(
const Entity& entity,
const std::string& name,
bool EntityFuncsSet::*flag,
std::function<int(lua::State*)> args
) {
const auto& script = entity.getScripting();
for (auto& component : script.components) {
if (component->funcsset.*flag) {
process_entity_callback(component->env, name, args);
}
}
}
void scripting::on_entity_despawn(const Entity& entity) {
process_entity_callback(
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
);
auto L = lua::get_main_state();
lua::get_from(L, "stdcomp", "remove_Entity", true);
lua::pushinteger(L, entity.getUID());
lua::call(L, 1, 0);
}
void scripting::on_entity_grounded(const Entity& entity, float force) {
process_entity_callback(
entity,
"on_grounded",
&EntityFuncsSet::on_grounded,
[force](auto L) { return lua::pushnumber(L, force); }
);
}
void scripting::on_entity_fall(const Entity& entity) {
process_entity_callback(
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
);
}
void scripting::on_entity_save(const Entity& entity) {
process_entity_callback(
entity, "on_save", &EntityFuncsSet::on_save, nullptr
);
}
void scripting::on_sensor_enter(
const Entity& entity, size_t index, entityid_t oid
) {
process_entity_callback(
entity,
"on_sensor_enter",
&EntityFuncsSet::on_sensor_enter,
[index, oid](auto L) {
lua::pushinteger(L, index);
lua::pushinteger(L, oid);
return 2;
}
);
}
void scripting::on_sensor_exit(
const Entity& entity, size_t index, entityid_t oid
) {
process_entity_callback(
entity,
"on_sensor_exit",
&EntityFuncsSet::on_sensor_exit,
[index, oid](auto L) {
lua::pushinteger(L, index);
lua::pushinteger(L, oid);
return 2;
}
);
}
void scripting::on_aim_on(const Entity& entity, Player* player) {
process_entity_callback(
entity,
"on_aim_on",
&EntityFuncsSet::on_aim_on,
[player](auto L) { return lua::pushinteger(L, player->getId()); }
);
}
void scripting::on_aim_off(const Entity& entity, Player* player) {
process_entity_callback(
entity,
"on_aim_off",
&EntityFuncsSet::on_aim_off,
[player](auto L) { return lua::pushinteger(L, player->getId()); }
);
}
void scripting::on_attacked(
const Entity& entity, Player* player, entityid_t attacker
) {
process_entity_callback(
entity,
"on_attacked",
&EntityFuncsSet::on_attacked,
[player, attacker](auto L) {
lua::pushinteger(L, attacker);
lua::pushinteger(L, player->getId());
return 2;
}
);
}
void scripting::on_entity_used(const Entity& entity, Player* player) {
process_entity_callback(
entity,
"on_used",
&EntityFuncsSet::on_used,
[player](auto L) { return lua::pushinteger(L, player->getId()); }
);
}
void scripting::on_entities_update(int tps, int parts, int part) {
auto L = lua::get_main_state();
lua::get_from(L, STDCOMP, "update", true);
lua::pushinteger(L, tps);
lua::pushinteger(L, parts);
lua::pushinteger(L, part);
lua::call_nothrow(L, 3, 0);
lua::pop(L);
}
void scripting::on_entities_render(float delta) {
auto L = lua::get_main_state();
lua::get_from(L, STDCOMP, "render", true);
lua::pushnumber(L, delta);
lua::call_nothrow(L, 1, 0);
lua::pop(L);
}
void scripting::on_ui_open(
UiDocument* layout, std::vector<dv::value> args
) {
@@ -920,6 +651,8 @@ void scripting::load_content_script(
register_event(env, "on_replaced", prefix + ".replaced");
funcsset.oninteract =
register_event(env, "on_interact", prefix + ".interact");
funcsset.onblocktick =
register_event(env, "on_block_tick", prefix + ".blocktick");
funcsset.onblockstick =
register_event(env, "on_blocks_tick", prefix + ".blockstick");
}
+1
View File
@@ -139,6 +139,7 @@ namespace scripting {
void on_entity_fall(const Entity& entity);
void on_entity_save(const Entity& entity);
void on_entities_update(int tps, int parts, int part);
void on_entities_physics_update(float delta);
void on_entities_render(float delta);
void on_sensor_enter(const Entity& entity, size_t index, entityid_t oid);
void on_sensor_exit(const Entity& entity, size_t index, entityid_t oid);
+296
View File
@@ -0,0 +1,296 @@
#include "scripting.hpp"
#include "lua/lua_engine.hpp"
#include "objects/Entities.hpp"
#include "objects/EntityDef.hpp"
#include "objects/Entity.hpp"
#include "objects/Player.hpp"
#include "util/stringutil.hpp"
using namespace scripting;
static inline const std::string STDCOMP = "stdcomp";
[[nodiscard]] static scriptenv create_component_environment(
const scriptenv& parent, int entityIdx, const std::string& name
) {
auto L = lua::get_main_state();
int id = lua::create_environment(L, *parent);
lua::pushvalue(L, entityIdx);
lua::pushenv(L, id);
lua::pushvalue(L, -1);
lua::setfield(L, "this");
lua::pushvalue(L, -2);
lua::setfield(L, "entity");
lua::pop(L);
if (lua::getfield(L, "components")) {
lua::pushenv(L, id);
lua::setfield(L, name);
lua::pop(L);
}
lua::pop(L);
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508
lua::remove_environment(L, *id);
delete id;
});
}
dv::value scripting::get_component_value(
const scriptenv& env, const std::string& name
) {
auto L = lua::get_main_state();
lua::pushenv(L, *env);
if (lua::getfield(L, name)) {
return lua::tovalue(L, -1);
}
return nullptr;
}
static void create_component(
lua::State* L,
int entityIdx,
UserComponent& component,
const dv::value& args,
const dv::value& saved
) {
lua::pushvalue(L, entityIdx);
auto compenv = create_component_environment(
get_root_environment(), -1, component.name
);
lua::get_from(L, lua::CHUNKS_TABLE, component.name, true);
lua::pushenv(L, *compenv);
if (args != nullptr) {
std::string compfieldname = component.name;
util::replaceAll(compfieldname, ":", "__");
if (args.has(compfieldname)) {
lua::pushvalue(L, args[compfieldname]);
} else {
lua::createtable(L, 0, 0);
}
} else if (component.params != nullptr) {
lua::pushvalue(L, component.params);
} else {
lua::createtable(L, 0, 0);
}
lua::setfield(L, "ARGS");
if (saved == nullptr) {
lua::createtable(L, 0, 0);
} else {
if (saved.has(component.name)) {
lua::pushvalue(L, saved[component.name]);
} else {
lua::createtable(L, 0, 0);
}
}
lua::setfield(L, "SAVED_DATA");
lua::setfenv(L);
lua::call_nothrow(L, 0, 0);
lua::pushenv(L, *compenv);
auto& funcsset = component.funcsset;
funcsset.on_grounded = lua::hasfield(L, "on_grounded");
funcsset.on_fall = lua::hasfield(L, "on_fall");
funcsset.on_despawn = lua::hasfield(L, "on_despawn");
funcsset.on_sensor_enter = lua::hasfield(L, "on_sensor_enter");
funcsset.on_sensor_exit = lua::hasfield(L, "on_sensor_exit");
funcsset.on_save = lua::hasfield(L, "on_save");
funcsset.on_aim_on = lua::hasfield(L, "on_aim_on");
funcsset.on_aim_off = lua::hasfield(L, "on_aim_off");
funcsset.on_attacked = lua::hasfield(L, "on_attacked");
funcsset.on_used = lua::hasfield(L, "on_used");
lua::pop(L, 2);
component.env = compenv;
}
void scripting::on_entity_spawn(
const EntityDef&,
entityid_t eid,
const std::vector<std::unique_ptr<UserComponent>>& components,
const dv::value& args,
const dv::value& saved
) {
auto L = lua::get_main_state();
lua::stackguard guard(L);
lua::requireglobal(L, STDCOMP);
if (lua::getfield(L, "new_Entity")) {
lua::pushinteger(L, eid);
lua::call(L, 1);
}
for (auto& component : components) {
create_component(L, -1, *component, args, saved);
}
}
static void process_entity_callback(
const scriptenv& env,
const std::string& name,
std::function<int(lua::State*)> args
) {
auto L = lua::get_main_state();
lua::pushenv(L, *env);
if (lua::hasfield(L, "__disabled")) {
lua::pop(L);
return;
}
if (lua::getfield(L, name)) {
if (args) {
lua::call_nothrow(L, args(L), 0);
} else {
lua::call_nothrow(L, 0, 0);
}
}
lua::pop(L);
}
static void process_entity_callback(
const Entity& entity,
const std::string& name,
bool EntityFuncsSet::*flag,
std::function<int(lua::State*)> args
) {
const auto& script = entity.getScripting();
for (auto& component : script.components) {
if (component->funcsset.*flag) {
process_entity_callback(component->env, name, args);
}
}
}
void scripting::on_entity_despawn(const Entity& entity) {
process_entity_callback(
entity, "on_despawn", &EntityFuncsSet::on_despawn, nullptr
);
auto L = lua::get_main_state();
lua::get_from(L, "stdcomp", "remove_Entity", true);
lua::pushinteger(L, entity.getUID());
lua::call(L, 1, 0);
}
void scripting::on_entity_grounded(const Entity& entity, float force) {
process_entity_callback(
entity,
"on_grounded",
&EntityFuncsSet::on_grounded,
[force](auto L) { return lua::pushnumber(L, force); }
);
}
void scripting::on_entity_fall(const Entity& entity) {
process_entity_callback(
entity, "on_fall", &EntityFuncsSet::on_fall, nullptr
);
}
void scripting::on_entity_save(const Entity& entity) {
process_entity_callback(
entity, "on_save", &EntityFuncsSet::on_save, nullptr
);
}
void scripting::on_sensor_enter(
const Entity& entity, size_t index, entityid_t oid
) {
process_entity_callback(
entity,
"on_sensor_enter",
&EntityFuncsSet::on_sensor_enter,
[index, oid](auto L) {
lua::pushinteger(L, index);
lua::pushinteger(L, oid);
return 2;
}
);
}
void scripting::on_sensor_exit(
const Entity& entity, size_t index, entityid_t oid
) {
process_entity_callback(
entity,
"on_sensor_exit",
&EntityFuncsSet::on_sensor_exit,
[index, oid](auto L) {
lua::pushinteger(L, index);
lua::pushinteger(L, oid);
return 2;
}
);
}
void scripting::on_aim_on(const Entity& entity, Player* player) {
process_entity_callback(
entity,
"on_aim_on",
&EntityFuncsSet::on_aim_on,
[player](auto L) { return lua::pushinteger(L, player->getId()); }
);
}
void scripting::on_aim_off(const Entity& entity, Player* player) {
process_entity_callback(
entity,
"on_aim_off",
&EntityFuncsSet::on_aim_off,
[player](auto L) { return lua::pushinteger(L, player->getId()); }
);
}
void scripting::on_attacked(
const Entity& entity, Player* player, entityid_t attacker
) {
process_entity_callback(
entity,
"on_attacked",
&EntityFuncsSet::on_attacked,
[player, attacker](auto L) {
lua::pushinteger(L, attacker);
lua::pushinteger(L, player->getId());
return 2;
}
);
}
void scripting::on_entity_used(const Entity& entity, Player* player) {
process_entity_callback(
entity,
"on_used",
&EntityFuncsSet::on_used,
[player](auto L) { return lua::pushinteger(L, player->getId()); }
);
}
void scripting::on_entities_update(int tps, int parts, int part) {
auto L = lua::get_main_state();
lua::get_from(L, STDCOMP, "update", true);
lua::pushinteger(L, tps);
lua::pushinteger(L, parts);
lua::pushinteger(L, part);
lua::call_nothrow(L, 3, 0);
lua::pop(L);
}
void scripting::on_entities_physics_update(float delta) {
auto L = lua::get_main_state();
lua::get_from(L, STDCOMP, "physics_update", true);
lua::pushnumber(L, delta);
lua::call_nothrow(L, 1, 0);
lua::pop(L);
}
void scripting::on_entities_render(float delta) {
auto L = lua::get_main_state();
lua::get_from(L, STDCOMP, "render", true);
lua::pushnumber(L, delta);
lua::call_nothrow(L, 1, 0);
lua::pop(L);
}
+48 -17
View File
@@ -57,6 +57,7 @@ struct Request {
long maxSize;
bool followLocation = false;
std::string data;
std::vector<std::string> headers;
};
class CurlRequests : public Requests {
@@ -86,10 +87,18 @@ public:
const std::string& url,
OnResponse onResponse,
OnReject onReject,
std::vector<std::string> headers,
long maxSize
) override {
Request request {
RequestType::GET, url, onResponse, onReject, maxSize, false, ""};
RequestType::GET,
url,
onResponse,
onReject,
maxSize,
true,
"",
std::move(headers)};
processRequest(std::move(request));
}
@@ -98,10 +107,18 @@ public:
const std::string& data,
OnResponse onResponse,
OnReject onReject=nullptr,
std::vector<std::string> headers = {},
long maxSize=0
) override {
Request request {
RequestType::POST, url, onResponse, onReject, maxSize, false, ""};
RequestType::POST,
url,
onResponse,
onReject,
maxSize,
false,
"",
std::move(headers)};
request.data = data;
processRequest(std::move(request));
}
@@ -121,6 +138,10 @@ public:
curl_easy_setopt(curl, CURLOPT_POST, request.type == RequestType::POST);
curl_slist* hs = NULL;
for (const auto& header : request.headers) {
hs = curl_slist_append(hs, header.c_str());
}
switch (request.type) {
case RequestType::GET:
@@ -152,7 +173,7 @@ public:
auto message = curl_multi_strerror(res);
logger.error() << message << " (" << url << ")";
if (onReject) {
onReject(HTTP_BAD_GATEWAY);
onReject(HTTP_BAD_GATEWAY, {});
}
url = "";
}
@@ -167,7 +188,7 @@ public:
auto message = curl_multi_strerror(res);
logger.error() << message << " (" << url << ")";
if (onReject) {
onReject(HTTP_BAD_GATEWAY);
onReject(HTTP_BAD_GATEWAY, {});
}
curl_multi_remove_handle(multiHandle, curl);
url = "";
@@ -192,9 +213,14 @@ public:
onResponse(std::move(buffer));
}
} else {
logger.error() << "response code " << response << " (" << url << ")";
logger.error()
<< "response code " << response << " (" << url << ")"
<< (buffer.empty()
? ""
: std::to_string(buffer.size()) + " byte(s)");
totalDownload += buffer.size();
if (onReject) {
onReject(response);
onReject(response, std::move(buffer));
}
}
url = "";
@@ -585,10 +611,12 @@ public:
}
int opt = 1;
int flags = SO_REUSEADDR;
# ifndef _WIN32
# if !defined(_WIN32) && !defined(__APPLE__)
flags |= SO_REUSEPORT;
# endif
if (setsockopt(descriptor, SOL_SOCKET, flags, (const char*)&opt, sizeof(opt))) {
logger.error() << "setsockopt(SO_REUSEADDR) failed with errno: "
<< errno << "(" << std::strerror(errno) << ")";
closesocket(descriptor);
throw std::runtime_error("setsockopt");
}
@@ -637,19 +665,19 @@ public:
) {
SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
if (descriptor == -1) {
throw std::runtime_error("could not create UDP socket");
throw std::runtime_error("could not create udp socket");
}
sockaddr_in serverAddr{};
serverAddr.sin_family = AF_INET;
if (inet_pton(AF_INET, address.c_str(), &serverAddr.sin_addr) <= 0) {
closesocket(descriptor);
throw std::runtime_error("invalid UDP address: " + address);
throw std::runtime_error("invalid udp address: " + address);
}
serverAddr.sin_port = htons(port);
if (::connect(descriptor, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
auto err = handle_socket_error("UDP connect failed");
auto err = handle_socket_error("udp connect failed");
closesocket(descriptor);
throw err;
}
@@ -738,7 +766,6 @@ public:
class SocketUdpServer : public UdpServer {
u64id_t id;
Network* network;
SOCKET descriptor;
bool open = true;
std::unique_ptr<std::thread> thread = nullptr;
@@ -747,13 +774,13 @@ class SocketUdpServer : public UdpServer {
public:
SocketUdpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
: id(id), network(network), descriptor(descriptor), port(port) {}
: id(id), descriptor(descriptor), port(port) {}
~SocketUdpServer() override {
SocketUdpServer::close();
}
void startListen(ServerDatagramCallback handler) {
void startListen(ServerDatagramCallback handler) override {
callback = std::move(handler);
thread = std::make_unique<std::thread>([this]() {
@@ -805,7 +832,7 @@ public:
u64id_t id, Network* network, int port, const ServerDatagramCallback& handler
) {
SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
if (descriptor == -1) throw std::runtime_error("Could not create UDP socket");
if (descriptor == -1) throw std::runtime_error("could not create udp socket");
sockaddr_in address{};
address.sin_family = AF_INET;
@@ -814,7 +841,7 @@ public:
if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) {
closesocket(descriptor);
throw std::runtime_error("Could not bind UDP port " + std::to_string(port));
throw std::runtime_error("could not bind udp port " + std::to_string(port));
}
auto server = std::make_shared<SocketUdpServer>(id, network, descriptor, port);
@@ -833,9 +860,10 @@ void Network::get(
const std::string& url,
OnResponse onResponse,
OnReject onReject,
std::vector<std::string> headers,
long maxSize
) {
requests->get(url, onResponse, onReject, maxSize);
requests->get(url, onResponse, onReject, std::move(headers), maxSize);
}
void Network::post(
@@ -843,9 +871,12 @@ void Network::post(
const std::string& fieldsData,
OnResponse onResponse,
OnReject onReject,
std::vector<std::string> headers,
long maxSize
) {
requests->post(url, fieldsData, onResponse, onReject, maxSize);
requests->post(
url, fieldsData, onResponse, onReject, std::move(headers), maxSize
);
}
Connection* Network::getConnection(u64id_t id) {
+5 -2
View File
@@ -11,12 +11,11 @@
namespace network {
using OnResponse = std::function<void(std::vector<char>)>;
using OnReject = std::function<void(int)>;
using OnReject = std::function<void(int, std::vector<char>)>;
using ConnectCallback = std::function<void(u64id_t, u64id_t)>;
using ServerDatagramCallback = std::function<void(u64id_t sid, const std::string& addr, int port, const char* buffer, size_t length)>;
using ClientDatagramCallback = std::function<void(u64id_t cid, const char* buffer, size_t length)>;
class Requests {
public:
virtual ~Requests() {}
@@ -25,6 +24,7 @@ namespace network {
const std::string& url,
OnResponse onResponse,
OnReject onReject=nullptr,
std::vector<std::string> headers = {},
long maxSize=0
) = 0;
@@ -33,6 +33,7 @@ namespace network {
const std::string& data,
OnResponse onResponse,
OnReject onReject=nullptr,
std::vector<std::string> headers = {},
long maxSize=0
) = 0;
@@ -146,6 +147,7 @@ namespace network {
const std::string& url,
OnResponse onResponse,
OnReject onReject = nullptr,
std::vector<std::string> headers = {},
long maxSize=0
);
@@ -154,6 +156,7 @@ namespace network {
const std::string& fieldsData,
OnResponse onResponse,
OnReject onReject = nullptr,
std::vector<std::string> headers = {},
long maxSize=0
);
+35 -205
View File
@@ -17,110 +17,25 @@
#include "maths/FrustumCulling.hpp"
#include "maths/rays.hpp"
#include "EntityDef.hpp"
#include "Entity.hpp"
#include "rigging.hpp"
#include "physics/Hitbox.hpp"
#include "physics/PhysicsSolver.hpp"
#include "world/Level.hpp"
static debug::Logger logger("entities");
static inline std::string COMP_TRANSFORM = "transform";
static inline std::string COMP_RIGIDBODY = "rigidbody";
static inline std::string COMP_SKELETON = "skeleton";
static inline std::string SAVED_DATA_VARNAME = "SAVED_DATA";
void Transform::refresh() {
combined = glm::mat4(1.0f);
combined = glm::translate(combined, pos);
combined = combined * glm::mat4(rot);
combined = glm::scale(combined, size);
displayPos = pos;
displaySize = size;
dirty = false;
}
void Entity::setInterpolatedPosition(const glm::vec3& position) {
getSkeleton().interpolation.refresh(position);
}
glm::vec3 Entity::getInterpolatedPosition() const {
const auto& skeleton = getSkeleton();
if (skeleton.interpolation.isEnabled()) {
return skeleton.interpolation.getCurrent();
}
return getTransform().pos;
}
void Entity::destroy() {
if (isValid()) {
entities.despawn(id);
}
}
rigging::Skeleton& Entity::getSkeleton() const {
return registry.get<rigging::Skeleton>(entity);
}
void Entity::setRig(const rigging::SkeletonConfig* rigConfig) {
auto& skeleton = registry.get<rigging::Skeleton>(entity);
skeleton.config = rigConfig;
skeleton.pose.matrices.resize(
rigConfig->getBones().size(), glm::mat4(1.0f)
);
skeleton.calculated.matrices.resize(
rigConfig->getBones().size(), glm::mat4(1.0f)
);
}
Entities::Entities(Level& level)
: level(level), sensorsTickClock(20, 3), updateTickClock(20, 3) {
: level(level),
sensorsTickClock(20, 3),
updateTickClock(20, 3) {
}
template <void (*callback)(const Entity&, size_t, entityid_t)>
static sensorcallback create_sensor_callback(Entities* entities) {
return [=](auto entityid, auto index, auto otherid) {
if (auto entity = entities->get(entityid)) {
if (entity->isValid()) {
callback(*entity, index, otherid);
}
}
};
}
static void initialize_body(
const EntityDef& def, Rigidbody& body, entityid_t id, Entities* entities
) {
body.sensors.resize(def.radialSensors.size() + def.boxSensors.size());
for (auto& [i, box] : def.boxSensors) {
SensorParams params {};
params.aabb = box;
body.sensors[i] = Sensor {
true,
SensorType::AABB,
i,
id,
params,
params,
{},
{},
create_sensor_callback<scripting::on_sensor_enter>(entities),
create_sensor_callback<scripting::on_sensor_exit>(entities)};
}
for (auto& [i, radius] : def.radialSensors) {
SensorParams params {};
params.radial = glm::vec4(radius);
body.sensors[i] = Sensor {
true,
SensorType::RADIUS,
i,
id,
params,
params,
{},
{},
create_sensor_callback<scripting::on_sensor_enter>(entities),
create_sensor_callback<scripting::on_sensor_exit>(entities)};
std::optional<Entity> Entities::get(entityid_t id) {
const auto& found = entities.find(id);
if (found != entities.end() && registry.valid(found->second)) {
return Entity(*this, id, registry, found->second);
}
return std::nullopt;
}
entityid_t Entities::spawn(
@@ -168,14 +83,14 @@ entityid_t Entities::spawn(
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
std::vector<Sensor> {}
);
initialize_body(def, body, id, this);
body.initialize(def, id, *this);
auto& scripting = registry.emplace<ScriptComponents>(entity);
registry.emplace<rigging::Skeleton>(entity, skeleton->instance());
for (auto& componentName : def.components) {
for (auto& instance : def.components) {
auto component = std::make_unique<UserComponent>(
componentName, EntityFuncsSet {}, nullptr
instance.component, EntityFuncsSet {}, nullptr, instance.params
);
scripting.components.emplace_back(std::move(component));
}
@@ -186,7 +101,8 @@ entityid_t Entities::spawn(
}
body.hitbox.position = tsf.pos;
scripting::on_entity_spawn(
def, id, scripting.components, args, componentsMap);
def, id, scripting.components, args, componentsMap
);
return id;
}
@@ -213,41 +129,18 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
auto& skeleton = entity.getSkeleton();
if (map.has(COMP_RIGIDBODY)) {
auto& bodymap = map[COMP_RIGIDBODY];
dv::get_vec(bodymap, "vel", body.hitbox.velocity);
std::string bodyTypeName;
map.at("type").get(bodyTypeName);
BodyTypeMeta.getItem(bodyTypeName, body.hitbox.type);
bodymap["crouch"].asBoolean(body.hitbox.crouching);
bodymap["damping"].asNumber(body.hitbox.linearDamping);
body.deserialize(map[COMP_RIGIDBODY]);
}
if (map.has(COMP_TRANSFORM)) {
auto& tsfmap = map[COMP_TRANSFORM];
dv::get_vec(tsfmap, "pos", transform.pos);
dv::get_vec(tsfmap, "size", transform.size);
dv::get_mat(tsfmap, "rot", transform.rot);
transform.deserialize(map[COMP_TRANSFORM]);
}
std::string skeletonName = skeleton.config->getName();
map.at("skeleton").get(skeletonName);
if (skeletonName != skeleton.config->getName()) {
skeleton.config = level.content.getSkeleton(skeletonName);
}
if (auto found = map.at(COMP_SKELETON)) {
auto& skeletonmap = *found;
if (auto found = skeletonmap.at("textures")) {
auto& texturesmap = *found;
for (auto& [slot, _] : texturesmap.asObject()) {
texturesmap.at(slot).get(skeleton.textures[slot]);
}
}
if (auto found = skeletonmap.at("pose")) {
auto& posearr = *found;
for (size_t i = 0;
i < std::min(skeleton.pose.matrices.size(), posearr.size());
i++) {
dv::get_mat(posearr[i], skeleton.pose.matrices[i]);
}
}
if (auto foundSkeleton = map.at(COMP_SKELETON)) {
skeleton.deserialize(*foundSkeleton);
}
}
@@ -284,7 +177,7 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
void Entities::loadEntities(dv::value root) {
clean();
auto& list = root["data"];
const auto& list = root["data"];
for (auto& map : list) {
try {
loadEntity(map);
@@ -298,74 +191,6 @@ void Entities::onSave(const Entity& entity) {
scripting::on_entity_save(entity);
}
dv::value Entities::serialize(const Entity& entity) {
auto root = dv::object();
auto& eid = entity.getID();
auto& def = eid.def;
root["def"] = def.name;
root["uid"] = eid.uid;
{
auto& transform = entity.getTransform();
auto& tsfmap = root.object(COMP_TRANSFORM);
tsfmap["pos"] = dv::to_value(transform.pos);
if (transform.size != glm::vec3(1.0f)) {
tsfmap["size"] = dv::to_value(transform.size);
}
if (transform.rot != glm::mat3(1.0f)) {
tsfmap["rot"] = dv::to_value(transform.rot);
}
}
{
auto& rigidbody = entity.getRigidbody();
auto& hitbox = rigidbody.hitbox;
auto& bodymap = root.object(COMP_RIGIDBODY);
if (!rigidbody.enabled) {
bodymap["enabled"] = false;
}
if (def.save.body.velocity) {
bodymap["vel"] = dv::to_value(rigidbody.hitbox.velocity);
}
if (def.save.body.settings) {
bodymap["damping"] = rigidbody.hitbox.linearDamping;
if (hitbox.type != def.bodyType) {
bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type);
}
if (hitbox.crouching) {
bodymap["crouch"] = hitbox.crouching;
}
}
}
auto& skeleton = entity.getSkeleton();
if (skeleton.config->getName() != def.skeletonName) {
root["skeleton"] = skeleton.config->getName();
}
if (def.save.skeleton.pose || def.save.skeleton.textures) {
auto& skeletonmap = root.object(COMP_SKELETON);
if (def.save.skeleton.textures) {
auto& map = skeletonmap.object("textures");
for (auto& [slot, texture] : skeleton.textures) {
map[slot] = texture;
}
}
if (def.save.skeleton.pose) {
auto& list = skeletonmap.list("pose");
for (auto& mat : skeleton.pose.matrices) {
list.add(dv::to_value(mat));
}
}
}
auto& scripts = entity.getScripting();
if (!scripts.components.empty()) {
auto& compsMap = root.object("comps");
for (auto& comp : scripts.components) {
auto data =
scripting::get_component_value(comp->env, SAVED_DATA_VARNAME);
compsMap[comp->name] = data;
}
}
return root;
}
dv::value Entities::serialize(const std::vector<Entity>& entities) {
auto list = dv::list();
for (auto& entity : entities) {
@@ -375,7 +200,7 @@ dv::value Entities::serialize(const std::vector<Entity>& entities) {
}
level.entities->onSave(entity);
if (!eid.destroyFlag) {
list.add(level.entities->serialize(entity));
list.add(entity.serialize());
}
}
return list;
@@ -474,7 +299,9 @@ void Entities::updatePhysics(float delta) {
int substeps = static_cast<int>(delta * vel * 20);
substeps = std::min(100, std::max(2, substeps));
physics->step(*level.chunks, hitbox, delta, substeps, eid.uid);
hitbox.linearDamping = hitbox.grounded * 24;
hitbox.friction = glm::abs(hitbox.gravityScale <= 1e-7f)
? 8.0f
: (!grounded ? 2.0f : 10.0f);
transform.setPos(hitbox.position);
if (hitbox.grounded && !grounded) {
scripting::on_entity_grounded(
@@ -495,6 +322,8 @@ void Entities::update(float delta) {
updateTickClock.getPart()
);
}
updatePhysics(delta);
scripting::on_entities_physics_update(delta);
}
static void debug_render_skeleton(
@@ -505,13 +334,10 @@ static void debug_render_skeleton(
size_t pindex = bone->getIndex();
for (auto& sub : bone->getSubnodes()) {
size_t sindex = sub->getIndex();
const auto& matrices = skeleton.calculated.matrices;
batch.line(
glm::vec3(
skeleton.calculated.matrices[pindex] * glm::vec4(0, 0, 0, 1)
),
glm::vec3(
skeleton.calculated.matrices[sindex] * glm::vec4(0, 0, 0, 1)
),
glm::vec3(matrices[pindex] * glm::vec4(0, 0, 0, 1)),
glm::vec3(matrices[sindex] * glm::vec4(0, 0, 0, 1)),
glm::vec4(0, 0.5f, 0, 1)
);
debug_render_skeleton(batch, sub.get(), skeleton);
@@ -568,10 +394,14 @@ void Entities::render(
ModelBatch& batch,
const Frustum* frustum,
float delta,
bool pause
bool pause,
entityid_t fpsEntity
) {
auto view = registry.view<Transform, rigging::Skeleton>();
for (auto [entity, transform, skeleton] : view.each()) {
auto view = registry.view<EntityId, Transform, rigging::Skeleton>();
for (auto [entity, eid, transform, skeleton] : view.each()) {
if (eid.uid == fpsEntity) {
continue;
}
if (transform.dirty) {
transform.refresh();
}
+9 -161
View File
@@ -5,101 +5,21 @@
#include <optional>
#include <vector>
#include "data/dv.hpp"
#include "physics/Hitbox.hpp"
#include "Transform.hpp"
#include "Rigidbody.hpp"
#include "ScriptComponents.hpp"
#include "typedefs.hpp"
#include "util/Clock.hpp"
#define GLM_ENABLE_EXPERIMENTAL
#include <entt/entity/registry.hpp>
#include <glm/gtx/norm.hpp>
#include <unordered_map>
struct EntityFuncsSet {
bool init;
bool on_despawn;
bool on_grounded;
bool on_fall;
bool on_sensor_enter;
bool on_sensor_exit;
bool on_save;
bool on_aim_on;
bool on_aim_off;
bool on_attacked;
bool on_used;
};
#include <entt/entity/registry.hpp>
#include <unordered_map>
struct EntityDef;
struct EntityId {
entityid_t uid;
const EntityDef& def;
bool destroyFlag = false;
int64_t player = -1;
};
struct Transform {
static inline constexpr float EPSILON = 0.0000001f;
glm::vec3 pos;
glm::vec3 size;
glm::mat3 rot;
glm::mat4 combined;
bool dirty = true;
glm::vec3 displayPos;
glm::vec3 displaySize;
void refresh();
inline void setRot(glm::mat3 m) {
rot = m;
dirty = true;
}
inline void setSize(glm::vec3 v) {
if (glm::distance2(displaySize, v) >= EPSILON) {
dirty = true;
}
size = v;
}
inline void setPos(glm::vec3 v) {
if (glm::distance2(displayPos, v) >= EPSILON) {
dirty = true;
}
pos = v;
}
};
struct Rigidbody {
bool enabled = true;
Hitbox hitbox;
std::vector<Sensor> sensors;
};
struct UserComponent {
std::string name;
EntityFuncsSet funcsset;
scriptenv env;
UserComponent(
const std::string& name, EntityFuncsSet funcsset, scriptenv env
)
: name(name), funcsset(funcsset), env(env) {
}
};
struct ScriptComponents {
std::vector<std::unique_ptr<UserComponent>> components;
ScriptComponents() = default;
ScriptComponents(ScriptComponents&& other)
: components(std::move(other.components)) {
}
};
class Level;
class Assets;
class Entity;
class LineBatch;
class ModelBatch;
class Frustum;
@@ -111,72 +31,6 @@ namespace rigging {
class SkeletonConfig;
}
class Entity {
Entities& entities;
entityid_t id;
entt::registry& registry;
const entt::entity entity;
public:
Entity(
Entities& entities,
entityid_t id,
entt::registry& registry,
const entt::entity entity
)
: entities(entities), id(id), registry(registry), entity(entity) {
}
EntityId& getID() const {
return registry.get<EntityId>(entity);
}
bool isValid() const {
return registry.valid(entity);
}
const EntityDef& getDef() const {
return registry.get<EntityId>(entity).def;
}
Transform& getTransform() const {
return registry.get<Transform>(entity);
}
Rigidbody& getRigidbody() const {
return registry.get<Rigidbody>(entity);
}
ScriptComponents& getScripting() const {
return registry.get<ScriptComponents>(entity);
}
rigging::Skeleton& getSkeleton() const;
void setRig(const rigging::SkeletonConfig* rigConfig);
entityid_t getUID() const {
return registry.get<EntityId>(entity).uid;
}
entt::entity getHandler() const {
return entity;
}
int64_t getPlayer() const {
return registry.get<EntityId>(entity).player;
}
void setPlayer(int64_t id) {
registry.get<EntityId>(entity).player = id;
}
void setInterpolatedPosition(const glm::vec3& position);
glm::vec3 getInterpolatedPosition() const;
void destroy();
};
class Entities {
entt::registry registry;
Level& level;
@@ -211,7 +65,8 @@ public:
ModelBatch& batch,
const Frustum* frustum,
float delta,
bool pause
bool pause,
entityid_t fpsEntity
);
entityid_t spawn(
@@ -222,13 +77,7 @@ public:
entityid_t uid = 0
);
std::optional<Entity> get(entityid_t id) {
const auto& found = entities.find(id);
if (found != entities.end() && registry.valid(found->second)) {
return Entity(*this, id, registry, found->second);
}
return std::nullopt;
}
std::optional<Entity> get(entityid_t id);
/// @brief Entities raycast. No blocks check included, use combined with
/// Chunks.rayCast
@@ -253,7 +102,6 @@ public:
std::vector<Entity> getAllInRadius(glm::vec3 center, float radius);
void despawn(entityid_t id);
void despawn(std::vector<Entity> entities);
dv::value serialize(const Entity& entity);
dv::value serialize(const std::vector<Entity>& entities);
void setNextID(entityid_t id) {
+119
View File
@@ -0,0 +1,119 @@
#include "Entity.hpp"
#include "Transform.hpp"
#include "Rigidbody.hpp"
#include "ScriptComponents.hpp"
#include "Entities.hpp"
#include "EntityDef.hpp"
#include "rigging.hpp"
#include "logic/scripting/scripting.hpp"
#include <entt/entt.hpp>
static inline std::string SAVED_DATA_VARNAME = "SAVED_DATA";
void Entity::setInterpolatedPosition(const glm::vec3& position) {
getSkeleton().interpolation.refresh(position);
}
glm::vec3 Entity::getInterpolatedPosition() const {
const auto& skeleton = getSkeleton();
if (skeleton.interpolation.isEnabled()) {
return skeleton.interpolation.getCurrent();
}
return getTransform().pos;
}
void Entity::destroy() {
if (isValid()) {
entities.despawn(id);
}
}
rigging::Skeleton& Entity::getSkeleton() const {
return registry.get<rigging::Skeleton>(entity);
}
void Entity::setRig(const rigging::SkeletonConfig* rigConfig) {
auto& skeleton = registry.get<rigging::Skeleton>(entity);
skeleton.config = rigConfig;
skeleton.pose.matrices.resize(
rigConfig->getBones().size(), glm::mat4(1.0f)
);
skeleton.calculated.matrices.resize(
rigConfig->getBones().size(), glm::mat4(1.0f)
);
}
dv::value Entity::serialize() const {
const auto& eid = getID();
const auto& def = eid.def;
const auto& transform = getTransform();
const auto& rigidbody = getRigidbody();
const auto& skeleton = getSkeleton();
const auto& scripts = getScripting();
auto root = dv::object();
root["def"] = def.name;
root["uid"] = eid.uid;
root[COMP_TRANSFORM] = transform.serialize();
root[COMP_RIGIDBODY] =
rigidbody.serialize(def.save.body.velocity, def.save.body.settings);
if (skeleton.config->getName() != def.skeletonName) {
root["skeleton"] = skeleton.config->getName();
}
if (def.save.skeleton.pose || def.save.skeleton.textures) {
root[COMP_SKELETON] = skeleton.serialize(
def.save.skeleton.pose, def.save.skeleton.textures
);
}
if (!scripts.components.empty()) {
auto& compsMap = root.object("comps");
for (auto& comp : scripts.components) {
auto data =
scripting::get_component_value(comp->env, SAVED_DATA_VARNAME);
compsMap[comp->name] = data;
}
}
return root;
}
EntityId& Entity::getID() const {
return registry.get<EntityId>(entity);
}
bool Entity::isValid() const {
return registry.valid(entity);
}
Transform& Entity::getTransform() const {
return registry.get<Transform>(entity);
}
ScriptComponents& Entity::getScripting() const {
return registry.get<ScriptComponents>(entity);
}
const EntityDef& Entity::getDef() const {
return registry.get<EntityId>(entity).def;
}
Rigidbody& Entity::getRigidbody() const {
return registry.get<Rigidbody>(entity);
}
entityid_t Entity::getUID() const {
return registry.get<EntityId>(entity).uid;
}
int64_t Entity::getPlayer() const {
return registry.get<EntityId>(entity).player;
}
void Entity::setPlayer(int64_t id) {
registry.get<EntityId>(entity).player = id;
}
+80
View File
@@ -0,0 +1,80 @@
#pragma once
#include "typedefs.hpp"
#include "data/dv_fwd.hpp"
#include <string>
#include <entt/fwd.hpp>
#include <glm/vec3.hpp>
class Entities;
struct EntityDef;
struct Transform;
struct Rigidbody;
struct ScriptComponents;
inline std::string COMP_TRANSFORM = "transform";
inline std::string COMP_RIGIDBODY = "rigidbody";
inline std::string COMP_SKELETON = "skeleton";
namespace rigging {
struct Skeleton;
class SkeletonConfig;
}
struct EntityId {
entityid_t uid;
const EntityDef& def;
bool destroyFlag = false;
int64_t player = -1;
};
class Entity {
Entities& entities;
entityid_t id;
entt::registry& registry;
const entt::entity entity;
public:
Entity(
Entities& entities,
entityid_t id,
entt::registry& registry,
const entt::entity entity
)
: entities(entities), id(id), registry(registry), entity(entity) {
}
dv::value serialize() const;
EntityId& getID() const;
bool isValid() const;
const EntityDef& getDef() const;
Transform& getTransform() const;
Rigidbody& getRigidbody() const;
ScriptComponents& getScripting() const;
rigging::Skeleton& getSkeleton() const;
void setRig(const rigging::SkeletonConfig* rigConfig);
entityid_t getUID() const;
int64_t getPlayer() const;
void setPlayer(int64_t id);
void setInterpolatedPosition(const glm::vec3& position);
glm::vec3 getInterpolatedPosition() const;
entt::entity getHandler() const {
return entity;
}
void destroy();
};
+8 -2
View File
@@ -5,6 +5,7 @@
#include <glm/glm.hpp>
#include "typedefs.hpp"
#include "data/dv.hpp"
#include "maths/aabb.hpp"
#include "physics/Hitbox.hpp"
@@ -12,12 +13,17 @@ namespace rigging {
class SkeletonConfig;
}
struct ComponentInstance {
std::string component;
dv::value params;
};
struct EntityDef {
/// @brief Entity string id (with prefix included)
std::string const name;
/// @brief Component IDs
std::vector<std::string> components;
/// @brief Component instances
std::vector<ComponentInstance> components;
/// @brief Physic body type
BodyType bodyType = BodyType::DYNAMIC;
+12 -73
View File
@@ -9,6 +9,7 @@
#include "content/ContentReport.hpp"
#include "items/Inventory.hpp"
#include "Entities.hpp"
#include "Entity.hpp"
#include "rigging.hpp"
#include "physics/Hitbox.hpp"
#include "physics/PhysicsSolver.hpp"
@@ -20,13 +21,6 @@
static debug::Logger logger("player");
constexpr float CROUCH_SPEED_MUL = 0.35f;
constexpr float RUN_SPEED_MUL = 1.5f;
constexpr float PLAYER_GROUND_DAMPING = 10.0f;
constexpr float PLAYER_AIR_DAMPING = 8.0f;
constexpr float FLIGHT_SPEED_MUL = 4.0f;
constexpr float CHEAT_SPEED_MUL = 5.0f;
constexpr float JUMP_FORCE = 8.0f;
constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64;
Player::Player(
@@ -81,17 +75,6 @@ void Player::updateEntity() {
"will be respawned";
eid = ENTITY_AUTO;
}
auto hitbox = getHitbox();
if (hitbox == nullptr) {
return;
}
hitbox->linearDamping = PLAYER_GROUND_DAMPING;
hitbox->verticalDamping = flight;
hitbox->gravityScale = flight ? 0.0f : 1.0f;
if (flight || !hitbox->grounded) {
hitbox->linearDamping = PLAYER_AIR_DAMPING;
}
hitbox->type = noclip ? BodyType::KINEMATIC : BodyType::DYNAMIC;
}
Hitbox* Player::getHitbox() {
@@ -101,57 +84,6 @@ Hitbox* Player::getHitbox() {
return nullptr;
}
void Player::updateInput(PlayerInput& input, float delta) {
auto hitbox = getHitbox();
if (hitbox == nullptr) {
return;
}
bool crouch = input.shift && hitbox->grounded && !input.sprint;
float speed = this->speed;
if (flight) {
speed *= FLIGHT_SPEED_MUL;
}
if (input.cheat) {
speed *= CHEAT_SPEED_MUL;
}
hitbox->crouching = crouch;
if (crouch) {
speed *= CROUCH_SPEED_MUL;
} else if (input.sprint) {
speed *= RUN_SPEED_MUL;
}
glm::vec3 dir(0, 0, 0);
if (input.moveForward) {
dir += fpCamera->dir;
}
if (input.moveBack) {
dir -= fpCamera->dir;
}
if (input.moveRight) {
dir += fpCamera->right;
}
if (input.moveLeft) {
dir -= fpCamera->right;
}
if (glm::length(dir) > 0.0f) {
dir = glm::normalize(dir);
hitbox->velocity += dir * speed * delta * 9.0f;
}
if (flight) {
if (input.jump) {
hitbox->velocity.y += speed * delta * 9;
}
if (input.shift) {
hitbox->velocity.y -= speed * delta * 9;
}
}
if (input.jump && hitbox->grounded) {
hitbox->velocity.y = JUMP_FORCE;
}
}
void Player::updateSelectedEntity() {
selectedEid = selection.entity;
}
@@ -172,10 +104,6 @@ void Player::postUpdate() {
attemptToFindSpawnpoint();
}
}
// TODO: ERASE & FORGET
auto& skeleton = entity->getSkeleton();
skeleton.visible = currentCamera != fpCamera;
}
void Player::teleport(glm::vec3 position) {
@@ -268,6 +196,14 @@ void Player::setLoadingChunks(bool flag) {
loadingChunks = flag;
}
float Player::getMaxInteractionDistance() const {
return interactionDistance;
}
void Player::setMaxInteractionDistance(float distance) {
interactionDistance = std::max(1.0f, std::min(200.0f, distance));
}
entityid_t Player::getEntity() const {
return eid;
}
@@ -322,6 +258,7 @@ dv::value Player::serialize() const {
root["rotation"] = dv::to_value(rotation);
root["spawnpoint"] = dv::to_value(spawnpoint);
root["interaction-distance"] = interactionDistance;
root["flight"] = flight;
root["noclip"] = noclip;
root["suspended"] = suspended;
@@ -355,6 +292,8 @@ void Player::deserialize(const dv::value& src) {
const auto& sparr = src["spawnpoint"];
setSpawnPoint(glm::vec3(
sparr[0].asNumber(), sparr[1].asNumber(), sparr[2].asNumber()));
src.at("interaction-distance").get(interactionDistance);
flight = src["flight"].asBoolean();
noclip = src["noclip"].asBoolean();
+7 -1
View File
@@ -46,6 +46,7 @@ class Player : public Serializable {
int64_t id;
std::string name;
float speed;
int chosenSlot;
glm::vec3 position;
glm::vec3 spawnpoint {};
@@ -56,6 +57,8 @@ class Player : public Serializable {
bool infiniteItems = true;
bool instantDestruction = true;
bool loadingChunks = true;
float interactionDistance = 10.0f;
entityid_t eid = ENTITY_AUTO;
entityid_t selectedEid = 0;
@@ -82,7 +85,6 @@ public:
void teleport(glm::vec3 position);
void updateEntity();
void updateInput(PlayerInput& input, float delta);
void updateSelectedEntity();
void postUpdate();
@@ -91,6 +93,7 @@ public:
void setChosenSlot(int index);
int getChosenSlot() const;
float getSpeed() const;
bool isSuspended() const;
@@ -111,6 +114,9 @@ public:
bool isLoadingChunks() const;
void setLoadingChunks(bool flag);
float getMaxInteractionDistance() const;
void setMaxInteractionDistance(float distance);
entityid_t getEntity() const;
void setEntity(entityid_t eid);
+83
View File
@@ -0,0 +1,83 @@
#define VC_ENABLE_REFLECTION
#include "Rigidbody.hpp"
#include "EntityDef.hpp"
#include "Entities.hpp"
#include "Entity.hpp"
#include "data/dv_util.hpp"
#include "logic/scripting/scripting.hpp"
dv::value Rigidbody::serialize(bool saveVelocity, bool saveBodySettings) const {
auto bodymap = dv::object();
if (!enabled) {
bodymap["enabled"] = false;
}
if (saveVelocity) {
bodymap["vel"] = dv::to_value(hitbox.velocity);
}
if (saveBodySettings) {
bodymap["damping"] = hitbox.linearDamping;
bodymap["type"] = BodyTypeMeta.getNameString(hitbox.type);
if (hitbox.crouching) {
bodymap["crouch"] = hitbox.crouching;
}
}
return bodymap;
}
void Rigidbody::deserialize(const dv::value& root) {
dv::get_vec(root, "vel", hitbox.velocity);
std::string bodyTypeName;
root.at("type").get(bodyTypeName);
BodyTypeMeta.getItem(bodyTypeName, hitbox.type);
root["crouch"].asBoolean(hitbox.crouching);
root["damping"].asNumber(hitbox.linearDamping);
}
template <void (*callback)(const Entity&, size_t, entityid_t)>
static sensorcallback create_sensor_callback(Entities& entities) {
return [&entities](auto entityid, auto index, auto otherid) {
if (auto entity = entities.get(entityid)) {
if (entity->isValid()) {
callback(*entity, index, otherid);
}
}
};
}
void Rigidbody::initialize(
const EntityDef& def, entityid_t id, Entities& entities
) {
sensors.resize(def.radialSensors.size() + def.boxSensors.size());
for (auto& [i, box] : def.boxSensors) {
SensorParams params {};
params.aabb = box;
sensors[i] = Sensor {
true,
SensorType::AABB,
i,
id,
params,
params,
{},
{},
create_sensor_callback<scripting::on_sensor_enter>(entities),
create_sensor_callback<scripting::on_sensor_exit>(entities)};
}
for (auto& [i, radius] : def.radialSensors) {
SensorParams params {};
params.radial = glm::vec4(radius);
sensors[i] = Sensor {
true,
SensorType::RADIUS,
i,
id,
params,
params,
{},
{},
create_sensor_callback<scripting::on_sensor_enter>(entities),
create_sensor_callback<scripting::on_sensor_exit>(entities)};
}
}
+23
View File
@@ -0,0 +1,23 @@
#pragma once
#include "data/dv_fwd.hpp"
#include "physics/Hitbox.hpp"
#include <vector>
#include <entt/fwd.hpp>
class Entities;
struct EntityDef;
struct Rigidbody {
bool enabled = true;
Hitbox hitbox;
std::vector<Sensor> sensors;
dv::value serialize(bool saveVelocity, bool saveBodySettings) const;
void deserialize(const dv::value& root);
void initialize(
const EntityDef& def, entityid_t id, Entities& entities
);
};
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include "typedefs.hpp"
#include "data/dv.hpp"
#include <string>
struct EntityFuncsSet {
bool init;
bool on_despawn;
bool on_grounded;
bool on_fall;
bool on_sensor_enter;
bool on_sensor_exit;
bool on_save;
bool on_aim_on;
bool on_aim_off;
bool on_attacked;
bool on_used;
};
struct UserComponent {
std::string name;
EntityFuncsSet funcsset;
scriptenv env;
dv::value params;
UserComponent(
const std::string& name,
EntityFuncsSet funcsset,
scriptenv env,
dv::value params
)
: name(name),
funcsset(funcsset),
env(std::move(env)),
params(std::move(params)) {
}
};
struct ScriptComponents {
std::vector<std::unique_ptr<UserComponent>> components;
ScriptComponents() = default;
ScriptComponents(ScriptComponents&& other)
: components(std::move(other.components)) {
}
};
+31
View File
@@ -0,0 +1,31 @@
#include "Transform.hpp"
#include "data/dv_util.hpp"
void Transform::refresh() {
combined = glm::mat4(1.0f);
combined = glm::translate(combined, pos);
combined = combined * glm::mat4(rot);
combined = glm::scale(combined, size);
displayPos = pos;
displaySize = size;
dirty = false;
}
dv::value Transform::serialize() const {
auto tsfmap = dv::object();
tsfmap["pos"] = dv::to_value(pos);
if (size != glm::vec3(1.0f)) {
tsfmap["size"] = dv::to_value(size);
}
if (rot != glm::mat3(1.0f)) {
tsfmap["rot"] = dv::to_value(rot);
}
return tsfmap;
}
void Transform::deserialize(const dv::value& root) {
dv::get_vec(root, "pos", pos);
dv::get_vec(root, "size", size);
dv::get_mat(root, "rot", rot);
}
+44
View File
@@ -0,0 +1,44 @@
#pragma once
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/vec3.hpp>
#include <glm/mat4x4.hpp>
#include <glm/gtx/norm.hpp>
#include <data/dv_fwd.hpp>
struct Transform {
static inline constexpr float EPSILON = 1e-7f;
glm::vec3 pos;
glm::vec3 size;
glm::mat3 rot;
glm::mat4 combined;
bool dirty = true;
glm::vec3 displayPos;
glm::vec3 displaySize;
dv::value serialize() const;
void deserialize(const dv::value& root);
void refresh();
inline void setRot(glm::mat3 m) {
rot = m;
dirty = true;
}
inline void setSize(glm::vec3 v) {
if (glm::distance2(displaySize, v) >= EPSILON) {
dirty = true;
}
size = v;
}
inline void setPos(glm::vec3 v) {
if (glm::distance2(displayPos, v) >= EPSILON) {
dirty = true;
}
pos = v;
}
};
+34 -1
View File
@@ -23,7 +23,7 @@ Bone::Bone(
std::string name,
std::string model,
std::vector<std::unique_ptr<Bone>> bones,
glm::vec3 offset
const glm::vec3& offset
)
: index(index),
name(std::move(name)),
@@ -53,6 +53,39 @@ Skeleton::Skeleton(const SkeletonConfig* config)
}
}
dv::value Skeleton::serialize(bool saveTextures, bool savePose) const {
auto root = dv::object();
if (saveTextures) {
auto& map = root.object("textures");
for (auto& [slot, texture] : textures) {
map[slot] = texture;
}
}
if (savePose) {
auto& list = root.list("pose");
for (auto& mat : pose.matrices) {
list.add(dv::to_value(mat));
}
}
return root;
}
void Skeleton::deserialize(const dv::value& root) {
if (auto found = root.at("textures")) {
auto& texturesmap = *found;
for (auto& [slot, _] : texturesmap.asObject()) {
texturesmap.at(slot).get(textures[slot]);
}
}
if (auto found = root.at("pose")) {
auto& posearr = *found;
auto& matrices = pose.matrices;
for (size_t i = 0; i < std::min(matrices.size(), posearr.size()); i++) {
dv::get_mat(posearr[i], pose.matrices[i]);
}
}
}
static void get_all_nodes(std::vector<Bone*>& nodes, Bone* node) {
nodes[node->getIndex()] = node;
for (auto& subnode : node->getSubnodes()) {
+5 -1
View File
@@ -9,6 +9,7 @@
#include <vector>
#include "typedefs.hpp"
#include "data/dv_fwd.hpp"
#include "util/Interpolation.hpp"
class Assets;
@@ -50,7 +51,7 @@ namespace rigging {
std::string name,
std::string model,
std::vector<std::unique_ptr<Bone>> bones,
glm::vec3 offset
const glm::vec3& offset
);
void setModel(const std::string& name);
@@ -89,6 +90,9 @@ namespace rigging {
util::VecInterpolation<3, float> interpolation {false};
Skeleton(const SkeletonConfig* config);
dv::value serialize(bool saveTextures, bool savePose) const;
void deserialize(const dv::value& root);
};
class SkeletonConfig {
+1 -2
View File
@@ -6,6 +6,5 @@ Hitbox::Hitbox(BodyType type, glm::vec3 position, glm::vec3 halfsize)
: type(type),
position(position),
halfsize(halfsize),
velocity(0.0f,0.0f,0.0f),
linearDamping(0.1f)
velocity(0.0f,0.0f,0.0f)
{}
+3 -2
View File
@@ -52,8 +52,9 @@ struct Hitbox {
glm::vec3 position;
glm::vec3 halfsize;
glm::vec3 velocity;
float linearDamping;
bool verticalDamping = false;
float linearDamping = 0.5;
float friction = 1.0f;
float verticalDamping = 1.0f;
bool grounded = false;
float gravityScale = 1.0f;
bool crouching = false;
+7 -6
View File
@@ -25,7 +25,7 @@ void PhysicsSolver::step(
entityid_t entity
) {
float dt = delta / static_cast<float>(substeps);
float linearDamping = hitbox.linearDamping;
float linearDamping = hitbox.linearDamping * hitbox.friction;
float s = 2.0f/BLOCK_AABB_GRID;
const glm::vec3& half = hitbox.halfsize;
@@ -45,11 +45,6 @@ void PhysicsSolver::step(
colisionCalc(chunks, hitbox, vel, pos, half,
(prevGrounded && gravityScale > 0.0f) ? 0.5f : 0.0f);
}
vel.x *= glm::max(0.0f, 1.0f - dt * linearDamping);
if (hitbox.verticalDamping) {
vel.y *= glm::max(0.0f, 1.0f - dt * linearDamping);
}
vel.z *= glm::max(0.0f, 1.0f - dt * linearDamping);
pos += vel * dt + gravity * gravityScale * dt * dt * 0.5f;
if (hitbox.grounded && pos.y < py) {
@@ -89,6 +84,12 @@ void PhysicsSolver::step(
hitbox.grounded = true;
}
}
vel.x /= 1.0f + delta * linearDamping;
vel.z /= 1.0f + delta * linearDamping;
if (hitbox.verticalDamping > 0.0f) {
vel.y /= 1.0f + delta * linearDamping * hitbox.verticalDamping;
}
AABB aabb;
aabb.a = hitbox.position - hitbox.halfsize;
aabb.b = hitbox.position + hitbox.halfsize;
+8
View File
@@ -85,11 +85,18 @@ struct GraphicsSettings {
IntegerSetting denseRenderDistance {56, 0, 10'000};
};
struct PathfindingSettings {
/// @brief Max visited blocks by an agent per async tick
IntegerSetting stepsPerAsyncAgent {128, 1, 2048};
};
struct DebugSettings {
/// @brief Turns off chunks saving/loading
FlagSetting generatorTestMode {false};
/// @brief Write lights cache
FlagSetting doWriteLights {true};
/// @brief Write preprocessed shaders code to user:export
FlagSetting doTraceShaders {false};
/// @brief Enable experimental optimizations and features
FlagSetting enableExperimental {false};
};
@@ -111,4 +118,5 @@ struct EngineSettings {
DebugSettings debug;
UiSettings ui;
NetworkSettings network;
PathfindingSettings pathfinding;
};
+1 -1
View File
@@ -10,7 +10,7 @@ Clock::Clock(int tickRate, int tickParts)
bool Clock::update(float delta) {
tickTimer += delta;
float delay = 1.0f / float(tickRate);
float delay = 1.0f / static_cast<float>(tickRate);
if (tickTimer > delay || tickPartsUndone) {
if (tickPartsUndone) {
tickPartsUndone--;
+48 -3
View File
@@ -65,6 +65,22 @@ int platform::get_process_id() {
return GetCurrentProcessId();
}
bool platform::open_url(const std::string& url) {
if (url.empty()) return false;
// UTF-8 → UTF-16
int wlen = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
if (wlen <= 0) return false;
std::wstring wurl(wlen, L'\0');
MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, &wurl[0], wlen);
HINSTANCE result = ShellExecuteW(
nullptr, L"open", wurl.c_str(), nullptr, nullptr, SW_SHOWNORMAL
);
return reinterpret_cast<intptr_t>(result) > 32;
}
#else // _WIN32
#include <unistd.h>
@@ -72,7 +88,7 @@ int platform::get_process_id() {
void platform::configure_encoding() {
}
std::string platform::detect_locale() {
const char* const programLocaleName = setlocale(LC_ALL, nullptr);
const char* const preferredLocaleName =
@@ -92,6 +108,34 @@ void platform::sleep(size_t millis) {
int platform::get_process_id() {
return getpid();
}
bool platform::open_url(const std::string& url) {
if (url.empty()) return false;
#ifdef __APPLE__
auto cmd = "open " + util::quote(url);
if (int res = system(cmd.c_str())) {
logger.warning() << "'" << cmd << "' returned code " << res;
} else {
return false;
}
#elif defined(_WIN32)
auto res = ShellExecuteW(NULL, L"open", util::quote(url).c_str(), NULL, NULL, SW_SHOWDEFAULT);
if (res <= 32) {
logger.warning() << "'open' returned code " << res;
} else {
return false;
}
#else
auto cmd = "xdg-open " + util::quote(url);
if (int res = system(cmd.c_str())) {
logger.warning() << "'" << cmd << "' returned code " << res;
} else {
return false;
}
#endif
return true;
}
#endif // _WIN32
void platform::open_folder(const std::filesystem::path& folder) {
@@ -101,9 +145,10 @@ void platform::open_folder(const std::filesystem::path& folder) {
}
#ifdef __APPLE__
auto cmd = "open " + util::quote(folder.u8string());
system(cmd.c_str());
if (int res = system(cmd.c_str())) {
logger.warning() << "'" << cmd << "' returned code " << res;
}
#elif defined(_WIN32)
auto cmd = "start explorer " + util::quote(folder.u8string());
ShellExecuteW(NULL, L"open", folder.wstring().c_str(), NULL, NULL, SW_SHOWDEFAULT);
#else
auto cmd = "xdg-open " + util::quote(folder.u8string());
+1
View File
@@ -13,4 +13,5 @@ namespace platform {
/// Makes the current thread sleep for the specified amount of milliseconds.
void sleep(size_t millis);
int get_process_id();
bool open_url(const std::string& url);
}
+44
View File
@@ -0,0 +1,44 @@
#include "random.hpp"
#include <random>
static std::random_device random_device;
static const char* uuid_hex_chars = "0123456789abcdef";
static const char* uuid_hex_variant_chars = "89ab";
std::string util::generate_uuid() {
auto randomEngine = seeded_random_engine(random_device);
static std::uniform_int_distribution<> dist(0, 15);
static std::uniform_int_distribution<> dist2(0, 3);
std::string uuid;
uuid.resize(36);
for (int i = 0; i < 8; i++) {
uuid[i] = uuid_hex_chars[dist(randomEngine)];
}
uuid[8] = '-';
for (int i = 9; i < 13; i++) {
uuid[i] = uuid_hex_chars[dist(randomEngine)];
}
uuid[13] = '-';
uuid[14] = '4';
for (int i = 15; i < 18; i++) {
uuid[i] = uuid_hex_chars[dist(randomEngine)];
}
uuid[18] = '-';
uuid[19] = uuid_hex_variant_chars[dist2(randomEngine)];
for (int i = 20; i < 23; i++) {
uuid[i] = uuid_hex_chars[dist(randomEngine)];
}
uuid[23] = '-';
for (int i = 24; i < 36; i++) {
uuid[i] = uuid_hex_chars[dist(randomEngine)];
}
return uuid;
}
+19
View File
@@ -0,0 +1,19 @@
#pragma once
#include <random>
#include <string>
#include <algorithm>
namespace util {
template <
class T = std::mt19937,
std::size_t N = T::state_size * sizeof(typename T::result_type)>
auto seeded_random_engine(std::random_device& source) {
std::random_device::result_type randomData[(N - 1) / sizeof(source()) + 1];
std::generate(std::begin(randomData), std::end(randomData), std::ref(source));
std::seed_seq seeds(std::begin(randomData), std::end(randomData));
return T(seeds);
}
std::string generate_uuid();
}
+1 -1
View File
@@ -11,7 +11,7 @@ namespace util {
stack_vector(const stack_vector<T, capacity>& other)
: size_(other.size_) {
for (int i = 0; i < size_; ++i) {
new (&data_[i]) T(data_[i]);
new (&data_[i]) T(other.data_[i]);
}
}
+1
View File
@@ -155,6 +155,7 @@ void Block::cloneTo(Block& dst) {
dst.tickInterval = tickInterval;
dst.overlayTexture = overlayTexture;
dst.translucent = translucent;
dst.tags = tags;
if (particles) {
dst.particles = std::make_unique<ParticlesPreset>(*particles);
}
+6
View File
@@ -4,6 +4,7 @@
#include <string>
#include <vector>
#include <array>
#include <set>
#include "data/dv.hpp"
#include "maths/UVRegion.hpp"
@@ -47,6 +48,7 @@ struct BlockFuncsSet {
bool onreplaced : 1;
bool oninteract : 1;
bool randupdate : 1;
bool onblocktick : 1;
bool onblockstick : 1;
};
@@ -261,6 +263,8 @@ public:
std::unique_ptr<Variants> variants;
std::vector<std::string> tags;
/// @brief Runtime indices (content indexing results)
struct {
/// @brief block runtime integer id
@@ -285,6 +289,8 @@ public:
itemid_t pickingItem = 0;
blockid_t surfaceReplacement = 0;
std::set<int> tags;
} rt {};
Block(const std::string& name);
+2 -2
View File
@@ -171,10 +171,10 @@ void Chunks::eraseSegments(
blocks_agent::erase_segments(*this, def, state, x, y, z);
}
void Chunks::repairSegments(
void Chunks::restoreSegments(
const Block& def, blockstate state, int x, int y, int z
) {
blocks_agent::repair_segments(*this, def, state, x, y, z);
blocks_agent::restore_segments(*this, def, state, x, y, z);
}
bool Chunks::checkReplaceability(
+1 -1
View File
@@ -28,7 +28,7 @@ class Chunks {
const ContentIndices& indices;
void eraseSegments(const Block& def, blockstate state, int x, int y, int z);
void repairSegments(
void restoreSegments(
const Block& def, blockstate state, int x, int y, int z
);
void setRotationExtended(

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