فهرست منبع

Exposed Skeleton to C#

BearishSun 9 سال پیش
والد
کامیت
f7cb54a2ad

+ 27 - 8
Source/BansheeCore/Source/BsMesh.cpp

@@ -120,12 +120,17 @@ namespace BansheeEngine
 
 
 		if (meshData.getIndexElementSize() != ibProps.getIndexSize())
 		if (meshData.getIndexElementSize() != ibProps.getIndexSize())
 		{
 		{
-			BS_EXCEPT(InvalidParametersException, "Provided index size doesn't match meshes index size. Needed: " +
+			LOGERR("Provided index size doesn't match meshes index size. Needed: " +
 				toString(ibProps.getIndexSize()) + ". Got: " + toString(meshData.getIndexElementSize()));
 				toString(ibProps.getIndexSize()) + ". Got: " + toString(meshData.getIndexElementSize()));
+
+			return;
 		}
 		}
 
 
 		if (indicesSize > mIndexBuffer->getSizeInBytes())
 		if (indicesSize > mIndexBuffer->getSizeInBytes())
-			BS_EXCEPT(InvalidParametersException, "Index buffer values are being written out of valid range.");
+		{
+			indicesSize = mIndexBuffer->getSizeInBytes();
+			LOGERR("Index buffer values are being written out of valid range.");
+		}
 
 
 		mIndexBuffer->writeData(0, indicesSize, srcIdxData, discardEntireBuffer ? BufferWriteType::Discard : BufferWriteType::Normal);
 		mIndexBuffer->writeData(0, indicesSize, srcIdxData, discardEntireBuffer ? BufferWriteType::Discard : BufferWriteType::Normal);
 
 
@@ -143,8 +148,10 @@ namespace BansheeEngine
 			UINT32 otherVertSize = meshData.getVertexDesc()->getVertexStride(i);
 			UINT32 otherVertSize = meshData.getVertexDesc()->getVertexStride(i);
 			if (myVertSize != otherVertSize)
 			if (myVertSize != otherVertSize)
 			{
 			{
-				BS_EXCEPT(InvalidParametersException, "Provided vertex size for stream " + toString(i) + " doesn't match meshes vertex size. Needed: " +
+				LOGERR("Provided vertex size for stream " + toString(i) + " doesn't match meshes vertex size. Needed: " +
 					toString(myVertSize) + ". Got: " + toString(otherVertSize));
 					toString(myVertSize) + ". Got: " + toString(otherVertSize));
+				
+				continue;
 			}
 			}
 
 
 			SPtr<VertexBufferCore> vertexBuffer = mVertexData->getBuffer(i);
 			SPtr<VertexBufferCore> vertexBuffer = mVertexData->getBuffer(i);
@@ -153,7 +160,10 @@ namespace BansheeEngine
 			UINT8* srcVertBufferData = meshData.getStreamData(i);
 			UINT8* srcVertBufferData = meshData.getStreamData(i);
 
 
 			if (bufferSize > vertexBuffer->getSizeInBytes())
 			if (bufferSize > vertexBuffer->getSizeInBytes())
-				BS_EXCEPT(InvalidParametersException, "Vertex buffer values for stream \"" + toString(i) + "\" are being written out of valid range.");
+			{
+				bufferSize = vertexBuffer->getSizeInBytes();
+				LOGERR("Vertex buffer values for stream \"" + toString(i) + "\" are being written out of valid range.");
+			}
 
 
 			if (RenderAPICore::instance().getAPIInfo().getVertexColorFlipRequired())
 			if (RenderAPICore::instance().getAPIInfo().getVertexColorFlipRequired())
 			{
 			{
@@ -205,8 +215,9 @@ namespace BansheeEngine
 
 
 			if (meshData.getIndexElementSize() != ibProps.getIndexSize())
 			if (meshData.getIndexElementSize() != ibProps.getIndexSize())
 			{
 			{
-				BS_EXCEPT(InvalidParametersException, "Provided index size doesn't match meshes index size. Needed: " +
+				LOGERR("Provided index size doesn't match meshes index size. Needed: " +
 					toString(ibProps.getIndexSize()) + ". Got: " + toString(meshData.getIndexElementSize()));
 					toString(ibProps.getIndexSize()) + ". Got: " + toString(meshData.getIndexElementSize()));
+				return;
 			}
 			}
 
 
 			UINT8* idxData = static_cast<UINT8*>(mIndexBuffer->lock(GBL_READ_ONLY));
 			UINT8* idxData = static_cast<UINT8*>(mIndexBuffer->lock(GBL_READ_ONLY));
@@ -223,7 +234,10 @@ namespace BansheeEngine
 
 
 			UINT32 indicesSize = numIndicesToCopy * idxElemSize;
 			UINT32 indicesSize = numIndicesToCopy * idxElemSize;
 			if (indicesSize > meshData.getIndexBufferSize())
 			if (indicesSize > meshData.getIndexBufferSize())
-				BS_EXCEPT(InvalidParametersException, "Provided buffer doesn't have enough space to store mesh indices.");
+			{
+				LOGERR("Provided buffer doesn't have enough space to store mesh indices.");
+				return;
+			}
 
 
 			memcpy(indices, idxData, numIndicesToCopy * idxElemSize);
 			memcpy(indices, idxData, numIndicesToCopy * idxElemSize);
 
 
@@ -248,15 +262,20 @@ namespace BansheeEngine
 				UINT32 otherVertSize = meshData.getVertexDesc()->getVertexStride(streamIdx);
 				UINT32 otherVertSize = meshData.getVertexDesc()->getVertexStride(streamIdx);
 				if (myVertSize != otherVertSize)
 				if (myVertSize != otherVertSize)
 				{
 				{
-					BS_EXCEPT(InvalidParametersException, "Provided vertex size for stream " + toString(streamIdx) + " doesn't match meshes vertex size. Needed: " +
+					LOGERR("Provided vertex size for stream " + toString(streamIdx) + " doesn't match meshes vertex size. Needed: " +
 						toString(myVertSize) + ". Got: " + toString(otherVertSize));
 						toString(myVertSize) + ". Got: " + toString(otherVertSize));
+
+					continue;
 				}
 				}
 
 
 				UINT32 numVerticesToCopy = meshData.getNumVertices();
 				UINT32 numVerticesToCopy = meshData.getNumVertices();
 				UINT32 bufferSize = vbProps.getVertexSize() * numVerticesToCopy;
 				UINT32 bufferSize = vbProps.getVertexSize() * numVerticesToCopy;
 
 
 				if (bufferSize > vertexBuffer->getSizeInBytes())
 				if (bufferSize > vertexBuffer->getSizeInBytes())
-					BS_EXCEPT(InvalidParametersException, "Vertex buffer values for stream \"" + toString(streamIdx) + "\" are being read out of valid range.");
+				{
+					LOGERR("Vertex buffer values for stream \"" + toString(streamIdx) + "\" are being read out of valid range.");
+					continue;
+				}
 
 
 				UINT8* vertDataPtr = static_cast<UINT8*>(vertexBuffer->lock(GBL_READ_ONLY));
 				UINT8* vertDataPtr = static_cast<UINT8*>(vertexBuffer->lock(GBL_READ_ONLY));
 
 

+ 6 - 2
Source/MBansheeEditor/Inspectors/BoneInspector.cs

@@ -117,8 +117,12 @@ namespace BansheeEditor
             if (mesh == null)
             if (mesh == null)
                 return null;
                 return null;
 
 
-            //Skeleton skeleton = mesh.Skeleton;
-            return null;
+            Skeleton skeleton = mesh.Skeleton;
+            string[] boneNames = new string[skeleton.NumBones];
+            for (int i = 0; i < boneNames.Length; i++)
+                boneNames[i] = skeleton.GetBoneInfo(i).Name;
+
+            return boneNames;
         }
         }
         
         
         /// <summary>
         /// <summary>

+ 71 - 0
Source/MBansheeEngine/Animation/Skeleton.cs

