Files
VoxelEngine/src/objects/rigging.cpp
T

235 lines
6.4 KiB
C++

#include "rigging.hpp"
#include <glm/ext/matrix_transform.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include "assets/Assets.hpp"
#include "coders/json.hpp"
#include "data/dv_util.hpp"
#include "graphics/commons/Model.hpp"
#include "graphics/render/ModelBatch.hpp"
using namespace rigging;
void ModelReference::refresh(const Assets& assets) {
if (updateFlag) {
model = assets.get<model::Model>(name);
updateFlag = false;
}
}
Bone::Bone(
size_t index,
std::string name,
std::string model,
std::vector<std::unique_ptr<Bone>> bones,
const glm::vec3& offset
)
: index(index),
name(std::move(name)),
bones(std::move(bones)),
offset(offset),
model({model, nullptr, true}) {
}
void Bone::setModel(const std::string& name) {
if (model.name == name) {
return;
}
model = {name, nullptr, true};
}
Skeleton::Skeleton(const SkeletonConfig* config)
: config(config),
pose(config->getBones().size()),
calculated(config->getBones().size()),
flags(config->getBones().size()),
textures(),
modelOverrides(config->getBones().size()),
visible(true) {
const auto& bones = config->getBones();
for (size_t i = 0; i < bones.size(); i++) {
flags[i].visible = true;
}
}
dv::value Skeleton::serialize(bool saveTextures, bool savePose) const {
auto root = dv::object();
if (saveTextures) {
auto& map = root.object("textures");
for (auto& [slot, texture] : textures) {
map[slot] = texture;
}
}
if (savePose) {
auto& list = root.list("pose");
for (auto& mat : pose.matrices) {
list.add(dv::to_value(mat));
}
}
return root;
}
void Skeleton::deserialize(const dv::value& root) {
if (auto found = root.at("textures")) {
auto& texturesmap = *found;
for (auto& [slot, _] : texturesmap.asObject()) {
texturesmap.at(slot).get(textures[slot]);
}
}
if (auto found = root.at("pose")) {
auto& posearr = *found;
auto& matrices = pose.matrices;
for (size_t i = 0; i < std::min(matrices.size(), posearr.size()); i++) {
dv::get_mat(posearr[i], pose.matrices[i]);
}
}
}
static void get_all_nodes(std::vector<Bone*>& nodes, Bone* node) {
nodes[node->getIndex()] = node;
for (auto& subnode : node->getSubnodes()) {
get_all_nodes(nodes, subnode.get());
}
}
SkeletonConfig::SkeletonConfig(
const std::string& name, std::unique_ptr<Bone> root, size_t nodesCount
)
: name(name), root(std::move(root)), nodes(nodesCount) {
get_all_nodes(nodes, this->root.get());
}
size_t SkeletonConfig::update(
size_t index, Skeleton& skeleton, Bone* node, const glm::mat4& matrix
) const {
auto boneMatrix = skeleton.pose.matrices[index];
auto boneOffset = node->getOffset();
glm::mat4 baseMatrix(1.0f);
if (glm::length2(boneOffset) > 0.0f) {
baseMatrix = glm::translate(glm::mat4(1.0f), boneOffset);
}
skeleton.calculated.matrices[index] = matrix * baseMatrix * boneMatrix;
size_t count = 1;
for (auto& subnode : node->getSubnodes()) {
count += update(
index + count,
skeleton,
subnode.get(),
skeleton.calculated.matrices[index]
);
}
return count;
}
static glm::mat4 build_matrix(
const glm::mat3& rot, const glm::vec3& pos, const glm::vec3& scale
) {
glm::mat4 combined(1.0f);
combined = glm::translate(combined, pos);
combined = combined * glm::mat4(rot);
combined = glm::scale(combined, scale);
return combined;
}
void SkeletonConfig::update(
Skeleton& skeleton,
const glm::mat3& rotation,
const glm::vec3& position,
const glm::vec3& scale
) const {
if (skeleton.interpolation.isEnabled()) {
const auto& interpolation = skeleton.interpolation;
update(
0,
skeleton,
root.get(),
build_matrix(rotation, interpolation.getCurrent(), scale)
);
} else {
update(0, skeleton, root.get(), build_matrix(rotation, position, scale));
}
}
void SkeletonConfig::render(
const Assets& assets,
ModelBatch& batch,
Skeleton& skeleton,
const glm::mat3& rotation,
const glm::vec3& position,
const glm::vec3& scale
) const {
update(skeleton, rotation, position, scale);
if (!skeleton.visible) {
return;
}
for (size_t i = 0; i < nodes.size(); i++) {
auto* node = nodes[i];
if (!skeleton.flags[i].visible) {
continue;
}
node->model.refresh(assets);
auto model = node->model.model;
auto& modelOverride = skeleton.modelOverrides.at(i);
if (modelOverride.updateFlag) {
modelOverride.refresh(assets);
}
model = modelOverride.model ? modelOverride.model : model;
if (model) {
batch.draw(
skeleton.calculated.matrices[i],
skeleton.tint,
model,
&skeleton.textures
);
}
}
}
Bone* SkeletonConfig::find(std::string_view str) const {
for (size_t i = 0; i < nodes.size(); i++) {
auto* node = nodes[i];
if (node->getName() == str) {
return node;
}
}
return nullptr;
}
static std::tuple<size_t, std::unique_ptr<Bone>> read_node(
const dv::value& root, size_t index
) {
std::string name;
std::string model;
root.at("name").get(name);
root.at("model").get(model);
glm::vec3 offset(0.0f);
dv::get_vec(root, "offset", offset);
std::vector<std::unique_ptr<Bone>> bones;
size_t count = 1;
if (auto found = root.at("nodes")) {
const auto& nodesList = *found;
for (const auto& map : nodesList) {
auto [subcount, subNode] = read_node(map, index + count);
count += subcount;
bones.push_back(std::move(subNode));
}
}
return {
count,
std::make_unique<Bone>(index, name, model, std::move(bones), offset)};
}
std::unique_ptr<SkeletonConfig> SkeletonConfig::parse(
std::string_view src, std::string_view file, std::string_view name
) {
auto root = json::parse(file, src);
const auto& rootNodeMap = root["root"];
auto [count, rootNode] = read_node(rootNodeMap, 0);
return std::make_unique<SkeletonConfig>(
std::string(name), std::move(rootNode), count
);
}