Przeglądaj źródła

Added the ability for C++ Animation to register scene object <-> bone mapping same as in C#

BearishSun 9 lat temu
rodzic
commit
9ed85be779

+ 2 - 1
Source/BansheeCore/Include/BsCorePrerequisites.h

@@ -529,7 +529,8 @@ namespace BansheeEngine
 		TID_AnimationSplitInfo = 1121,
 		TID_CAnimation = 1122,
 		TID_AnimationEvent = 1123,
-		TID_ImportedAnimationEvents = 1124
+		TID_ImportedAnimationEvents = 1124,
+		TID_CBone = 1125
 	};
 }
 

+ 3 - 0
Source/BansheeEngine/CMakeSources.cmake

@@ -3,6 +3,7 @@ set(BS_BANSHEEENGINE_INC_COMPONENTS
 	"Include/BsCRenderable.h"
 	"Include/BsCLight.h"
 	"Include/BsCAnimation.h"
+	"Include/BsCBone.h"
 )
 
 set(BS_BANSHEEENGINE_SRC_RESOURCES
@@ -152,6 +153,7 @@ set(BS_BANSHEEENGINE_INC_RTTI
 	"Include/BsGameSettingsRTTI.h"
 	"Include/BsResourceMappingRTTI.h"
 	"Include/BsCAnimationRTTI.h"
+	"Include/BsCBoneRTTI.h"
 )
 
 set(BS_BANSHEEENGINE_INC_NOFILTER
@@ -175,6 +177,7 @@ set(BS_BANSHEEENGINE_SRC_COMPONENTS
 	"Source/BsCRenderable.cpp"
 	"Source/BsCLight.cpp"
 	"Source/BsCAnimation.cpp"
+	"Source/BsCBone.cpp"
 )
 
 set(BS_BANSHEEENGINE_INC_SCRIPT

+ 31 - 0
Source/BansheeEngine/Include/BsCAnimation.h

@@ -19,6 +19,14 @@ namespace BansheeEngine
 	 */
     class BS_EXPORT CAnimation : public Component
     {
+		/** Information about scene objects bound to a specific animation curve. */
+		struct SceneObjectMappingInfo
+		{
+			HSceneObject sceneObject;
+			bool isMappedToBone;
+			HBone bone;
+		};
+
     public:
 		CAnimation(const HSceneObject& parent);
 		virtual ~CAnimation() {}
@@ -76,6 +84,18 @@ namespace BansheeEngine
 		/** Returns the Animation implementation wrapped by this component. */
 		SPtr<Animation> _getInternal() const { return mInternal; }
 
+		/** 
+		 * Registers a new bone component, creating a new transform mapping from the bone name to the scene object the
+		 * component is attached to. 
+		 */
+		void _addBone(const HBone& bone);
+
+		/** Unregisters a bone component, removing the bone -> scene object mapping. */
+		void _removeBone(const HBone& bone);
+
+		/** Called whenever the bone name the Bone component points to changes. */
+		void _notifyBoneChanged(const HBone& bone);
+
 		/** @} */
 
 		/************************************************************************/
@@ -107,12 +127,23 @@ namespace BansheeEngine
 		/** Callback triggered whenever an animation event is triggered. */
 		void eventTriggered(const HAnimationClip& clip, const String& name);
 
+		/** 
+		 * Finds any scene objects that are mapped to bone transforms. Such object's transforms will be affected by 
+		 * skeleton bone animation.
+		 */
+		void setBoneMappings();
+
+		/** Searches child scene objects for Bone components and returns any found ones. */
+		Vector<HBone> findChildBones();
+
 		SPtr<Animation> mInternal;
 
 		HAnimationClip mDefaultClip;
 		AnimWrapMode mWrapMode;
 		float mSpeed;
 
+		Vector<SceneObjectMappingInfo> mMappingInfos;
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/

+ 84 - 0
Source/BansheeEngine/Include/BsCBone.h

@@ -0,0 +1,84 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsComponent.h"
+
+namespace BansheeEngine 
+{
+	/** @addtogroup Components
+	 *  @{
+	 */
+
+	/**
+	 * Component that maps animation for specific bone also be applied to the SceneObject this component is attached to. 
+	 * The component will attach to the first found parent Animation component.
+	 */
+    class BS_EXPORT CBone : public Component
+    {
+    public:
+		CBone(const HSceneObject& parent);
+		virtual ~CBone() {}
+		
+		/** Changes the name of the bone the component is referencing. */
+		void setBoneName(const String& name);
+
+		/** Returns the name of the bone the component is referencing. */
+		const String& getBoneName() const { return mBoneName; }
+
+		/** @name Internal
+		 *  @{
+		 */
+
+		/**
+		 * Changes the parent animation of this component.
+		 *
+		 * @param[in]	animation	New animation parent, can be null.
+		 * @param[in]	isInternal	If true the bone will just be changed internally, but parent animation will not be
+		 *							notified.
+		 */
+		void _setParent(const HAnimation& animation, bool isInternal = false);
+
+		/** @} */
+	private:
+		/** Attempts to find the parent Animation component and registers itself with it. */
+		void updateParentAnimation();
+
+		/************************************************************************/
+		/* 						COMPONENT OVERRIDES                      		*/
+		/************************************************************************/
+	protected:
+		friend class SceneObject;
+
+		/** @copydoc Component::onDestroyed() */
+		void onDestroyed() override;
+
+		/** @copydoc Component::onDisabled() */
+		void onDisabled() override;
+
+		/** @copydoc Component::onEnabled() */
+		void onEnabled() override;
+
+		/** @copydoc Component::onTransformChanged() */
+		void onTransformChanged(TransformChangedFlags flags) override;
+    protected:
+		using Component::destroyInternal;
+
+		String mBoneName;
+		HAnimation mParent;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class CBoneRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		RTTITypeBase* getRTTI() const override;
+
+	protected:
+		CBone() {} // Serialization only
+     };
+
+	 /** @} */
+}

+ 46 - 0
Source/BansheeEngine/Include/BsCBoneRTTI.h

@@ -0,0 +1,46 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsRTTIType.h"
+#include "BsCBone.h"
+#include "BsGameObjectRTTI.h"
+
+namespace BansheeEngine
+{
+	/** @cond RTTI */
+	/** @addtogroup RTTI-Impl-Engine
+	 *  @{
+	 */
+
+	class BS_EXPORT CBoneRTTI : public RTTIType<CBone, Component, CBoneRTTI>
+	{
+		BS_BEGIN_RTTI_MEMBERS
+			BS_RTTI_MEMBER_PLAIN(mBoneName, 0)
+		BS_END_RTTI_MEMBERS
+	public:
+		CBoneRTTI()
+			:mInitMembers(this)
+		{ }
+
+		const String& getRTTIName() override
+		{
+			static String name = "CBone";
+			return name;
+		}
+
+		UINT32 getRTTIId() override
+		{
+			return TID_CBone;
+		}
+
+		SPtr<IReflectable> newRTTIObject() override
+		{
+			return GameObjectRTTI::createGameObject<CBone>();
+		}
+	};
+
+	/** @} */
+	/** @endcond */
+}

+ 2 - 0
Source/BansheeEngine/Include/BsPrerequisites.h

@@ -200,11 +200,13 @@ namespace BansheeEngine
 	class CCamera;
 	class CLight;
 	class CAnimation;
+	class CBone;
 
 	typedef GameObjectHandle<CGUIWidget> HGUIWidget;
 	typedef GameObjectHandle<CCamera> HCamera;
 	typedef GameObjectHandle<CRenderable> HRenderable;
 	typedef GameObjectHandle<CAnimation> HAnimation;
+	typedef GameObjectHandle<CBone> HBone;
 	typedef GameObjectHandle<ProfilerOverlay> HProfilerOverlay;
 
 	typedef ResourceHandle<SpriteTexture> HSpriteTexture;

+ 100 - 0
Source/BansheeEngine/Source/BsCAnimation.cpp

@@ -3,6 +3,7 @@
 #include "BsCAnimation.h"
 #include "BsSceneObject.h"
 #include "BsCRenderable.h"
+#include "BsCBone.h"
 #include "BsCAnimationRTTI.h"
 
 using namespace std::placeholders;
@@ -139,6 +140,8 @@ namespace BansheeEngine
 		if (mDefaultClip.isLoaded())
 			mInternal->play(mDefaultClip);
 
+		setBoneMappings();
+
 		HRenderable renderableComponent = SO()->getComponent<CRenderable>();
 		if (renderableComponent == nullptr)
 			return;
@@ -164,6 +167,103 @@ namespace BansheeEngine
 		mInternal = nullptr;
 	}
 
+	void CAnimation::_addBone(const HBone& bone)
+	{
+		if (mInternal == nullptr)
+			return;
+
+		HSceneObject currentSO = bone->SO();
+
+		SceneObjectMappingInfo newMapping;
+		newMapping.sceneObject = currentSO;
+		newMapping.isMappedToBone = true;
+		newMapping.bone = bone;
+
+		mMappingInfos.push_back(newMapping);
+		mInternal->mapCurveToSceneObject(bone->getName(), newMapping.sceneObject);
+	}
+
+	void CAnimation::_removeBone(const HBone& bone)
+	{
+		if (mInternal == nullptr)
+			return;
+
+		HSceneObject newSO;
+		for (UINT32 i = 0; i < (UINT32)mMappingInfos.size(); i++)
+		{
+			if (mMappingInfos[i].bone == bone)
+			{
+				mMappingInfos.erase(mMappingInfos.begin() + i);
+				mInternal->unmapSceneObject(mMappingInfos[i].sceneObject);
+				i--;
+			}
+		}
+	}
+
+	void CAnimation::_notifyBoneChanged(const HBone& bone)
+	{
+		if (mInternal == nullptr)
+			return;
+
+		for (UINT32 i = 0; i < (UINT32)mMappingInfos.size(); i++)
+		{
+			if (mMappingInfos[i].bone == bone)
+			{
+				mInternal->unmapSceneObject(mMappingInfos[i].sceneObject);
+				mInternal->mapCurveToSceneObject(bone->getName(), mMappingInfos[i].sceneObject);
+				break;
+			}
+		}
+	}
+
+	void CAnimation::setBoneMappings()
+	{
+		mMappingInfos.clear();
+
+		SceneObjectMappingInfo rootMapping;
+		rootMapping.sceneObject = SO();
+		rootMapping.isMappedToBone = true;
+
+		mMappingInfos.push_back(rootMapping);
+		mInternal->mapCurveToSceneObject("", rootMapping.sceneObject);
+
+		Vector<HBone> childBones = findChildBones();
+		for (auto& entry : childBones)
+			_addBone(entry);
+	}
+
+	Vector<HBone> CAnimation::findChildBones()
+	{
+		Stack<HSceneObject> todo;
+		todo.push(SO());
+
+		Vector<HBone> bones;
+		while (todo.size() > 0)
+		{
+			HSceneObject currentSO = todo.top();
+			todo.pop();
+
+			HBone bone = currentSO->getComponent<CBone>();
+			if (bone != nullptr)
+			{
+				bone->_setParent(getHandle(), true);
+				bones.push_back(bone);
+			}
+
+			int childCount = currentSO->getNumChildren();
+			for (int i = 0; i < childCount; i++)
+			{
+				HSceneObject child = currentSO->getChild(i);
+				if (child->getComponent<CAnimation>() != nullptr)
+					continue;
+
+				todo.push(child);
+			}
+		}
+
+		return bones;
+	}
+
 	void CAnimation::eventTriggered(const HAnimationClip& clip, const String& name)
 	{
 		onEventTriggered(clip, name);

+ 109 - 0
Source/BansheeEngine/Source/BsCBone.cpp

@@ -0,0 +1,109 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsCBone.h"
+#include "BsSceneObject.h"
+#include "BsCAnimation.h"
+#include "BsCBoneRTTI.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	CBone::CBone(const HSceneObject& parent)
+		: Component(parent)
+	{
+		setName("Bone");
+
+		mNotifyFlags = TCF_Parent;
+	}
+
+	void CBone::setBoneName(const String& name)
+	{
+		if (mBoneName == name)
+			return;
+
+		mBoneName = name;
+
+		if (mParent != nullptr)
+			mParent->_notifyBoneChanged(getHandle());
+	}
+
+	void CBone::onDestroyed()
+	{
+		if (mParent != nullptr)
+			mParent->_removeBone(getHandle());
+
+		mParent = nullptr;
+	}
+
+	void CBone::onDisabled()
+	{
+		if (mParent != nullptr)
+			mParent->_removeBone(getHandle());
+
+		mParent = nullptr;
+	}
+
+	void CBone::onEnabled()
+	{
+		updateParentAnimation();
+	}
+
+	void CBone::onTransformChanged(TransformChangedFlags flags)
+	{
+		if (!SO()->getActive())
+			return;
+
+		if ((flags & TCF_Parent) != 0)
+			updateParentAnimation();
+	}
+
+	void CBone::updateParentAnimation()
+	{
+		HSceneObject currentSO = SO();
+		while (currentSO != nullptr)
+		{
+			HAnimation parent = currentSO->getComponent<CAnimation>();
+			if (parent != nullptr)
+			{
+				if (currentSO->getActive())
+					_setParent(parent);
+				else
+					_setParent(HAnimation());
+
+				return;
+			}
+
+			currentSO = currentSO->getParent();
+		}
+
+		_setParent(HAnimation());
+	}
+
+	void CBone::_setParent(const HAnimation& animation, bool isInternal)
+	{
+		if (animation == mParent)
+			return;
+
+		if (!isInternal)
+		{
+			if (mParent != nullptr)
+				mParent->_removeBone(getHandle());
+
+			if (animation != nullptr)
+				animation->_addBone(getHandle());
+		}
+
+		mParent = animation;
+	}
+	
+	RTTITypeBase* CBone::getRTTIStatic()
+	{
+		return CBoneRTTI::instance();
+	}
+
+	RTTITypeBase* CBone::getRTTI() const
+	{
+		return CBone::getRTTIStatic();
+	}
+}

+ 28 - 20
Source/MBansheeEngine/Animation/Animation.cs

@@ -631,35 +631,43 @@ namespace BansheeEngine
             if (primaryClip != null)
             {
                 SceneObject root = SceneObject;
-                AnimationCurves curves = primaryClip.Curves;
-                foreach (var curve in curves.PositionCurves)
+
+                Action<NamedVector3Curve[]> findMappings = x =>
                 {
-                    if (curve.Flags.HasFlag(AnimationCurveFlags.ImportedCurve))
-                        continue;
+                    foreach (var curve in x)
+                    {
+                        if (curve.Flags.HasFlag(AnimationCurveFlags.ImportedCurve))
+                            continue;
 
-                    SceneObject currentSO = FindSceneObject(root, curve.Name);
+                        SceneObject currentSO = FindSceneObject(root, curve.Name);
 
-                    bool found = false;
-                    for (int i = 0; i < newMappingInfos.Count; i++)
-                    {
-                        if (newMappingInfos[i].sceneObject == currentSO)
+                        bool found = false;
+                        for (int i = 0; i < newMappingInfos.Count; i++)
                         {
-                            found = true;
-                            break;
+                            if (newMappingInfos[i].sceneObject == currentSO)
+                            {
+                                found = true;
+                                break;
+                            }
                         }
-                    }
 
-                    if (!found)
-                    {
-                        SceneObjectMappingInfo newMappingInfo = new SceneObjectMappingInfo();
-                        newMappingInfo.isMappedToBone = false;
-                        newMappingInfo.sceneObject = currentSO;
+                        if (!found)
+                        {
+                            SceneObjectMappingInfo newMappingInfo = new SceneObjectMappingInfo();
+                            newMappingInfo.isMappedToBone = false;
+                            newMappingInfo.sceneObject = currentSO;
 
-                        newMappingInfos.Add(newMappingInfo);
+                            newMappingInfos.Add(newMappingInfo);
 
-                        _native.MapCurveToSceneObject(curve.Name, currentSO);
+                            _native.MapCurveToSceneObject(curve.Name, currentSO);
+                        }
                     }
-                }
+                };
+
+                AnimationCurves curves = primaryClip.Curves;
+                findMappings(curves.PositionCurves);
+                findMappings(curves.RotationCurves);
+                findMappings(curves.ScaleCurves);
             }
 
             mappingInfo = newMappingInfos;

+ 1 - 1
Source/MBansheeEngine/Animation/Bone.cs

@@ -39,7 +39,7 @@ namespace BansheeEngine
 
         private void OnInitialize()
         {
-            NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
+            NotifyFlags = TransformChangedFlags.Parent;
         }
 
         private void OnEnable()