@@ -0,0 +1,71 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+using System.Runtime.CompilerServices;
+
+namespace BansheeEngine
+{
+    /** @addtogroup Animation
+     *  @{
+     */
+
+    /// <summary>
+    /// Contains information about bones required for skeletal animation.
+    /// </summary>
+    public class Skeleton : ScriptObject
+    {
+        /// <summary>
+        /// Constructor for internal runtime use only.
+        /// </summary>
+        private Skeleton()
+        { }
+
+        /// <summary>
+        /// Returns the total number of bones in the skeleton.
+        /// </summary>
+        /// <returns>Number of bones.</returns>
+        public int NumBones
+        {
+            get { return Internal_GetNumBones(mCachedPtr); }
+        }
+
+        /// <summary>
+        /// Returns information about a bone at the provided index.
+        /// </summary>
+        /// <param name="boneIdx">Index of the bone to retrieve information for.</param>
+        /// <returns>Information about the bone, or null if index was out of range.</returns>
+        public BoneInfo GetBoneInfo(int boneIdx)
+        {
+            return Internal_GetBoneInfo(mCachedPtr, boneIdx);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern int Internal_GetNumBones(IntPtr thisPtr);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern BoneInfo Internal_GetBoneInfo(IntPtr thisPtr, int boneIdx);
+    }
+
+    /// <summary>
+    /// Contains internal information about a single bone in a <see cref="Skeleton"/>.
+    /// </summary>
+    public class BoneInfo
+    {
+        /// <summary>
+        /// Unique name of the bone.
+        /// </summary>
+        public string Name;
+
+        /// <summary>
+        /// Index of the parent bone (within the relevant <see cref="Skeleton"/> object). -1 if root bone.
+        /// </summary>
+        public int Parent;
+
+        /// <summary>
+        /// Inverse transform of the pose the skeleton was initially created in.
+        /// </summary>
+        public Matrix4 InvBindPose;
+    }
+
+    /** @} */
+}

+ 1 - 0
Source/MBansheeEngine/MBansheeEngine.csproj

@@ -49,6 +49,7 @@
     <Compile Include="Animation\AnimationCurves.cs" />
     <Compile Include="Animation\AnimationCurves.cs" />
     <Compile Include="Animation\Bone.cs" />
     <Compile Include="Animation\Bone.cs" />
     <Compile Include="Animation\Interop\NativeAnimation.cs" />
     <Compile Include="Animation\Interop\NativeAnimation.cs" />
+    <Compile Include="Animation\Skeleton.cs" />
     <Compile Include="Audio\Audio.cs" />
     <Compile Include="Audio\Audio.cs" />
     <Compile Include="Audio\AudioClip.cs" />
     <Compile Include="Audio\AudioClip.cs" />
     <Compile Include="Audio\AudioListener.cs" />
     <Compile Include="Audio\AudioListener.cs" />

+ 19 - 0
Source/MBansheeEngine/Rendering/Mesh.cs

@@ -142,6 +142,22 @@ namespace BansheeEngine
         public MeshData MeshData
         public MeshData MeshData
         {
         {
             get { return Internal_GetMeshData(mCachedPtr); }
             get { return Internal_GetMeshData(mCachedPtr); }
+            set
+            {
+                IntPtr meshDataPtr = IntPtr.Zero;
+                if (value != null)
+                    meshDataPtr = value.GetCachedPtr();
+
+                Internal_SetMeshData(mCachedPtr, meshDataPtr);
+            }
+        }
+
+        /// <summary>
+        /// Gets the skeleton required for animation of this mesh, if any is available.
+        /// </summary>
+        public Skeleton Skeleton
+        {
+            get { return Internal_GetSkeleton(mCachedPtr); }
         }
         }
 
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
@@ -158,6 +174,9 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern int Internal_GetSubMeshCount(IntPtr thisPtr);
         private static extern int Internal_GetSubMeshCount(IntPtr thisPtr);
 
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Skeleton Internal_GetSkeleton(IntPtr thisPtr);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_GetBounds(IntPtr thisPtr, out AABox box, out Sphere sphere);
         private static extern void Internal_GetBounds(IntPtr thisPtr, out AABox box, out Sphere sphere);
 
 

+ 2 - 0
Source/SBansheeEngine/CMakeSources.cmake

@@ -145,6 +145,7 @@ set(BS_SBANSHEEENGINE_INC_WRAPPERS
 	"Include/BsScriptAnimation.h"
 	"Include/BsScriptAnimation.h"
 	"Include/BsScriptAnimationCurve.h"
 	"Include/BsScriptAnimationCurve.h"
 	"Include/BsScriptAnimationCurves.h"
 	"Include/BsScriptAnimationCurves.h"
+	"Include/BsScriptSkeleton.h"
 )
 )
 
 
 set(BS_SBANSHEEENGINE_INC_WRAPPERS_GUI
 set(BS_SBANSHEEENGINE_INC_WRAPPERS_GUI
@@ -280,6 +281,7 @@ set(BS_SBANSHEEENGINE_SRC_WRAPPERS
 	"Source/BsScriptAnimation.cpp"
 	"Source/BsScriptAnimation.cpp"
 	"Source/BsScriptAnimationCurve.cpp"
 	"Source/BsScriptAnimationCurve.cpp"
 	"Source/BsScriptAnimationCurves.cpp"
 	"Source/BsScriptAnimationCurves.cpp"
+	"Source/BsScriptSkeleton.cpp"
 )
 )
 
 
 set(BS_SBANSHEEENGINE_INC_SERIALIZATION
 set(BS_SBANSHEEENGINE_INC_SERIALIZATION

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

@@ -72,7 +72,7 @@ namespace BansheeEngine
 	public:
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "BlendClipInfo")
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "BlendClipInfo")
 
 
-		/** Converts managed split info to its native counterpart. */
+		/** Converts managed object to its native counterpart. */
 		static BlendClipInfo fromManaged(MonoObject* object);
 		static BlendClipInfo fromManaged(MonoObject* object);
 
 
 	private:
 	private:
@@ -91,7 +91,7 @@ namespace BansheeEngine
 	public:
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "Blend1DInfo")
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "Blend1DInfo")
 
 
-		/** Converts managed split info to its native counterpart. */
+		/** Converts managed object to its native counterpart. */
 		static Blend1DInfo fromManaged(MonoObject* object);
 		static Blend1DInfo fromManaged(MonoObject* object);
 
 
 	private:
 	private:
