Merge branch 'MihailRis:main' into main

This commit is contained in:
Astazium 2026-09-09 21:50:10 +03:00 • committed by GitHub
commit 469edd1e9d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 1717 additions and 538 deletions

View file

@ -13,7 +13,7 @@ using namespace rigging;
void ModelReference::refresh(const Assets& assets) {
if (updateFlag) {
model = assets.get<model::Model>(name);
model = assets.getShared<model::Model>(name);
updateFlag = false;
}
}
@ -29,24 +29,27 @@ Bone::Bone(
name(std::move(name)),
bones(std::move(bones)),
offset(offset),
model({model, nullptr, true}) {
model({model, {}, true}) {
}
void Bone::setModel(const std::string& name) {
if (model.name == name) {
return;
}
model = {name, nullptr, true};
model = {name, {}, true};
}
Skeleton::Skeleton(std::shared_ptr<const SkeletonConfig> config)
: config(config),
pose(config->getBones().size()),
calculated(config->getBones().size()),
flags(config->getBones().size()),
pose(config ? config->getBones().size() : 0),
calculated(config ? config->getBones().size() : 0),
flags(config ? config->getBones().size() : 0),
textures(),
modelOverrides(config->getBones().size()),
modelOverrides(config ? config->getBones().size() : 0),
visible(true) {
if (config == nullptr) {
return;
}
const auto& bones = config->getBones();
for (size_t i = 0; i < bones.size(); i++) {
flags[i].visible = true;
@ -89,19 +92,15 @@ void Skeleton::deserialize(const dv::value& root) {
void Skeleton::setConfig(std::shared_ptr<const SkeletonConfig> rigConfig) {
config = std::move(rigConfig);
const auto& bones = config->getBones();
int bonesCount = config ? config->getBones().size() : 0;
pose.matrices.resize(bonesCount, glm::mat4(1.0f));
calculated.matrices.resize(bonesCount, glm::mat4(1.0f));
pose.matrices.resize(
bones.size(), glm::mat4(1.0f)
);
calculated.matrices.resize(
bones.size(), glm::mat4(1.0f)
);
modelOverrides.resize(bonesCount);
flags.resize(bonesCount);
modelOverrides.resize(bones.size());
flags.resize(bones.size());
for (size_t i = 0; i < bones.size(); i++) {
for (size_t i = 0; i < bonesCount; i++) {
flags[i].visible = true;
}
}
@ -194,12 +193,14 @@ void SkeletonConfig::render(
continue;
}
node->model.refresh(assets);
auto model = node->model.model;
auto model = node->model.model.lock().get(); // TODO: cache model pointer during frame
auto& modelOverride = skeleton.modelOverrides.at(i);
if (modelOverride.updateFlag) {
modelOverride.refresh(assets);
}
model = modelOverride.model ? modelOverride.model : model;
if (auto foundOverride = modelOverride.model.lock()) {
model = foundOverride.get();
}
if (model) {
batch.draw(
skeleton.calculated.matrices[i],
@ -207,7 +208,9 @@ void SkeletonConfig::render(
model,
&skeleton.textures
);
}
} else if (!node->model.name.empty()) {
node->model.updateFlag = true;
}
}
}

View file

@ -33,8 +33,8 @@ namespace rigging {
struct ModelReference {
std::string name;
model::Model* model;
bool updateFlag;
std::weak_ptr<model::Model> model;
bool updateFlag = false;
void refresh(const Assets& assets);
};

View file

@ -1,6 +1,8 @@
#include "Assets.hpp"
#include "AssetsLoader.hpp"
#include "io/path.hpp"
Assets::Assets(util::ObjectsKeeper* vault) : vault(vault) {
Assets::Assets(util::ObjectsKeeper* vault) : vault(vault), assetsLoadInfo(std::make_unique<AssetsLoadInfo>()) {
}
Assets::~Assets() {

View file

@ -1,5 +1,10 @@
#pragma once
#include "util/stringutil.hpp"
#include "util/ObjectsKeeper.hpp"
#include "graphics/core/TextureAnimation.hpp"
#include "io/fwd.hpp"
#include <functional>
#include <memory>
#include <optional>
@ -11,13 +16,12 @@
#include <unordered_map>
#include <vector>
#include "util/stringutil.hpp"
#include "util/ObjectsKeeper.hpp"
#include "graphics/core/TextureAnimation.hpp"
class Assets;
struct AssetsLoadInfo;
class AssetsLoader;
enum class AssetType {
ANIMATION,
TEXTURE,
SHADER,
FONT,
@ -31,12 +35,10 @@ enum class AssetType {
namespace assetload {
/// @brief final work to do in the main thread
using postfunc = std::function<void(Assets*)>;
using setupfunc = std::function<void(const Assets*)>;
using postfunc = std::function<void(Assets&)>;
template <class T>
void assets_setup(const Assets*);
void assets_setup(const Assets&);
class error : public std::runtime_error {
AssetType type;
@ -62,16 +64,13 @@ namespace assetload {
return reason;
}
};
}
class Assets {
util::ObjectsKeeper* vault;
std::vector<TextureAnimation> animations;
using assets_map = std::unordered_map<std::string, std::shared_ptr<void>>;
std::unordered_map<std::type_index, assets_map> assets;
std::vector<assetload::setupfunc> setupFuncs;
public:
using assets_map = std::unordered_map<std::string, std::shared_ptr<void>>;
Assets(util::ObjectsKeeper* vault);
Assets(const Assets&) = delete;
~Assets();
@ -143,20 +142,20 @@ public:
return &mapIter->second;
}
void setup() {
for (auto& setupFunc : setupFuncs) {
setupFunc(this);
}
AssetsLoadInfo& getLoadInfo() const {
return *assetsLoadInfo;
}
private:
util::ObjectsKeeper* vault;
std::vector<TextureAnimation> animations;
void addSetupFunc(assetload::setupfunc setupfunc) {
setupFuncs.push_back(setupfunc);
}
std::unordered_map<std::type_index, assets_map> assets;
std::unique_ptr<AssetsLoadInfo> assetsLoadInfo;
};
template <class T>
void assetload::assets_setup(const Assets* assets) {
if (auto mapPtr = assets->getMap<T>()) {
void assetload::assets_setup(const Assets& assets) {
if (auto mapPtr = assets.getMap<T>()) {
for (const auto& entry : **mapPtr) {
static_cast<T*>(entry.second.get())->setup();
}

View file

@ -1,5 +1,6 @@
#include "AssetsLoader.hpp"
#include "animation/rigging.hpp"
#include "assetload_funcs.hpp"
#include "Assets.hpp"
#include "coders/commons.hpp"
@ -15,7 +16,6 @@
#include "items/ItemDef.hpp"
#include "logic/scripting/scripting.hpp"
#include "objects/EntityDef.hpp"
#include "objects/rigging.hpp"
#include "util/ThreadPool.hpp"
#include "voxels/Block.hpp"
@ -26,17 +26,23 @@ namespace fs = std::filesystem;
static debug::Logger logger("assets-loader");
AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths)
: engine(engine), assets(assets), paths(paths) {
addLoader(AssetType::SHADER, assetload::shader);
addLoader(AssetType::TEXTURE, assetload::texture);
addLoader(AssetType::FONT, assetload::font);
AssetsLoader::AssetsLoader(
Engine& engine, Assets& assets, const ResPaths& paths
)
: engine(engine),
assets(assets),
paths(paths),
assetsLoadInfo(assets.getLoadInfo()) {
addLoader(AssetType::ANIMATION, assetload::animation);
addLoader(AssetType::ATLAS, assetload::atlas);
addLoader(AssetType::FONT, assetload::font);
addLoader(AssetType::LAYOUT, assetload::layout);
addLoader(AssetType::SOUND, assetload::sound);
addLoader(AssetType::MODEL, assetload::model);
addLoader(AssetType::POST_EFFECT, assetload::posteffect);
addLoader(AssetType::SHADER, assetload::shader);
addLoader(AssetType::SKELETON, assetload::skeleton);
addLoader(AssetType::SOUND, assetload::sound);
addLoader(AssetType::TEXTURE, assetload::texture);
}
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
@ -50,11 +56,11 @@ void AssetsLoader::add(
std::shared_ptr<AssetCfg> settings,
bool overwrite
) {
if (!overwrite && enqueued.find({tag, alias}) != enqueued.end()){
if (!overwrite && enqueued.find({alias, tag}) != enqueued.end()){
return;
}
entries.push(aloader_entry {tag, filename, alias, std::move(settings)});
enqueued.insert({tag, alias});
enqueued.insert({alias, tag});
}
bool AssetsLoader::hasNext() const {
@ -72,15 +78,15 @@ aloader_func AssetsLoader::getLoader(AssetType tag) {
}
void AssetsLoader::loadNext() {
const aloader_entry& entry = entries.front();
aloader_entry& entry = entries.front();
logger.info() << "loading " << entry.filename << " as " << entry.alias;
std::string error {};
try {
aloader_func loader = getLoader(entry.tag);
auto postfunc =
loader(this, paths, entry.filename, entry.alias, entry.config);
postfunc(&assets);
loader(*this, paths, entry.filename, entry.alias, entry.config);
postfunc(assets);
} catch (const parsing_error& err) {
error = err.errorLog();
} catch (const std::runtime_error& err) {
@ -93,6 +99,8 @@ void AssetsLoader::loadNext() {
entries.pop();
throw assetload::error(tag, std::move(filename), std::move(error));
}
assetsLoadInfo.processedEntries[{entry.alias, entry.tag}] =
std::move(entry);
entries.pop();
}
@ -117,34 +125,28 @@ static void add_layouts(
}
}
void AssetsLoader::tryAddSound(const std::string& name) {
if (name.empty()) {
return;
}
std::string file = SOUNDS_FOLDER + "/" + name;
add(AssetType::SOUND, file, name);
}
static std::string assets_def_folder(AssetType tag) {
switch (tag) {
case AssetType::FONT:
return FONTS_FOLDER;
case AssetType::SHADER:
return SHADERS_FOLDER;
case AssetType::TEXTURE:
return TEXTURES_FOLDER;
case AssetType::ANIMATION:
return ANIMATION_FOLDER;
case AssetType::ATLAS:
return TEXTURES_FOLDER;
case AssetType::FONT:
return FONTS_FOLDER;
case AssetType::LAYOUT:
return LAYOUTS_FOLDER;
case AssetType::SOUND:
return SOUNDS_FOLDER;
case AssetType::MODEL:
return MODELS_FOLDER;
case AssetType::POST_EFFECT:
return POST_EFFECTS_FOLDER;
case AssetType::SHADER:
return SHADERS_FOLDER;
case AssetType::SKELETON:
return SKELETONS_FOLDER;
case AssetType::SOUND:
return SOUNDS_FOLDER;
case AssetType::TEXTURE:
return TEXTURES_FOLDER;
}
return "<error>";
}
@ -160,7 +162,8 @@ void AssetsLoader::processPreload(
}
std::shared_ptr<AssetCfg> config = nullptr;
map.at("path").get(path);
logger.debug() << "processing preload " << util::quote(name) << " path: " << util::quote(path);
logger.debug() << "processing preload " << util::quote(name)
<< " path: " << util::quote(path);
switch (tag) {
case AssetType::SOUND: {
bool keepPCM = false;
@ -221,14 +224,15 @@ void AssetsLoader::processPreloadList(AssetType tag, const dv::value& list) {
void AssetsLoader::processPreloadConfig(const io::path& file) {
auto root = io::read_json(file);
processPreloadList(AssetType::ANIMATION, root["animation"]);
processPreloadList(AssetType::ATLAS, root["atlases"]);
processPreloadList(AssetType::FONT, root["fonts"]);
processPreloadList(AssetType::SHADER, root["shaders"]);
processPreloadList(AssetType::TEXTURE, root["textures"]);
processPreloadList(AssetType::SOUND, root["sounds"]);
processPreloadList(AssetType::MODEL, root["models"]);
processPreloadList(AssetType::POST_EFFECT, root["post-effects"]);
processPreloadList(AssetType::SHADER, root["shaders"]);
processPreloadList(AssetType::SKELETON, root["skeletons"]);
processPreloadList(AssetType::SOUND, root["sounds"]);
processPreloadList(AssetType::TEXTURE, root["textures"]);
// layouts are loaded automatically
}
@ -263,65 +267,73 @@ static void add_variant(AssetsLoader& loader, const Variant& variant) {
}
}
void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
loader.processPreloadConfigs(content);
if (content) {
for (auto& entry : content->getBlockMaterials()) {
auto& material = *entry.second;
loader.tryAddSound(material.stepsSound);
loader.tryAddSound(material.placeSound);
loader.tryAddSound(material.breakSound);
loader.tryAddSound(material.hitSound);
void AssetsLoader::addDefaults(const Content* content) {
processPreloadConfigs(content);
if (content == nullptr) {
return;
}
auto tryAddSound = [this](const std::string& name){
if (name.empty()) {
return;
}
std::string file = SOUNDS_FOLDER + "/" + name;
add(AssetType::SOUND, file, name);
};
for (auto& entry : content->getBlockMaterials()) {
auto& material = *entry.second;
tryAddSound(material.stepsSound);
tryAddSound(material.placeSound);
tryAddSound(material.breakSound);
tryAddSound(material.hitSound);
}
for (auto& entry : content->getPacks()) {
auto pack = entry.second.get();
auto& info = pack->getInfo();
io::path folder = info.folder / "layouts";
add_layouts(pack->getEnvironment(), info.id, folder, loader);
}
for (auto& entry : content->getPacks()) {
auto pack = entry.second.get();
auto& info = pack->getInfo();
io::path folder = info.folder / "layouts";
add_layouts(pack->getEnvironment(), info.id, folder, *this);
}
for (const auto& entry : content->getPacks()) {
io::path skeletonsDir = entry.first + ":skeletons";
if (!io::is_directory(skeletonsDir)) {
continue;
}
for (const auto& file : io::directory_iterator(skeletonsDir)) {
loader.add(
AssetType::SKELETON,
(file.parent() / file.stem()).string(),
entry.first + ":" + file.stem()
);
}
for (const auto& entry : content->getPacks()) {
io::path skeletonsDir = entry.first + ":skeletons";
if (!io::is_directory(skeletonsDir)) {
continue;
}
for (const auto& file : io::directory_iterator(skeletonsDir)) {
add(
AssetType::SKELETON,
(file.parent() / file.stem()).string(),
entry.first + ":" + file.stem()
);
}
}
for (const auto& [_, def] : content->blocks.getDefs()) {
if (def->variants) {
for (const auto& variant : def->variants->variants) {
add_variant(loader, variant);
}
} else {
add_variant(loader, def->defaults);
for (const auto& [_, def] : content->blocks.getDefs()) {
if (def->variants) {
for (const auto& variant : def->variants->variants) {
add_variant(*this, variant);
}
} else {
add_variant(*this, def->defaults);
}
for (const auto& [_, def] : content->items.getDefs()) {
if (def->modelName.find(':') == std::string::npos) {
loader.add(
AssetType::MODEL,
MODELS_FOLDER + "/" + def->modelName,
def->modelName
);
}
}
for (const auto& [_, def] : content->items.getDefs()) {
if (def->modelName.find(':') == std::string::npos) {
add(
AssetType::MODEL,
MODELS_FOLDER + "/" + def->modelName,
def->modelName
);
}
for (const auto& [_, def] : content->entities.getDefs()) {
if (def->skeletonName.find(':') == std::string::npos) {
// expecting a VCM with skeleton
loader.add(
AssetType::MODEL,
MODELS_FOLDER + "/" + def->skeletonName,
def->skeletonName
);
}
}
for (const auto& [_, def] : content->entities.getDefs()) {
if (def->skeletonName.find(':') == std::string::npos) {
// expecting a VCM with skeleton
add(
AssetType::MODEL,
MODELS_FOLDER + "/" + def->skeletonName,
def->skeletonName
);
}
}
}
@ -361,17 +373,17 @@ const ResPaths& AssetsLoader::getPaths() const {
}
class LoaderWorker : public util::Worker<aloader_entry, assetload::postfunc> {
AssetsLoader* loader;
AssetsLoader& loader;
public:
LoaderWorker(AssetsLoader* loader) : loader(loader) {
LoaderWorker(AssetsLoader& loader) : loader(loader) {
}
assetload::postfunc operator()(const aloader_entry& entry
) override {
aloader_func loadfunc = loader->getLoader(entry.tag);
aloader_func loadfunc = loader.getLoader(entry.tag);
return loadfunc(
loader,
loader->getPaths(),
loader.getPaths(),
entry.filename,
entry.alias,
entry.config
@ -383,8 +395,8 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
auto pool =
std::make_shared<util::ThreadPool<aloader_entry, assetload::postfunc>>(
"assets-loader-pool",
[=]() { return std::make_unique<LoaderWorker>(this); },
[this](const assetload::postfunc& func) { func(&assets); },
[=]() { return std::make_unique<LoaderWorker>(*this); },
[this](const assetload::postfunc& func) { func(assets); },
maxWorkers
);
pool->setOnComplete(std::move(onDone));
@ -404,3 +416,22 @@ std::shared_ptr<Task> AssetsLoader::startTask(runnable onDone, int maxWorkers) {
}
return pool;
}
void AssetsLoader::attachToFile(const io::path& file, AssetFullId assetId) {
assetsLoadInfo.referencedAssets.insert({file, std::move(assetId)});
}
int AssetsLoader::addReload(const io::path& path) {
int added = 0;
auto range = assetsLoadInfo.referencedAssets.equal_range(path);
for (auto it = range.first; it != range.second; ++it) {
const auto& recipe = assetsLoadInfo.processedEntries.find(it->second);
if (recipe != assetsLoadInfo.processedEntries.end()) {
entries.push(recipe->second);
added++;
}
}
return added;
}

View file

@ -1,5 +1,12 @@
#pragma once
#include "delegates.hpp"
#include "interfaces/Task.hpp"
#include "typedefs.hpp"
#include "Assets.hpp"
#include "data/dv.hpp"
#include "io/fwd.hpp"
#include <functional>
#include <map>
#include <set>
@ -8,13 +15,6 @@
#include <string>
#include <utility>
#include "delegates.hpp"
#include "interfaces/Task.hpp"
#include "typedefs.hpp"
#include "Assets.hpp"
#include "data/dv.hpp"
#include "io/fwd.hpp"
class ResPaths;
class AssetsLoader;
class Content;
@ -72,7 +72,12 @@ struct ModelCfg : AssetCfg {
using aloader_func = std::function<
assetload::
postfunc(AssetsLoader*, const ResPaths&, const std::string&, const std::string&, std::shared_ptr<AssetCfg>)>;
postfunc(
AssetsLoader&,
const ResPaths&,
const std::string&,
const std::string&,
std::shared_ptr<AssetCfg>)>;
struct aloader_entry {
AssetType tag;
@ -81,24 +86,18 @@ struct aloader_entry {
std::shared_ptr<AssetCfg> config;
};
using AssetFullId = std::pair<std::string, AssetType>;
struct AssetsLoadInfo {
std::map<AssetFullId, aloader_entry> processedEntries;
std::multimap<io::path, AssetFullId> referencedAssets;
};
class AssetsLoader {
Engine& engine;
Assets& assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
std::set<std::pair<AssetType, std::string>> enqueued;
const ResPaths& paths;
void tryAddSound(const std::string& name);
void processPreload(
AssetType tag, const std::string& name, const dv::value& map
);
void processPreloadList(AssetType tag, const dv::value& list);
void processPreloadConfig(const io::path& file);
void processPreloadConfigs(const Content* content);
public:
AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths);
AssetsLoader(const AssetsLoader&) = delete;
void addLoader(AssetType tag, aloader_func func);
/// @brief Enqueue asset load
@ -125,9 +124,8 @@ public:
aloader_func getLoader(AssetType tag);
/// @brief Enqueue core and content assets
/// @param loader target loader
/// @param content engine content
static void addDefaults(AssetsLoader& loader, const Content* content);
void addDefaults(const Content* content);
static bool loadExternalTexture(
AssetsLoader& loader,
@ -135,6 +133,25 @@ public:
const std::vector<io::path>& alternatives
);
int addReload(const io::path& path);
void attachToFile(const io::path& file, AssetFullId assetId);
Assets& getAssets();
Engine& getEngine();
private:
Engine& engine;
Assets& assets;
std::map<AssetType, aloader_func> loaders;
std::queue<aloader_entry> entries;
std::set<AssetFullId> enqueued;
const ResPaths& paths;
AssetsLoadInfo& assetsLoadInfo;
void processPreload(
AssetType tag, const std::string& name, const dv::value& map
);
void processPreloadList(AssetType tag, const dv::value& list);
void processPreloadConfig(const io::path& file);
void processPreloadConfigs(const Content* content);
};

View file

@ -1,13 +1,11 @@
#include "assetload_funcs.hpp"
#include <array>
#include <filesystem>
#include <iostream>
#include <stdexcept>
#include "animation/rigging.hpp"
#include "Assets.hpp"
#include "AssetsLoader.hpp"
#include "audio/audio.hpp"
#include "coders/GLSLExtension.hpp"
#include "coders/commons.hpp"
#include "coders/GLSLExtension.hpp"
#include "coders/imageio.hpp"
#include "coders/json.hpp"
#include "coders/obj.hpp"
@ -17,26 +15,28 @@
#include "constants.hpp"
#include "debug/Logger.hpp"
#include "engine/EnginePaths.hpp"
#include "io/io.hpp"
#include "frontend/UiDocument.hpp"
#include "graphics/commons/Model.hpp"
#include "graphics/core/Atlas.hpp"
#include "graphics/core/Font.hpp"
#include "graphics/core/ImageData.hpp"
#include "graphics/core/Shader.hpp"
#include "graphics/core/Texture.hpp"
#include "graphics/core/TextureAnimation.hpp"
#include "graphics/commons/Model.hpp"
#include "objects/rigging.hpp"
#include "logic/scripting/scripting.hpp"
#include "io/io.hpp"
#include "util/stringutil.hpp"
#include "Assets.hpp"
#include "AssetsLoader.hpp"
#include <array>
#include <filesystem>
#include <stdexcept>
static debug::Logger logger("assetload-funcs");
namespace fs = std::filesystem;
static bool load_animation(
Assets* assets,
static bool load_animated_texture(
Assets& assets,
const ResPaths& paths,
const std::string& atlasName,
const std::string& directory,
@ -44,22 +44,39 @@ static bool load_animation(
Atlas* dstAtlas
);
assetload::postfunc assetload::texture(
AssetsLoader*,
assetload::postfunc assetload::animation(
AssetsLoader& loader,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
auto actualFile = paths.find(filename + ".png");
auto path = paths.find(filename + ".vca");
loader.attachToFile(path, {name, AssetType::ANIMATION});
if (io::is_regular_file(path)) {
scripting::load_vca_animation(path, nullptr, name);
}
return [](auto&) {};
}
assetload::postfunc assetload::texture(
AssetsLoader& loader,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>&
) {
auto file = paths.find(filename + ".png");
loader.attachToFile(file, {name, AssetType::TEXTURE});
try {
std::shared_ptr<ImageData> image(imageio::read(actualFile));
return [name, image, actualFile](auto assets) {
assets->store(Texture::from(image.get()), name);
std::shared_ptr<ImageData> image(imageio::read(file));
return [name, image, file](auto& assets) {
assets.store(Texture::from(image.get()), name);
};
} catch (const std::runtime_error& err) {
logger.error() << actualFile.string() << ": " << err.what();
return [](auto) {};
logger.error() << file.string() << ": " << err.what();
return [](auto&) {};
}
}
@ -86,7 +103,7 @@ static auto read_program(const ResPaths& paths, const std::string& filename) {
}
assetload::postfunc assetload::shader(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
@ -99,8 +116,8 @@ assetload::postfunc assetload::shader(
io::path vertexFile = paths.find(filename + ".glslv");
io::path fragmentFile = paths.find(filename + ".glslf");
return [=](auto assets) {
assets->store(
return [=](auto& assets) {
assets.store(
Shader::create(
{vertexFile.string(), vertex},
{fragmentFile.string(), fragment}
@ -111,13 +128,15 @@ assetload::postfunc assetload::shader(
}
assetload::postfunc assetload::posteffect(
AssetsLoader*,
AssetsLoader& loader,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
) {
io::path effectFile = paths.find(file + ".glsl");
loader.attachToFile(effectFile, {name, AssetType::POST_EFFECT});
std::string effectSource = io::read_string(effectFile);
auto& preprocessor = *Shader::preprocessor;
@ -131,7 +150,7 @@ assetload::postfunc assetload::posteffect(
std::string vertexSource = std::move(vertex.code);
std::string fragmentSource = std::move(fragment.code);
return [=](auto assets) {
return [=](auto& assets) {
auto program = Shader::create(
{effectFile.string(), vertexSource},
{effectFile.string(), fragmentSource}
@ -140,7 +159,7 @@ assetload::postfunc assetload::posteffect(
if (settings) {
advanced = dynamic_cast<const PostEffectCfg*>(settings.get())->advanced;
}
assets->store(
assets.store(
std::make_shared<PostEffect>(advanced, std::move(program), params),
name
);
@ -160,7 +179,7 @@ static bool append_atlas(AtlasBuilder& atlas, const io::path& file) {
}
assetload::postfunc assetload::atlas(
AssetsLoader* loader,
AssetsLoader& loader,
const ResPaths& paths,
const std::string& directory,
const std::string& name,
@ -171,13 +190,13 @@ assetload::postfunc assetload::atlas(
for (const auto& file : paths.listdir(directory)) {
if (!imageio::is_read_supported(file.extension()))
continue;
loader->add(
loader.add(
AssetType::TEXTURE,
directory + "/" + file.stem(),
name + "/" + file.stem()
);
}
return [](auto){};
return [](auto&){};
}
AtlasBuilder builder;
for (const auto& file : paths.listdir(directory)) {
@ -186,17 +205,17 @@ assetload::postfunc assetload::atlas(
}
std::set<std::string> names = builder.getNames();
Atlas* atlas = builder.build(ATLAS_EXTRUSION, false).release();
return [=](auto assets) {
return [=](Assets& assets) {
atlas->prepare();
assets->store(std::unique_ptr<Atlas>(atlas), name);
assets.store(std::unique_ptr<Atlas>(atlas), name);
for (const auto& file : names) {
load_animation(assets, paths, name, directory, file, atlas);
load_animated_texture(assets, paths, name, directory, file, atlas);
}
};
}
assetload::postfunc assetload::font(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
@ -204,17 +223,17 @@ assetload::postfunc assetload::font(
) {
auto cfg = std::dynamic_pointer_cast<FontCfg>(config);
auto ext = fs::path(filename).extension().string();
if (ext == ".ttf" || ext == ".otf") {
if (!ext.empty()) {
logger.info() << "loading vector font " << util::quote(filename);
return [=](Assets* assets) {
return [=](Assets& assets) {
using FontFile = vector_fonts::FontFile;
FontFile* fontFile;
if ((fontFile = assets->get<FontFile>(filename)) == nullptr) {
if ((fontFile = assets.get<FontFile>(filename)) == nullptr) {
auto fontFilePtr = vector_fonts::load_font(paths.find(filename).string());
fontFile = fontFilePtr.get();
assets->store(fontFilePtr, filename);
assets.store(fontFilePtr, filename);
}
assets->store(fontFile->createInstance(cfg ? cfg->size : 16), name);
assets.store(fontFile->createInstance(cfg ? cfg->size : 16), name);
};
}
@ -230,24 +249,24 @@ assetload::postfunc assetload::font(
pages->push_back(nullptr);
}
}
return [=](auto assets) {
assets->store(Font::createBitmapFont(std::move(*pages)), name);
return [=](auto& assets) {
assets.store(Font::createBitmapFont(std::move(*pages)), name);
};
}
assetload::postfunc assetload::layout(
AssetsLoader*,
AssetsLoader&,
const ResPaths&,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& config
) {
return [=](auto assets) {
return [=](auto& assets) {
try {
auto cfg = std::dynamic_pointer_cast<LayoutCfg>(config);
size_t pos = name.find(':');
auto prefix = name.substr(0, pos);
assets->store(
assets.store(
UiDocument::read(
*cfg->gui,
cfg->env,
@ -265,7 +284,7 @@ assetload::postfunc assetload::layout(
};
}
assetload::postfunc assetload::sound(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& file,
const std::string& name,
@ -307,81 +326,162 @@ assetload::postfunc assetload::sound(
}
auto sound = baseSound.release();
return [=](auto assets) {
assets->store(std::unique_ptr<audio::Sound>(sound), name);
return [=](auto& assets) {
assets.store(std::unique_ptr<audio::Sound>(sound), name);
};
}
static void request_textures(AssetsLoader* loader, const model::Model& model) {
static void request_textures(AssetsLoader& loader, const model::Model& model) {
for (auto& mesh : model.meshes) {
if (mesh.texture.find('$') == std::string::npos &&
mesh.texture.find(':') == std::string::npos) {
auto filename = TEXTURES_FOLDER + "/" + mesh.texture;
loader->add(
loader.add(
AssetType::TEXTURE, filename, mesh.texture, nullptr
);
}
}
}
static assetload::postfunc load_vec3_model(
const io::path& file,
AssetsLoader& loader,
const std::string& name,
const std::shared_ptr<ModelCfg>& config
) {
loader.attachToFile(file, {name, AssetType::MODEL});
auto bytes = io::read_bytes_buffer(file);
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(file.string(), bytes));
return [&loader, name, file, config, modelVEC3 = std::move(modelVEC3)](
Assets& assets
) {
loader.attachToFile(file, {name, AssetType::MODEL});
if (config && config->squashed) {
model::Model fullModel;
for (auto& entry : modelVEC3->models) {
auto& vec3model = entry.second;
auto& model = vec3model.model;
model.translate(vec3model.origin);
fullModel.merge(std::move(model));
}
request_textures(loader, fullModel);
assets.store(
std::make_unique<model::Model>(fullModel),
name
);
logger.info() << "store model " << util::quote(name);
return;
}
for (auto& [modelName, model] : modelVEC3->models) {
request_textures(loader, model.model);
std::string fullName = name;
if (name != modelName) {
fullName += "." + modelName;
}
assets.store(
std::make_unique<model::Model>(model.model),
fullName
);
logger.info() << "store model " << util::quote(modelName)
<< " as " << util::quote(fullName);
}
};
}
static assetload::postfunc load_obj_model(
const io::path& file,
AssetsLoader& loader,
const std::string& name,
const std::shared_ptr<ModelCfg>&
) {
loader.attachToFile(file, {name, AssetType::MODEL});
auto text = io::read_string(file);
try {
auto model = obj::parse(file.string(), text).release();
return [=, &loader](Assets& assets) {
request_textures(loader, *model);
assets.store(std::unique_ptr<model::Model>(model), name);
};
} catch (const parsing_error& err) {
logger.error() << "error on loading model " << name << ": "
<< err.errorLog();
throw;
}
}
static assetload::postfunc load_vcm_model(
const io::path& file,
AssetsLoader& loader,
const std::string& name,
const std::shared_ptr<ModelCfg>& cfg
) {
loader.attachToFile(file, {name, AssetType::MODEL});
auto text = io::read_string(file);
try {
auto vcmModel = vcm::parse(file.string(), text, file.extension() == ".xml");
assert(vcmModel.parts.size() > 0);
if (vcmModel.parts.size() == 1 || (cfg && cfg->squashed)) {
auto modelPtr =
std::make_unique<model::Model>(std::move(vcmModel.squash()))
.release();
return [=, &loader](Assets& assets) {
auto model = std::unique_ptr<model::Model>(modelPtr);
request_textures(loader, *model);
assets.store(std::move(model), name);
logger.info() << "store model " << util::quote(name);
};
}
auto vcmModelPtr =
std::make_unique<vcm::VcmModel>(std::move(vcmModel)).release();
return [=, &loader](Assets& assets) {
auto vcmModel = std::unique_ptr<vcm::VcmModel>(vcmModelPtr);
for (auto& [partName, model] : vcmModel->parts) {
auto fullName = name + "." + partName;
logger.info()
<< "store model part " << util::quote(partName)
<< " as " << util::quote(fullName);
request_textures(loader, model);
assets.store(
std::make_unique<model::Model>(std::move(model)),
fullName
);
}
for (auto& bone : vcmModel->skeleton->getBones()) {
bone->setModel(name + "." + bone->model.name);
}
logger.info() << "store skeleton " << util::quote(name);
assets.store<rigging::SkeletonConfig>(
std::make_unique<rigging::SkeletonConfig>(
std::move(*vcmModel->skeleton)
),
name
);
};
} catch (const parsing_error& err) {
logger.error() << "error on loading model " << name << ": "
<< err.errorLog();
throw;
}
}
assetload::postfunc assetload::model(
AssetsLoader* loader,
AssetsLoader& loader,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& config
) {
auto cfg = std::dynamic_pointer_cast<ModelCfg>(config);
auto modelConfig = std::dynamic_pointer_cast<ModelCfg>(config);
auto path = paths.find(file + ".vec3");
if (io::exists(path)) {
auto bytes = io::read_bytes_buffer(path);
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
return [loader, name, cfg, modelVEC3=std::move(modelVEC3)](Assets* assets) {
if (cfg && cfg->squashed) {
model::Model fullModel;
for (auto& entry : modelVEC3->models) {
auto& vec3model = entry.second;
auto& model = vec3model.model;
model.translate(vec3model.origin);
fullModel.merge(std::move(model));
}
request_textures(loader, fullModel);
assets->store(
std::make_unique<model::Model>(fullModel),
name
);
logger.info() << "store model " << util::quote(name);
return;
}
for (auto& [modelName, model] : modelVEC3->models) {
request_textures(loader, model.model);
std::string fullName = name;
if (name != modelName) {
fullName += "." + modelName;
}
assets->store(
std::make_unique<model::Model>(model.model),
fullName
);
logger.info() << "store model " << util::quote(modelName)
<< " as " << util::quote(fullName);
}
};
return load_vec3_model(path, loader, name, modelConfig);
}
path = paths.find(file + ".obj");
if (io::exists(path)) {
auto text = io::read_string(path);
try {
auto model = obj::parse(path.string(), text).release();
return [=](Assets* assets) {
request_textures(loader, *model);
assets->store(std::unique_ptr<model::Model>(model), name);
};
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw;
}
return load_obj_model(path, loader, name, modelConfig);
}
std::array<std::string, 2> extensions {
@ -401,60 +501,17 @@ assetload::postfunc assetload::model(
throw std::runtime_error("could not to find model " + util::quote(file));
}
auto text = io::read_string(path);
try {
auto vcmModel = vcm::parse(path.string(), text, path.extension() == ".xml");
assert(vcmModel.parts.size() > 0);
if (vcmModel.parts.size() == 1 || (cfg && cfg->squashed)) {
auto modelPtr = std::make_unique<model::Model>(std::move(vcmModel.squash())).release();
return [=](Assets* assets) {
auto model = std::unique_ptr<model::Model>(modelPtr);
request_textures(loader, *model);
assets->store(std::move(model), name);
logger.info() << "store model " << util::quote(name);
};
} else {
auto vcmModelPtr = std::make_unique<vcm::VcmModel>(std::move(vcmModel)).release();
return [=](Assets* assets) {
auto vcmModel = std::unique_ptr<vcm::VcmModel>(vcmModelPtr);
for (auto& [partName, model] : vcmModel->parts) {
auto fullName = name + "." + partName;
logger.info()
<< "store model part " << util::quote(partName)
<< " as " << util::quote(fullName);
assets->store(
std::make_unique<model::Model>(std::move(model)),
fullName
);
}
for (auto& bone : vcmModel->skeleton->getBones()) {
bone->setModel(name + "." + bone->model.name);
}
logger.info() << "store skeleton " << util::quote(name);
assets->store<rigging::SkeletonConfig>(
std::make_unique<rigging::SkeletonConfig>(
std::move(*vcmModel->skeleton)
),
name
);
};
}
} catch (const parsing_error& err) {
std::cerr << err.errorLog() << std::endl;
throw;
}
return load_vcm_model(path, loader, name, modelConfig);
}
assetload::postfunc assetload::skeleton(
AssetsLoader* loader,
AssetsLoader& loader,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
) {
return [=](auto assets) {
return [&loader, &paths, file, name](auto& assets) {
auto path = paths.find(file + ".json");
std::string text = io::read_string(path);
auto skeleton = rigging::SkeletonConfig::parse(text, file, name);
@ -465,12 +522,12 @@ assetload::postfunc assetload::skeleton(
model = model.substr(0, pos);
}
if (!model.empty()) {
loader->add(
loader.add(
AssetType::MODEL, MODELS_FOLDER + "/" + model, model
);
}
}
assets->store(std::move(skeleton), name);
assets.store(std::move(skeleton), name);
};
}
@ -552,8 +609,8 @@ inline bool contains(
return false;
}
static bool load_animation(
Assets* assets,
static bool load_animated_texture(
Assets& assets,
const ResPaths& paths,
const std::string& atlasName,
const std::string& directory,
@ -576,8 +633,7 @@ static bool load_animation(
read_anim_file(animFile, frameList);
}
for (const auto& file : paths.listdir(animsDir + "/" + name)) {
if (!frameList.empty() &&
!contains(frameList, file.stem())) {
if (!frameList.empty() && !contains(frameList, file.stem())) {
continue;
}
if (!append_atlas(builder, file)) continue;
@ -591,10 +647,10 @@ static bool load_animation(
auto animation = create_animation(
srcAtlas.get(), dstAtlas, name, builder.getNames(), frameList
);
assets->store(
assets.store(
std::move(srcAtlas), atlasName + "/" + name + "_animation"
);
assets->store(animation);
assets.store(animation);
return true;
}
return true;

View file

@ -1,10 +1,10 @@
#pragma once
#include "Assets.hpp"
#include <memory>
#include <string>
#include "Assets.hpp"
class ResPaths;
class Assets;
class AssetsLoader;
@ -13,57 +13,64 @@ struct AssetCfg;
/// @brief see AssetsLoader.h: aloader_func
namespace assetload {
postfunc animation(
AssetsLoader&,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc texture(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc shader(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc atlas(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& directory,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc font(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& filename,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc layout(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc sound(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc model(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& file,
const std::string& name,
const std::shared_ptr<AssetCfg>& settings
);
postfunc posteffect(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& file,
const std::string& name,
@ -71,7 +78,7 @@ namespace assetload {
);
postfunc skeleton(
AssetsLoader*,
AssetsLoader&,
const ResPaths& paths,
const std::string& file,
const std::string& name,

View file

@ -37,16 +37,22 @@ void Syntax::deserialize(const dv::value& src) {
std::string multilineCommentEnd;
std::string multilineStringStart;
std::string multilineStringEnd;
std::string identifierStartChars;
std::string identifierPartChars;
src.at("line-comment").get(lineComment);
src.at("multiline-comment-start").get(multilineCommentStart);
src.at("multiline-comment-end").get(multilineCommentEnd);
src.at("multiline-string-start").get(multilineStringStart);
src.at("multiline-string-end").get(multilineStringEnd);
src.at("identifier-start-chars").get(identifierStartChars);
src.at("identifier-part-chars").get(identifierPartChars);
this->lineComment = util::str2wstr_utf8(lineComment);
this->multilineCommentStart = util::str2wstr_utf8(multilineCommentStart);
this->multilineCommentEnd = util::str2wstr_utf8(multilineCommentEnd);
this->multilineStringStart = util::str2wstr_utf8(multilineStringStart);
this->multilineStringEnd = util::str2wstr_utf8(multilineStringEnd);
this->identifierStarts = util::str2wstr_utf8(identifierStartChars);
this->identifierParts = util::str2wstr_utf8(identifierPartChars);
if (src.has("extensions")) {
const auto& extsList = src["extensions"];
@ -77,6 +83,8 @@ inline bool is_lua_operator_start(int c) {
|| c == '.';
}
#include <iostream>
class Tokenizer : BasicParser<wchar_t> {
const Syntax& syntax;
std::vector<Token> tokens;
@ -87,13 +95,28 @@ public:
: BasicParser(file, source), syntax(syntax) {
}
std::wstring parseLuaName() {
char c = peek();
if (!is_identifier_start(c)) {
bool isIdentifierStart(wchar_t c) {
if (!syntax.identifierStarts.empty()) {
return syntax.identifierStarts.find(c) != std::wstring::npos;
}
return is_common_identifier_start(c);
}
bool isIdentifierPart(wchar_t c) {
if (!syntax.identifierParts.empty()) {
return syntax.identifierParts.find(c) != std::wstring::npos;
}
return is_common_identifier_part(c);
}
std::wstring parseIdentifier() {
wchar_t c = peek();
if (!isIdentifierStart(c)) {
throw error("identifier expected");
}
int start = pos;
while (hasNext() && is_minimal_identifier_part(source[pos])) {
while (hasNext() && isIdentifierPart(source[pos])) {
pos++;
}
return std::wstring(source.substr(start, pos - start));
@ -167,7 +190,7 @@ public:
wchar_t c = peek();
auto start = currentLocation();
if (is_common_identifier_start(c)) {
auto name = parseLuaName();
auto name = parseIdentifier();
TokenTag tag =
(syntax.keywords.find(name) == syntax.keywords.end()
? TokenTag::NAME

View file

@ -17,6 +17,8 @@ namespace devtools {
std::wstring multilineCommentEnd;
std::wstring multilineStringStart;
std::wstring multilineStringEnd;
std::wstring identifierStarts;
std::wstring identifierParts;
dv::value serialize() const override;
void deserialize(const dv::value& src) override;

View file

@ -192,7 +192,12 @@ static void perform_rect(const xmlelement& root, ModelBuilder& builder) {
}
std::string texture = root.attr("texture", "$0").getText();
auto normal = glm::cross(glm::normalize(right), glm::normalize(up));
glm::vec3 normal;
if (root.has("normal")) {
normal = root.attr("normal").asVec3();
} else {
normal = glm::cross(glm::normalize(right), glm::normalize(up));
}
builder.addRect(
texture,
shading,
@ -218,7 +223,12 @@ static void perform_triangle(const xmlelement& root, ModelBuilder& builder) {
glm::vec3 ba = pointB - pointA;
glm::vec3 ca = pointC - pointA;
glm::vec3 normal = glm::normalize(glm::cross(ba, ca));
glm::vec3 normal;
if (root.has("normal")) {
normal = root.attr("normal").asVec3();
} else {
normal = glm::normalize(glm::cross(ba, ca));
}
if (root.has("uv")) {
root.attr("uv").asNumbers(glm::value_ptr(uvs[0]), 6);

View file

@ -1,13 +1,12 @@
#pragma once
#include <memory>
#include "animation/rigging.hpp"
#include "graphics/commons/Model.hpp"
#include <string>
#include <optional>
#include <unordered_map>
#include "graphics/commons/Model.hpp"
#include "objects/rigging.hpp"
namespace rigging {
class SkeletonConfig;
}

View file

@ -75,13 +75,14 @@ inline constexpr int DEFAULT_FONT_SIZE = 16;
inline constexpr int DEFAULT_PRE_RENDER_FONT_PAGES = 1; // must be at least 1
inline const std::string SHADERS_FOLDER = "shaders";
inline const std::string TEXTURES_FOLDER = "textures";
inline const std::string ANIMATION_FOLDER = "animation";
inline const std::string FONTS_FOLDER = "fonts";
inline const std::string LAYOUTS_FOLDER = "layouts";
inline const std::string SOUNDS_FOLDER = "sounds";
inline const std::string MODELS_FOLDER = "models";
inline const std::string SKELETONS_FOLDER = "skeletons";
inline const std::string POST_EFFECTS_FOLDER = "shaders/effects";
inline const std::string SHADERS_FOLDER = "shaders";
inline const std::string SKELETONS_FOLDER = "skeletons";
inline const std::string SOUNDS_FOLDER = "sounds";
inline const std::string TEXTURES_FOLDER = "textures";
inline const std::string FONT_DEFAULT = "normal";

View file

@ -1,16 +1,16 @@
#include "ContentControl.hpp"
#include "io/io.hpp"
#include "engine/EnginePaths.hpp"
#include "animation/rigging.hpp"
#include "Content.hpp"
#include "ContentPack.hpp"
#include "ContentBuilder.hpp"
#include "ContentLoader.hpp"
#include "PacksManager.hpp"
#include "objects/rigging.hpp"
#include "devtools/Project.hpp"
#include "logic/scripting/scripting.hpp"
#include "ContentPack.hpp"
#include "core_defs.hpp"
#include "devtools/Project.hpp"
#include "engine/EnginePaths.hpp"
#include "io/io.hpp"
#include "logic/scripting/scripting.hpp"
#include "PacksManager.hpp"
static void load_configs(Input* input, const io::path& root) {
auto configFolder = root / "config";

17
src/debug/debug.hpp Normal file
View file

@ -0,0 +1,17 @@
#pragma once
#include <iostream>
#include <glm/glm.hpp>
template<int N, typename T>
std::ostream& operator<<(std::ostream& out, const glm::vec<N, T>& vec) {
out << "{";
for (int i = 0; i < N; i++) {
if (i > 0) {
out << ", ";
}
out << vec[i];
}
out << "}";
return out;
}

View file

@ -1,9 +1,13 @@
#include "DebuggingServer.hpp"
#include "engine/Engine.hpp"
#include "network/Network.hpp"
#include "debug/Logger.hpp"
#include "assets/AssetsLoader.hpp"
#include "coders/json.hpp"
#include "data/dv.hpp"
#include "debug/Logger.hpp"
#include "engine/AssetsManagement.hpp"
#include "engine/Engine.hpp"
#include "io/path.hpp"
#include "network/Network.hpp"
using namespace devtools;
@ -260,6 +264,16 @@ bool DebuggingServer::performCommand(
}
});
return true;
} else if (type == "hot-reload") {
if (!map.has("file")) {
return false;
}
logger.info() << "hot-reload: " << map["file"].asString();
int count = engine.getAssetsManagement().acquireBackgroundLoader().addReload(
map["file"].asString()
);
logger.info() << "enqueued to reload: " << count;
return false;
} else {
logger.error() << "unsupported command '" << type << "'";
}

View file

@ -9,6 +9,7 @@
#include "graphics/core/Shader.hpp"
#include "graphics/render/ModelsGenerator.hpp"
#include "graphics/ui/GUI.hpp"
#include "logic/scripting/scripting.hpp"
static debug::Logger logger("assets-management");
@ -49,35 +50,41 @@ AssetsLoader& AssetsManagement::acquireBackgroundLoader() {
void AssetsManagement::loadAssets(Content* content) {
finishBackgroundLoader();
scripting::on_assets_loading();
const auto& paths = engine.getPaths();
logger.info() << "loading assets";
Shader::preprocessor->setPaths(&paths.resPaths);
auto new_assets = std::make_unique<Assets>(
auto newAssets = std::make_unique<Assets>(
settings.system.preserveAssetsDuringFrame.get() ? &assetsVault : nullptr
);
AssetsLoader loader(engine, *new_assets, paths.resPaths);
AssetsLoader::addDefaults(loader, content);
AssetsLoader loader(engine, *newAssets, paths.resPaths);
loader.addDefaults(content);
// no need
// correct log messages order is more useful
// todo: before setting to true, check if GLSLExtension thread safe
bool threading = false; // look at three upper lines
if (threading) {
auto task = loader.startTask(
[=]() {}, 0
);
task->waitForEnd();
} else {
while (loader.hasNext()) {
loader.loadNext();
try {
// no need
// correct log messages order is more useful
// todo: before setting to true, check if GLSLExtension thread safe
bool threading = false; // look at three upper lines
if (threading) {
auto task = loader.startTask(
[=]() {}, 0
);
task->waitForEnd();
} else {
while (loader.hasNext()) {
loader.loadNext();
}
}
} catch (...) {
scripting::revert_assets_loading();
throw;
}
assets = std::move(new_assets);
assets = std::move(newAssets);
if (content) {
ModelsGenerator::prepare(*content, *assets);
}
assets->setup();
engine.getGUI().onAssetsLoad(assets.get());
}

View file

@ -179,7 +179,7 @@ void Engine::initialize(CoreParameters coreParameters) {
content = std::make_unique<ContentControl>(
*project, *paths, input.get(), [this]() { onContentLoad(); }
);
scripting::initialize(this);
scripting::initialize(*this);
if (!isHeadless()) {
gui->getMenu()->setPageLoader(scripting::create_page_loader());

View file

@ -190,5 +190,9 @@ public:
return *appScripts;
}
AssetsManagement& getAssetsManagement() const {
return *assets;
}
void detachDebugger();
};

View file

@ -144,6 +144,7 @@ void BlockWrapsRenderer::draw(const DrawContext& pctx) {
auto& shader = assets.require<Shader>("entity");
shader.use();
shader.uniform1i("u_alphaClip", false);
shader.uniform1i("u_dithering", 0);
for (const auto& [_, wrapper] : wrappers) {
if (wrapper->dirtySides) {
@ -198,4 +199,4 @@ void BlockWrapsRenderer::clearOrder(const BlockWrapper* const wrapper) {
++it;
}
}
}
}

View file

@ -1,14 +1,14 @@
#include "HandsRenderer.hpp"
#include "animation/rigging.hpp"
#include "content/Content.hpp"
#include "graphics/commons/Model.hpp"
#include "ModelBatch.hpp"
#include "window/Camera.hpp"
#include <glm/ext.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "ModelBatch.hpp"
#include "content/Content.hpp"
#include "graphics/commons/Model.hpp"
#include "objects/rigging.hpp"
#include "window/Camera.hpp"
using namespace rigging;
HandsRenderer::HandsRenderer(

View file

@ -1,6 +1,6 @@
#include "NamedSkeletons.hpp"
#include "objects/rigging.hpp"
#include "animation/rigging.hpp"
using namespace rigging;

View file

@ -1,5 +1,6 @@
#include "WorldRenderer.hpp"
#include "animation/rigging.hpp"
#include "BlockWrapsRenderer.hpp"
#include "ChunksRenderer.hpp"
#include "CloudsRenderer.hpp"
@ -42,7 +43,6 @@
#include "maths/voxmaths.hpp"
#include "objects/Entities.hpp"
#include "objects/Player.hpp"
#include "objects/rigging.hpp"
#include "settings.hpp"
#include "voxels/Block.hpp"
#include "voxels/Chunk.hpp"
@ -244,6 +244,7 @@ void WorldRenderer::renderOpaque(
}
entityShader.uniform1i("u_alphaClip", true);
entityShader.uniform1i("u_dithering", 1);
entityShader.uniform1f("u_opacity", 1.0f);
level.entities->render(
assets,
@ -513,6 +514,7 @@ void WorldRenderer::renderWeatherEffects(Camera& camera) {
float one = weather->fall.maxOpacity;
float t = (weather->intensity * (one - zero)) * maxIntensity + zero;
entityShader.uniform1i("u_alphaClip", weather->fall.opaque);
entityShader.uniform1i("u_dithering", 0);
entityShader.uniform1f(
"u_opacity", weather->fall.opaque ? t * t : t
);

View file

@ -1,6 +1,6 @@
#include "objects/rigging.hpp"
#include "libentity.hpp"
#include "animation/rigging.hpp"
#include "engine/Engine.hpp"
#include "graphics/render/WorldRenderer.hpp"
#include "graphics/render/NamedSkeletons.hpp"
@ -42,7 +42,7 @@ static int l_get_model(lua::State* L) {
auto& rigConfig = *skeleton->config;
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
const auto& modelOverride = skeleton->modelOverrides[index];
if (modelOverride.model) {
if (modelOverride.model.expired()) {
return lua::pushstring(L, modelOverride.name);
}
return lua::pushstring(L, rigConfig.getBones()[index]->model.name);
@ -55,9 +55,9 @@ static int l_set_model(lua::State* L) {
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
auto& modelOverride = skeleton->modelOverrides[index];
if (lua::isnoneornil(L, 3)) {
modelOverride = {"", nullptr, true};
modelOverride = {"", {}, true};
} else {
modelOverride = {lua::require_string(L, 3), nullptr, true};
modelOverride = {lua::require_string(L, 3), {}, true};
}
}
return 0;
@ -66,19 +66,34 @@ static int l_set_model(lua::State* L) {
static int l_get_matrix(lua::State* L) {
if (auto skeleton = get_skeleton(L)) {
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
return lua::pushmat4(L, skeleton->pose.matrices[index]);
if (lua::istable(L, 3)) {
lua::setmat4(L, 3, skeleton->pose.matrices[index]);
} else {
return lua::pushmat4(L, skeleton->pose.matrices[index]);
}
}
return 0;
}
static int l_set_matrix(lua::State* L) {
if (auto skeleton = get_skeleton(L)) {
if (lua::isnoneornil(L, 2)) {
return 0;
}
auto index = index_range_check(*skeleton, lua::tointeger(L, 2));
skeleton->pose.matrices[index] = lua::tomat4(L, 3);
}
return 0;
}
static int l_reset_pose(lua::State* L) {
if (auto skeleton = get_skeleton(L)) {
auto& pose = skeleton->pose;
std::fill(pose.matrices.begin(), pose.matrices.end(), glm::mat4(1.0f));
}
return 0;
}
static int l_get_texture(lua::State* L) {
if (auto skeleton = get_skeleton(L)) {
const auto& found = skeleton->textures.find(lua::require_string(L, 2));
@ -169,6 +184,7 @@ const luaL_Reg skeletonlib[] = {
{"set_model", lua::wrap<l_set_model>},
{"get_matrix", lua::wrap<l_get_matrix>},
{"set_matrix", lua::wrap<l_set_matrix>},
{"reset_pose", lua::wrap<l_reset_pose>},
{"get_texture", lua::wrap<l_get_texture>},
{"set_texture", lua::wrap<l_set_texture>},
{"index", lua::wrap<l_index>},

View file

@ -11,6 +11,7 @@
#include "graphics/commons/Model.hpp"
#include "graphics/core/Atlas.hpp"
#include "graphics/core/Texture.hpp"
#include "io/io.hpp"
#include "util/Buffer.hpp"
using namespace scripting;
@ -66,6 +67,20 @@ static int l_load_texture(lua::State* L) {
return 0;
}
static int l_parse_animation(lua::State* L) {
auto format = lua::require_lstring(L, 1);
auto string = lua::require_lstring(L, 2);
std::string name = lua::require_string(L, 3);
if (format != "vca") {
throw std::runtime_error(
"unknown format " + util::quote(std::string(format))
);
}
scripting::load_vca_animation(name, &string, name);
return 0;
}
static int l_parse_model(lua::State* L) {
auto& assets = engine->requireAssets();
@ -161,6 +176,7 @@ static int l_to_canvas(lua::State* L) {
const luaL_Reg assetslib[] = {
{"request_texture", lua::wrap<l_request_texture>},
{"load_texture", lua::wrap<l_load_texture>},
{"parse_animation", lua::wrap<l_parse_animation>},
{"parse_model", lua::wrap<l_parse_model>},
{"to_canvas", lua::wrap<l_to_canvas>},
{nullptr, nullptr}

View file

@ -1,20 +1,20 @@
#include "libentity.hpp"
#include "animation/rigging.hpp"
#include "assets/Assets.hpp"
#include "content/Content.hpp"
#include "content/ContentPack.hpp"
#include "engine/Engine.hpp"
#include "engine/EnginePaths.hpp"
#include "objects/Entities.hpp"
#include "objects/EntityDef.hpp"
#include "objects/Entity.hpp"
#include "objects/Rigidbody.hpp"
#include "objects/EntityDef.hpp"
#include "objects/Player.hpp"
#include "objects/rigging.hpp"
#include "objects/Rigidbody.hpp"
#include "physics/Hitbox.hpp"
#include "voxels/Chunks.hpp"
#include "voxels/Block.hpp"
#include "voxels/blocks_agent.hpp"
#include "voxels/Chunks.hpp"
#include "window/Camera.hpp"
#include <set>
@ -119,6 +119,10 @@ static int l_set_skeleton(lua::State* L) {
return 0;
}
if (auto entity = get_entity(L, 1)) {
if (lua::isnil(L, 2)) {
entity->setRig(nullptr);
return 0;
}
std::string skeletonName = lua::require_string(L, 2);
auto rigConfig = assets->getShared<rigging::SkeletonConfig>(skeletonName);
if (rigConfig == nullptr) {

View file

@ -77,7 +77,7 @@ static void create_libs(State* L, StateType stateType) {
createtable(L, 0, 0);
pushvalue(L, -1);
setglobal(L, "__vc_internals");
setregistry(L, "internals");
setregistry(L, lua::INTERNALS_TABLE);
if (stateType == StateType::SCRIPT) {
getregistry(L, "app");

View file

@ -16,6 +16,7 @@ namespace lua {
inline std::string CHUNKS_TABLE = "$C"; // precompiled lua chunks
inline std::string PACK_ENVS_TABLE = "$P";
inline std::string ENVS_TABLE = "$E";
inline std::string INTERNALS_TABLE = "$I";
extern std::unordered_map<std::type_index, std::string> usertypeNames;
int userdata_destructor(lua::State* L);
@ -635,6 +636,13 @@ namespace lua {
int call(lua::State*, int argc, int nresults = -1);
int call_nothrow(lua::State*, int argc, int nresults = 1);
inline void call_internal(lua::State* L, const std::string& name) {
if (lua::get_from_registry(L, lua::INTERNALS_TABLE, name, true)) {
lua::call_nothrow(L, 0, 0);
pop(L);
}
}
inline int eval(
lua::State* L,
int env,

View file

@ -65,14 +65,13 @@ int scripting::load_script(
return lua::execute(lua::get_main_state(), env, src, fileName);
}
void scripting::initialize(Engine* engine) {
scripting::engine = engine;
scripting::content_control = &engine->getContentControl();
lua::initialize(engine->getPaths(), engine->getCoreParameters());
void scripting::initialize(Engine& engine) {
scripting::engine = &engine;
scripting::content_control = &engine.getContentControl();
lua::initialize(engine.getPaths(), engine.getCoreParameters());
load_script(io::path("stdlib.lua"), true);
load_script(io::path("classes.lua"), true);
load_script(io::path("internal_events.lua"), true);
}
class LuaCoroutine : public Process {
@ -244,9 +243,7 @@ std::unique_ptr<Process> scripting::start_app_script(const io::path& script) {
void scripting::process_post_runnables() {
auto L = lua::get_main_state();
if (lua::getglobal(L, "__vc__process_post_runnables")) {
lua::call_nothrow(L, 0, 0);
}
lua::call_internal(L, "process_post_runnables");
}
template <class T, typename IdType>
@ -263,6 +260,14 @@ static int push_properties_tables(
return 1;
}
void scripting::on_assets_loading() {
lua::call_internal(lua::get_main_state(), "backup_and_clear_animation");
}
void scripting::revert_assets_loading() {
lua::call_internal(lua::get_main_state(), "restore_animation_backup");
}
void scripting::on_content_load(Content* content) {
scripting::content = content;
scripting::indices = content->getIndices();
@ -291,13 +296,15 @@ void scripting::on_content_load(Content* content) {
lua::getregistry(L, "app");
lua::setglobal(L, "__vc_app");
lua::getregistry(L, "internals");
lua::getregistry(L, lua::INTERNALS_TABLE);
lua::setglobal(L, "__vc_internals");
try {
load_script("post_content.lua", true);
} catch (const std::exception&) {
lua::pushnil(L);
lua::setglobal(L, "__vc_app");
lua::pushnil(L);
lua::setglobal(L, "__vc_internals");
throw;
}
lua::pushnil(L);
@ -319,9 +326,7 @@ void scripting::on_world_load(LevelController* controller) {
scripting::controller = controller;
auto L = lua::get_main_state();
if (lua::getglobal(L, "__vc_on_world_open")) {
lua::call_nothrow(L, 0, 0);
}
lua::call_internal(L, "on_world_open");
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(L, pack.id + ":.worldopen", [](auto L) {
@ -334,9 +339,10 @@ void scripting::on_world_load(LevelController* controller) {
void scripting::on_world_tick(int tps) {
auto L = lua::get_main_state();
if (lua::getglobal(L, "__vc_on_world_tick")) {
if (lua::get_from_registry(L, lua::INTERNALS_TABLE, "on_world_tick", true)) {
lua::pushinteger(L, tps);
lua::call_nothrow(L, 1, 0);
lua::pop(L);
}
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(L, pack.id + ":.worldtick");
@ -348,16 +354,12 @@ void scripting::on_world_save() {
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(L, pack.id + ":.worldsave");
}
if (lua::getglobal(L, "__vc_on_world_save")) {
lua::call_nothrow(L, 0, 0);
}
lua::call_internal(L, "on_world_save");
}
void scripting::process_before_quit() {
auto L = lua::get_main_state();
if (lua::getglobal(L, "__vc_process_before_quit")) {
lua::call_nothrow(L, 0, 0);
}
lua::call_internal(L, "process_before_quit");
}
void scripting::on_world_quit() {
@ -365,9 +367,7 @@ void scripting::on_world_quit() {
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(L, pack.id + ":.worldquit");
}
if (lua::getglobal(L, "__vc_on_world_quit")) {
lua::call_nothrow(L, 0, 0);
}
lua::call_internal(L, "on_world_quit");
scripting::level = nullptr;
scripting::content = nullptr;
scripting::indices = nullptr;
@ -842,6 +842,27 @@ void scripting::load_layout_script(
lua::pop(L);
}
void scripting::load_vca_animation(
const io::path& file,
const std::string_view* content,
const std::string& identifier
) {
auto L = lua::get_main_state();
if (lua::get_from_registry(L, lua::INTERNALS_TABLE, "load_vca_animation", true)) {
lua::pushlstring(L, file.string());
if (content) {
lua::pushlstring(L, *content);
} else {
lua::pushnil(L);
}
lua::pushlstring(L, identifier);
if (lua::call(L, 3, 0)) {
lua::pop(L);
}
lua::pop(L);
}
}
void scripting::close() {
lua::finalize();
content = nullptr;

View file

@ -48,7 +48,10 @@ namespace scripting {
extern std::ostream* output_stream;
extern std::ostream* error_stream;
void initialize(Engine* engine);
void initialize(Engine& engine);
void on_assets_loading();
void revert_assets_loading();
void on_content_load(Content* content);
void on_content_reset();
@ -251,6 +254,12 @@ namespace scripting {
const std::string& fileName,
UiDocScript& script
);
void load_vca_animation(
const io::path& file,
const std::string_view* content,
const std::string& identifier
);
/// @brief Finalize lua state. Using scripting after will lead to Lua panic
void close();

View file

@ -37,6 +37,11 @@ static inline const std::string STDCOMP = "stdcomp";
lua::setfield(L, name);
lua::pop(L);
}
if (lua::getfield(L, "ordered_components")) {
lua::pushenv(L, id);
lua::rawseti(L, lua::objlen(L, -2) + 1);
lua::pop(L);
}
lua::pop(L);
return std::shared_ptr<int>(new int(id), [=](int* id) { //-V508

View file

@ -47,9 +47,7 @@ void scripting::on_frontend_init(
load_script("hud_classes.lua");
if (lua::getglobal(L, "__vc_on_hud_open")) {
lua::call_nothrow(L, 0, 0);
}
lua::call_internal(L, "on_hud_open");
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(
@ -63,12 +61,13 @@ void scripting::on_frontend_init(
}
void scripting::on_frontend_render() {
auto L = lua::get_main_state();
lua::call_internal(L, "on_render");
for (auto& pack : content_control->getAllContentPacks()) {
lua::emit_event(
lua::get_main_state(),
pack.id + ":.hudrender",
[](lua::State* L) { return 0; }
);
lua::emit_event(L, pack.id + ":.hudrender", [](lua::State* L) {
return 0;
});
}
}

View file

@ -1,6 +1,7 @@
#define VC_ENABLE_REFLECTION
#include "Entities.hpp"
#include "animation/rigging.hpp"
#include "assets/Assets.hpp"
#include "content/Content.hpp"
#include "data/dv_util.hpp"
@ -17,7 +18,6 @@
#include "maths/rays.hpp"
#include "maths/util.hpp"
#include "physics/PhysicsSolver.hpp"
#include "rigging.hpp"
#include "world/Level.hpp"
#include <entt/entity/registry.hpp>
@ -59,7 +59,11 @@ entityid_t Entities::spawn(
if (assets) {
skeleton = assets->get<rigging::SkeletonConfig>(def.skeletonName);
if (skeleton == nullptr) {
throw std::runtime_error("skeleton " + def.skeletonName + " not found");
if (def.skeletonName == def.name) {
logger.warning() << "skeleton " + def.skeletonName + " not found";
} else {
throw std::runtime_error("skeleton " + def.skeletonName + " not found");
}
}
}
entityid_t id;
@ -100,7 +104,9 @@ entityid_t Entities::spawn(
auto& scripting = registry->emplace<ScriptComponents>(entity);
if (assets) {
registry->emplace<rigging::Skeleton>(entity, skeleton->instance());
registry->emplace<rigging::Skeleton>(
entity, skeleton ? skeleton->instance() : rigging::Skeleton(nullptr)
);
}
for (auto& instance : def.components) {

View file

@ -1,12 +1,12 @@
#include "Entity.hpp"
#include "Transform.hpp"
#include "Rigidbody.hpp"
#include "ScriptComponents.hpp"
#include "animation/rigging.hpp"
#include "Entities.hpp"
#include "EntityDef.hpp"
#include "rigging.hpp"
#include "logic/scripting/scripting.hpp"
#include "Rigidbody.hpp"
#include "ScriptComponents.hpp"
#include "Transform.hpp"
#include <entt/entt.hpp>

View file

@ -1,27 +1,28 @@
#include "Player.hpp"
#include <algorithm>
#define GLM_ENABLE_EXPERIMENTAL
#include "animation/rigging.hpp"
#include "content/ContentReport.hpp"
#include "data/dv_util.hpp"
#include "debug/Logger.hpp"
#include "Entities.hpp"
#include "Entity.hpp"
#include "items/Inventory.hpp"
#include "physics/Hitbox.hpp"
#include "physics/PhysicsSolver.hpp"
#include "util/stringutil.hpp"
#include "voxels/Chunks.hpp"
#include "window/Camera.hpp"
#include "world/generator/GeneratorDef.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
#include <algorithm>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <utility>
#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"
#include "voxels/Chunks.hpp"
#include "window/Camera.hpp"
#include "world/Level.hpp"
#include "world/World.hpp"
#include "world/generator/GeneratorDef.hpp"
#include "data/dv_util.hpp"
#include "debug/Logger.hpp"
#include "util/stringutil.hpp"
static debug::Logger logger("player");
constexpr int SPAWN_ATTEMPTS_PER_UPDATE = 64;

View file

@ -103,6 +103,22 @@ namespace util {
return data_.ptr()[index];
}
T& front() {
return data_.ptr()[0];
}
T& back() {
return data_.ptr()[size_ - 1];
}
const T& front() const {
return data_.ptr()[0];
}
const T& back() const {
return data_.ptr()[size_ - 1];
}
int size() const {
return size_;
}

View file

@ -167,6 +167,9 @@ void Block::cloneTo(Block& dst) {
dst.translucent = translucent;
dst.explictlySolid = explictlySolid;
dst.tags = tags;
if (dataStruct) {
dst.dataStruct = std::make_unique<data::StructLayout>(*dataStruct);
}
if (particles) {
dst.particles = std::make_unique<ParticlesPreset>(*particles);
}