stk-code_catmod/lib/graphics_engine/include/ge_animation.hpp
2022-07-25 10:54:27 +08:00

267 lines
9.3 KiB
C++

/* ==========================================================================
* Copyright (c) 2022 SuperTuxKart-Team
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* ==========================================================================
*/
#ifndef HEADER_GE_ANIMATION_HPP
#define HEADER_GE_ANIMATION_HPP
#include <IReadFile.h>
#include <matrix4.h>
#include <quaternion.h>
#include <cassert>
#include <cstdint>
#include <cstdlib>
#include <vector>
#include <string>
using namespace irr;
namespace GE
{
struct LocRotScale
{
core::vector3df m_loc;
core::quaternion m_rot;
core::vector3df m_scale;
// ------------------------------------------------------------------------
inline core::matrix4 toMatrix() const
{
core::matrix4 lm, sm, rm;
lm.setTranslation(m_loc);
sm.setScale(m_scale);
m_rot.getMatrix(rm);
return lm * rm * sm;
}
// ------------------------------------------------------------------------
void read(irr::io::IReadFile* spm)
{
float tmp[10];
spm->read(&tmp, 40);
m_loc = core::vector3df(tmp[0], tmp[1], tmp[2]);
m_rot = core::quaternion(tmp[3], tmp[4], tmp[5], tmp[6]);
m_rot.normalize();
m_scale = core::vector3df(tmp[7], tmp[8], tmp[9]);
}
};
struct Armature
{
unsigned m_joint_used;
std::vector<std::string> m_joint_names;
std::vector<core::matrix4> m_joint_matrices;
std::vector<LocRotScale> m_interpolated_matrices;
std::vector<std::pair<core::matrix4, bool> > m_world_matrices;
std::vector<int> m_parent_infos;
std::vector<std::pair<int, std::vector<LocRotScale> > >
m_frame_pose_matrices;
// ------------------------------------------------------------------------
void read(irr::io::IReadFile* spm)
{
LocRotScale lrs;
spm->read(&m_joint_used, 2);
assert(m_joint_used > 0);
unsigned all_joints_size = 0;
spm->read(&all_joints_size, 2);
assert(all_joints_size > 0);
m_joint_names.resize(all_joints_size);
for (unsigned i = 0; i < all_joints_size; i++)
{
unsigned str_len = 0;
spm->read(&str_len, 1);
m_joint_names[i].resize(str_len);
spm->read(&m_joint_names[i].front(), str_len);
}
m_joint_matrices.resize(all_joints_size);
m_interpolated_matrices.resize(all_joints_size);
for (unsigned i = 0; i < all_joints_size; i++)
{
lrs.read(spm);
m_joint_matrices[i] = lrs.toMatrix();
}
m_world_matrices.resize(m_interpolated_matrices.size(),
std::make_pair(core::matrix4(), false));
m_parent_infos.resize(all_joints_size);
bool non_parent_bone = false;
for (unsigned i = 0; i < all_joints_size; i++)
{
int16_t info = 0;
spm->read(&info, 2);
if (info == -1)
{
non_parent_bone = true;
}
m_parent_infos[i] = info;
}
if (!non_parent_bone)
{
printf("SPMeshLoader::Armature: Non-parent bone missing in armature");
exit(-1);
}
unsigned frame_size = 0;
spm->read(&frame_size, 2);
m_frame_pose_matrices.resize(frame_size);
for (unsigned i = 0; i < frame_size; i++)
{
m_frame_pose_matrices[i].second.resize(all_joints_size);
unsigned frame_index = 0;
spm->read(&frame_index, 2);
m_frame_pose_matrices[i].first = frame_index;
for (unsigned j = 0; j < m_frame_pose_matrices[i].second.size(); j++)
{
m_frame_pose_matrices[i].second[j].read(spm);
}
}
}
// ------------------------------------------------------------------------
void getPose(float frame, core::matrix4* dest,
float frame_interpolating = -1.0f, float rate = -1.0f)
{
getInterpolatedMatrices(frame);
if (frame_interpolating != -1.0f && rate != -1.0f)
{
auto copied = m_interpolated_matrices;
getInterpolatedMatrices(frame_interpolating);
for (unsigned i = 0; i < m_interpolated_matrices.size(); i++)
{
m_interpolated_matrices[i].m_loc =
copied[i].m_loc.getInterpolated(
m_interpolated_matrices[i].m_loc, rate);
m_interpolated_matrices[i].m_rot =
m_interpolated_matrices[i].m_rot.slerp(
m_interpolated_matrices[i].m_rot, copied[i].m_rot, rate);
m_interpolated_matrices[i].m_scale =
copied[i].m_scale.getInterpolated(
m_interpolated_matrices[i].m_scale, rate);
}
}
for (auto& p : m_world_matrices)
{
p.second = false;
}
for (unsigned i = 0; i < m_joint_used; i++)
{
dest[i] = getWorldMatrix(m_interpolated_matrices, i) *
m_joint_matrices[i];
}
}
// ------------------------------------------------------------------------
void getPose(core::matrix4* dest, float frame)
{
getInterpolatedMatrices(frame);
for (auto& p : m_world_matrices)
{
p.second = false;
}
for (unsigned i = 0; i < m_joint_used; i++)
{
dest[i] = getWorldMatrix(m_interpolated_matrices, i) *
m_joint_matrices[i];
}
}
// ------------------------------------------------------------------------
void getInterpolatedMatrices(float frame)
{
if (frame < float(m_frame_pose_matrices.front().first) ||
frame >= float(m_frame_pose_matrices.back().first))
{
for (unsigned i = 0; i < m_interpolated_matrices.size(); i++)
{
m_interpolated_matrices[i] =
frame >= float(m_frame_pose_matrices.back().first) ?
m_frame_pose_matrices.back().second[i] :
m_frame_pose_matrices.front().second[i];
}
return;
}
int frame_1 = -1;
int frame_2 = -1;
float interpolation = 0.0f;
for (unsigned i = 0; i < m_frame_pose_matrices.size(); i++)
{
assert(i + 1 < m_frame_pose_matrices.size());
if (frame >= float(m_frame_pose_matrices[i].first) &&
frame < float(m_frame_pose_matrices[i + 1].first))
{
frame_1 = i;
frame_2 = i + 1;
interpolation =
(frame - float(m_frame_pose_matrices[i].first)) /
float(m_frame_pose_matrices[i + 1].first -
m_frame_pose_matrices[i].first);
break;
}
}
assert(frame_1 != -1);
assert(frame_2 != -1);
for (unsigned i = 0; i < m_interpolated_matrices.size(); i++)
{
LocRotScale interpolated;
interpolated.m_loc =
m_frame_pose_matrices[frame_2].second[i].m_loc.getInterpolated
(m_frame_pose_matrices[frame_1].second[i].m_loc, interpolation);
interpolated.m_rot.slerp
(m_frame_pose_matrices[frame_1].second[i].m_rot,
m_frame_pose_matrices[frame_2].second[i].m_rot, interpolation);
interpolated.m_scale =
m_frame_pose_matrices[frame_2].second[i].m_scale.getInterpolated
(m_frame_pose_matrices[frame_1].second[i].m_scale, interpolation);
m_interpolated_matrices[i] = interpolated;
}
}
// ------------------------------------------------------------------------
core::matrix4 getWorldMatrix(const std::vector<LocRotScale>& lrs,
unsigned id)
{
core::matrix4 mat = lrs[id].toMatrix();
int parent_id = m_parent_infos[id];
if (parent_id == -1)
{
m_world_matrices[id] = std::make_pair(mat, true);
return mat;
}
if (!m_world_matrices[parent_id].second)
{
m_world_matrices[parent_id] = std::make_pair
(getWorldMatrix(lrs, parent_id), true);
}
m_world_matrices[id] =
std::make_pair(m_world_matrices[parent_id].first * mat, true);
return m_world_matrices[id].first;
}
};
}
#endif