Browse Source

Skeleton: More work

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
c0e7d1b820

+ 8 - 8
src/anki/resource/Animation.cpp

@@ -30,8 +30,8 @@ Error Animation::load(const ResourceFilename& filename, Bool async)
 	I64 tmp;
 	I64 tmp;
 	F64 ftmp;
 	F64 ftmp;
 
 
-	m_startTime = MAX_F32;
-	F32 maxTime = MIN_F32;
+	m_startTime = MAX_F64;
+	F64 maxTime = MIN_F64;
 
 
 	// Document
 	// Document
 	XmlDocument doc;
 	XmlDocument doc;
@@ -99,7 +99,7 @@ Error Animation::load(const ResourceFilename& filename, Bool async)
 			count = 0;
 			count = 0;
 			do
 			do
 			{
 			{
-				Key<Vec3>& key = ch.m_positions[count++];
+				AnimationKeyframe<Vec3>& key = ch.m_positions[count++];
 
 
 				// <time>
 				// <time>
 				ANKI_CHECK(keyEl.getChildElement("time", el));
 				ANKI_CHECK(keyEl.getChildElement("time", el));
@@ -136,7 +136,7 @@ Error Animation::load(const ResourceFilename& filename, Bool async)
 			count = 0;
 			count = 0;
 			do
 			do
 			{
 			{
-				Key<Quat>& key = ch.m_rotations[count++];
+				AnimationKeyframe<Quat>& key = ch.m_rotations[count++];
 
 
 				// <time>
 				// <time>
 				ANKI_CHECK(keyEl.getChildElement("time", el));
 				ANKI_CHECK(keyEl.getChildElement("time", el));
@@ -175,7 +175,7 @@ Error Animation::load(const ResourceFilename& filename, Bool async)
 			count = 0;
 			count = 0;
 			do
 			do
 			{
 			{
-				Key<F32>& key = ch.m_scales[count++];
+				AnimationKeyframe<F32>& key = ch.m_scales[count++];
 
 
 				// <time>
 				// <time>
 				ANKI_CHECK(keyEl.getChildElement("time", el));
 				ANKI_CHECK(keyEl.getChildElement("time", el));
@@ -226,7 +226,7 @@ Error Animation::load(const ResourceFilename& filename, Bool async)
 	return ErrorCode::NONE;
 	return ErrorCode::NONE;
 }
 }
 
 
-void Animation::interpolate(U channelIndex, F32 time, Vec3& pos, Quat& rot, F32& scale) const
+void Animation::interpolate(U channelIndex, F64 time, Vec3& pos, Quat& rot, F32& scale) const
 {
 {
 	// Audjust time
 	// Audjust time
 	if(m_repeat && time > m_startTime + m_duration)
 	if(m_repeat && time > m_startTime + m_duration)
@@ -251,7 +251,7 @@ void Animation::interpolate(U channelIndex, F32 time, Vec3& pos, Quat& rot, F32&
 		ANKI_ASSERT(next != channel.m_positions.end());
 		ANKI_ASSERT(next != channel.m_positions.end());
 		auto prev = next - 1;
 		auto prev = next - 1;
 
 
-		F32 u = (time - prev->m_time) / (next->m_time - prev->m_time);
+		F64 u = (time - prev->m_time) / (next->m_time - prev->m_time);
 		pos = linearInterpolate(prev->m_value, next->m_value, u);
 		pos = linearInterpolate(prev->m_value, next->m_value, u);
 	}
 	}
 
 
@@ -267,7 +267,7 @@ void Animation::interpolate(U channelIndex, F32 time, Vec3& pos, Quat& rot, F32&
 		ANKI_ASSERT(next != channel.m_rotations.end());
 		ANKI_ASSERT(next != channel.m_rotations.end());
 		auto prev = next - 1;
 		auto prev = next - 1;
 
 
-		F32 u = (time - prev->m_time) / (next->m_time - prev->m_time);
+		F64 u = (time - prev->m_time) / (next->m_time - prev->m_time);
 		rot = prev->m_value.slerp(next->m_value, u);
 		rot = prev->m_value.slerp(next->m_value, u);
 	}
 	}
 }
 }

+ 12 - 12
src/anki/resource/Animation.h

@@ -20,12 +20,12 @@ class XmlElement;
 
 
 /// A keyframe
 /// A keyframe
 template<typename T>
 template<typename T>
-class Key
+class AnimationKeyframe
 {
 {
 	friend class Animation;
 	friend class Animation;
 
 
 public:
 public:
-	F32 getTime() const
+	F64 getTime() const
 	{
 	{
 		return m_time;
 		return m_time;
 	}
 	}
@@ -36,7 +36,7 @@ public:
 	}
 	}
 
 
 private:
 private:
-	F32 m_time;
+	F64 m_time;
 	T m_value;
 	T m_value;
 };
 };
 
 