@@ -109,7 +109,7 @@ namespace BansheeEngine
 	public:
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "Blend2DInfo")
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "Blend2DInfo")
 
 
-		/** Converts managed split info to its native counterpart. */
+		/** Converts managed object to its native counterpart. */
 		static Blend2DInfo fromManaged(MonoObject* object);
 		static Blend2DInfo fromManaged(MonoObject* object);
 
 
 	private:
 	private:

+ 1 - 0
Source/SBansheeEngine/Include/BsScriptMesh.h

@@ -84,6 +84,7 @@ namespace BansheeEngine
 			MeshUsage usage);
 			MeshUsage usage);
 		static MonoArray* internal_GetSubMeshes(ScriptMesh* thisPtr);
 		static MonoArray* internal_GetSubMeshes(ScriptMesh* thisPtr);
 		static UINT32 internal_GetSubMeshCount(ScriptMesh* thisPtr);
 		static UINT32 internal_GetSubMeshCount(ScriptMesh* thisPtr);
+		static MonoObject* internal_GetSkeleton(ScriptMesh* thisPtr);
 		static void internal_GetBounds(ScriptMesh* thisPtr, AABox* box, Sphere* sphere);
 		static void internal_GetBounds(ScriptMesh* thisPtr, AABox* box, Sphere* sphere);
 		static MonoObject* internal_GetMeshData(ScriptMesh* thisPtr);
 		static MonoObject* internal_GetMeshData(ScriptMesh* thisPtr);
 		static void internal_SetMeshData(ScriptMesh* thisPtr, ScriptMeshData* value);
 		static void internal_SetMeshData(ScriptMesh* thisPtr, ScriptMeshData* value);

+ 62 - 0
Source/SBansheeEngine/Include/BsScriptSkeleton.h

@@ -0,0 +1,62 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsSkeleton.h"
+
+namespace BansheeEngine
+{
+	class ScriptAnimationClip;
+
+	/** @addtogroup ScriptInteropEngine
+	 *  @{
+	 */
+
+	/**	Interop class between C++ & CLR for Skeleton. */
+	class BS_SCR_BE_EXPORT ScriptSkeleton : public ScriptObject <ScriptSkeleton>
+	{
+	public:
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "Skeleton")
+
+		/**	Returns the native wrapped animation. */
+		SPtr<Skeleton> getInternal() const { return mSkeleton; }
+
+		/**	Creates a new managed Skeleton instance wrapping the provided native skeleton. */
+		static MonoObject* create(const SPtr<Skeleton>& skeleton);
+
+	private:
+		ScriptSkeleton(MonoObject* managedInstance, const SPtr<Skeleton>& skeleton);
+
+		SPtr<Skeleton> mSkeleton;
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static int internal_GetNumBones(ScriptSkeleton* thisPtr);
+		static MonoObject* internal_GetBoneInfo(ScriptSkeleton* thisPtr, UINT32 boneIdx);
+	};
+
+	/** Helper class for dealing with SkeletonBoneInfo structure. */
+	class BS_SCR_BE_EXPORT ScriptBoneInfo : public ScriptObject<ScriptBoneInfo>
+	{
+	public:
+		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "BoneInfo")
+
+		/** Converts native bone info to its native counterpart. */
+		static MonoObject* toManaged(const SkeletonBoneInfo& boneInfo, const Matrix4& invBindPose);
+
+	private:
+		ScriptBoneInfo(MonoObject* instance);
+
+		/************************************************************************/
+		/* 								CLR HOOKS						   		*/
+		/************************************************************************/
+		static MonoField* sNameField;
+		static MonoField* sParentField;
+		static MonoField* sInvBindPoseField;
+	};
+	
+	/** @} */
+}

+ 13 - 0
Source/SBansheeEngine/Source/BsScriptMesh.cpp

@@ -8,6 +8,7 @@
 #include "BsMonoArray.h"
 #include "BsMonoArray.h"
 #include "BsMonoManager.h"
 #include "BsMonoManager.h"
 #include "BsCoreThread.h"
 #include "BsCoreThread.h"
+#include "BsScriptSkeleton.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -41,6 +42,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_CreateInstanceMeshData", &ScriptMesh::internal_CreateInstanceMeshData);
 		metaData.scriptClass->addInternalCall("Internal_CreateInstanceMeshData", &ScriptMesh::internal_CreateInstanceMeshData);
 		metaData.scriptClass->addInternalCall("Internal_GetSubMeshes", &ScriptMesh::internal_GetSubMeshes);
 		metaData.scriptClass->addInternalCall("Internal_GetSubMeshes", &ScriptMesh::internal_GetSubMeshes);
 		metaData.scriptClass->addInternalCall("Internal_GetSubMeshCount", &ScriptMesh::internal_GetSubMeshCount);
 		metaData.scriptClass->addInternalCall("Internal_GetSubMeshCount", &ScriptMesh::internal_GetSubMeshCount);
+		metaData.scriptClass->addInternalCall("Internal_GetSkeleton", &ScriptMesh::internal_GetSkeleton);
 		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptMesh::internal_GetBounds);
 		metaData.scriptClass->addInternalCall("Internal_GetBounds", &ScriptMesh::internal_GetBounds);
 		metaData.scriptClass->addInternalCall("Internal_GetMeshData", &ScriptMesh::internal_GetMeshData);
 		metaData.scriptClass->addInternalCall("Internal_GetMeshData", &ScriptMesh::internal_GetMeshData);
 		metaData.scriptClass->addInternalCall("Internal_SetMeshData", &ScriptMesh::internal_SetMeshData);
 		metaData.scriptClass->addInternalCall("Internal_SetMeshData", &ScriptMesh::internal_SetMeshData);
@@ -105,6 +107,17 @@ namespace BansheeEngine
 		return mesh->getProperties().getNumSubMeshes();
 		return mesh->getProperties().getNumSubMeshes();
 	}
 	}
 
 
+	MonoObject* ScriptMesh::internal_GetSkeleton(ScriptMesh* thisPtr)
+	{
+		HMesh mesh = thisPtr->getHandle();
+
+		SPtr<Skeleton> skeleton = mesh->getSkeleton();
+		if (skeleton == nullptr)
+			return nullptr;
+
+		return ScriptSkeleton::create(skeleton);
+	}
+
 	void ScriptMesh::internal_GetBounds(ScriptMesh* thisPtr, AABox* box, Sphere* sphere)
 	void ScriptMesh::internal_GetBounds(ScriptMesh* thisPtr, AABox* box, Sphere* sphere)
 	{
 	{
 		HMesh mesh = thisPtr->getHandle();
 		HMesh mesh = thisPtr->getHandle();

+ 82 - 0
Source/SBansheeEngine/Source/BsScriptSkeleton.cpp

@@ -0,0 +1,82 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+#include "BsScriptSkeleton.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoMethod.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+
+using namespace std::placeholders;
+
+namespace BansheeEngine
+{
+	ScriptSkeleton::ScriptSkeleton(MonoObject* managedInstance, const SPtr<Skeleton>& skeleton)
+		:ScriptObject(managedInstance), mSkeleton(skeleton)
+	{
+
+	}
+
+	void ScriptSkeleton::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_GetNumBones", &ScriptSkeleton::internal_GetNumBones);
+		metaData.scriptClass->addInternalCall("Internal_GetBoneInfo", &ScriptSkeleton::internal_GetBoneInfo);
+	}
+
+	MonoObject* ScriptSkeleton::create(const SPtr<Skeleton>& skeleton)
+	{
+		MonoObject* instance = metaData.scriptClass->createInstance();
+
+		new (bs_alloc<ScriptSkeleton>()) ScriptSkeleton(instance, skeleton);
+		return instance;
+	}
+
+	int ScriptSkeleton::internal_GetNumBones(ScriptSkeleton* thisPtr)
+	{
+		return thisPtr->getInternal()->getNumBones();
+	}
+
+	MonoObject* ScriptSkeleton::internal_GetBoneInfo(ScriptSkeleton* thisPtr, UINT32 boneIdx)
+	{
+		SPtr<Skeleton> skeleton = thisPtr->getInternal();
+
+		UINT32 numBones = skeleton->getNumBones();
+		if (boneIdx >= numBones)
+			return nullptr;
+
+		const SkeletonBoneInfo& boneInfo = skeleton->getBoneInfo(boneIdx);
+		const Matrix4& invBindPose = skeleton->getInvBindPose(boneIdx);
+
+		return ScriptBoneInfo::toManaged(boneInfo, invBindPose);
+	}
+	
+	MonoField* ScriptBoneInfo::sNameField = nullptr;
+	MonoField* ScriptBoneInfo::sParentField = nullptr;
+	MonoField* ScriptBoneInfo::sInvBindPoseField = nullptr;
+
+	ScriptBoneInfo::ScriptBoneInfo(MonoObject* instance)
+		:ScriptObject(instance)
+	{ }
+
+	void ScriptBoneInfo::initRuntimeData()
+	{
+		sNameField = metaData.scriptClass->getField("Name");
+		sParentField = metaData.scriptClass->getField("Parent");
+		sInvBindPoseField = metaData.scriptClass->getField("InvBindPose");
+	}
+
+	MonoObject* ScriptBoneInfo::toManaged(const SkeletonBoneInfo& boneInfo, const Matrix4& invBindPose)
+	{
+		MonoString* monoName = MonoUtil::stringToMono(boneInfo.name);
+		int parentIdx = boneInfo.parent;
+		Matrix4 monoInvBindPose = invBindPose;
+
+		MonoObject* instance = metaData.scriptClass->createInstance();
+		sNameField->setValue(instance, monoName);
+		sParentField->setValue(instance, &parentIdx);
+		sInvBindPoseField->setValue(instance, &monoInvBindPose);
+
+		return instance;
+	}
+}