Pārlūkot izejas kodu

Hooking up animation component so it can update scene object transforms (WIP)

BearishSun 9 gadi atpakaļ
vecāks
revīzija
59f9e3ae5b

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

@@ -338,7 +338,8 @@ namespace BansheeEngine
 		 * the provided scene object. Also allow the opposite operation which can allow scene object transform changes
 		 * to manipulate object bones.
 		 *
-		 * @param[in]	curve	Name of the curve (bone) to connect the scene object with.
+		 * @param[in]	curve	Name of the curve (bone) to connect the scene object with. Use empty string to map to the
+		 *						root bone, regardless of the bone name.
 		 * @param[in]	so		Scene object to influence by the curve modifications, and vice versa.
 		 */
 		void mapCurveToSceneObject(const String& curve, const HSceneObject& so);

+ 3 - 0
Source/BansheeCore/Include/BsSkeleton.h

@@ -150,6 +150,9 @@ namespace BansheeEngine
 		/** Returns information about a bone at the provided index. */
 		const SkeletonBoneInfo& getBoneInfo(UINT32 idx) const { return mBoneInfo[idx]; }
 
+		/** Searches all bones to find a root bone. Returns -1 if no root can be found. */
+		UINT32 getRootBoneIndex() const;
+
 		/** Returns the inverse bind pose for the bone at the provided index. */
 		const Matrix4& getInvBindPose(UINT32 idx) const { return mInvBindPoses[idx]; }
 

+ 21 - 8
Source/BansheeCore/Source/BsAnimation.cpp