@@ -48,10 +48,10 @@ public:
 
 
 	I32 m_boneIndex = -1; ///< For skeletal animations
 	I32 m_boneIndex = -1; ///< For skeletal animations
 
 
-	DynamicArray<Key<Vec3>> m_positions;
-	DynamicArray<Key<Quat>> m_rotations;
-	DynamicArray<Key<F32>> m_scales;
-	DynamicArray<Key<F32>> m_cameraFovs;
+	DynamicArray<AnimationKeyframe<Vec3>> m_positions;
+	DynamicArray<AnimationKeyframe<Quat>> m_rotations;
+	DynamicArray<AnimationKeyframe<F32>> m_scales;
+	DynamicArray<AnimationKeyframe<F32>> m_cameraFovs;
 
 
 	void destroy(ResourceAllocator<U8> alloc)
 	void destroy(ResourceAllocator<U8> alloc)
 	{
 	{
@@ -80,13 +80,13 @@ public:
 	}
 	}
 
 
 	/// Get the duration of the animation in seconds
 	/// Get the duration of the animation in seconds
-	F32 getDuration() const
+	F64 getDuration() const
 	{
 	{
 		return m_duration;
 		return m_duration;
 	}
 	}
 
 
 	/// Get the time (in seconds) the animation should start
 	/// Get the time (in seconds) the animation should start
-	F32 getStartingTime() const
+	F64 getStartingTime() const
 	{
 	{
 		return m_startTime;
 		return m_startTime;
 	}
 	}
@@ -98,12 +98,12 @@ public:
 	}
 	}
 
 
 	/// Get the interpolated data
 	/// Get the interpolated data
-	void interpolate(U channelIndex, F32 time, Vec3& position, Quat& rotation, F32& scale) const;
+	void interpolate(U channelIndex, F64 time, Vec3& position, Quat& rotation, F32& scale) const;
 
 
 private:
 private:
 	DynamicArray<AnimationChannel> m_channels;
 	DynamicArray<AnimationChannel> m_channels;
-	F32 m_duration;
-	F32 m_startTime;
+	F64 m_duration;
+	F64 m_startTime;
 	Bool8 m_repeat;
 	Bool8 m_repeat;
 };
 };
 /// @}
 /// @}

+ 10 - 0
src/anki/resource/Forward.h

@@ -6,3 +6,13 @@
 #pragma once
 #pragma once
 
 
 #include <anki/resource/Common.h>
 #include <anki/resource/Common.h>
+
+namespace anki
+{
+
+template<typename T>
+class AnimationKeyframe;
+
+class Bone;
+
+} // end namespace anki

+ 17 - 7
src/anki/resource/Skeleton.cpp

@@ -32,21 +32,22 @@ Error Skeleton::load(const ResourceFilename& filename, Bool async)
 
 
 	// count the bones count
 	// count the bones count
 	XmlElement boneEl;
 	XmlElement boneEl;
-	U32 bonesCount = 0;
+	U32 boneCount = 0;
 
 
 	ANKI_CHECK(bonesEl.getChildElement("bone", boneEl));
 	ANKI_CHECK(bonesEl.getChildElement("bone", boneEl));
-	ANKI_CHECK(boneEl.getSiblingElementsCount(bonesCount));
-	++bonesCount;
+	ANKI_CHECK(boneEl.getSiblingElementsCount(boneCount));
+	++boneCount;
 
 
-	m_bones.create(getAllocator(), bonesCount);
+	m_bones.create(getAllocator(), boneCount);
 
 
 	StringListAuto boneParents(getAllocator());
 	StringListAuto boneParents(getAllocator());
 
 
 	// Load every bone
 	// Load every bone
