Added support for skeletal models (WIP)
Former-commit-id: 6a6c2d9f2c68267df79dc48fa56450d2a563e323
This commit is contained in:
parent
58fbed1dbd
commit
e55c521e96
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifndef NAZARA_SKELETALMODEL_HPP
|
||||
#define NAZARA_SKELETALMODEL_HPP
|
||||
|
||||
#include <Nazara/Prerequesites.hpp>
|
||||
#include <Nazara/Core/ResourceLoader.hpp>
|
||||
#include <Nazara/Core/Updatable.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Utility/Animation.hpp>
|
||||
#include <Nazara/Utility/Buffer.hpp>
|
||||
#include <Nazara/Utility/VertexBuffer.hpp>
|
||||
#include <vector>
|
||||
|
||||
struct NAZARA_API NzSkeletalModelParameters : public NzModelParameters
|
||||
{
|
||||
bool loadAnimation = true;
|
||||
NzAnimationParams animation;
|
||||
|
||||
bool IsValid() const;
|
||||
};
|
||||
|
||||
class NzSkeletalModel;
|
||||
|
||||
using NzSkeletalModelLoader = NzResourceLoader<NzSkeletalModel, NzSkeletalModelParameters>;
|
||||
|
||||
class NAZARA_API NzSkeletalModel : public NzModel, NzUpdatable
|
||||
{
|
||||
friend NzSkeletalModelLoader;
|
||||
//friend class NzScene;
|
||||
|
||||
public:
|
||||
NzSkeletalModel();
|
||||
NzSkeletalModel(const NzSkeletalModel& model);
|
||||
NzSkeletalModel(NzSkeletalModel&& model);
|
||||
~NzSkeletalModel();
|
||||
|
||||
void AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const override;
|
||||
void AdvanceAnimation(float elapsedTime);
|
||||
|
||||
void EnableAnimation(bool animation);
|
||||
|
||||
NzAnimation* GetAnimation() const;
|
||||
NzSkeleton* GetSkeleton();
|
||||
const NzSkeleton* GetSkeleton() const;
|
||||
|
||||
bool HasAnimation() const;
|
||||
|
||||
bool IsAnimated() const;
|
||||
bool IsAnimationEnabled() const;
|
||||
bool IsDrawable() const;
|
||||
|
||||
bool LoadFromFile(const NzString& filePath, const NzSkeletalModelParameters& params = NzSkeletalModelParameters());
|
||||
bool LoadFromMemory(const void* data, std::size_t size, const NzSkeletalModelParameters& params = NzSkeletalModelParameters());
|
||||
bool LoadFromStream(NzInputStream& stream, const NzSkeletalModelParameters& params = NzSkeletalModelParameters());
|
||||
|
||||
void Reset();
|
||||
|
||||
bool SetAnimation(NzAnimation* animation);
|
||||
void SetMesh(NzMesh* mesh) override;
|
||||
bool SetSequence(const NzString& sequenceName);
|
||||
void SetSequence(unsigned int sequenceIndex);
|
||||
|
||||
NzSkeletalModel& operator=(const NzSkeletalModel& node);
|
||||
NzSkeletalModel& operator=(NzSkeletalModel&& node);
|
||||
|
||||
private:
|
||||
void Register() override;
|
||||
void Unregister() override;
|
||||
void Update() override;
|
||||
void UpdateBoundingVolume() const;
|
||||
|
||||
NzAnimationRef m_animation;
|
||||
NzSkeleton m_skeleton;
|
||||
const NzSequence* m_currentSequence;
|
||||
bool m_animationEnabled;
|
||||
float m_interpolation;
|
||||
unsigned int m_currentFrame;
|
||||
unsigned int m_nextFrame;
|
||||
|
||||
static NzSkeletalModelLoader::LoaderList s_loaders;
|
||||
};
|
||||
|
||||
#endif // NAZARA_SKELETALMODEL_HPP
|
||||
|
|
@ -5,6 +5,7 @@
|
|||
#include <Nazara/Graphics/Loaders/Mesh.hpp>
|
||||
#include <Nazara/Graphics/Material.hpp>
|
||||
#include <Nazara/Graphics/Model.hpp>
|
||||
#include <Nazara/Graphics/SkeletalModel.hpp>
|
||||
#include <Nazara/Utility/Mesh.hpp>
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
|
@ -32,11 +33,71 @@ namespace
|
|||
}
|
||||
|
||||
if (mesh->IsAnimable())
|
||||
{
|
||||
NazaraError("Can't load animated mesh into static model");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nous ne pouvons plus avoir recours au smart pointeur à partir d'ici si nous voulons être exception-safe
|
||||
NzMesh* meshPtr = mesh.get();
|
||||
|
||||
model->Reset();
|
||||
model->SetMesh(meshPtr);
|
||||
mesh.release();
|
||||
|
||||
if (parameters.loadMaterials)
|
||||
{
|
||||
unsigned int matCount = model->GetMaterialCount();
|
||||
|
||||
for (unsigned int i = 0; i < matCount; ++i)
|
||||
{
|
||||
NzString mat = meshPtr->GetMaterial(i);
|
||||
if (!mat.IsEmpty())
|
||||
{
|
||||
std::unique_ptr<NzMaterial> material(new NzMaterial);
|
||||
material->SetPersistent(false);
|
||||
|
||||
if (material->LoadFromFile(mat, parameters.material))
|
||||
{
|
||||
model->SetMaterial(i, material.get());
|
||||
material.release();
|
||||
}
|
||||
else
|
||||
NazaraWarning("Failed to load material #" + NzString::Number(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nzTernary CheckAnimated(NzInputStream& stream, const NzSkeletalModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(stream);
|
||||
NazaraUnused(parameters);
|
||||
|
||||
return nzTernary_Unknown;
|
||||
}
|
||||
|
||||
bool LoadAnimated(NzSkeletalModel* model, NzInputStream& stream, const NzSkeletalModelParameters& parameters)
|
||||
{
|
||||
NazaraUnused(parameters);
|
||||
|
||||
std::unique_ptr<NzMesh> mesh(new NzMesh);
|
||||
mesh->SetPersistent(false);
|
||||
if (!mesh->LoadFromStream(stream, parameters.mesh))
|
||||
{
|
||||
NazaraError("Failed to load model mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mesh->IsAnimable())
|
||||
{
|
||||
NazaraError("Can't load static mesh into animated model");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Nous ne pouvons plus avoir recours au smart pointeur à partir d'ici si nous voulons être exception-safe
|
||||
NzMesh* meshPtr = mesh.get();
|
||||
|
||||
|
|
@ -74,9 +135,11 @@ namespace
|
|||
void NzLoaders_Mesh_Register()
|
||||
{
|
||||
NzModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
|
||||
NzSkeletalModelLoader::RegisterLoader(NzMeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
|
||||
}
|
||||
|
||||
void NzLoaders_Mesh_Unregister()
|
||||
{
|
||||
NzModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, CheckStatic, LoadStatic);
|
||||
NzSkeletalModelLoader::UnregisterLoader(NzMeshLoader::IsExtensionSupported, CheckAnimated, LoadAnimated);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,357 @@
|
|||
// Copyright (C) 2014 Jérôme Leclercq
|
||||
// This file is part of the "Nazara Engine - Graphics module"
|
||||
// For conditions of distribution and use, see copyright notice in Config.hpp
|
||||
|
||||
#include <Nazara/Graphics/SkeletalModel.hpp>
|
||||
#include <Nazara/Graphics/AbstractRenderQueue.hpp>
|
||||
#include <Nazara/Graphics/Camera.hpp>
|
||||
#include <Nazara/Graphics/Config.hpp>
|
||||
#include <Nazara/Graphics/SkinningManager.hpp>
|
||||
#include <Nazara/Utility/BufferMapper.hpp>
|
||||
#include <Nazara/Utility/MeshData.hpp>
|
||||
#include <Nazara/Utility/SkeletalMesh.hpp>
|
||||
#include <memory>
|
||||
#include <Nazara/Graphics/Debug.hpp>
|
||||
|
||||
bool NzSkeletalModelParameters::IsValid() const
|
||||
{
|
||||
if (!NzModelParameters::IsValid())
|
||||
return false;
|
||||
|
||||
if (loadAnimation && !animation.IsValid())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NzSkeletalModel::NzSkeletalModel() :
|
||||
m_currentSequence(nullptr),
|
||||
m_animationEnabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
NzSkeletalModel::NzSkeletalModel(const NzSkeletalModel& model) :
|
||||
NzModel(model),
|
||||
m_skeleton(model.m_skeleton),
|
||||
m_currentSequence(model.m_currentSequence),
|
||||
m_animationEnabled(model.m_animationEnabled),
|
||||
m_interpolation(model.m_interpolation),
|
||||
m_currentFrame(model.m_currentFrame),
|
||||
m_nextFrame(model.m_nextFrame)
|
||||
{
|
||||
}
|
||||
|
||||
NzSkeletalModel::~NzSkeletalModel()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue) const
|
||||
{
|
||||
const NzMatrix4f& transformMatrix = GetTransformMatrix();
|
||||
|
||||
unsigned int submeshCount = m_mesh->GetSubMeshCount();
|
||||
for (unsigned int i = 0; i < submeshCount; ++i)
|
||||
{
|
||||
const NzSkeletalMesh* mesh = static_cast<const NzSkeletalMesh*>(m_mesh->GetSubMesh(i));
|
||||
const NzMaterial* material = m_materials[mesh->GetMaterialIndex()];
|
||||
|
||||
NzMeshData meshData;
|
||||
meshData.indexBuffer = mesh->GetIndexBuffer();
|
||||
meshData.primitiveMode = mesh->GetPrimitiveMode();
|
||||
meshData.vertexBuffer = NzSkinningManager::GetBuffer(mesh, &m_skeleton);
|
||||
|
||||
renderQueue->AddMesh(material, meshData, m_skeleton.GetAABB(), transformMatrix);
|
||||
}
|
||||
}
|
||||
|
||||
void NzSkeletalModel::AdvanceAnimation(float elapsedTime)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_animation)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_interpolation += m_currentSequence->frameRate * elapsedTime;
|
||||
while (m_interpolation > 1.f)
|
||||
{
|
||||
m_interpolation -= 1.f;
|
||||
|
||||
unsigned lastFrame = m_currentSequence->firstFrame + m_currentSequence->frameCount - 1;
|
||||
if (m_nextFrame+1 > lastFrame)
|
||||
{
|
||||
if (m_animation->IsLoopPointInterpolationEnabled())
|
||||
{
|
||||
m_currentFrame = m_nextFrame;
|
||||
m_nextFrame = m_currentSequence->firstFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentFrame = m_currentSequence->firstFrame;
|
||||
m_nextFrame = m_currentFrame+1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_currentFrame = m_nextFrame;
|
||||
m_nextFrame++;
|
||||
}
|
||||
}
|
||||
|
||||
m_animation->AnimateSkeleton(&m_skeleton, m_currentFrame, m_nextFrame, m_interpolation);
|
||||
|
||||
InvalidateBoundingVolume();
|
||||
}
|
||||
|
||||
void NzSkeletalModel::EnableAnimation(bool animation)
|
||||
{
|
||||
m_animationEnabled = animation;
|
||||
}
|
||||
|
||||
NzAnimation* NzSkeletalModel::GetAnimation() const
|
||||
{
|
||||
return m_animation;
|
||||
}
|
||||
|
||||
NzSkeleton* NzSkeletalModel::GetSkeleton()
|
||||
{
|
||||
InvalidateBoundingVolume();
|
||||
|
||||
return &m_skeleton;
|
||||
}
|
||||
|
||||
const NzSkeleton* NzSkeletalModel::GetSkeleton() const
|
||||
{
|
||||
return &m_skeleton;
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::HasAnimation() const
|
||||
{
|
||||
return m_animation != nullptr;
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::IsAnimated() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::IsAnimationEnabled() const
|
||||
{
|
||||
return m_animationEnabled;
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::IsDrawable() const
|
||||
{
|
||||
return m_mesh != nullptr && m_mesh->GetSubMeshCount() >= 1;
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::LoadFromFile(const NzString& filePath, const NzSkeletalModelParameters& params)
|
||||
{
|
||||
return NzSkeletalModelLoader::LoadFromFile(this, filePath, params);
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::LoadFromMemory(const void* data, std::size_t size, const NzSkeletalModelParameters& params)
|
||||
{
|
||||
return NzSkeletalModelLoader::LoadFromMemory(this, data, size, params);
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::LoadFromStream(NzInputStream& stream, const NzSkeletalModelParameters& params)
|
||||
{
|
||||
return NzSkeletalModelLoader::LoadFromStream(this, stream, params);
|
||||
}
|
||||
|
||||
void NzSkeletalModel::Reset()
|
||||
{
|
||||
NzModel::Reset();
|
||||
|
||||
m_skeleton.Destroy();
|
||||
|
||||
if (m_scene)
|
||||
m_scene->UnregisterForUpdate(this);
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::SetAnimation(NzAnimation* animation)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_mesh)
|
||||
{
|
||||
NazaraError("Model has no mesh");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animation)
|
||||
{
|
||||
if (!animation->IsValid())
|
||||
{
|
||||
NazaraError("Invalid animation");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animation->GetType() != m_mesh->GetAnimationType())
|
||||
{
|
||||
NazaraError("Animation type must match mesh animation type");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (animation->GetJointCount() != m_mesh->GetJointCount())
|
||||
{
|
||||
NazaraError("Animation joint count must match mesh joint count");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
m_animation = animation;
|
||||
if (m_animation)
|
||||
{
|
||||
m_currentFrame = 0;
|
||||
m_interpolation = 0.f;
|
||||
|
||||
SetSequence(0);
|
||||
|
||||
if (m_scene)
|
||||
m_scene->RegisterForUpdate(this);
|
||||
}
|
||||
else if (m_scene)
|
||||
m_scene->UnregisterForUpdate(this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSkeletalModel::SetMesh(NzMesh* mesh)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (mesh && mesh->GetAnimationType() != nzAnimationType_Skeletal)
|
||||
{
|
||||
NazaraError("Mesh animation type must be skeletal");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NzModel::SetMesh(mesh);
|
||||
|
||||
if (m_mesh)
|
||||
{
|
||||
if (m_animation && m_animation->GetJointCount() != m_mesh->GetJointCount())
|
||||
{
|
||||
NazaraWarning("Animation joint count is not matching new mesh joint count, disabling animation...");
|
||||
SetAnimation(nullptr);
|
||||
}
|
||||
|
||||
m_skeleton = *m_mesh->GetSkeleton(); // Copie du squelette template
|
||||
}
|
||||
}
|
||||
|
||||
bool NzSkeletalModel::SetSequence(const NzString& sequenceName)
|
||||
{
|
||||
///TODO: Rendre cette erreur "safe" avec le nouveau système de gestions d'erreur (No-log)
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_animation)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
const NzSequence* currentSequence = m_animation->GetSequence(sequenceName);
|
||||
if (!currentSequence)
|
||||
{
|
||||
NazaraError("Sequence not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_currentSequence = currentSequence;
|
||||
m_nextFrame = m_currentSequence->firstFrame;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NzSkeletalModel::SetSequence(unsigned int sequenceIndex)
|
||||
{
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!m_animation)
|
||||
{
|
||||
NazaraError("Model has no animation");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const NzSequence* currentSequence = m_animation->GetSequence(sequenceIndex);
|
||||
#if NAZARA_GRAPHICS_SAFE
|
||||
if (!currentSequence)
|
||||
{
|
||||
NazaraError("Sequence not found");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
m_currentSequence = currentSequence;
|
||||
m_nextFrame = m_currentSequence->firstFrame;
|
||||
}
|
||||
|
||||
NzSkeletalModel& NzSkeletalModel::operator=(const NzSkeletalModel& node)
|
||||
{
|
||||
NzSkeletalModel::operator=(node);
|
||||
|
||||
m_animation = node.m_animation;
|
||||
m_animationEnabled = node.m_animationEnabled;
|
||||
m_currentFrame = node.m_currentFrame;
|
||||
m_currentSequence = node.m_currentSequence;
|
||||
m_interpolation = node.m_interpolation;
|
||||
m_nextFrame = node.m_nextFrame;
|
||||
m_skeleton = node.m_skeleton;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
NzSkeletalModel& NzSkeletalModel::operator=(NzSkeletalModel&& node)
|
||||
{
|
||||
NzModel::operator=(node);
|
||||
|
||||
// Ressources
|
||||
m_animation = std::move(node.m_animation);
|
||||
m_skeleton = std::move(node.m_skeleton);
|
||||
|
||||
// Paramètres
|
||||
m_animationEnabled = node.m_animationEnabled;
|
||||
m_currentFrame = node.m_currentFrame;
|
||||
m_currentSequence = node.m_currentSequence;
|
||||
m_interpolation = node.m_interpolation;
|
||||
m_nextFrame = node.m_nextFrame;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void NzSkeletalModel::Register()
|
||||
{
|
||||
if (m_animation)
|
||||
m_scene->RegisterForUpdate(this);
|
||||
}
|
||||
|
||||
void NzSkeletalModel::Unregister()
|
||||
{
|
||||
m_scene->UnregisterForUpdate(this);
|
||||
}
|
||||
|
||||
void NzSkeletalModel::Update()
|
||||
{
|
||||
if (m_animationEnabled && m_animation)
|
||||
AdvanceAnimation(m_scene->GetUpdateTime());
|
||||
}
|
||||
|
||||
void NzSkeletalModel::UpdateBoundingVolume() const
|
||||
{
|
||||
if (m_boundingVolume.IsNull())
|
||||
m_boundingVolume.Set(m_skeleton.GetAABB());
|
||||
|
||||
if (!m_transformMatrixUpdated)
|
||||
UpdateTransformMatrix();
|
||||
|
||||
m_boundingVolume.Update(m_transformMatrix);
|
||||
m_boundingVolumeUpdated = true;
|
||||
}
|
||||
|
||||
NzSkeletalModelLoader::LoaderList NzSkeletalModel::s_loaders;
|
||||
Loading…
Reference in New Issue