@@ -215,17 +215,30 @@ namespace BansheeEngine
 			{
 				for (UINT32 i = 0; i < numSceneObjects; i++)
 				{
-					for (UINT32 j = 0; j < numBones; j++)
-					{
-						if (sceneObjects[i].so.isDestroyed(true))
-							continue;
+					if (sceneObjects[i].so.isDestroyed(true))
+						continue;
 
-						if (skeleton->getBoneInfo(j).name == sceneObjects[i].curveName)
+					// Empty string always means root bone
+					if (sceneObjects[i].curveName.empty())
+					{
+						UINT32 rootBoneIdx = skeleton->getRootBoneIndex();
+						if (rootBoneIdx != (UINT32)-1)
 						{
-							mappedBoneIndices[i] = j;
-
+							mappedBoneIndices[i] = rootBoneIdx;
 							numBoneMappedSOs++;
-							break;
+						}
+					}
+					else
+					{
+						for (UINT32 j = 0; j < numBones; j++)
+						{
+							if (skeleton->getBoneInfo(j).name == sceneObjects[i].curveName)
+							{
+								mappedBoneIndices[i] = j;
+
+								numBoneMappedSOs++;
+								break;
+							}
 						}
 					}
 				}

+ 11 - 0
Source/BansheeCore/Source/BsSkeleton.cpp

@@ -289,6 +289,17 @@ namespace BansheeEngine
 		bs_stack_free(isGlobal);
 	}
 
+	UINT32 Skeleton::getRootBoneIndex() const
+	{
+		for (UINT32 i = 0; i < mNumBones; i++)
+		{
+			if (mBoneInfo[i].parent == (UINT32)-1)
+				return i;
+		}
+
+		return (UINT32)-1;
+	}
+
 	SPtr<Skeleton> Skeleton::createEmpty()
 	{
 		Skeleton* rawPtr = new (bs_alloc<Skeleton>()) Skeleton();

+ 143 - 0
Source/MBansheeEditor/Inspectors/BoneInspector.cs

@@ -0,0 +1,143 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System.Collections;
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /** @addtogroup Inspectors
+     *  @{
+     */
+
+    /// <summary>
+    /// Renders an inspector for the <see cref="Bone"/> component.
+    /// </summary>
+    [CustomInspector(typeof(Bone))]
+    internal class BoneInspector : Inspector
+    {
+        private GUIListBoxField boneField;
+        private InspectableState modifyState;
+
+        private string selectedBoneName;
+
+        /// <inheritdoc/>
+        protected internal override void Initialize()
+        {
+            BuildGUI();
+        }
+
+        /// <inheritdoc/>
+        protected internal override InspectableState Refresh()
+        {
+            Bone bone = InspectedObject as Bone;
+            if (bone == null)
+                return InspectableState.NotModified;
+
+            if (selectedBoneName != bone.Name)
+            {
+                string[] boneNames = GetBoneNames(bone);
+
+                if (boneNames != null)
+                {
+                    for (int i = 0; i < boneNames.Length; i++)
+                    {
+                        if (bone.Name == boneNames[i])
+                        {
+                            boneField.Index = i;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            InspectableState oldState = modifyState;
+            if (modifyState.HasFlag(InspectableState.Modified))
+                modifyState = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Recreates all the GUI elements used by this inspector.
+        /// </summary>
+        private void BuildGUI()
+        {
+            Layout.Clear();
+
+            Bone bone = InspectedObject as Bone;
+            if (bone == null)
+                return;
+
+            string[] boneNames = GetBoneNames(bone);
+            if(boneNames == null)
+                boneNames = new string[0];
+
+            boneField = new GUIListBoxField(boneNames, false, new LocEdString("Bone"));
+
+            Layout.AddElement(boneField);
+
+            boneField.OnSelectionChanged += x =>
+            {
+                selectedBoneName = boneNames[x];
+                bone.Name = selectedBoneName;
+                
+                MarkAsModified();
+                ConfirmModify();
+            };
+        }
+
+        /// <summary>
+        /// Finds all available bones for the animation the provided bone is a part of.
+        /// </summary>
+        /// <param name="bone">Bone for which to return the parent skeleton's bones.</param>
+        /// <returns>List of bones if parent skeleton is found, or null.</returns>
+        private string[] GetBoneNames(Bone bone)
+        {
+            Animation animParent = null;
+
+            SceneObject currentSO = bone.SceneObject;
+            while (currentSO != null)
+            {
+                animParent = currentSO.GetComponent<Animation>();
+                if (animParent != null)
+                    break;
+
+                currentSO = currentSO.Parent;
+            }
+
+            if(animParent == null)
+                return null;
+
+            Renderable renderable = animParent.SceneObject.GetComponent<Renderable>();
+            if (renderable == null)
+                return null;
+
+            Mesh mesh = renderable.Mesh;
+            if (mesh == null)
+                return null;
+
+            //Skeleton skeleton = mesh.Skeleton;
+            return null;
+        }
+        
+        /// <summary>
+        /// Marks the contents of the inspector as modified.
+        /// </summary>
+        protected void MarkAsModified()
+        {
+            modifyState |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Confirms any queued modifications.
+        /// </summary>
+        protected void ConfirmModify()
+        {
+            if (modifyState.HasFlag(InspectableState.ModifyInProgress))
+                modifyState |= InspectableState.Modified;
+        }
+    }
+
+    /** @} */
+}

+ 1 - 0
Source/MBansheeEditor/MBansheeEditor.csproj

@@ -45,6 +45,7 @@
     <Compile Include="Inspectors\AudioClipInspector.cs" />
     <Compile Include="Inspectors\AudioListenerInspector.cs" />
     <Compile Include="Inspectors\AudioSourceInspector.cs" />
+    <Compile Include="Inspectors\BoneInspector.cs" />
     <Compile Include="Inspectors\PostProcessSettingsInspector.cs" />
     <Compile Include="Utility\EdAnimationCurve.cs" />
     <Compile Include="Utility\SerializedDiff.cs" />

+ 248 - 10
Source/MBansheeEngine/Animation/Animation.cs

@@ -109,7 +109,8 @@ namespace BansheeEngine
         [SerializeField] private SerializableData serializableData = new SerializableData();
 
         private FloatCurvePropertyInfo[] floatProperties;
-        private AnimationClip floatPropertyClip;
+        private List<SceneObjectMappingInfo> mappingInfo = new List<SceneObjectMappingInfo>();
+        private AnimationClip primaryClip;
 
         /// <summary>
         /// Contains mapping for a suffix used by property paths used for curve identifiers, to their index and type.
@@ -457,6 +458,49 @@ namespace BansheeEngine
             }
         }
 
+        /// <summary>
+        /// Searches the scene object hierarchy to find a child scene object using the provided path.
+        /// </summary>
+        /// <param name="root">Root scene object to which the path is relative to.</param>
+        /// <param name="path">Path to the property, where each element of the path is separated with "/". 
+        /// 
+        ///                    Path elements signify names of child scene objects (first one relative to 
+        ///                    <paramref name="root"/>. Name of the root element should not be included in the path.
+        ///                    Elements must be prefixed with "!" in order to match the path format of 
+        ///                    <see cref="FindProperty"/>.</param>
+        /// <returns>Child scene object if found, or null otherwise.</returns>
+        internal static SceneObject FindSceneObject(SceneObject root, string path)
+        {
+            if (string.IsNullOrEmpty(path) || root == null)
+                return null;
+
+            string trimmedPath = path.Trim('/');
+            string[] entries = trimmedPath.Split('/');
+
+            // Find scene object referenced by the path
+            SceneObject so = root;
+            int pathIdx = 0;
+            for (; pathIdx < entries.Length; pathIdx++)
+            {
+                string entry = entries[pathIdx];
+
+                if (string.IsNullOrEmpty(entry))
+                    continue;
+
+                // Not a scene object, break
+                if (entry[0] != '!')
+                    break;
+
+                string childName = entry.Substring(1, entry.Length - 1);
+                so = so.FindChild(childName);
+
+                if (so == null)
+                    break;
+            }
+
+            return so;
+        }
+
         /// <summary>
         /// Changes the state of a playing animation clip. If animation clip is not currently playing the state change is
         /// ignored.
@@ -474,19 +518,24 @@ namespace BansheeEngine
             if (_native == null)
                 return;
 
-            AnimationClip primaryClip = _native.GetClip(0);
-            if (primaryClip != floatPropertyClip)
+            AnimationClip newPrimaryClip = _native.GetClip(0);
+            if (newPrimaryClip != primaryClip)
             {
-                RebuildFloatProperties(primaryClip);
-                floatPropertyClip = primaryClip;
+                RebuildFloatProperties(newPrimaryClip);
+                primaryClip = newPrimaryClip;
+
+                UpdateSceneObjectMapping();
             }
 
-            // Apply values from generic float curves 
-            foreach (var entry in floatProperties)
+            // Apply values from generic float curves
+            if (floatProperties != null)
             {
-                float curveValue;
-                if (_native.GetGenericCurveValue(entry.curveIdx, out curveValue))
-                    entry.setter(curveValue);
+                foreach (var entry in floatProperties)
+                {
+                    float curveValue;
+                    if (_native.GetGenericCurveValue(entry.curveIdx, out curveValue))
+                        entry.setter(curveValue);
+                }
             }
         }
 
@@ -523,6 +572,13 @@ namespace BansheeEngine
             if (serializableData.defaultClip != null)
                 _native.Play(serializableData.defaultClip);
 
+            primaryClip = _native.GetClip(0);
+            if (primaryClip != null)
+                RebuildFloatProperties(primaryClip);
+
+            SetBoneMappings();
+            UpdateSceneObjectMapping();
+
             Renderable renderable = SceneObject.GetComponent<Renderable>();
             if (renderable == null)
                 return;
@@ -551,6 +607,178 @@ namespace BansheeEngine
                 _native.Destroy();
                 _native = null;
             }
+
+            primaryClip = null;
+            mappingInfo.Clear();
+            floatProperties = null;
+        }
+
+        /// <summary>
+        /// Finds any curves that affect a transform of a specific scene object, and ensures that animation properly updates
+        /// those transforms. This does not include curves referencing bones.
+        /// </summary>
+        private void UpdateSceneObjectMapping()
+        {
+            List<SceneObjectMappingInfo> newMappingInfos = new List<SceneObjectMappingInfo>();
+            foreach(var entry in mappingInfo)
+            {
+                if (entry.isMappedToBone)
+                    newMappingInfos.Add(entry);
+                else
+                    _native.UnmapSceneObject(entry.sceneObject);
+            }
+
+            if (primaryClip != null)
+            {
+                SceneObject root = SceneObject;
+                AnimationCurves curves = primaryClip.Curves;
+                foreach (var curve in curves.PositionCurves)
+                {
+                    if (curve.Flags.HasFlag(AnimationCurveFlags.ImportedCurve))
+                        continue;
+
+                    SceneObject currentSO = FindSceneObject(root, curve.Name);
+
+                    bool found = false;
+                    for (int i = 0; i < newMappingInfos.Count; i++)
+                    {
+                        if (newMappingInfos[i].sceneObject == currentSO)
+                        {
+                            found = true;
+                            break;
+                        }
+                    }
+
+                    if (!found)
+                    {
+                        SceneObjectMappingInfo newMappingInfo = new SceneObjectMappingInfo();
+                        newMappingInfo.isMappedToBone = false;
+                        newMappingInfo.sceneObject = currentSO;
+
+                        newMappingInfos.Add(newMappingInfo);
+
+                        _native.MapCurveToSceneObject(curve.Name, currentSO);
+                    }
+                }
+            }
+
+            mappingInfo = newMappingInfos;
+        }
+
+        /// <summary>
+        /// Registers a new bone component, creating a new transform mapping from the bone name to the scene object
+        /// the component is attached to.
+        /// </summary>
+        /// <param name="bone">Bone component to register.</param>
+        internal void AddBone(Bone bone)
+        {
+            if (_native == null)
+                return;
+
+            SceneObject currentSO = bone.SceneObject;
+
+            SceneObjectMappingInfo newMapping = new SceneObjectMappingInfo();
+            newMapping.sceneObject = currentSO;
+            newMapping.isMappedToBone = true;
+            newMapping.bone = bone;
+
+            mappingInfo.Add(newMapping);
+            _native.MapCurveToSceneObject(bone.Name, newMapping.sceneObject);
+        }
+
+        /// <summary>
+        /// Unregisters a bone component, removing the bone -> scene object mapping.
+        /// </summary>
+        /// <param name="bone">Bone to unregister.</param>
+        internal void RemoveBone(Bone bone)
+        {
+            if (_native == null)
+                return;
+
+            SceneObject newSO = null;
+            for (int i = 0; i < mappingInfo.Count; i++)
+            {
+                if (mappingInfo[i].bone == bone)
+                {
+                    mappingInfo.RemoveAt(i);
+                    _native.UnmapSceneObject(mappingInfo[i].sceneObject);
+                    i--;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Called whenever the bone the <see cref="Bone"/> component points to changed.
+        /// </summary>
+        /// <param name="bone">Bone component to modify.</param>
+        internal void NotifyBoneChanged(Bone bone)
+        {
+            if (_native == null)
+                return;
+
+            for (int i = 0; i < mappingInfo.Count; i++)
+            {
+                if (mappingInfo[i].bone == bone)
+                {
+                    _native.UnmapSceneObject(mappingInfo[i].sceneObject);
+                    _native.MapCurveToSceneObject(bone.Name, mappingInfo[i].sceneObject);
+                    break;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Finds any scene objects that are mapped to bone transforms. Such object's transforms will be affected by
+        /// skeleton bone animation.
+        /// </summary>
+        private void SetBoneMappings()
+        {
+            mappingInfo.Clear();
+
+            SceneObjectMappingInfo rootMapping = new SceneObjectMappingInfo();
+            rootMapping.sceneObject = SceneObject;
+            rootMapping.isMappedToBone = true;
+
+            mappingInfo.Add(rootMapping);
+            _native.MapCurveToSceneObject("", rootMapping.sceneObject);
+
+            Bone[] childBones = FindChildBones();
+            foreach (var entry in childBones)
+                AddBone(entry);
+        }
+
+        /// <summary>
+        /// Searches child scene objects for <see cref="Bone"/> components and returns any found ones.
+        /// </summary>
+        private Bone[] FindChildBones()
+        {
+            Stack<SceneObject> todo = new Stack<SceneObject>();
+            todo.Push(SceneObject);
+
+            List<Bone> bones = new List<Bone>();
+            while (todo.Count > 0)
+            {
+                SceneObject currentSO = todo.Pop();
+
+                Bone bone = currentSO.GetComponent<Bone>();
+                if (bone != null)
+                {
+                    bone.SetParent(this, true);
+                    bones.Add(bone);
+                }
+
+                int childCount = currentSO.GetNumChildren();
+                for (int i = 0; i < childCount; i++)
+                {
+                    SceneObject child = currentSO.GetChild(i);
+                    if (child.GetComponent<Animation>() != null)
+                        continue;
+
+                    todo.Push(child);
+                }
+            }
+
+            return bones.ToArray();
         }
 
         /// <summary>
@@ -740,6 +968,16 @@ namespace BansheeEngine
             public int elementIdx;
             public bool isVector;
         }
+
+        /// <summary>
+        /// Information about scene objects bound to a specific animation curve.
+        /// </summary>
+        internal struct SceneObjectMappingInfo
+        {
+            public SceneObject sceneObject;
+            public bool isMappedToBone;
+            public Bone bone;
+        }
     }
 
     /** @} */

+ 125 - 0
Source/MBansheeEngine/Animation/Bone.cs

@@ -0,0 +1,125 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+
+namespace BansheeEngine
+{
+    /** @addtogroup Animation
+     *  @{
+     */
+
+    /// <summary>
+    /// Component that maps animation for specific bone also be applied to the <see cref="SceneObject"/> this component
+    /// is attached to. The component will attach to the first found parent <see cref="Animation"/> component.
+    /// </summary>
+    public class Bone : Component
+    {
+        [SerializeField]
+        private string name;
+
+        private Animation parent;
+
+        /// <summary>
+        /// Name of the bone to attach to.
+        /// </summary>
+        public string Name
+        {
+            get { return name; }
+            set
+            {
+                if (name != value)
+                {
+                    name = value;
+
+                    if (parent != null)
+                        parent.NotifyBoneChanged(this);
+                }
+            }
+        }
+
+        private void OnInitialize()
+        {
+            NotifyFlags = TransformChangedFlags.Transform | TransformChangedFlags.Parent;
+        }
+
+        private void OnEnable()
+        {
+            UpdateParentAnimation();
+        }
+
+        private void OnDisable()
+        {
+            if (parent != null)
+                parent.RemoveBone(this);
+
+            parent = null;
+        }
+
+        private void OnDestroy()
+        {
+            if (parent != null)
+                parent.RemoveBone(this);
+
+            parent = null;
+        }
+
+        private void OnTransformChanged(TransformChangedFlags flags)
+        {
+            if (!SceneObject.Active)
+                return;
+
+            if ((flags & TransformChangedFlags.Parent) != 0)
+                UpdateParentAnimation();
+        }
+
+        /// <summary>
+        /// Attempts to find the parent <see cref="Animation"/> component and registers itself with it.
+        /// </summary>
+        private void UpdateParentAnimation()
+        {
+            SceneObject currentSO = SceneObject;
+            while (currentSO != null)
+            {
+                Animation parent = currentSO.GetComponent<Animation>();
+                if (parent != null)
+                {
+                    if (currentSO.Active)
+                        SetParent(parent);
+                    else
+                        SetParent(null);
+
+                    return;
+                }
+
+                currentSO = currentSO.Parent;
+            }
+
+            SetParent(null);
+        }
+
+        /// <summary>
+        /// Changes the parent animation of this component.
+        /// </summary>
+        /// <param name="animation">New animation parent, can be null.</param>
+        /// <param name="isInternal">If true the bone will just be changed internally, but parent animation will not be
+        ///                          notified.</param>
+        internal void SetParent(Animation animation, bool isInternal = false)
+        {
+            if (animation == parent)
+                return;
+
+            if (!isInternal)
+            {
+                if (parent != null)
+                    parent.RemoveBone(this);
+
+                if (animation != null)
+                    animation.AddBone(this);
+            }
+
+            parent = animation;
+        }
+    }
+
+    /** @} */
+}

+ 22 - 0
Source/MBansheeEngine/Animation/Interop/NativeAnimation.cs

@@ -116,6 +116,22 @@ namespace BansheeEngine
             return Internal_GetGenericCurveValue(mCachedPtr, curveIdx, out value);
         }
 
+        public void MapCurveToSceneObject(string curve, SceneObject sceneObject)
+        {
+            if (string.IsNullOrEmpty(curve) || sceneObject == null)
+                return;
+
+            Internal_MapCurveToSceneObject(mCachedPtr, curve, sceneObject.GetCachedPtr());
+        }
+
+        public void UnmapSceneObject(SceneObject sceneObject)
+        {
+            if (sceneObject == null)
+                return;
+
+            Internal_UnmapSceneObject(mCachedPtr, sceneObject.GetCachedPtr());
+        }
+
         internal NativeAnimation()
         {
             Internal_Create(this);
@@ -182,6 +198,12 @@ namespace BansheeEngine
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_GetGenericCurveValue(IntPtr thisPtr, int curveIdx, out float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_MapCurveToSceneObject(IntPtr thisPtr, string curve, IntPtr sceneObjectPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_UnmapSceneObject(IntPtr thisPtr, IntPtr sceneObjectPtr);
     }
 
     /** @endcond */

+ 1 - 0
Source/MBansheeEngine/MBansheeEngine.csproj

@@ -47,6 +47,7 @@
     <Compile Include="Animation\AnimationClip.cs" />
     <Compile Include="Animation\AnimationCurve.cs" />
     <Compile Include="Animation\AnimationCurves.cs" />
+    <Compile Include="Animation\Bone.cs" />
     <Compile Include="Animation\Interop\NativeAnimation.cs" />
     <Compile Include="Audio\Audio.cs" />
     <Compile Include="Audio\AudioClip.cs" />

+ 1 - 1
Source/MBansheeEngine/Physics/Collider.cs

@@ -222,7 +222,7 @@ namespace BansheeEngine
         internal abstract NativeCollider CreateCollider();
 
         /// <summary>
-        /// Changes the rigidbody parent of the collider. Meant to be called from the Rigidbody itself. 
+        /// Changes the rigidbody parent of the collider.
         /// </summary>
         /// <param name="rigidbody">New rigidbody to assign as the parent to the collider.</param>
         /// <param name="isInternal">If true the rigidbody will just be changed internally, but parent rigidbody will not be

+ 3 - 0
Source/SBansheeEngine/Include/BsScriptAnimation.h

@@ -57,6 +57,9 @@ namespace BansheeEngine
 		static UINT32 internal_GetNumClips(ScriptAnimation* thisPtr);
 		static MonoObject* internal_GetClip(ScriptAnimation* thisPtr, UINT32 idx);
 
+		static void internal_MapCurveToSceneObject(ScriptAnimation* thisPtr, MonoString* curve, ScriptSceneObject* so);
+		static void internal_UnmapSceneObject(ScriptAnimation* thisPtr, ScriptSceneObject* so);
+
 		static bool internal_GetGenericCurveValue(ScriptAnimation* thisPtr, UINT32 curveIdx, float* value);
 
 		typedef void(__stdcall *OnEventTriggeredThunkDef) (MonoObject*, MonoObject*, MonoString*, MonoException**);

+ 18 - 0
Source/SBansheeEngine/Source/BsScriptAnimation.cpp

@@ -7,6 +7,7 @@
 #include "BsMonoClass.h"
 #include "BsMonoManager.h"
 #include "BsMonoUtil.h"
+#include "BsScriptSceneObject.h"
 #include "BsScriptResourceManager.h"
 #include "BsScriptAnimationClip.h"
 
@@ -50,6 +51,9 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetNumClips", &ScriptAnimation::internal_GetNumClips);
 		metaData.scriptClass->addInternalCall("Internal_GetClip", &ScriptAnimation::internal_GetClip);
 
+		metaData.scriptClass->addInternalCall("Internal_MapCurveToSceneObject", &ScriptAnimation::internal_MapCurveToSceneObject);
+		metaData.scriptClass->addInternalCall("Internal_UnmapSceneObject", &ScriptAnimation::internal_UnmapSceneObject);
+
 		metaData.scriptClass->addInternalCall("Internal_GetGenericCurveValue", &ScriptAnimation::internal_GetGenericCurveValue);
 
 		sOnEventTriggeredThunk = (OnEventTriggeredThunkDef)metaData.scriptClass->getMethod("Internal_OnEventTriggered", 2)->getThunk();
@@ -180,6 +184,20 @@ namespace BansheeEngine
 		return thisPtr->getInternal()->getGenericCurveValue(curveIdx, *value);
 	}
 
+	void ScriptAnimation::internal_MapCurveToSceneObject(ScriptAnimation* thisPtr, MonoString* curve, ScriptSceneObject* so)
+	{
+		String curveName = MonoUtil::monoToString(curve);
+		HSceneObject soHandle = so->getNativeHandle();
+
+		thisPtr->getInternal()->mapCurveToSceneObject(curveName, soHandle);
+	}
+
+	void ScriptAnimation::internal_UnmapSceneObject(ScriptAnimation* thisPtr, ScriptSceneObject* so)
+	{
+		HSceneObject soHandle = so->getNativeHandle();
+		thisPtr->getInternal()->unmapSceneObject(soHandle);
+	}
+
 	MonoField* ScriptBlendClipInfo::clipField = nullptr;
 	MonoField* ScriptBlendClipInfo::positionField = nullptr;