-	bonesCount = 0;
+	boneCount = 0;
 	do
 	do
 	{
 	{
-		Bone& bone = m_bones[bonesCount++];
+		Bone& bone = m_bones[boneCount];
+		bone.m_idx = boneCount;
 
 
 		// <name>
 		// <name>
 		XmlElement nameEl;
 		XmlElement nameEl;
@@ -63,7 +64,7 @@ Error Skeleton::load(const ResourceFilename& filename, Bool async)
 		// <boneTransform>
 		// <boneTransform>
 		XmlElement btrfEl;
 		XmlElement btrfEl;
 		ANKI_CHECK(boneEl.getChildElement("boneTransform", btrfEl));
 		ANKI_CHECK(boneEl.getChildElement("boneTransform", btrfEl));
-		ANKI_CHECK(btrfEl.getMat4(bone.m_boneTrf));
+		ANKI_CHECK(btrfEl.getMat4(bone.m_vertTrf));
 
 
 		// <parent>
 		// <parent>
 		XmlElement parentEl;
 		XmlElement parentEl;
@@ -77,10 +78,19 @@ Error Skeleton::load(const ResourceFilename& filename, Bool async)
 		else
 		else
 		{
 		{
 			boneParents.pushBack("");
 			boneParents.pushBack("");
+
+			if(m_rootBoneIdx != MAX_U32)
+			{
+				ANKI_RESOURCE_LOGE("Skeleton cannot have more than one root nodes");
+				return ErrorCode::USER_DATA;
+			}
+
+			m_rootBoneIdx = boneCount;
 		}
 		}
 
 
 		// Advance
 		// Advance
 		ANKI_CHECK(boneEl.getNextSiblingElement("bone", boneEl));
 		ANKI_CHECK(boneEl.getNextSiblingElement("bone", boneEl));
+		++boneCount;
 	} while(boneEl);
 	} while(boneEl);
 
 
 	// Resolve the parents
 	// Resolve the parents

+ 40 - 2
src/anki/resource/Skeleton.h

@@ -17,7 +17,7 @@ namespace anki
 const U32 MAX_CHILDREN_PER_BONE = 8;
 const U32 MAX_CHILDREN_PER_BONE = 8;
 
 
 /// Skeleton bone
 /// Skeleton bone
-struct Bone
+class Bone
 {
 {
 	friend class Skeleton; ///< For loading
 	friend class Skeleton; ///< For loading
 
 
@@ -36,11 +36,29 @@ public:
 		return m_transform;
 		return m_transform;
 	}
 	}
 
 
+	const Mat4& getVertexTransform() const
+	{
+		return m_vertTrf;
+	}
+
+	U getIndex() const
+	{
+		return m_idx;
+	}
+
+	WeakArray<const Bone*> getChildren() const
+	{
+		const Bone** b = const_cast<const Bone**>(&m_children[0]);
+		return WeakArray<const Bone*>((m_childrenCount) ? b : nullptr, m_childrenCount);
+	}
+
 private:
 private:
 	String m_name; ///< The name of the bone
 	String m_name; ///< The name of the bone
 
 
 	Mat4 m_transform; ///< See the class notes.
 	Mat4 m_transform; ///< See the class notes.
-	Mat4 m_boneTrf;
+	Mat4 m_vertTrf;
+
+	U32 m_idx;
 
 
 	Bone* m_parent = nullptr;
 	Bone* m_parent = nullptr;
 	Array<Bone*, MAX_CHILDREN_PER_BONE> m_children = {};
 	Array<Bone*, MAX_CHILDREN_PER_BONE> m_children = {};
@@ -87,8 +105,28 @@ public:
 		return m_bones;
 		return m_bones;
 	}
 	}
 
 
+	const Bone* tryFindBone(CString name) const
+	{
+		// TODO Optimize
+		for(const Bone& b : m_bones)
+		{
+			if(b.m_name == name)
+			{
+				return &b;
+			}
+		}
+
+		return nullptr;
+	}
+
+	const Bone& getRootBone() const
+	{
+		return m_bones[m_rootBoneIdx];
+	}
+
 private:
 private:
 	DynamicArray<Bone> m_bones;
 	DynamicArray<Bone> m_bones;
+	U32 m_rootBoneIdx = MAX_U32;
 };
 };
 /// @}
 /// @}
 
 

+ 89 - 1
src/anki/scene/SkinComponent.cpp

@@ -4,8 +4,96 @@
 // http://www.anki3d.org/LICENSE
 // http://www.anki3d.org/LICENSE
 
 
 #include <anki/scene/SkinComponent.h>
 #include <anki/scene/SkinComponent.h>
