mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-11 05:51:51 +00:00
Merge branch 'main' into volume-renderer
This commit is contained in:
commit
b2df0a7e91
28 changed files with 642 additions and 184 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
## VCM
|
# VCM
|
||||||
|
|
||||||
Текстовый формат описания 3D моделей, являющийся заменой устаревшего свойства `model-primitives`.
|
Текстовый формат описания 3D моделей, являющийся заменой устаревшего свойства `model-primitives`.
|
||||||
|
|
||||||
|
|
@ -27,6 +27,8 @@
|
||||||
> При размерах более 1.0 результат не определён и может измениться в последующих обновлениях.
|
> При размерах более 1.0 результат не определён и может измениться в последующих обновлениях.
|
||||||
> Используйте `region` или `region-scale` для ручной настройки.
|
> Используйте `region` или `region-scale` для ручной настройки.
|
||||||
|
|
||||||
|
## Примитивы
|
||||||
|
|
||||||
### Свойства `tri`
|
### Свойства `tri`
|
||||||
|
|
||||||
Примитив описывает треугольник.
|
Примитив описывает треугольник.
|
||||||
|
|
@ -75,3 +77,25 @@
|
||||||
- `texture` - отображаемая текстура. По-умолчанию: `$индекс_стороны`.
|
- `texture` - отображаемая текстура. По-умолчанию: `$индекс_стороны`.
|
||||||
- `region` - UV регион в границах выбранной текстуры, определяется позициями противоположных углов. Пример: `region (0,0,1,1)`
|
- `region` - UV регион в границах выбранной текстуры, определяется позициями противоположных углов. Пример: `region (0,0,1,1)`
|
||||||
- `region-scale` вектор-множитель для автоматически выбранного UV региона. Пример: `region-scale (0.5,1)`
|
- `region-scale` вектор-множитель для автоматически выбранного UV региона. Пример: `region-scale (0.5,1)`
|
||||||
|
|
||||||
|
## Скелет
|
||||||
|
|
||||||
|
VCM позволяет описывать [скелет](rigging.md) модели. Для этого используется элемент `@bone` (кость).
|
||||||
|
|
||||||
|
Существует два вида костей:
|
||||||
|
- безымянные - используются только для трансформаций на влюженные примитивы, не попадает в итоговый скелет.
|
||||||
|
- именованные - будет включено в итоговый скелет. Имя указывается через атрибут `name`.
|
||||||
|
|
||||||
|
Главная кость (`root`) создаётся автоматически для всей модели.
|
||||||
|
При наличии явно указанных костей модель будет разделена на несколько частей именованных в формате `имя_модели.имя_кости`.
|
||||||
|
Так как модель относится к ассетам и не имеет префикса, загруженный скелет получает имя модели, так же, без префикса.
|
||||||
|
Загруженный скелет можно использовать у [сущности](entity-properties.md#имя-скелета---skeleton-name) через свойство `skeleton-name`.
|
||||||
|
|
||||||
|
В списке `пак/preload.json` при указании параметра `"squash": true` vcm будет загружен как единая модель, без скелета.
|
||||||
|
|
||||||
|
### Свойства `bone`
|
||||||
|
|
||||||
|
- `name` - имя кости, при отсутствии которого кость будет "растворена" и не попадёт в итоговый скелет, при этом, трансформации будут применены.
|
||||||
|
- `move` - смещение вложенных элементов.
|
||||||
|
- `scale` - масштабирование вложенных элементов. Применяется перед смещением.
|
||||||
|
- `rotate` - вращение вложенных элементов вокруг осей (x,y,z) в градусах, или кватернион (x,y,z,w). Пример: `rotate (45,0,0)` или `rotate (0.3826834, 0, 0, 0.9238795)`
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,8 @@ enum class AssetType {
|
||||||
LAYOUT,
|
LAYOUT,
|
||||||
SOUND,
|
SOUND,
|
||||||
MODEL,
|
MODEL,
|
||||||
POST_EFFECT
|
POST_EFFECT,
|
||||||
|
SKELETON
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace assetload {
|
namespace assetload {
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
#include "AssetsLoader.hpp"
|
#include "AssetsLoader.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include "assetload_funcs.hpp"
|
||||||
#include <memory>
|
#include "Assets.hpp"
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
#include "coders/imageio.hpp"
|
|
||||||
#include "coders/commons.hpp"
|
#include "coders/commons.hpp"
|
||||||
|
#include "coders/imageio.hpp"
|
||||||
#include "constants.hpp"
|
#include "constants.hpp"
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "content/ContentPack.hpp"
|
#include "content/ContentPack.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
|
#include "engine/Engine.hpp"
|
||||||
#include "engine/EnginePaths.hpp"
|
#include "engine/EnginePaths.hpp"
|
||||||
#include "io/io.hpp"
|
|
||||||
#include "graphics/core/Texture.hpp"
|
#include "graphics/core/Texture.hpp"
|
||||||
|
#include "io/io.hpp"
|
||||||
|
#include "items/ItemDef.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
#include "objects/EntityDef.hpp"
|
||||||
#include "objects/rigging.hpp"
|
#include "objects/rigging.hpp"
|
||||||
#include "util/ThreadPool.hpp"
|
#include "util/ThreadPool.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "items/ItemDef.hpp"
|
|
||||||
#include "Assets.hpp"
|
#include <memory>
|
||||||
#include "assetload_funcs.hpp"
|
#include <utility>
|
||||||
#include "engine/Engine.hpp"
|
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
|
@ -36,6 +36,7 @@ AssetsLoader::AssetsLoader(Engine& engine, Assets& assets, const ResPaths& paths
|
||||||
addLoader(AssetType::SOUND, assetload::sound);
|
addLoader(AssetType::SOUND, assetload::sound);
|
||||||
addLoader(AssetType::MODEL, assetload::model);
|
addLoader(AssetType::MODEL, assetload::model);
|
||||||
addLoader(AssetType::POST_EFFECT, assetload::posteffect);
|
addLoader(AssetType::POST_EFFECT, assetload::posteffect);
|
||||||
|
addLoader(AssetType::SKELETON, assetload::skeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
void AssetsLoader::addLoader(AssetType tag, aloader_func func) {
|
||||||
|
|
@ -141,6 +142,8 @@ static std::string assets_def_folder(AssetType tag) {
|
||||||
return MODELS_FOLDER;
|
return MODELS_FOLDER;
|
||||||
case AssetType::POST_EFFECT:
|
case AssetType::POST_EFFECT:
|
||||||
return POST_EFFECTS_FOLDER;
|
return POST_EFFECTS_FOLDER;
|
||||||
|
case AssetType::SKELETON:
|
||||||
|
return SKELETONS_FOLDER;
|
||||||
}
|
}
|
||||||
return "<error>";
|
return "<error>";
|
||||||
}
|
}
|
||||||
|
|
@ -178,6 +181,12 @@ void AssetsLoader::processPreload(
|
||||||
config = std::make_shared<PostEffectCfg>(advanced);
|
config = std::make_shared<PostEffectCfg>(advanced);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case AssetType::MODEL: {
|
||||||
|
bool squashed = false;
|
||||||
|
map.at("squash").get(squashed);
|
||||||
|
config = std::make_shared<ModelCfg>(squashed);
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -211,6 +220,7 @@ void AssetsLoader::processPreloadConfig(const io::path& file) {
|
||||||
processPreloadList(AssetType::SOUND, root["sounds"]);
|
processPreloadList(AssetType::SOUND, root["sounds"]);
|
||||||
processPreloadList(AssetType::MODEL, root["models"]);
|
processPreloadList(AssetType::MODEL, root["models"]);
|
||||||
processPreloadList(AssetType::POST_EFFECT, root["post-effects"]);
|
processPreloadList(AssetType::POST_EFFECT, root["post-effects"]);
|
||||||
|
processPreloadList(AssetType::SKELETON, root["skeletons"]);
|
||||||
// layouts are loaded automatically
|
// layouts are loaded automatically
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,21 +273,20 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||||
add_layouts(pack->getEnvironment(), info.id, folder, loader);
|
add_layouts(pack->getEnvironment(), info.id, folder, loader);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto& entry : content->getSkeletons()) {
|
for (const auto& entry : content->getPacks()) {
|
||||||
auto& skeleton = *entry.second;
|
io::path skeletonsDir = entry.first + ":skeletons";
|
||||||
for (auto& bone : skeleton.getBones()) {
|
if (!io::is_directory(skeletonsDir)) {
|
||||||
std::string model = bone->model.name;
|
continue;
|
||||||
size_t pos = model.rfind('.');
|
}
|
||||||
if (pos != std::string::npos) {
|
for (const auto& file : io::directory_iterator(skeletonsDir)) {
|
||||||
model = model.substr(0, pos);
|
loader.add(
|
||||||
}
|
AssetType::SKELETON,
|
||||||
if (!model.empty()) {
|
file.string(),
|
||||||
loader.add(
|
entry.first + ":" + file.stem()
|
||||||
AssetType::MODEL, MODELS_FOLDER + "/" + model, model
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& [_, def] : content->blocks.getDefs()) {
|
for (const auto& [_, def] : content->blocks.getDefs()) {
|
||||||
if (def->variants) {
|
if (def->variants) {
|
||||||
for (const auto& variant : def->variants->variants) {
|
for (const auto& variant : def->variants->variants) {
|
||||||
|
|
@ -296,6 +305,16 @@ void AssetsLoader::addDefaults(AssetsLoader& loader, const Content* content) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,12 @@ struct PostEffectCfg : AssetCfg {
|
||||||
PostEffectCfg(bool advanced) : advanced(advanced) {}
|
PostEffectCfg(bool advanced) : advanced(advanced) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ModelCfg : AssetCfg {
|
||||||
|
bool squashed;
|
||||||
|
|
||||||
|
ModelCfg(bool squashed) : squashed(squashed) {}
|
||||||
|
};
|
||||||
|
|
||||||
using aloader_func = std::function<
|
using aloader_func = std::function<
|
||||||
assetload::
|
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>)>;
|
||||||
|
|
|
||||||
|
|
@ -325,13 +325,31 @@ assetload::postfunc assetload::model(
|
||||||
const ResPaths& paths,
|
const ResPaths& paths,
|
||||||
const std::string& file,
|
const std::string& file,
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::shared_ptr<AssetCfg>&
|
const std::shared_ptr<AssetCfg>& config
|
||||||
) {
|
) {
|
||||||
|
auto cfg = std::dynamic_pointer_cast<ModelCfg>(config);
|
||||||
|
|
||||||
auto path = paths.find(file + ".vec3");
|
auto path = paths.find(file + ".vec3");
|
||||||
if (io::exists(path)) {
|
if (io::exists(path)) {
|
||||||
auto bytes = io::read_bytes_buffer(path);
|
auto bytes = io::read_bytes_buffer(path);
|
||||||
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
|
auto modelVEC3 = std::make_shared<vec3::File>(vec3::load(path.string(), bytes));
|
||||||
return [loader, name, modelVEC3=std::move(modelVEC3)](Assets* assets) {
|
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) {
|
for (auto& [modelName, model] : modelVEC3->models) {
|
||||||
request_textures(loader, model.model);
|
request_textures(loader, model.model);
|
||||||
std::string fullName = name;
|
std::string fullName = name;
|
||||||
|
|
@ -381,18 +399,76 @@ assetload::postfunc assetload::model(
|
||||||
|
|
||||||
auto text = io::read_string(path);
|
auto text = io::read_string(path);
|
||||||
try {
|
try {
|
||||||
auto model = vcm::parse(path.string(), text, path.extension() == ".xml")
|
auto vcmModel = vcm::parse(path.string(), text, path.extension() == ".xml");
|
||||||
.release();
|
|
||||||
return [=](Assets* assets) {
|
assert(vcmModel.parts.size() > 0);
|
||||||
request_textures(loader, *model);
|
|
||||||
assets->store(std::unique_ptr<model::Model>(model), name);
|
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) {
|
} catch (const parsing_error& err) {
|
||||||
std::cerr << err.errorLog() << std::endl;
|
std::cerr << err.errorLog() << std::endl;
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assetload::postfunc assetload::skeleton(
|
||||||
|
AssetsLoader* loader,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
|
) {
|
||||||
|
return [=](auto assets) {
|
||||||
|
std::string text = io::read_string(file);
|
||||||
|
auto skeleton = rigging::SkeletonConfig::parse(text, file, name);
|
||||||
|
for (auto& bone : skeleton->getBones()) {
|
||||||
|
std::string model = bone->model.name;
|
||||||
|
size_t pos = model.rfind('.');
|
||||||
|
if (pos != std::string::npos) {
|
||||||
|
model = model.substr(0, pos);
|
||||||
|
}
|
||||||
|
if (!model.empty()) {
|
||||||
|
loader->add(
|
||||||
|
AssetType::MODEL, MODELS_FOLDER + "/" + model, model
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assets->store(std::move(skeleton), name);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
static void read_anim_file(
|
static void read_anim_file(
|
||||||
const std::string& animFile,
|
const std::string& animFile,
|
||||||
std::vector<std::pair<std::string, int>>& frameList
|
std::vector<std::pair<std::string, int>>& frameList
|
||||||
|
|
|
||||||
|
|
@ -69,4 +69,12 @@ namespace assetload {
|
||||||
const std::string& name,
|
const std::string& name,
|
||||||
const std::shared_ptr<AssetCfg>& settings
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
);
|
);
|
||||||
|
|
||||||
|
postfunc skeleton(
|
||||||
|
AssetsLoader*,
|
||||||
|
const ResPaths& paths,
|
||||||
|
const std::string& file,
|
||||||
|
const std::string& name,
|
||||||
|
const std::shared_ptr<AssetCfg>& settings
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,58 @@
|
||||||
#include "vcm.hpp"
|
#include "vcm.hpp"
|
||||||
|
|
||||||
|
#include "xml.hpp"
|
||||||
|
#include "util/stringutil.hpp"
|
||||||
|
#include "io/io.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include <glm/gtc/matrix_transform.hpp>
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
||||||
#include "xml.hpp"
|
|
||||||
#include "util/stringutil.hpp"
|
|
||||||
#include "graphics/commons/Model.hpp"
|
|
||||||
#include "io/io.hpp"
|
|
||||||
|
|
||||||
using namespace vcm;
|
using namespace vcm;
|
||||||
using namespace xml;
|
using namespace xml;
|
||||||
|
using namespace model;
|
||||||
|
using namespace rigging;
|
||||||
|
|
||||||
|
static int calc_offsets(
|
||||||
|
const Bone& bone, std::vector<glm::vec3>& dst, int index, int depth, int parent
|
||||||
|
) {
|
||||||
|
if (depth == 0) {
|
||||||
|
dst[0] = bone.getOffset();
|
||||||
|
} else {
|
||||||
|
dst[index] = dst[parent] + bone.getOffset();
|
||||||
|
}
|
||||||
|
const auto& subBones = bone.getBones();
|
||||||
|
|
||||||
|
int subIndex = index + 1;
|
||||||
|
for (int i = 0; i < subBones.size(); i++) {
|
||||||
|
subIndex += calc_offsets(*subBones[i], dst, subIndex, depth + 1, index);
|
||||||
|
}
|
||||||
|
return subIndex - index;
|
||||||
|
}
|
||||||
|
|
||||||
|
model::Model& VcmModel::squash() {
|
||||||
|
std::vector<glm::vec3> fullOffsets(skeleton->getBones().size());
|
||||||
|
calc_offsets(*skeleton->getRoot(), fullOffsets, 0, 0, 0);
|
||||||
|
|
||||||
|
Model squashed;
|
||||||
|
for (auto& [name, model] : parts) {
|
||||||
|
if (auto bone = skeleton->find(name)) {
|
||||||
|
model.translate(fullOffsets[bone->getIndex()]);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("invalid state: bones/parts mismatch");
|
||||||
|
}
|
||||||
|
squashed.merge(std::move(model));
|
||||||
|
}
|
||||||
|
parts = { {"", std::move(squashed)} };
|
||||||
|
skeleton.reset();
|
||||||
|
return parts.at("");
|
||||||
|
}
|
||||||
|
|
||||||
|
model::Model VcmModel::squashed() const {
|
||||||
|
return std::move(VcmModel {parts, std::nullopt}).squash();
|
||||||
|
}
|
||||||
|
|
||||||
static const std::unordered_map<std::string, int> side_indices {
|
static const std::unordered_map<std::string, int> side_indices {
|
||||||
{"north", 0},
|
{"north", 0},
|
||||||
|
|
@ -26,7 +67,97 @@ static bool to_boolean(const xml::Attribute& attr) {
|
||||||
return attr.getText() != "off";
|
return attr.getText() != "off";
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perform_rect(const xmlelement& root, model::Model& model) {
|
class ModelBuilder {
|
||||||
|
public:
|
||||||
|
ModelBuilder(Model& model) : model(model) {}
|
||||||
|
|
||||||
|
void push(const glm::mat4& matrix) {
|
||||||
|
matrices.push_back(matrix);
|
||||||
|
calculateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop() {
|
||||||
|
matrices.pop_back();
|
||||||
|
calculateMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
const glm::mat4& getTransform() const {
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addBox(
|
||||||
|
const std::string& texture,
|
||||||
|
bool shading,
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& size,
|
||||||
|
const UVRegion (&uvs)[6],
|
||||||
|
const bool enabledSides[6]
|
||||||
|
) {
|
||||||
|
auto& mesh = model.addMesh(texture, shading);
|
||||||
|
mesh.addBox(pos, size, uvs, enabledSides, combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addTriangle(
|
||||||
|
const std::string& texture,
|
||||||
|
bool shading,
|
||||||
|
const glm::vec3& a,
|
||||||
|
const glm::vec3& b,
|
||||||
|
const glm::vec3& c,
|
||||||
|
const glm::vec3& norm,
|
||||||
|
const glm::vec2& uvA,
|
||||||
|
const glm::vec2& uvB,
|
||||||
|
const glm::vec2& uvC
|
||||||
|
) {
|
||||||
|
auto& mesh = model.addMesh(texture, shading);
|
||||||
|
mesh.addTriangle(
|
||||||
|
combined * glm::vec4(a, 1.0f),
|
||||||
|
combined * glm::vec4(b, 1.0f),
|
||||||
|
combined * glm::vec4(c, 1.0f),
|
||||||
|
norm,
|
||||||
|
uvA,
|
||||||
|
uvB,
|
||||||
|
uvC
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addRect(
|
||||||
|
const std::string& texture,
|
||||||
|
bool shading,
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& right,
|
||||||
|
const glm::vec3& up,
|
||||||
|
const glm::vec3& norm,
|
||||||
|
const UVRegion& uv
|
||||||
|
) {
|
||||||
|
auto& mesh = model.addMesh(texture, shading);
|
||||||
|
mesh.addRect(pos, right, up, norm, uv, combined);
|
||||||
|
}
|
||||||
|
|
||||||
|
Model& getModel() {
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
void calculateMatrix() {
|
||||||
|
combined = glm::mat4(1.0f);
|
||||||
|
for (const auto& matrix : matrices) {
|
||||||
|
combined *= matrix;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Model& model;
|
||||||
|
std::vector<glm::mat4> matrices;
|
||||||
|
glm::mat4 combined {1.0f};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Context {
|
||||||
|
VcmModel& vcmModel;
|
||||||
|
std::vector<std::unique_ptr<Bone>>& bones;
|
||||||
|
size_t& boneIndex;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void perform_element(const xmlelement& root, ModelBuilder& builder, Context& ctx);
|
||||||
|
|
||||||
|
static void perform_rect(const xmlelement& root, ModelBuilder& builder) {
|
||||||
auto from = root.attr("from").asVec3();
|
auto from = root.attr("from").asVec3();
|
||||||
auto right = root.attr("right").asVec3();
|
auto right = root.attr("right").asVec3();
|
||||||
auto up = root.attr("up").asVec3();
|
auto up = root.attr("up").asVec3();
|
||||||
|
|
@ -60,10 +191,11 @@ static void perform_rect(const xmlelement& root, model::Model& model) {
|
||||||
from -= up;
|
from -= up;
|
||||||
}
|
}
|
||||||
std::string texture = root.attr("texture", "$0").getText();
|
std::string texture = root.attr("texture", "$0").getText();
|
||||||
auto& mesh = model.addMesh(texture, shading);
|
|
||||||
|
|
||||||
auto normal = glm::cross(glm::normalize(right), glm::normalize(up));
|
auto normal = glm::cross(glm::normalize(right), glm::normalize(up));
|
||||||
mesh.addRect(
|
builder.addRect(
|
||||||
|
texture,
|
||||||
|
shading,
|
||||||
from + right * 0.5f + up * 0.5f,
|
from + right * 0.5f + up * 0.5f,
|
||||||
right * 0.5f,
|
right * 0.5f,
|
||||||
up * 0.5f,
|
up * 0.5f,
|
||||||
|
|
@ -72,7 +204,7 @@ static void perform_rect(const xmlelement& root, model::Model& model) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perform_triangle(const xmlelement& root, model::Model& model) {
|
static void perform_triangle(const xmlelement& root, ModelBuilder& builder) {
|
||||||
auto pointA = root.attr("a").asVec3();
|
auto pointA = root.attr("a").asVec3();
|
||||||
auto pointB = root.attr("b").asVec3();
|
auto pointB = root.attr("b").asVec3();
|
||||||
auto pointC = root.attr("c").asVec3();
|
auto pointC = root.attr("c").asVec3();
|
||||||
|
|
@ -104,11 +236,10 @@ static void perform_triangle(const xmlelement& root, model::Model& model) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string texture = root.attr("texture", "$0").getText();
|
std::string texture = root.attr("texture", "$0").getText();
|
||||||
auto& mesh = model.addMesh(texture, shading);
|
builder.addTriangle(texture, shading, pointA, pointB, pointC, normal, uvs[0], uvs[1], uvs[2]);
|
||||||
mesh.addTriangle(pointA, pointB, pointC, normal, uvs[0], uvs[1], uvs[2]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void perform_box(const xmlelement& root, model::Model& model) {
|
static void perform_box(const xmlelement& root, ModelBuilder& builder) {
|
||||||
auto from = root.attr("from").asVec3();
|
auto from = root.attr("from").asVec3();
|
||||||
auto to = root.attr("to").asVec3();
|
auto to = root.attr("to").asVec3();
|
||||||
|
|
||||||
|
|
@ -186,7 +317,7 @@ static void perform_box(const xmlelement& root, model::Model& model) {
|
||||||
|
|
||||||
bool deleted[6] {};
|
bool deleted[6] {};
|
||||||
if (root.has("delete")) {
|
if (root.has("delete")) {
|
||||||
// todo: replace by expression parsing
|
// todo: replace with expression parsing
|
||||||
auto names = util::split(root.attr("delete").getText(), ',');
|
auto names = util::split(root.attr("delete").getText(), ',');
|
||||||
for (auto& name : names) {
|
for (auto& name : names) {
|
||||||
util::trim(name);
|
util::trim(name);
|
||||||
|
|
@ -197,36 +328,106 @@ static void perform_box(const xmlelement& root, model::Model& model) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder.push(tsf);
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
if (deleted[i]) {
|
if (deleted[i]) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
bool enabled[6] {};
|
bool enabled[6] {};
|
||||||
enabled[i] = true;
|
enabled[i] = true;
|
||||||
auto& mesh = model.addMesh(texfaces[i], shading);
|
|
||||||
mesh.addBox(center, halfsize, regions, enabled, tsf);
|
builder.addBox(texfaces[i], shading, center, halfsize, regions, enabled);
|
||||||
}
|
}
|
||||||
|
builder.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<model::Model> load_model(const xmlelement& root) {
|
static void perform_bone(const xmlelement& root, ModelBuilder& builder, Context& ctx) {
|
||||||
model::Model model;
|
std::string name = root.attr("name", "").getText();
|
||||||
|
|
||||||
for (const auto& elem : root.getElements()) {
|
glm::mat4 tsf(1.0f);
|
||||||
auto tag = elem->getTag();
|
if (root.has("move")) {
|
||||||
|
tsf = glm::translate(tsf, root.attr("move").asVec3());
|
||||||
if (tag == "rect") {
|
}
|
||||||
perform_rect(*elem, model);
|
if (root.has("rotate")) {
|
||||||
} else if (tag == "box") {
|
auto text = root.attr("rotate").getText();
|
||||||
perform_box(*elem, model);
|
if (std::count(text.begin(), text.end(), ',') == 3) {
|
||||||
} else if (tag == "tri") {
|
auto quat = root.attr("rotate").asVec4();
|
||||||
perform_triangle(*elem, model);
|
tsf *= glm::mat4_cast(glm::quat(quat.w, quat.x, quat.y, quat.z));
|
||||||
|
} else {
|
||||||
|
auto rot = root.attr("rotate").asVec3();
|
||||||
|
tsf = glm::rotate(tsf, glm::radians(rot.x), glm::vec3(1, 0, 0));
|
||||||
|
tsf = glm::rotate(tsf, glm::radians(rot.y), glm::vec3(0, 1, 0));
|
||||||
|
tsf = glm::rotate(tsf, glm::radians(rot.z), glm::vec3(0, 0, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (root.has("scale")) {
|
||||||
|
tsf = glm::scale(tsf, root.attr("scale").asVec3());
|
||||||
|
}
|
||||||
|
|
||||||
return std::make_unique<model::Model>(std::move(model));
|
if (name.empty()) {
|
||||||
|
builder.push(std::move(tsf));
|
||||||
|
for (const auto& elem : root.getElements()) {
|
||||||
|
perform_element(*elem, builder, ctx);
|
||||||
|
}
|
||||||
|
builder.pop();
|
||||||
|
} else {
|
||||||
|
glm::vec3 origin = builder.getTransform() * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
size_t boneIndex = ctx.boneIndex++;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<Bone>> bones;
|
||||||
|
Context boneContext {ctx.vcmModel, bones, ctx.boneIndex};
|
||||||
|
Model boneModel;
|
||||||
|
ModelBuilder boneModelBuilder(boneModel);
|
||||||
|
boneModelBuilder.push(std::move(tsf));
|
||||||
|
for (const auto& elem : root.getElements()) {
|
||||||
|
perform_element(*elem, boneModelBuilder, boneContext);
|
||||||
|
}
|
||||||
|
ctx.bones.push_back(std::make_unique<Bone>(
|
||||||
|
boneIndex, name, name, std::move(bones), std::move(origin)
|
||||||
|
));
|
||||||
|
ctx.vcmModel.parts[std::move(name)] = std::move(boneModel);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<model::Model> vcm::parse(
|
static void perform_element(const xmlelement& root, ModelBuilder& builder, Context& ctx) {
|
||||||
|
auto tag = root.getTag();
|
||||||
|
|
||||||
|
if (tag == "rect") {
|
||||||
|
perform_rect(root, builder);
|
||||||
|
} else if (tag == "box") {
|
||||||
|
perform_box(root, builder);
|
||||||
|
} else if (tag == "tri") {
|
||||||
|
perform_triangle(root, builder);
|
||||||
|
} else if (tag == "bone") {
|
||||||
|
perform_bone(root, builder, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VcmModel load_model(const xmlelement& root) {
|
||||||
|
VcmModel vcmModel {};
|
||||||
|
Model model;
|
||||||
|
ModelBuilder builder(model);
|
||||||
|
|
||||||
|
size_t boneIndex = 1;
|
||||||
|
std::vector<std::unique_ptr<Bone>> bones;
|
||||||
|
Context ctx { vcmModel, bones, boneIndex };
|
||||||
|
|
||||||
|
for (const auto& elem : root.getElements()) {
|
||||||
|
perform_element(*elem, builder, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
vcmModel.parts["root"] = std::move(model);
|
||||||
|
vcmModel.skeleton = SkeletonConfig(
|
||||||
|
"",
|
||||||
|
std::make_unique<Bone>(
|
||||||
|
0, "root", "root", std::move(bones), glm::vec3(0.0f)
|
||||||
|
),
|
||||||
|
boneIndex
|
||||||
|
);
|
||||||
|
return vcmModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
VcmModel vcm::parse(
|
||||||
std::string_view file, std::string_view src, bool usexml
|
std::string_view file, std::string_view src, bool usexml
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,26 @@
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace model {
|
#include "graphics/commons/Model.hpp"
|
||||||
struct Model;
|
#include "objects/rigging.hpp"
|
||||||
|
|
||||||
|
namespace rigging {
|
||||||
|
class SkeletonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace vcm {
|
namespace vcm {
|
||||||
std::unique_ptr<model::Model> parse(
|
struct VcmModel {
|
||||||
|
std::unordered_map<std::string, model::Model> parts;
|
||||||
|
std::optional<rigging::SkeletonConfig> skeleton;
|
||||||
|
|
||||||
|
model::Model& squash();
|
||||||
|
model::Model squashed() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
VcmModel parse(
|
||||||
std::string_view file, std::string_view src, bool usexml
|
std::string_view file, std::string_view src, bool usexml
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,11 @@ public:
|
||||||
void parseSubElements(Node& node) {
|
void parseSubElements(Node& node) {
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
while (hasNext()) {
|
while (hasNext()) {
|
||||||
if (peek() != '@') {
|
char c = peek();
|
||||||
|
if (c == '}') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c != '@') {
|
||||||
throw error("unexpected character in element");
|
throw error("unexpected character in element");
|
||||||
}
|
}
|
||||||
nextChar();
|
nextChar();
|
||||||
|
|
@ -415,14 +419,12 @@ public:
|
||||||
if (!hasNext()) {
|
if (!hasNext()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
char c = peek();
|
c = peek();
|
||||||
if (c == '{') {
|
if (c == '{') {
|
||||||
nextChar();
|
nextChar();
|
||||||
parseSubElements(*subnode);
|
parseSubElements(*subnode);
|
||||||
expect('}');
|
expect('}');
|
||||||
skipWhitespace();
|
skipWhitespace();
|
||||||
} else if (c == '}') {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
#include "items/ItemDef.hpp"
|
#include "items/ItemDef.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
#include "objects/EntityDef.hpp"
|
#include "objects/EntityDef.hpp"
|
||||||
#include "objects/rigging.hpp"
|
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "world/generator/VoxelFragment.hpp"
|
#include "world/generator/VoxelFragment.hpp"
|
||||||
#include "world/generator/GeneratorDef.hpp"
|
#include "world/generator/GeneratorDef.hpp"
|
||||||
|
|
@ -33,7 +32,6 @@ Content::Content(
|
||||||
ContentUnitDefs<GeneratorDef> generators,
|
ContentUnitDefs<GeneratorDef> generators,
|
||||||
UptrsMap<std::string, ContentPackRuntime> packs,
|
UptrsMap<std::string, ContentPackRuntime> packs,
|
||||||
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
|
||||||
ResourceIndicesSet resourceIndices,
|
ResourceIndicesSet resourceIndices,
|
||||||
dv::value defaults,
|
dv::value defaults,
|
||||||
std::unordered_map<std::string, int> tags
|
std::unordered_map<std::string, int> tags
|
||||||
|
|
@ -41,7 +39,6 @@ Content::Content(
|
||||||
: indices(std::move(indices)),
|
: indices(std::move(indices)),
|
||||||
packs(std::move(packs)),
|
packs(std::move(packs)),
|
||||||
blockMaterials(std::move(blockMaterials)),
|
blockMaterials(std::move(blockMaterials)),
|
||||||
skeletons(std::move(skeletons)),
|
|
||||||
defaults(std::move(defaults)),
|
defaults(std::move(defaults)),
|
||||||
tags(std::move(tags)),
|
tags(std::move(tags)),
|
||||||
blocks(std::move(blocks)),
|
blocks(std::move(blocks)),
|
||||||
|
|
@ -56,23 +53,6 @@ Content::Content(
|
||||||
|
|
||||||
Content::~Content() = default;
|
Content::~Content() = default;
|
||||||
|
|
||||||
const rigging::SkeletonConfig* Content::getSkeleton(const std::string& id
|
|
||||||
) const {
|
|
||||||
auto found = skeletons.find(id);
|
|
||||||
if (found == skeletons.end()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return found->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
const rigging::SkeletonConfig& Content::requireSkeleton(const std::string& id) const {
|
|
||||||
auto skeleton = getSkeleton(id);
|
|
||||||
if (skeleton == nullptr) {
|
|
||||||
throw std::runtime_error("skeleton '" + id + "' not loaded");
|
|
||||||
}
|
|
||||||
return *skeleton;
|
|
||||||
}
|
|
||||||
|
|
||||||
const BlockMaterial* Content::findBlockMaterial(const std::string& id) const {
|
const BlockMaterial* Content::findBlockMaterial(const std::string& id) const {
|
||||||
auto found = blockMaterials.find(id);
|
auto found = blockMaterials.find(id);
|
||||||
if (found == blockMaterials.end()) {
|
if (found == blockMaterials.end()) {
|
||||||
|
|
@ -104,8 +84,3 @@ const UptrsMap<std::string, BlockMaterial>& Content::getBlockMaterials() const {
|
||||||
const UptrsMap<std::string, ContentPackRuntime>& Content::getPacks() const {
|
const UptrsMap<std::string, ContentPackRuntime>& Content::getPacks() const {
|
||||||
return packs;
|
return packs;
|
||||||
}
|
}
|
||||||
|
|
||||||
const UptrsMap<std::string, rigging::SkeletonConfig>& Content::getSkeletons(
|
|
||||||
) const {
|
|
||||||
return skeletons;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -20,10 +20,6 @@ struct ItemDef;
|
||||||
struct EntityDef;
|
struct EntityDef;
|
||||||
struct GeneratorDef;
|
struct GeneratorDef;
|
||||||
|
|
||||||
namespace rigging {
|
|
||||||
class SkeletonConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
class namereuse_error : public std::runtime_error {
|
class namereuse_error : public std::runtime_error {
|
||||||
ContentType type;
|
ContentType type;
|
||||||
public:
|
public:
|
||||||
|
|
@ -183,7 +179,6 @@ class Content {
|
||||||
std::unique_ptr<ContentIndices> indices;
|
std::unique_ptr<ContentIndices> indices;
|
||||||
UptrsMap<std::string, ContentPackRuntime> packs;
|
UptrsMap<std::string, ContentPackRuntime> packs;
|
||||||
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
|
||||||
dv::value defaults = nullptr;
|
dv::value defaults = nullptr;
|
||||||
std::unordered_map<std::string, int> tags;
|
std::unordered_map<std::string, int> tags;
|
||||||
public:
|
public:
|
||||||
|
|
@ -203,7 +198,6 @@ public:
|
||||||
ContentUnitDefs<GeneratorDef> generators,
|
ContentUnitDefs<GeneratorDef> generators,
|
||||||
UptrsMap<std::string, ContentPackRuntime> packs,
|
UptrsMap<std::string, ContentPackRuntime> packs,
|
||||||
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
UptrsMap<std::string, BlockMaterial> blockMaterials,
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons,
|
|
||||||
ResourceIndicesSet resourceIndices,
|
ResourceIndicesSet resourceIndices,
|
||||||
dv::value defaults,
|
dv::value defaults,
|
||||||
std::unordered_map<std::string, int> tags
|
std::unordered_map<std::string, int> tags
|
||||||
|
|
@ -230,13 +224,10 @@ public:
|
||||||
return found->second;
|
return found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
const rigging::SkeletonConfig* getSkeleton(const std::string& id) const;
|
|
||||||
const rigging::SkeletonConfig& requireSkeleton(const std::string& id) const;
|
|
||||||
const BlockMaterial* findBlockMaterial(const std::string& id) const;
|
const BlockMaterial* findBlockMaterial(const std::string& id) const;
|
||||||
const ContentPackRuntime* getPackRuntime(const std::string& id) const;
|
const ContentPackRuntime* getPackRuntime(const std::string& id) const;
|
||||||
ContentPackRuntime* getPackRuntime(const std::string& id);
|
ContentPackRuntime* getPackRuntime(const std::string& id);
|
||||||
|
|
||||||
const UptrsMap<std::string, BlockMaterial>& getBlockMaterials() const;
|
const UptrsMap<std::string, BlockMaterial>& getBlockMaterials() const;
|
||||||
const UptrsMap<std::string, ContentPackRuntime>& getPacks() const;
|
const UptrsMap<std::string, ContentPackRuntime>& getPacks() const;
|
||||||
const UptrsMap<std::string, rigging::SkeletonConfig>& getSkeletons() const;
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,11 @@
|
||||||
#include "ContentBuilder.hpp"
|
#include "ContentBuilder.hpp"
|
||||||
|
|
||||||
#include "objects/rigging.hpp"
|
|
||||||
|
|
||||||
ContentBuilder::~ContentBuilder() = default;
|
ContentBuilder::~ContentBuilder() = default;
|
||||||
|
|
||||||
void ContentBuilder::add(std::unique_ptr<ContentPackRuntime> pack) {
|
void ContentBuilder::add(std::unique_ptr<ContentPackRuntime> pack) {
|
||||||
packs[pack->getId()] = std::move(pack);
|
packs[pack->getId()] = std::move(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ContentBuilder::add(std::unique_ptr<rigging::SkeletonConfig> skeleton) {
|
|
||||||
skeletons[skeleton->getName()] = std::move(skeleton);
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockMaterial& ContentBuilder::createBlockMaterial(const std::string& id) {
|
BlockMaterial& ContentBuilder::createBlockMaterial(const std::string& id) {
|
||||||
blockMaterials[id] = std::make_unique<BlockMaterial>();
|
blockMaterials[id] = std::make_unique<BlockMaterial>();
|
||||||
auto& material = *blockMaterials[id];
|
auto& material = *blockMaterials[id];
|
||||||
|
|
@ -98,7 +92,6 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
||||||
generators.build(),
|
generators.build(),
|
||||||
std::move(packs),
|
std::move(packs),
|
||||||
std::move(blockMaterials),
|
std::move(blockMaterials),
|
||||||
std::move(skeletons),
|
|
||||||
std::move(resourceIndices),
|
std::move(resourceIndices),
|
||||||
std::move(defaults),
|
std::move(defaults),
|
||||||
std::move(tags.map)
|
std::move(tags.map)
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,6 @@ struct TagsIndices {
|
||||||
|
|
||||||
class ContentBuilder {
|
class ContentBuilder {
|
||||||
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
UptrsMap<std::string, BlockMaterial> blockMaterials;
|
||||||
UptrsMap<std::string, rigging::SkeletonConfig> skeletons;
|
|
||||||
UptrsMap<std::string, ContentPackRuntime> packs;
|
UptrsMap<std::string, ContentPackRuntime> packs;
|
||||||
std::unordered_map<std::string, ContentType> allNames;
|
std::unordered_map<std::string, ContentType> allNames;
|
||||||
public:
|
public:
|
||||||
|
|
@ -100,7 +99,6 @@ public:
|
||||||
~ContentBuilder();
|
~ContentBuilder();
|
||||||
|
|
||||||
void add(std::unique_ptr<ContentPackRuntime> pack);
|
void add(std::unique_ptr<ContentPackRuntime> pack);
|
||||||
void add(std::unique_ptr<rigging::SkeletonConfig> skeleton);
|
|
||||||
|
|
||||||
BlockMaterial& createBlockMaterial(const std::string& id);
|
BlockMaterial& createBlockMaterial(const std::string& id);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,12 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include "loading/ContentUnitLoader.hpp"
|
#include "loading/ContentUnitLoader.hpp"
|
||||||
#include "ContentBuilder.hpp"
|
#include "ContentBuilder.hpp"
|
||||||
#include "ContentPack.hpp"
|
#include "ContentPack.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
#include "objects/rigging.hpp"
|
|
||||||
#include "util/listutil.hpp"
|
#include "util/listutil.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "engine/EnginePaths.hpp"
|
#include "engine/EnginePaths.hpp"
|
||||||
|
|
@ -399,16 +397,6 @@ void ContentLoader::load() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load skeletons
|
|
||||||
io::path skeletonsDir = folder / "skeletons";
|
|
||||||
foreach_file(skeletonsDir, [this](const io::path& file) {
|
|
||||||
std::string name = pack->id + ":" + file.stem();
|
|
||||||
std::string text = io::read_string(file);
|
|
||||||
builder.add(
|
|
||||||
rigging::SkeletonConfig::parse(text, file.string(), name)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Process content.json and load defined content units
|
// Process content.json and load defined content units
|
||||||
auto contentFile = pack->getContentFile();
|
auto contentFile = pack->getContentFile();
|
||||||
if (io::exists(contentFile)) {
|
if (io::exists(contentFile)) {
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
#include "logic/scripting/scripting_hud.hpp"
|
#include "logic/scripting/scripting_hud.hpp"
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
|
#include "objects/Entities.hpp"
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
#include "voxels/Chunks.hpp"
|
#include "voxels/Chunks.hpp"
|
||||||
|
|
@ -49,6 +50,7 @@ LevelScreen::LevelScreen(
|
||||||
gui(engine.getGUI()),
|
gui(engine.getGUI()),
|
||||||
input(engine.getInput()) {
|
input(engine.getInput()) {
|
||||||
Level* level = levelPtr.get();
|
Level* level = levelPtr.get();
|
||||||
|
level->entities->setAssets(*engine.getAssets());
|
||||||
|
|
||||||
auto& settings = engine.getSettings();
|
auto& settings = engine.getSettings();
|
||||||
auto& assets = *engine.getAssets();
|
auto& assets = *engine.getAssets();
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
|
||||||
#include "Model.hpp"
|
#include "Model.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
#include <glm/gtx/matrix_decompose.hpp>
|
||||||
|
|
||||||
using namespace model;
|
using namespace model;
|
||||||
|
|
||||||
|
|
@ -71,6 +75,39 @@ void Mesh::addRect(
|
||||||
vertices.push_back({pos-right+up, {uv.u1, uv.v2}, norm});
|
vertices.push_back({pos-right+up, {uv.u1, uv.v2}, norm});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static glm::mat4 extract_rotation(const glm::mat4& matrix) {
|
||||||
|
glm::vec3 scale;
|
||||||
|
glm::quat rotation;
|
||||||
|
glm::vec3 translation;
|
||||||
|
glm::vec3 skew;
|
||||||
|
glm::vec4 perspective;
|
||||||
|
glm::decompose(matrix, scale, rotation, translation, skew, perspective);
|
||||||
|
return glm::mat4_cast(rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::addRect(
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& right,
|
||||||
|
const glm::vec3& up,
|
||||||
|
const glm::vec3& norm,
|
||||||
|
const UVRegion& uv,
|
||||||
|
const glm::mat4& transform
|
||||||
|
) {
|
||||||
|
auto a = transform * glm::vec4(pos - right - up, 1.0f);
|
||||||
|
auto b = transform * glm::vec4(pos + right - up, 1.0f);
|
||||||
|
auto c = transform * glm::vec4(pos + right + up, 1.0f);
|
||||||
|
auto d = transform * glm::vec4(pos - right + up, 1.0f);
|
||||||
|
auto tsfnorm = extract_rotation(transform) * glm::vec4(norm, 1.0f);
|
||||||
|
|
||||||
|
vertices.push_back({a, {uv.u1, uv.v1}, tsfnorm});
|
||||||
|
vertices.push_back({b, {uv.u2, uv.v1}, tsfnorm});
|
||||||
|
vertices.push_back({c, {uv.u2, uv.v2}, tsfnorm});
|
||||||
|
|
||||||
|
vertices.push_back({a, {uv.u1, uv.v1}, tsfnorm});
|
||||||
|
vertices.push_back({c, {uv.u2, uv.v2}, tsfnorm});
|
||||||
|
vertices.push_back({d, {uv.u1, uv.v2}, tsfnorm});
|
||||||
|
}
|
||||||
|
|
||||||
void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) {
|
void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) {
|
||||||
UVRegion fullRegion (0, 0, 1, 1);
|
UVRegion fullRegion (0, 0, 1, 1);
|
||||||
addPlane(pos+Z*size, X*size, Y*size, Z, fullRegion);
|
addPlane(pos+Z*size, X*size, Y*size, Z, fullRegion);
|
||||||
|
|
@ -119,6 +156,18 @@ void Mesh::scale(const glm::vec3& size) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mesh::transform(const glm::mat4& matrix) {
|
||||||
|
for (auto& vertex : vertices) {
|
||||||
|
vertex.coord = matrix * glm::vec4(vertex.coord, 1.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::translate(const glm::vec3& offset) {
|
||||||
|
for (auto& vertex : vertices) {
|
||||||
|
vertex.coord += offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Model::clean() {
|
void Model::clean() {
|
||||||
meshes.erase(
|
meshes.erase(
|
||||||
std::remove_if(meshes.begin(), meshes.end(),
|
std::remove_if(meshes.begin(), meshes.end(),
|
||||||
|
|
@ -128,3 +177,41 @@ void Model::clean() {
|
||||||
meshes.end()
|
meshes.end()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Model::transform(const glm::mat4& matrix) {
|
||||||
|
for (auto& mesh : meshes) {
|
||||||
|
mesh.transform(matrix);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::translate(const glm::vec3& offset) {
|
||||||
|
for (auto& mesh : meshes) {
|
||||||
|
mesh.translate(offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::merge(const Model& source) {
|
||||||
|
for (const auto& srcMesh : source.meshes) {
|
||||||
|
auto& dstMesh = addMesh(srcMesh.texture, srcMesh.shading);
|
||||||
|
dstMesh.vertices.insert(
|
||||||
|
dstMesh.vertices.end(),
|
||||||
|
srcMesh.vertices.begin(),
|
||||||
|
srcMesh.vertices.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Model::merge(Model&& source) {
|
||||||
|
for (auto& srcMesh : source.meshes) {
|
||||||
|
auto& dstMesh = addMesh(srcMesh.texture, srcMesh.shading);
|
||||||
|
if (dstMesh.vertices.empty()) {
|
||||||
|
dstMesh = std::move(srcMesh);
|
||||||
|
} else {
|
||||||
|
dstMesh.vertices.insert(
|
||||||
|
dstMesh.vertices.end(),
|
||||||
|
srcMesh.vertices.begin(),
|
||||||
|
srcMesh.vertices.end()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,14 @@ namespace model {
|
||||||
const glm::vec3& norm,
|
const glm::vec3& norm,
|
||||||
const UVRegion& region
|
const UVRegion& region
|
||||||
);
|
);
|
||||||
|
void addRect(
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& right,
|
||||||
|
const glm::vec3& up,
|
||||||
|
const glm::vec3& norm,
|
||||||
|
const UVRegion& uv,
|
||||||
|
const glm::mat4& transform
|
||||||
|
);
|
||||||
void addTriangle(
|
void addTriangle(
|
||||||
const glm::vec3& a,
|
const glm::vec3& a,
|
||||||
const glm::vec3& b,
|
const glm::vec3& b,
|
||||||
|
|
@ -64,6 +72,12 @@ namespace model {
|
||||||
const glm::mat4& transform
|
const glm::mat4& transform
|
||||||
);
|
);
|
||||||
void scale(const glm::vec3& size);
|
void scale(const glm::vec3& size);
|
||||||
|
|
||||||
|
/// @brief Apply transformation matrix to vertices coordinates
|
||||||
|
void transform(const glm::mat4& matrix);
|
||||||
|
|
||||||
|
/// @brief Move vertices coordinates by offset
|
||||||
|
void translate(const glm::vec3& offset);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Model {
|
struct Model {
|
||||||
|
|
@ -81,7 +95,22 @@ namespace model {
|
||||||
meshes.push_back({texture, {}, shading});
|
meshes.push_back({texture, {}, shading});
|
||||||
return meshes[meshes.size() - 1];
|
return meshes[meshes.size() - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @brief Remove all empty meshes
|
/// @brief Remove all empty meshes
|
||||||
void clean();
|
void clean();
|
||||||
|
|
||||||
|
/// @brief Apply transformation matrix to vertices coordinates
|
||||||
|
void transform(const glm::mat4& matrix);
|
||||||
|
|
||||||
|
/// @brief Move vertices coordinates by offset
|
||||||
|
void translate(const glm::vec3& offset);
|
||||||
|
|
||||||
|
/// @brief Copy or merge meshes from source model to this.
|
||||||
|
/// Does not remove duplicates
|
||||||
|
void merge(const Model& source);
|
||||||
|
|
||||||
|
/// @brief Move or merge meshes from source model to this.
|
||||||
|
/// Does not remove duplicates
|
||||||
|
void merge(Model&& source);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include "maths/voxmaths.hpp"
|
#include "maths/voxmaths.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
|
#include "objects/rigging.hpp"
|
||||||
#include "settings.hpp"
|
#include "settings.hpp"
|
||||||
#include "voxels/Block.hpp"
|
#include "voxels/Block.hpp"
|
||||||
#include "voxels/Chunk.hpp"
|
#include "voxels/Chunk.hpp"
|
||||||
|
|
@ -110,7 +111,7 @@ WorldRenderer::WorldRenderer(
|
||||||
|
|
||||||
const auto& content = level.content;
|
const auto& content = level.content;
|
||||||
skeletons = std::make_unique<NamedSkeletons>();
|
skeletons = std::make_unique<NamedSkeletons>();
|
||||||
const auto& skeletonConfig = content.requireSkeleton(
|
const auto& skeletonConfig = assets->require<rigging::SkeletonConfig>(
|
||||||
content.getDefaults()["hand-skeleton"].asString()
|
content.getDefaults()["hand-skeleton"].asString()
|
||||||
);
|
);
|
||||||
hands = std::make_unique<HandsRenderer>(
|
hands = std::make_unique<HandsRenderer>(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "objects/rigging.hpp"
|
#include "objects/rigging.hpp"
|
||||||
#include "libentity.hpp"
|
#include "libentity.hpp"
|
||||||
|
|
||||||
|
#include "engine/Engine.hpp"
|
||||||
#include "graphics/render/WorldRenderer.hpp"
|
#include "graphics/render/WorldRenderer.hpp"
|
||||||
#include "graphics/render/NamedSkeletons.hpp"
|
#include "graphics/render/NamedSkeletons.hpp"
|
||||||
|
|
||||||
|
|
@ -21,11 +22,14 @@ static int index_range_check(
|
||||||
}
|
}
|
||||||
|
|
||||||
static rigging::Skeleton* get_skeleton(lua::State* L) {
|
static rigging::Skeleton* get_skeleton(lua::State* L) {
|
||||||
|
if (scripting::engine->isHeadless()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
if (lua::isstring(L, 1)) {
|
if (lua::isstring(L, 1)) {
|
||||||
return scripting::renderer->skeletons->getSkeleton(lua::tostring(L, 1));
|
return scripting::renderer->skeletons->getSkeleton(lua::tostring(L, 1));
|
||||||
}
|
}
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
return &entity->getSkeleton();
|
return entity->getSkeleton();
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -57,11 +57,14 @@ static int l_parse_model(lua::State* L) {
|
||||||
auto name = lua::require_string(L, 3);
|
auto name = lua::require_string(L, 3);
|
||||||
|
|
||||||
if (format == "xml" || format == "vcm") {
|
if (format == "xml" || format == "vcm") {
|
||||||
|
auto vcmModel = vcm::parse(name, string, format == "xml");
|
||||||
engine->getAssets()->store(
|
engine->getAssets()->store(
|
||||||
vcm::parse(name, string, format == "xml"), name
|
std::make_unique<model::Model>(std::move(vcmModel.squash())), name
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
throw std::runtime_error("unknown format " + util::quote(std::string(format)));
|
throw std::runtime_error(
|
||||||
|
"unknown format " + util::quote(std::string(format))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "libentity.hpp"
|
#include "libentity.hpp"
|
||||||
|
|
||||||
|
#include "assets/Assets.hpp"
|
||||||
#include "content/Content.hpp"
|
#include "content/Content.hpp"
|
||||||
#include "content/ContentPack.hpp"
|
#include "content/ContentPack.hpp"
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
|
|
@ -82,7 +83,11 @@ static int l_despawn(lua::State* L) {
|
||||||
|
|
||||||
static int l_get_skeleton(lua::State* L) {
|
static int l_get_skeleton(lua::State* L) {
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
return lua::pushstring(L, entity->getSkeleton().config->getName());
|
auto skeleton = entity->getSkeleton();
|
||||||
|
if (skeleton == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return lua::pushstring(L, skeleton->config->getName());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -97,9 +102,13 @@ static int l_get_player(lua::State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_set_skeleton(lua::State* L) {
|
static int l_set_skeleton(lua::State* L) {
|
||||||
|
auto assets = engine->getAssets();
|
||||||
|
if (assets == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (auto entity = get_entity(L, 1)) {
|
if (auto entity = get_entity(L, 1)) {
|
||||||
std::string skeletonName = lua::require_string(L, 2);
|
std::string skeletonName = lua::require_string(L, 2);
|
||||||
auto rigConfig = content->getSkeleton(skeletonName);
|
auto rigConfig = assets->get<rigging::SkeletonConfig>(skeletonName);
|
||||||
if (rigConfig == nullptr) {
|
if (rigConfig == nullptr) {
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"skeleton not found '" + skeletonName + "'"
|
"skeleton not found '" + skeletonName + "'"
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#define VC_ENABLE_REFLECTION
|
#define VC_ENABLE_REFLECTION
|
||||||
#include "Entities.hpp"
|
#include "Entities.hpp"
|
||||||
|
|
||||||
|
#include <entt/entity/registry.hpp>
|
||||||
#include <glm/ext/matrix_transform.hpp>
|
#include <glm/ext/matrix_transform.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
|
@ -25,15 +26,22 @@
|
||||||
static debug::Logger logger("entities");
|
static debug::Logger logger("entities");
|
||||||
|
|
||||||
Entities::Entities(Level& level)
|
Entities::Entities(Level& level)
|
||||||
: level(level),
|
: registry(std::make_unique<entt::registry>()),
|
||||||
|
level(level),
|
||||||
sensorsTickClock(20, 3),
|
sensorsTickClock(20, 3),
|
||||||
updateTickClock(20, 3) {
|
updateTickClock(20, 3) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Entities::~Entities() = default;
|
||||||
|
|
||||||
|
void Entities::setAssets(Assets& assets) {
|
||||||
|
this->assets = &assets;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<Entity> Entities::get(entityid_t id) {
|
std::optional<Entity> Entities::get(entityid_t id) {
|
||||||
const auto& found = entities.find(id);
|
const auto& found = entities.find(id);
|
||||||
if (found != entities.end() && registry.valid(found->second)) {
|
if (found != entities.end() && registry->valid(found->second)) {
|
||||||
return Entity(*this, id, registry, found->second);
|
return Entity(*this, id, *registry, found->second);
|
||||||
}
|
}
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
@ -45,9 +53,12 @@ entityid_t Entities::spawn(
|
||||||
dv::value saved,
|
dv::value saved,
|
||||||
entityid_t uid
|
entityid_t uid
|
||||||
) {
|
) {
|
||||||
auto skeleton = level.content.getSkeleton(def.skeletonName);
|
rigging::SkeletonConfig* skeleton = nullptr;
|
||||||
if (skeleton == nullptr) {
|
if (assets) {
|
||||||
throw std::runtime_error("skeleton " + def.skeletonName + " not found");
|
skeleton = assets->get<rigging::SkeletonConfig>(def.skeletonName);
|
||||||
|
if (skeleton == nullptr) {
|
||||||
|
throw std::runtime_error("skeleton " + def.skeletonName + " not found");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
entityid_t id;
|
entityid_t id;
|
||||||
if (uid == 0) {
|
if (uid == 0) {
|
||||||
|
|
@ -64,12 +75,12 @@ entityid_t Entities::spawn(
|
||||||
throw std::runtime_error(ss.str());
|
throw std::runtime_error(ss.str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto entity = registry.create();
|
auto entity = registry->create();
|
||||||
entities[id] = entity;
|
entities[id] = entity;
|
||||||
uids[entity] = id;
|
uids[entity] = id;
|
||||||
|
|
||||||
registry.emplace<EntityId>(entity, static_cast<entityid_t>(id), def);
|
registry->emplace<EntityId>(entity, static_cast<entityid_t>(id), def);
|
||||||
const auto& tsf = registry.emplace<Transform>(
|
const auto& tsf = registry->emplace<Transform>(
|
||||||
entity,
|
entity,
|
||||||
position,
|
position,
|
||||||
glm::vec3(1.0f),
|
glm::vec3(1.0f),
|
||||||
|
|
@ -77,7 +88,7 @@ entityid_t Entities::spawn(
|
||||||
glm::mat4(1.0f),
|
glm::mat4(1.0f),
|
||||||
true
|
true
|
||||||
);
|
);
|
||||||
auto& body = registry.emplace<Rigidbody>(
|
auto& body = registry->emplace<Rigidbody>(
|
||||||
entity,
|
entity,
|
||||||
true,
|
true,
|
||||||
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
|
Hitbox {def.bodyType, position, def.hitbox * 0.5f},
|
||||||
|
|
@ -85,8 +96,10 @@ entityid_t Entities::spawn(
|
||||||
);
|
);
|
||||||
body.initialize(def, id, *this);
|
body.initialize(def, id, *this);
|
||||||
|
|
||||||
auto& scripting = registry.emplace<ScriptComponents>(entity);
|
auto& scripting = registry->emplace<ScriptComponents>(entity);
|
||||||
registry.emplace<rigging::Skeleton>(entity, skeleton->instance());
|
if (assets) {
|
||||||
|
registry->emplace<rigging::Skeleton>(entity, skeleton->instance());
|
||||||
|
}
|
||||||
|
|
||||||
for (auto& instance : def.components) {
|
for (auto& instance : def.components) {
|
||||||
auto component = std::make_unique<UserComponent>(
|
auto component = std::make_unique<UserComponent>(
|
||||||
|
|
@ -126,7 +139,7 @@ void Entities::loadEntity(const dv::value& map) {
|
||||||
void Entities::loadEntity(const dv::value& map, Entity entity) {
|
void Entities::loadEntity(const dv::value& map, Entity entity) {
|
||||||
auto& transform = entity.getTransform();
|
auto& transform = entity.getTransform();
|
||||||
auto& body = entity.getRigidbody();
|
auto& body = entity.getRigidbody();
|
||||||
auto& skeleton = entity.getSkeleton();
|
auto skeleton = entity.getSkeleton();
|
||||||
|
|
||||||
if (map.has(COMP_RIGIDBODY)) {
|
if (map.has(COMP_RIGIDBODY)) {
|
||||||
body.deserialize(map[COMP_RIGIDBODY]);
|
body.deserialize(map[COMP_RIGIDBODY]);
|
||||||
|
|
@ -134,13 +147,16 @@ void Entities::loadEntity(const dv::value& map, Entity entity) {
|
||||||
if (map.has(COMP_TRANSFORM)) {
|
if (map.has(COMP_TRANSFORM)) {
|
||||||
transform.deserialize(map[COMP_TRANSFORM]);
|
transform.deserialize(map[COMP_TRANSFORM]);
|
||||||
}
|
}
|
||||||
std::string skeletonName = skeleton.config->getName();
|
if (skeleton == nullptr) {
|
||||||
map.at("skeleton").get(skeletonName);
|
return;
|
||||||
if (skeletonName != skeleton.config->getName()) {
|
}
|
||||||
skeleton.config = level.content.getSkeleton(skeletonName);
|
std::string skeletonName = skeleton->config->getName();
|
||||||
|
map.at("skeleton-name").get(skeletonName);
|
||||||
|
if (skeletonName != skeleton->config->getName()) {
|
||||||
|
skeleton->config = assets->get<rigging::SkeletonConfig>(skeletonName);
|
||||||
}
|
}
|
||||||
if (auto foundSkeleton = map.at(COMP_SKELETON)) {
|
if (auto foundSkeleton = map.at(COMP_SKELETON)) {
|
||||||
skeleton.deserialize(*foundSkeleton);
|
skeleton->deserialize(*foundSkeleton);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -148,7 +164,7 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
||||||
glm::vec3 start, glm::vec3 dir, float maxDistance, entityid_t ignore
|
glm::vec3 start, glm::vec3 dir, float maxDistance, entityid_t ignore
|
||||||
) {
|
) {
|
||||||
Ray ray(start, dir);
|
Ray ray(start, dir);
|
||||||
auto view = registry.view<EntityId, Transform, Rigidbody>();
|
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||||
|
|
||||||
entityid_t foundUID = 0;
|
entityid_t foundUID = 0;
|
||||||
glm::ivec3 foundNormal;
|
glm::ivec3 foundNormal;
|
||||||
|
|
@ -214,17 +230,17 @@ void Entities::despawn(std::vector<Entity> entities) {
|
||||||
|
|
||||||
void Entities::clean() {
|
void Entities::clean() {
|
||||||
for (auto it = entities.begin(); it != entities.end();) {
|
for (auto it = entities.begin(); it != entities.end();) {
|
||||||
if (!registry.get<EntityId>(it->second).destroyFlag) {
|
if (!registry->get<EntityId>(it->second).destroyFlag) {
|
||||||
++it;
|
++it;
|
||||||
} else {
|
} else {
|
||||||
auto& rigidbody = registry.get<Rigidbody>(it->second);
|
auto& rigidbody = registry->get<Rigidbody>(it->second);
|
||||||
// todo: refactor
|
// todo: refactor
|
||||||
auto physics = level.physics.get();
|
auto physics = level.physics.get();
|
||||||
for (auto& sensor : rigidbody.sensors) {
|
for (auto& sensor : rigidbody.sensors) {
|
||||||
physics->removeSensor(&sensor);
|
physics->removeSensor(&sensor);
|
||||||
}
|
}
|
||||||
uids.erase(it->second);
|
uids.erase(it->second);
|
||||||
registry.destroy(it->second);
|
registry->destroy(it->second);
|
||||||
it = entities.erase(it);
|
it = entities.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -266,7 +282,7 @@ void Entities::preparePhysics(float delta) {
|
||||||
auto part = sensorsTickClock.getPart();
|
auto part = sensorsTickClock.getPart();
|
||||||
auto parts = sensorsTickClock.getParts();
|
auto parts = sensorsTickClock.getParts();
|
||||||
|
|
||||||
auto view = registry.view<EntityId, Transform, Rigidbody>();
|
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||||
auto physics = level.physics.get();
|
auto physics = level.physics.get();
|
||||||
std::vector<Sensor*> sensors;
|
std::vector<Sensor*> sensors;
|
||||||
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
||||||
|
|
@ -285,7 +301,7 @@ void Entities::preparePhysics(float delta) {
|
||||||
void Entities::updatePhysics(float delta) {
|
void Entities::updatePhysics(float delta) {
|
||||||
preparePhysics(delta);
|
preparePhysics(delta);
|
||||||
|
|
||||||
auto view = registry.view<EntityId, Transform, Rigidbody>();
|
auto view = registry->view<EntityId, Transform, Rigidbody>();
|
||||||
auto physics = level.physics.get();
|
auto physics = level.physics.get();
|
||||||
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
for (auto [entity, eid, transform, rigidbody] : view.each()) {
|
||||||
if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) {
|
if (!rigidbody.enabled || rigidbody.hitbox.type == BodyType::STATIC) {
|
||||||
|
|
@ -333,7 +349,7 @@ static void debug_render_skeleton(
|
||||||
const rigging::Skeleton& skeleton
|
const rigging::Skeleton& skeleton
|
||||||
) {
|
) {
|
||||||
size_t pindex = bone->getIndex();
|
size_t pindex = bone->getIndex();
|
||||||
for (auto& sub : bone->getSubnodes()) {
|
for (auto& sub : bone->getBones()) {
|
||||||
size_t sindex = sub->getIndex();
|
size_t sindex = sub->getIndex();
|
||||||
const auto& matrices = skeleton.calculated.matrices;
|
const auto& matrices = skeleton.calculated.matrices;
|
||||||
batch.line(
|
batch.line(
|
||||||
|
|
@ -351,7 +367,7 @@ void Entities::renderDebug(
|
||||||
{
|
{
|
||||||
auto ctx = pctx.sub(&batch);
|
auto ctx = pctx.sub(&batch);
|
||||||
ctx.setLineWidth(1);
|
ctx.setLineWidth(1);
|
||||||
auto view = registry.view<Transform, Rigidbody>();
|
auto view = registry->view<Transform, Rigidbody>();
|
||||||
for (auto [entity, transform, rigidbody] : view.each()) {
|
for (auto [entity, transform, rigidbody] : view.each()) {
|
||||||
const auto& hitbox = rigidbody.hitbox;
|
const auto& hitbox = rigidbody.hitbox;
|
||||||
const auto& pos = transform.pos;
|
const auto& pos = transform.pos;
|
||||||
|
|
@ -374,7 +390,7 @@ void Entities::renderDebug(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
auto view = registry.view<Transform, rigging::Skeleton>();
|
auto view = registry->view<Transform, rigging::Skeleton>();
|
||||||
auto ctx = pctx.sub(&batch);
|
auto ctx = pctx.sub(&batch);
|
||||||
ctx.setDepthTest(false);
|
ctx.setDepthTest(false);
|
||||||
ctx.setDepthMask(false);
|
ctx.setDepthMask(false);
|
||||||
|
|
@ -400,7 +416,7 @@ void Entities::render(
|
||||||
bool pause,
|
bool pause,
|
||||||
entityid_t fpsEntity
|
entityid_t fpsEntity
|
||||||
) {
|
) {
|
||||||
auto view = registry.view<EntityId, Transform, rigging::Skeleton>();
|
auto view = registry->view<EntityId, Transform, rigging::Skeleton>();
|
||||||
for (auto [entity, eid, transform, skeleton] : view.each()) {
|
for (auto [entity, eid, transform, skeleton] : view.each()) {
|
||||||
if (eid.uid == fpsEntity) {
|
if (eid.uid == fpsEntity) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -425,7 +441,7 @@ void Entities::render(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Entities::hasBlockingInside(AABB aabb) {
|
bool Entities::hasBlockingInside(AABB aabb) {
|
||||||
auto view = registry.view<EntityId, Rigidbody>();
|
auto view = registry->view<EntityId, Rigidbody>();
|
||||||
for (auto [entity, eid, body] : view.each()) {
|
for (auto [entity, eid, body] : view.each()) {
|
||||||
if (eid.def.blocking && aabb.intersect(body.hitbox.getAABB(), -0.05f)) {
|
if (eid.def.blocking && aabb.intersect(body.hitbox.getAABB(), -0.05f)) {
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -436,7 +452,7 @@ bool Entities::hasBlockingInside(AABB aabb) {
|
||||||
|
|
||||||
std::vector<Entity> Entities::getAllInside(AABB aabb) {
|
std::vector<Entity> Entities::getAllInside(AABB aabb) {
|
||||||
std::vector<Entity> collected;
|
std::vector<Entity> collected;
|
||||||
auto view = registry.view<EntityId, Transform>();
|
auto view = registry->view<EntityId, Transform>();
|
||||||
for (auto [entity, eid, transform] : view.each()) {
|
for (auto [entity, eid, transform] : view.each()) {
|
||||||
if (!eid.destroyFlag && aabb.contains(transform.pos)) {
|
if (!eid.destroyFlag && aabb.contains(transform.pos)) {
|
||||||
const auto& found = uids.find(entity);
|
const auto& found = uids.find(entity);
|
||||||
|
|
@ -453,7 +469,7 @@ std::vector<Entity> Entities::getAllInside(AABB aabb) {
|
||||||
|
|
||||||
std::vector<Entity> Entities::getAllInRadius(glm::vec3 center, float radius) {
|
std::vector<Entity> Entities::getAllInRadius(glm::vec3 center, float radius) {
|
||||||
std::vector<Entity> collected;
|
std::vector<Entity> collected;
|
||||||
auto view = registry.view<Transform>();
|
auto view = registry->view<Transform>();
|
||||||
for (auto [entity, transform] : view.each()) {
|
for (auto [entity, transform] : view.each()) {
|
||||||
if (glm::distance2(transform.pos, center) <= radius * radius) {
|
if (glm::distance2(transform.pos, center) <= radius * radius) {
|
||||||
const auto& found = uids.find(entity);
|
const auto& found = uids.find(entity);
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/Clock.hpp"
|
#include "util/Clock.hpp"
|
||||||
|
|
||||||
#include <entt/entity/registry.hpp>
|
#include <entt/entity/fwd.hpp>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
struct EntityDef;
|
struct EntityDef;
|
||||||
|
|
@ -31,14 +31,15 @@ namespace rigging {
|
||||||
class SkeletonConfig;
|
class SkeletonConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Entities {
|
class Entities final {
|
||||||
entt::registry registry;
|
std::unique_ptr<entt::registry> registry;
|
||||||
Level& level;
|
Level& level;
|
||||||
std::unordered_map<entityid_t, entt::entity> entities;
|
std::unordered_map<entityid_t, entt::entity> entities;
|
||||||
std::unordered_map<entt::entity, entityid_t> uids;
|
std::unordered_map<entt::entity, entityid_t> uids;
|
||||||
entityid_t nextID = 1;
|
entityid_t nextID = 1;
|
||||||
util::Clock sensorsTickClock;
|
util::Clock sensorsTickClock;
|
||||||
util::Clock updateTickClock;
|
util::Clock updateTickClock;
|
||||||
|
Assets* assets = nullptr;
|
||||||
|
|
||||||
void updateSensors(
|
void updateSensors(
|
||||||
Rigidbody& body, const Transform& tsf, std::vector<Sensor*>& sensors
|
Rigidbody& body, const Transform& tsf, std::vector<Sensor*>& sensors
|
||||||
|
|
@ -53,6 +54,10 @@ public:
|
||||||
|
|
||||||
Entities(Level& level);
|
Entities(Level& level);
|
||||||
|
|
||||||
|
~Entities();
|
||||||
|
|
||||||
|
void setAssets(Assets& assets);
|
||||||
|
|
||||||
void clean();
|
void clean();
|
||||||
void updatePhysics(float delta);
|
void updatePhysics(float delta);
|
||||||
void update(float delta);
|
void update(float delta);
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,16 @@
|
||||||
static inline std::string SAVED_DATA_VARNAME = "SAVED_DATA";
|
static inline std::string SAVED_DATA_VARNAME = "SAVED_DATA";
|
||||||
|
|
||||||
void Entity::setInterpolatedPosition(const glm::vec3& position) {
|
void Entity::setInterpolatedPosition(const glm::vec3& position) {
|
||||||
getSkeleton().interpolation.refresh(position);
|
if (auto skeleton = getSkeleton()) {
|
||||||
|
skeleton->interpolation.refresh(position);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 Entity::getInterpolatedPosition() const {
|
glm::vec3 Entity::getInterpolatedPosition() const {
|
||||||
const auto& skeleton = getSkeleton();
|
if (auto skeleton = getSkeleton()) {
|
||||||
if (skeleton.interpolation.isEnabled()) {
|
if (skeleton->interpolation.isEnabled()) {
|
||||||
return skeleton.interpolation.getCurrent();
|
return skeleton->interpolation.getCurrent();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return getTransform().pos;
|
return getTransform().pos;
|
||||||
}
|
}
|
||||||
|
|
@ -30,8 +33,8 @@ void Entity::destroy() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rigging::Skeleton& Entity::getSkeleton() const {
|
rigging::Skeleton* Entity::getSkeleton() const {
|
||||||
return registry.get<rigging::Skeleton>(entity);
|
return registry.try_get<rigging::Skeleton>(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Entity::setRig(const rigging::SkeletonConfig* rigConfig) {
|
void Entity::setRig(const rigging::SkeletonConfig* rigConfig) {
|
||||||
|
|
@ -50,8 +53,8 @@ dv::value Entity::serialize() const {
|
||||||
const auto& def = eid.def;
|
const auto& def = eid.def;
|
||||||
const auto& transform = getTransform();
|
const auto& transform = getTransform();
|
||||||
const auto& rigidbody = getRigidbody();
|
const auto& rigidbody = getRigidbody();
|
||||||
const auto& skeleton = getSkeleton();
|
|
||||||
const auto& scripts = getScripting();
|
const auto& scripts = getScripting();
|
||||||
|
auto skeleton = getSkeleton();
|
||||||
|
|
||||||
auto root = dv::object();
|
auto root = dv::object();
|
||||||
root["def"] = def.name;
|
root["def"] = def.name;
|
||||||
|
|
@ -61,13 +64,15 @@ dv::value Entity::serialize() const {
|
||||||
root[COMP_RIGIDBODY] =
|
root[COMP_RIGIDBODY] =
|
||||||
rigidbody.serialize(def.save.body.velocity, def.save.body.settings);
|
rigidbody.serialize(def.save.body.velocity, def.save.body.settings);
|
||||||
|
|
||||||
if (skeleton.config->getName() != def.skeletonName) {
|
if (skeleton != nullptr) {
|
||||||
root["skeleton"] = skeleton.config->getName();
|
if (skeleton->config->getName() != def.skeletonName) {
|
||||||
}
|
root["skeleton-name"] = skeleton->config->getName();
|
||||||
if (def.save.skeleton.pose || def.save.skeleton.textures) {
|
}
|
||||||
root[COMP_SKELETON] = skeleton.serialize(
|
if (def.save.skeleton.pose || def.save.skeleton.textures) {
|
||||||
def.save.skeleton.pose, def.save.skeleton.textures
|
root[COMP_SKELETON] = skeleton->serialize(
|
||||||
);
|
def.save.skeleton.pose, def.save.skeleton.textures
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!scripts.components.empty()) {
|
if (!scripts.components.empty()) {
|
||||||
auto& compsMap = root.object("comps");
|
auto& compsMap = root.object("comps");
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ public:
|
||||||
|
|
||||||
ScriptComponents& getScripting() const;
|
ScriptComponents& getScripting() const;
|
||||||
|
|
||||||
rigging::Skeleton& getSkeleton() const;
|
rigging::Skeleton* getSkeleton() const;
|
||||||
|
|
||||||
void setRig(const rigging::SkeletonConfig* rigConfig);
|
void setRig(const rigging::SkeletonConfig* rigConfig);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ void Skeleton::deserialize(const dv::value& root) {
|
||||||
|
|
||||||
static void get_all_nodes(std::vector<Bone*>& nodes, Bone* node) {
|
static void get_all_nodes(std::vector<Bone*>& nodes, Bone* node) {
|
||||||
nodes[node->getIndex()] = node;
|
nodes[node->getIndex()] = node;
|
||||||
for (auto& subnode : node->getSubnodes()) {
|
for (auto& subnode : node->getBones()) {
|
||||||
get_all_nodes(nodes, subnode.get());
|
get_all_nodes(nodes, subnode.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -111,7 +111,7 @@ size_t SkeletonConfig::update(
|
||||||
}
|
}
|
||||||
skeleton.calculated.matrices[index] = matrix * baseMatrix * boneMatrix;
|
skeleton.calculated.matrices[index] = matrix * baseMatrix * boneMatrix;
|
||||||
size_t count = 1;
|
size_t count = 1;
|
||||||
for (auto& subnode : node->getSubnodes()) {
|
for (auto& subnode : node->getBones()) {
|
||||||
count += update(
|
count += update(
|
||||||
index + count,
|
index + count,
|
||||||
skeleton,
|
skeleton,
|
||||||
|
|
@ -187,7 +187,7 @@ void SkeletonConfig::render(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Bone* SkeletonConfig::find(std::string_view str) const {
|
const Bone* SkeletonConfig::find(std::string_view str) const {
|
||||||
for (size_t i = 0; i < nodes.size(); i++) {
|
for (size_t i = 0; i < nodes.size(); i++) {
|
||||||
auto* node = nodes[i];
|
auto* node = nodes[i];
|
||||||
if (node->getName() == str) {
|
if (node->getName() == str) {
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ namespace rigging {
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto& getSubnodes() const {
|
const auto& getBones() const {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -138,7 +138,7 @@ namespace rigging {
|
||||||
return Skeleton(this);
|
return Skeleton(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
Bone* find(std::string_view str) const;
|
const Bone* find(std::string_view str) const;
|
||||||
|
|
||||||
static std::unique_ptr<SkeletonConfig> parse(
|
static std::unique_ptr<SkeletonConfig> parse(
|
||||||
std::string_view src, std::string_view file, std::string_view name
|
std::string_view src, std::string_view file, std::string_view name
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,9 @@ static void on_chunk_register_event(
|
||||||
if ((bits & 0x80) == 0) {
|
if ((bits & 0x80) == 0) {
|
||||||
const auto& def = indices.blocks.require(id);
|
const auto& def = indices.blocks.require(id);
|
||||||
bits = get_events_bits(def);
|
bits = get_events_bits(def);
|
||||||
flagsCache[id] = bits | 0x80;
|
if (id < sizeof(flagsCache)) {
|
||||||
|
flagsCache[id] = bits | 0x80;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
bits &= 0x7F;
|
bits &= 0x7F;
|
||||||
if (bits == 0) {
|
if (bits == 0) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue