113 lines
3.5 KiB
C++
113 lines
3.5 KiB
C++
// Copyright (C) 2023 Jérôme "Lynix" Leclercq (lynix680@gmail.com)
|
|
// This file is part of the "Nazara Engine - Utility module"
|
|
// For conditions of distribution and use, see copyright notice in Config.hpp
|
|
|
|
#include <Nazara/Utility/Formats/MD5AnimLoader.hpp>
|
|
#include <Nazara/Utility/Animation.hpp>
|
|
#include <Nazara/Utility/Sequence.hpp>
|
|
#include <Nazara/Utility/Formats/MD5AnimParser.hpp>
|
|
#include <Nazara/Utility/Debug.hpp>
|
|
|
|
namespace Nz
|
|
{
|
|
namespace
|
|
{
|
|
bool IsMD5AnimSupported(const std::string_view& extension)
|
|
{
|
|
return extension == ".md5anim";
|
|
}
|
|
|
|
Result<std::shared_ptr<Animation>, ResourceLoadingError> LoadMD5Anim(Stream& stream, const AnimationParams& /*parameters*/)
|
|
{
|
|
// TODO: Use parameters
|
|
|
|
MD5AnimParser parser(stream);
|
|
|
|
UInt64 streamPos = stream.GetCursorPos();
|
|
|
|
if (!parser.Check())
|
|
return Err(ResourceLoadingError::Unrecognized);
|
|
|
|
stream.SetCursorPos(streamPos);
|
|
|
|
if (!parser.Parse())
|
|
{
|
|
NazaraError("MD5Anim parser failed");
|
|
return Err(ResourceLoadingError::DecodingError);
|
|
}
|
|
|
|
const MD5AnimParser::Frame* frames = parser.GetFrames();
|
|
UInt32 frameCount = parser.GetFrameCount();
|
|
UInt32 frameRate = parser.GetFrameRate();
|
|
const MD5AnimParser::Joint* joints = parser.GetJoints();
|
|
UInt32 jointCount = parser.GetJointCount();
|
|
|
|
// À ce stade, nous sommes censés avoir assez d'informations pour créer l'animation
|
|
std::shared_ptr<Animation> animation = std::make_shared<Animation>();
|
|
animation->CreateSkeletal(frameCount, jointCount);
|
|
|
|
Sequence sequence;
|
|
sequence.firstFrame = 0;
|
|
sequence.frameCount = frameCount;
|
|
sequence.frameRate = frameRate;
|
|
sequence.name = stream.GetPath().filename().generic_u8string();
|
|
|
|
animation->AddSequence(sequence);
|
|
|
|
SequenceJoint* sequenceJoints = animation->GetSequenceJoints();
|
|
|
|
// Pour que le squelette soit correctement aligné, il faut appliquer un quaternion "de correction" aux joints à la base du squelette
|
|
Quaternionf rotationQuat = Quaternionf::RotationBetween(Vector3f::UnitX(), Vector3f::Forward()) *
|
|
Quaternionf::RotationBetween(Vector3f::UnitZ(), Vector3f::Up());
|
|
|
|
//Matrix4f matrix = Matrix4f::Transform(Nz::Vector3f::Zero(), rotationQuat, Vector3f(1.f / 40.f));
|
|
//matrix *= parameters.matrix;
|
|
|
|
rotationQuat = Quaternionf::Identity();
|
|
|
|
for (UInt32 frameIndex = 0; frameIndex < frameCount; ++frameIndex)
|
|
{
|
|
for (UInt32 jointIndex = 0; jointIndex < jointCount; ++jointIndex)
|
|
{
|
|
SequenceJoint& sequenceJoint = sequenceJoints[frameIndex * jointCount + jointIndex];
|
|
|
|
Int32 parentId = joints[jointIndex].parent;
|
|
if (parentId >= 0)
|
|
{
|
|
sequenceJoint.position = frames[frameIndex].joints[jointIndex].pos;
|
|
sequenceJoint.rotation = frames[frameIndex].joints[jointIndex].orient;
|
|
}
|
|
else
|
|
{
|
|
sequenceJoint.position = rotationQuat * frames[frameIndex].joints[jointIndex].pos;
|
|
sequenceJoint.rotation = rotationQuat * frames[frameIndex].joints[jointIndex].orient;
|
|
}
|
|
|
|
sequenceJoint.scale.Set(1.f);
|
|
}
|
|
}
|
|
|
|
return animation;
|
|
}
|
|
}
|
|
|
|
namespace Loaders
|
|
{
|
|
AnimationLoader::Entry GetAnimationLoader_MD5Anim()
|
|
{
|
|
AnimationLoader::Entry loader;
|
|
loader.extensionSupport = IsMD5AnimSupported;
|
|
loader.streamLoader = LoadMD5Anim;
|
|
loader.parameterFilter = [](const AnimationParams& parameters)
|
|
{
|
|
if (auto result = parameters.custom.GetBooleanParameter("SkipBuiltinMD5AnimLoader"); result.GetValueOr(false))
|
|
return false;
|
|
|
|
return true;
|
|
};
|
|
|
|
return loader;
|
|
}
|
|
}
|
|
}
|