+#include <anki/resource/Skeleton.h>
+#include <anki/resource/Animation.h>
+#include <anki/util/BitSet.h>
 
 
 namespace anki
 namespace anki
 {
 {
 
 
-} // end namespace anki
+SkinComponent::SkinComponent(SceneNode* node, SkeletonResourcePtr skeleton)
+	: SceneComponent(CLASS_TYPE, node)
+	, m_skeleton(skeleton)
+{
+	m_boneTrfs.create(getAllocator(), m_skeleton->getBones().getSize());
+}
+
+SkinComponent::~SkinComponent()
+{
+	m_boneTrfs.destroy(getAllocator());
+}
+
+void SkinComponent::playAnimation(U track, AnimationResourcePtr anim, F64 startTime, Bool repeat)
+{
+	m_tracks[track].m_anim = anim;
+	m_tracks[track].m_time = startTime;
+	m_tracks[track].m_repeat = repeat;
+}
+
+Error SkinComponent::update(SceneNode& node, F32 prevTime, F32 crntTime, Bool& updated)
+{
+	updated = false;
+	const F64 timeDiff = crntTime - prevTime;
+
+	for(Track& track : m_tracks)
+	{
+		if(!track.m_anim.isCreated())
+		{
+			continue;
+		}
+
+		updated = true;
+
+		const F64 animTime = track.m_time;
+		track.m_time += timeDiff;
+
+		// Iterate the animation channels and interpolate
+		BitSet<128> bonesAnimated(false);
+		for(U i = 0; i < track.m_anim->getChannels().getSize(); ++i)
+		{
+			const AnimationChannel& channel = track.m_anim->getChannels()[i];
+			const Bone* bone = m_skeleton->tryFindBone(channel.m_name.toCString());
+			if(!bone)
+			{
+				ANKI_SCENE_LOGW("Animation is referencing unknown bone \"%s\"", &channel.m_name[0]);
+				continue;
+			}
+
+			// Interpolate
+			Vec3 position;
+			Quat rotation;
+			F32 scale;
+			track.m_anim->interpolate(i, animTime, position, rotation, scale);
+
+			// Store
+			bonesAnimated.set(bone->getIndex());
+			m_boneTrfs[bone->getIndex()] = Mat4(position.xyz1(), Mat3(rotation), 1.0f) * bone->getVertexTransform();
+		}
+
+		// Walk the bone hierarchy to add additional transforms
+		visitBones(m_skeleton->getRootBone(), Mat4::getIdentity(), bonesAnimated);
+	}
+
+	return ErrorCode::NONE;
+}
+
+void SkinComponent::visitBones(const Bone& bone, const Mat4& parentTrf, const BitSet<128>& bonesAnimated)
+{
+	Mat4 myTrf = parentTrf * bone.getTransform();
+
+	if(bonesAnimated.get(bone.getIndex()))
+	{
+		m_boneTrfs[bone.getIndex()] = myTrf * m_boneTrfs[bone.getIndex()];
+	}
+	else
+	{
+		m_boneTrfs[bone.getIndex()] = myTrf * bone.getVertexTransform();
+	}
+
+	for(const Bone* child : bone.getChildren())
+	{
+		visitBones(*child, myTrf, bonesAnimated);
+	}
+}
+
+} // end namespace anki

+ 29 - 1
src/anki/scene/SkinComponent.h

@@ -6,6 +6,9 @@
 #pragma once
 #pragma once
 
 
 #include <anki/scene/SceneComponent.h>
 #include <anki/scene/SceneComponent.h>
+#include <anki/resource/Forward.h>
+#include <anki/util/Forward.h>
+#include <anki/Math.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -17,7 +20,32 @@ namespace anki
 class SkinComponent : public SceneComponent
 class SkinComponent : public SceneComponent
 {
 {
 public:
 public:
+	static const SceneComponentType CLASS_TYPE = SceneComponentType::SKIN;
+	static const U MAX_ANIMATION_TRACKS = 2;
+
+	SkinComponent(SceneNode* node, SkeletonResourcePtr skeleton);
+
+	~SkinComponent();
+
+	ANKI_USE_RESULT Error update(SceneNode&, F32, F32, Bool& updated) override;
+
+	void playAnimation(U track, AnimationResourcePtr anim, F64 startTime, Bool repeat);
+
+private:
+	class Track
+	{
+	public:
+		AnimationResourcePtr m_anim;
+		F64 m_time;
+		Bool8 m_repeat;
+	};
+
+	SkeletonResourcePtr m_skeleton;
+	DynamicArray<Mat4> m_boneTrfs;
+	Array<Track, MAX_ANIMATION_TRACKS> m_tracks;
+
+	void visitBones(const Bone& bone, const Mat4& parentTrf, const BitSet<128, U8>& bonesAnimated);
 };
 };
 /// @}
 /// @}
 
 
-} // end namespace anki
+} // end namespace anki

+ 16 - 0
src/anki/util/Forward.h

@@ -0,0 +1,16 @@
+// Copyright (C) 2009-2017, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+#pragma once
+
+#include <anki/util/StdTypes.h>
+
+namespace anki
+{
+
+template<U N, typename TChunkType>
+class BitSet;
+
+} // end namespace anki