Commit 80ad7d89 authored by Florian Oetke's avatar Florian Oetke
Browse files

enabled pose/skeleton/animation sharing between multiple entities and sub-meshes

parent c41b3356
......@@ -26,7 +26,7 @@ build_gcc:
- build/bin
expire_in: 1 day
cache:
key: "${CI_PIPELINE_ID}-${CI_COMMIT_REF_SLUG}"
key: "${CI_COMMIT_REF_SLUG}"
paths:
- build
......@@ -50,7 +50,7 @@ build_clang:
- build/bin
expire_in: 1 week
cache:
key: "${CI_PIPELINE_ID}-${CI_COMMIT_REF_SLUG}"
key: "${CI_COMMIT_REF_SLUG}"
paths:
- build
......
......@@ -184,3 +184,13 @@ namespace mirrage::ecs {
Freelist _free;
};
} // namespace mirrage::ecs
namespace std {
template <>
struct hash<mirrage::ecs::Entity_handle> {
size_t operator()(mirrage::ecs::Entity_handle handle) const noexcept
{
return static_cast<std::size_t>(handle.pack());
}
};
} // namespace std
......@@ -11,16 +11,26 @@
namespace mirrage::renderer {
class Skeleton_comp : public ecs::Component<Skeleton_comp> {
class Pose_comp : public ecs::Component<Pose_comp> {
public:
static constexpr const char* name() { return "Skeleton"; }
static constexpr const char* name() { return "Pose"; }
Skeleton_comp() = default;
Skeleton_comp(ecs::Entity_handle owner, ecs::Entity_manager& em) : Component(owner, em) {}
Pose_comp() = default;
Pose_comp(ecs::Entity_handle owner, ecs::Entity_manager& em) : Component(owner, em) {}
std::vector<glm::mat4> bone_transforms;
};
class Shared_pose_comp : public ecs::Component<Shared_pose_comp> {
public:
static constexpr const char* name() { return "Shared_pose"; }
Shared_pose_comp() = default;
Shared_pose_comp(ecs::Entity_handle owner, ecs::Entity_manager& em) : Component(owner, em) {}
ecs::Entity_handle pose_owner;
};
class Animation_comp : public ecs::Component<Animation_comp> {
public:
static constexpr const char* name() { return "Animation"; }
......@@ -48,7 +58,8 @@ namespace mirrage::renderer {
std::vector<Animation_key> _animation_keys;
float _time = 0.f;
float _time = 0.f;
bool _dirty = false;
};
} // namespace mirrage::renderer
......@@ -19,7 +19,7 @@ namespace mirrage::renderer {
Deferred_renderer& _renderer;
ecs::Entity_manager& _ecs;
void _update_animation(Animation_comp& anim, Skeleton_comp&);
void _update_animation(Animation_comp& anim, Pose_comp&);
};
class Animation_pass_factory : public Render_pass_factory {
......
......@@ -36,5 +36,8 @@ namespace mirrage::renderer {
util::iter_range<std::vector<Geometry>::iterator> _geometry_range;
util::iter_range<std::vector<Geometry>::iterator> _rigged_geometry_range;
std::unordered_map<ecs::Entity_handle, std::uint32_t> _animation_uniform_offsets;
std::unordered_set<ecs::Entity_handle> _dead_shared_poses;
};
} // namespace mirrage::renderer
......@@ -42,8 +42,6 @@ namespace mirrage::renderer {
const Model* model;
std::uint32_t sub_mesh;
std::uint32_t culling_mask;
std::uint32_t animation_buffer_offset;
// TODO: animation data
Geometry() = default;
Geometry(ecs::Entity_handle entity,
......
......@@ -15,7 +15,7 @@ namespace mirrage::renderer {
Animation_pass::Animation_pass(Deferred_renderer& renderer, ecs::Entity_manager& entities)
: _renderer(renderer), _ecs(entities)
{
_ecs.register_component_type<Skeleton_comp>();
_ecs.register_component_type<Pose_comp>();
_ecs.register_component_type<Animation_comp>();
}
......@@ -24,7 +24,8 @@ namespace mirrage::renderer {
for(auto& anim : _ecs.list<Animation_comp>()) {
// TODO: transitions and stuff
if(anim.animation()) {
anim._time = std::fmod(anim._time + time.value(), anim.animation()->duration());
anim._time = std::fmod(anim._time + time.value(), anim.animation()->duration());
anim._dirty = true;
}
}
}
......@@ -39,16 +40,15 @@ namespace mirrage::renderer {
return; // not animated
auto& anim = anim_mb.get_or_throw();
if(!anim._skeleton || !anim._current_animation)
if(!anim._skeleton || !anim._current_animation || !anim._dirty)
return; // no animation playing
entity.get<Skeleton_comp>().process(
[&](auto& skeleton) { _update_animation(anim, skeleton); });
entity.get<Pose_comp>().process([&](auto& skeleton) { _update_animation(anim, skeleton); });
});
}
}
void Animation_pass::_update_animation(Animation_comp& anim_comp, Skeleton_comp& result)
void Animation_pass::_update_animation(Animation_comp& anim_comp, Pose_comp& result)
{
const auto& skeleton_data = *anim_comp._skeleton;
const auto bone_count = skeleton_data.bone_count();
......@@ -57,6 +57,7 @@ namespace mirrage::renderer {
result.bone_transforms.reserve(std::size_t(bone_count));
result.bone_transforms.clear();
anim_comp._dirty = false;
anim_comp._animation_keys.resize(std::size_t(bone_count), Animation_key{});
for(auto i : util::range(bone_count)) {
......@@ -68,7 +69,6 @@ namespace mirrage::renderer {
auto local = animation.bone_transform(i, anim_comp._time, key).get_or([&] {
return skeleton_data.node_transform(i);
});
//local = skeleton_data.node_transform(i);
result.bone_transforms.emplace_back(parent * local);
}
......@@ -77,10 +77,6 @@ namespace mirrage::renderer {
for(auto i : util::range(bone_count)) {
result.bone_transforms[std::size_t(i)] =
inv_root * result.bone_transforms[std::size_t(i)] * skeleton_data.inv_bind_pose(i);
//LOG(plog::debug) << "Bone[" << i
// << "]: " << glm::to_string(result.bone_transforms[std::size_t(i)]);
//result.bone_transforms[std::size_t(i)] = glm::mat4(1);
}
}
......
......@@ -27,7 +27,8 @@ namespace mirrage::renderer {
, _animation_uniforms(r.device(), initial_animation_capacity, vk::BufferUsageFlagBits::eUniformBuffer)
{
entities.register_component_type<Model_comp>();
entities.register_component_type<Skeleton_comp>();
entities.register_component_type<Pose_comp>();
entities.register_component_type<Shared_pose_comp>();
auto buffers = _animation_uniforms.buffer_count();
......@@ -132,19 +133,45 @@ namespace mirrage::renderer {
_rigged_geometry_range = util::range(rigged_begin, geo_range.end());
// update upload skeleton pose
// upload skeleton pose
_animation_uniform_offsets.clear();
_dead_shared_poses.clear();
auto required_size = std::int32_t(0);
auto alignment = std::int32_t(
_renderer.device().physical_device_properties().limits.minUniformBufferOffsetAlignment);
auto aligned_byte_size = [&](auto bone_count) {
auto size = bone_count * std::int32_t(sizeof(glm::mat4));
return size < alignment ? alignment : size + (alignment - size % alignment);
};
for(auto& geo : _rigged_geometry_range) {
geo.animation_buffer_offset = gsl::narrow<std::uint32_t>(required_size);
auto entity = _ecs.get(geo.entity).get_or_throw("Invalid entity in render queue");
if(!entity.has<Pose_comp>())
continue;
auto size = geo.model->bone_count() * std::int32_t(sizeof(glm::mat4));
auto [_, success] = _animation_uniform_offsets.try_emplace(
geo.entity, gsl::narrow<std::uint32_t>(required_size));
(void) _;
// align
size = size < alignment ? alignment : size + (alignment - size % alignment);
required_size += size;
if(success) {
required_size += aligned_byte_size(geo.model->bone_count());
}
}
for(auto& geo : _rigged_geometry_range) {
auto entity = _ecs.get(geo.entity).get_or_throw("Invalid entity in render queue");
entity.get<Shared_pose_comp>().process([&](auto& sp) {
auto pose_offset = util::find_maybe(_animation_uniform_offsets, sp.pose_owner);
if(pose_offset.is_some()) {
_animation_uniform_offsets.emplace(geo.entity, pose_offset.get_or_throw());
} else {
// TODO: upload default matrices instead
_animation_uniform_offsets.emplace(geo.entity, gsl::narrow<std::uint32_t>(required_size));
required_size += aligned_byte_size(geo.model->bone_count());
_dead_shared_poses.emplace(geo.entity);
}
});
}
if(_animation_uniforms.resize(required_size)) {
......@@ -165,19 +192,19 @@ namespace mirrage::renderer {
_animation_uniforms.update_objects<glm::mat4>(0, [&](auto uniform_matrices) {
for(auto& geo : _rigged_geometry_range) {
auto entity = _ecs.get(geo.entity).get_or_throw("Invalid entity in render queue");
auto skeleton = entity.get<Skeleton_comp>();
auto entity = _ecs.get(geo.entity).get_or_throw("Invalid entity in render queue");
auto pose = entity.get<Pose_comp>();
auto shared_pose = entity.get<Shared_pose_comp>();
auto offset = geo.animation_buffer_offset / std::int32_t(sizeof(glm::mat4));
auto offset = _animation_uniform_offsets.at(geo.entity) / std::int32_t(sizeof(glm::mat4));
auto geo_matrices = uniform_matrices.subspan(offset, geo.model->bone_count());
if(skeleton.is_some()
&& skeleton.get_or_throw().bone_transforms.size()
== std::size_t(geo.model->bone_count())) {
auto& sm = skeleton.get_or_throw().bone_transforms;
if(pose.is_some()
&& pose.get_or_throw().bone_transforms.size() == std::size_t(geo.model->bone_count())) {
auto& sm = pose.get_or_throw().bone_transforms;
std::memcpy(geo_matrices.data(), sm.data(), sm.size() * sizeof(glm::mat4));
} else {
} else if(shared_pose.is_nothing() || _dead_shared_poses.count(geo.entity)) {
std::fill(geo_matrices.begin(), geo_matrices.end(), glm::mat4(1));
}
}
......@@ -255,7 +282,7 @@ namespace mirrage::renderer {
render_pass.bind_descriptor_set(
2,
*_animation_desc_sets.at(std::size_t(_animation_uniforms.read_buffer_index())),
{&geo.animation_buffer_offset, 1u});
{&_animation_uniform_offsets.at(geo.entity), 1u});
frame.main_command_buffer.drawIndexed(sub_mesh.index_count, 1, sub_mesh.index_offset, 0, 0);
}
......
......@@ -23,8 +23,6 @@ namespace mirrage::renderer {
auto is_visible(const Culling_viewer& viewer, glm::vec3 position, float radius)
{
return true; // FIXME: DEBUG ONLY!!! DON'T COMMIT
/*
const auto p = glm::vec4(position, 1.f);
auto result = true;
......@@ -32,7 +30,6 @@ namespace mirrage::renderer {
result = result & (glm::dot(plane, p) > -radius);
return result;
*/
}
auto norm_plane(glm::vec4 p) { return p / glm::length(glm::vec3(p.x, p.y, p.z)); }
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment