<commit_before>// Copyright (C) 2015 <NAME>
// 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/Graphics/Scene.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)
{
}

void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const
{
	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();
}

NzSkeletalModel* NzSkeletalModel::Clone() const
{
	return new NzSkeletalModel(*this);
}

NzSkeletalModel* NzSkeletalModel::Create() const
{
	return new NzSkeletalModel;
}

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::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();
}

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);
	}

	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;
}

void NzSkeletalModel::MakeBoundingVolume() const
{
	m_boundingVolume.Set(m_skeleton.GetAABB());
}

void NzSkeletalModel::Update()
{
	/*if (m_animationEnabled && m_animation)
		AdvanceAnimation(m_scene->GetUpdateTime());*/
}

NzSkeletalModelLoader::LoaderList NzSkeletalModel::s_loaders;
<commit_msg>Graphics/SkeletalModel: Fix crash<commit_after>// Copyright (C) 2015 <NAME>
// 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/Graphics/Scene.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)
{
}

void NzSkeletalModel::AddToRenderQueue(NzAbstractRenderQueue* renderQueue, const NzMatrix4f& transformMatrix) const
{
	if (!m_mesh)
		return;

	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();
}

NzSkeletalModel* NzSkeletalModel::Clone() const
{
	return new NzSkeletalModel(*this);
}

NzSkeletalModel* NzSkeletalModel::Create() const
{
	return new NzSkeletalModel;
}

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::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();
}

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);
	}

	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;
}

void NzSkeletalModel::MakeBoundingVolume() const
{
	m_boundingVolume.Set(m_skeleton.GetAABB());
}

void NzSkeletalModel::Update()
{
	/*if (m_animationEnabled && m_animation)
		AdvanceAnimation(m_scene->GetUpdateTime());*/
}

NzSkeletalModelLoader::LoaderList NzSkeletalModel::s_loaders;

{'fraction_non_alphanumeric': 0.11631360900492457, 'fraction_numerical': 0.0021886969436410537, 'mean_word_length': 5.730142030510258, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 87, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6944401', 'n_tokens_mistral': 4904, 'n_tokens_neox': 4449, 'n_words': 869}