mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-05 11:01:49 +00:00
Merge branch 'MihailRis:main' into main
This commit is contained in:
commit
469edd1e9d
66 changed files with 1717 additions and 538 deletions
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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
17
src/debug/debug.hpp
Normal 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;
|
||||
}
|
||||
|
|
@ -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 << "'";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -190,5 +190,9 @@ public:
|
|||
return *appScripts;
|
||||
}
|
||||
|
||||
AssetsManagement& getAssetsManagement() const {
|
||||
return *assets;
|
||||
}
|
||||
|
||||
void detachDebugger();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#include "NamedSkeletons.hpp"
|
||||
|
||||
#include "objects/rigging.hpp"
|
||||
#include "animation/rigging.hpp"
|
||||
|
||||
using namespace rigging;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>},
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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_;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue