Przeglądaj źródła

Finalizing Skin stuff

Panagiotis Christopoulos Charitos 14 lat temu
rodzic
commit
448dc6409f
3 zmienionych plików z 293 dodań i 287 usunięć
  1. 3 3
      anki/resource/Mesh.cpp
  2. 72 104
      anki/scene/SkinNode.cpp
  3. 218 180
      anki/scene/SkinNode.h

+ 3 - 3
anki/resource/Mesh.cpp

@@ -23,9 +23,9 @@ void Mesh::load(const char* filename)
 		//
 		// Sanity checks
 		//
-		if(meshData.getVertIndeces().size() < 1 ||
-			meshData.getVertCoords().size() < 1 ||
-			meshData.getVertNormals().size() < 1)
+		if(meshData.getVertIndeces().size() < 1
+			|| meshData.getVertCoords().size() < 1
+			|| meshData.getVertNormals().size() < 1)
 		{
 			throw ANKI_EXCEPTION("Empty one of the required vectors");
 		}

+ 72 - 104
anki/scene/SkinNode.cpp

@@ -3,12 +3,15 @@
 #include "anki/resource/Skeleton.h"
 #include "anki/resource/SkelAnim.h"
 #include "anki/resource/MeshLoader.h"
-#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
 
 
 namespace anki {
 
 
+#define BUFFER_OFFSET(i) ((char *)NULL + (i))
+
+
 //==============================================================================
 // SkinMesh                                                                    =
 //==============================================================================
@@ -31,7 +34,7 @@ const Vbo* SkinMesh::getVbo(VboId id) const
 
 
 //==============================================================================
-SkinMesh::SkinMesh(const Mesh* mesh_)
+SkinMesh::SkinMesh(const MeshBase* mesh_)
 	: mesh(mesh_)
 {
 	// Positions
@@ -39,7 +42,7 @@ SkinMesh::SkinMesh(const Mesh* mesh_)
 	{
 		tfVbos[VBO_TF_POSITIONS].create(
 			GL_ARRAY_BUFFER,
-			mesh->getVbo(VBO_POSITIONS).getSizeInBytes(),
+			mesh->getVbo(VBO_POSITIONS)->getSizeInBytes(),
 			NULL,
 			GL_STATIC_DRAW);
 	}
@@ -49,7 +52,7 @@ SkinMesh::SkinMesh(const Mesh* mesh_)
 	{
 		tfVbos[VBO_TF_NORMALS].create(
 			GL_ARRAY_BUFFER,
-			mesh->getVbo(VBO_NORMALS).getSizeInBytes(),
+			mesh->getVbo(VBO_NORMALS)->getSizeInBytes(),
 			NULL,
 			GL_STATIC_DRAW);
 	}
@@ -59,7 +62,7 @@ SkinMesh::SkinMesh(const Mesh* mesh_)
 	{
 		tfVbos[VBO_TF_TANGENTS].create(
 			GL_ARRAY_BUFFER,
-			mesh->getVbo(VBO_TANGENTS).getSizeInBytes(),
+			mesh->getVbo(VBO_TANGENTS)->getSizeInBytes(),
 			NULL,
 			GL_STATIC_DRAW);
 	}
@@ -67,67 +70,27 @@ SkinMesh::SkinMesh(const Mesh* mesh_)
 
 
 //==============================================================================
-// SkinPatchNode                                                               =
+// SkinModelPatch                                                              =
 //==============================================================================
 
-#define BUFFER_OFFSET(i) ((char *)NULL + (i))
-
-//==============================================================================
 //==============================================================================
-SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_,
-	const char* name, Scene* scene,
-	uint movableFlags, Movable* movParent)
-	: SceneNode(name, scene), Movable(movableFlags, movParent, *this),
-	  Spatial(spatialCs)
-{
-	skinModelPatch.reset(new SkinModelPatch(modelPatch_));
-
-	tfVao.create();
-	if(skinModelPatch->getMeshBase().getVbo(MeshBase::VBO_NORMALS))
-	{
-		tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_POSITIONS),
-			POSITION_LOC,
-			3,
-			GL_FLOAT,
-			false,
-			0,
-			NULL);
-	}
-
-	Renderable::init(*this);
-}
-
-SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_, SceneNode* parent)
-	: SceneNode(SNT_RENDERABLE_NODE, parent->getScene(),
-		SNF_INHERIT_PARENT_TRANSFORM, parent),
-		modelPatch(modelPatch_),
-		mtlr(new MaterialRuntime(modelPatch_->getMaterial()))
+SkinModelPatch::SkinModelPatch(const ModelPatch* mpatch_)
+	: mpatch(mpatch_)
 {
-	ModelPatch::VboArray vboArr;
-	const Mesh& mesh = modelPatch->getMesh();
+	skinMesh.reset(new SkinMesh(&mpatch->getMeshBase()));
+	create();
 
-	for(uint i = 0; i < Mesh::VBOS_NUM; i++)
-	{
-		vboArr[i] = &mesh.getVbo((Mesh::Vbos)i);
-	}
-
-	//
-	// Create the VAOs
+	// Create the VAO
 	//
-
+	const MeshBase& mesh = mpatch->getMeshBase();
 	tfVao.create();
+	const Vbo* vbo;
 
 	// Positions
-	if(mesh.getVbo(Mesh::VBO_VERT_POSITIONS).isCreated())
+	vbo = mesh.getVbo(MeshBase::VBO_POSITIONS);
+	if(vbo)
 	{
-		tfVbos[TFV_POSITIONS].create(GL_ARRAY_BUFFER,
-			mesh.getVbo(Mesh::VBO_VERT_POSITIONS).getSizeInBytes(),
-			NULL,
-			GL_STATIC_DRAW);
-
-		vboArr[Mesh::VBO_VERT_POSITIONS] = &tfVbos[TFV_POSITIONS];
-
-		tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_POSITIONS),
+		tfVao.attachArrayBufferVbo(*vbo,
 			POSITION_LOC,
 			3,
 			GL_FLOAT,
@@ -137,16 +100,10 @@ SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_, SceneNode* parent)
 	}
 
 	// Normals
-	if(mesh.getVbo(Mesh::VBO_VERT_NORMALS).isCreated())
+	vbo = mesh.getVbo(MeshBase::VBO_NORMALS);
+	if(vbo)
 	{
-		tfVbos[TFV_NORMALS].create(GL_ARRAY_BUFFER,
-			mesh.getVbo(Mesh::VBO_VERT_NORMALS).getSizeInBytes(),
-			NULL,
-			GL_STATIC_DRAW);
-
-		vboArr[Mesh::VBO_VERT_NORMALS] = &tfVbos[TFV_NORMALS];
-
-		tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_NORMALS),
+		tfVao.attachArrayBufferVbo(*vbo,
 			NORMAL_LOC,
 			3,
 			GL_FLOAT,
@@ -156,16 +113,10 @@ SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_, SceneNode* parent)
 	}
 
 	// Tangents
-	if(mesh.getVbo(Mesh::VBO_VERT_TANGENTS).isCreated())
+	vbo = mesh.getVbo(MeshBase::VBO_TANGENTS);
+	if(vbo)
 	{
-		tfVbos[TFV_TANGENTS].create(GL_ARRAY_BUFFER,
-			mesh.getVbo(Mesh::VBO_VERT_TANGENTS).getSizeInBytes(),
-			NULL,
-			GL_STATIC_DRAW);
-
-		vboArr[Mesh::VBO_VERT_TANGENTS] = &tfVbos[TFV_TANGENTS];
-
-		tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_TANGENTS),
+		tfVao.attachArrayBufferVbo(*vbo,
 			TANGENT_LOC,
 			4,
 			GL_FLOAT,
@@ -174,10 +125,10 @@ SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_, SceneNode* parent)
 			NULL);
 	}
 
-	// Attach some extra stuff to the tfVao
-	ANKI_ASSERT(mesh.getVbo(Mesh::VBO_VERT_WEIGHTS).isCreated());
+	vbo = mesh.getVbo(Mesh::VBO_WEIGHTS);
+	ANKI_ASSERT(vbo);
 
-	tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_WEIGHTS),
+	tfVao.attachArrayBufferVbo(*vbo,
 		VERT_WEIGHT_BONES_NUM_LOC,
 		1,
 		GL_FLOAT,
@@ -185,7 +136,7 @@ SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_, SceneNode* parent)
 		sizeof(MeshLoader::VertexWeight),
 		BUFFER_OFFSET(0));
 
-	tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_WEIGHTS),
+	tfVao.attachArrayBufferVbo(*vbo,
 		VERT_WEIGHT_BONE_IDS_LOC,
 		4,
 		GL_FLOAT,
@@ -193,43 +144,55 @@ SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_, SceneNode* parent)
 		sizeof(MeshLoader::VertexWeight),
 		BUFFER_OFFSET(4));
 
-	tfVao.attachArrayBufferVbo(mesh.getVbo(Mesh::VBO_VERT_WEIGHTS),
+	tfVao.attachArrayBufferVbo(*vbo,
 		VERT_WEIGHT_WEIGHTS_LOC,
 		4,
 		GL_FLOAT,
 		GL_FALSE,
 		sizeof(MeshLoader::VertexWeight),
 		BUFFER_OFFSET(20));
-
-
-	ModelPatch::createVaos(getMaterialRuntime().getMaterial(),
-		vboArr, vaos, vaosHashMap);
 }
 
 
 //==============================================================================
-// SkinNode                                                                    =
+// SkinPatchNode                                                               =
 //==============================================================================
 
 //==============================================================================
-SkinNode::SkinNode(Scene& scene, ulong flags, SceneNode* parent)
-:	SceneNode(SNT_SKIN_NODE, scene, flags, parent)
-{}
+SkinPatchNode::SkinPatchNode(const ModelPatch* modelPatch_,
+	const char* name, Scene* scene,
+	uint movableFlags, Movable* movParent,
+	CollisionShape* spatialCs)
+	: SceneNode(name, scene), Movable(movableFlags, movParent, *this),
+		Spatial(spatialCs)
+{
+	skinModelPatch.reset(new SkinModelPatch(modelPatch_));
+	Renderable::init(*this);
+}
 
 
 //==============================================================================
-SkinNode::~SkinNode()
-{}
-
+// SkinNode                                                                    =
+//==============================================================================
 
 //==============================================================================
-void SkinNode::init(const char* filename)
+SkinNode::SkinNode(const char* skinFname,
+	const char* name, Scene* scene, // SceneNode
+	uint movableFlags, Movable* movParent) // Movable
+	: SceneNode(name, scene), Movable(movableFlags, movParent, *this)
 {
-	skin.load(filename);
+	skin.load(skinFname);
 
-	BOOST_FOREACH(const ModelPatch& patch, skin->getModelPatches())
+	uint i = 0;
+	for(const ModelPatch& patch : skin->getModel().getModelPatches())
 	{
-		patches.push_back(new SkinPatchNode(&patch, this));
+		std::string name = skin.getResourceName()
+			+ boost::lexical_cast<std::string>(i);
+
+		patches.push_back(new SkinPatchNode(&patch,
+			name.c_str(), scene,
+			Movable::MF_IGNORE_LOCAL_TRANSFORM, this,
+			&visibilityShapeWSpace));
 	}
 
 	uint bonesNum = skin->getSkeleton().getBones().size();
@@ -241,17 +204,22 @@ void SkinNode::init(const char* filename)
 
 
 //==============================================================================
-void SkinNode::moveUpdate()
+SkinNode::~SkinNode()
+{}
+
+//==============================================================================
+void SkinNode::movableUpdate()
 {
 	visibilityShapeWSpace.set(tails);
-	visibilityShapeWSpace = visibilityShapeWSpace.getTransformed(
-		getWorldTransform());
+	visibilityShapeWSpace.transform(getWorldTransform());
 }
 
 
 //==============================================================================
-void SkinNode::frameUpdate(float /*prevUpdateTime*/, float /*crntTime*/)
+void SkinNode::frameUpdate(float prevUpdateTime, float crntTime, int f)
 {
+	SceneNode::frameUpdate(prevUpdateTime, crntTime, f);
+
 	frame += step;
 
 	if(frame > anim->getFramesNum())
@@ -259,11 +227,11 @@ void SkinNode::frameUpdate(float /*prevUpdateTime*/, float /*crntTime*/)
 		frame = 0.0;
 	}
 
-	// A nasty optimization that may produse ugly results
+	/*	// A nasty optimization that may produse ugly results
 	if(!isFlagEnabled(SNF_VISIBLE))
 	{
 		return;
-	}
+	}*/
 
 	interpolate(*anim, frame, boneTranslations,
 	    boneRotations);
@@ -298,17 +266,17 @@ void SkinNode::interpolate(const SkelAnim& animation, float frame,
 		}
 		else if((float)keyframes[j] > frame)
 		{
-			lPose = j-1;
+			lPose = j - 1;
 			rPose = j;
-			t = (frame - (float)keyframes[lPose]) / float(keyframes[rPose] -
-				keyframes[lPose]);
+			t = (frame - (float)keyframes[lPose])
+				/ float(keyframes[rPose] - keyframes[lPose]);
 			break;
 		}
 	}
 
 	// now for all bones update bone's poses
 	ANKI_ASSERT(boneRotations.size() >= 1);
-	for(uint i=0; i < boneRotations.size(); i++)
+	for(uint i = 0; i < boneRotations.size(); i++)
 	{
 		const BoneAnim& banim = animation.getBoneAnimations()[i];
 

+ 218 - 180
anki/scene/SkinNode.h

@@ -3,9 +3,11 @@
 
 #include "anki/scene/SceneNode.h"
 #include "anki/scene/Renderable.h"
-#include "anki/scene/MaterialRuntime.h"
+#include "anki/scene/Movable.h"
+#include "anki/scene/Spatial.h"
 #include "anki/resource/Model.h"
 #include "anki/math/Math.h"
+
 #include <boost/range/iterator_range.hpp>
 #include <vector>
 
@@ -30,7 +32,7 @@ public:
 	};
 
 	/// Create the @a tfVbos with empty data
-	SkinMesh(const Mesh* mesh_);
+	SkinMesh(const MeshBase* mesh_);
 
 	/// @name Accessors
 	/// @{
@@ -72,7 +74,7 @@ public:
 
 private:
 	boost::array<Vbo, VBOS_TF_COUNT> tfVbos;
-	const Mesh* mesh; ///< The resource
+	const MeshBase* mesh; ///< The resource
 };
 
 
@@ -80,13 +82,37 @@ private:
 class SkinModelPatch: public ModelPatchBase
 {
 public:
-	SkinModelPatch(const ModelPatch* mpatch_)
-		: mpatch(mpatch_)
+	/// See TfHwSkinningGeneric.glsl for the locations
+	enum TfShaderProgAttribLoc
 	{
-		skinMesh.reset(new SkinMesh(&mpatch->getMeshBase()));
-		create();
+		POSITION_LOC,
+		NORMAL_LOC,
+		TANGENT_LOC,
+		VERT_WEIGHT_BONES_NUM_LOC,
+		VERT_WEIGHT_BONE_IDS_LOC,
+		VERT_WEIGHT_WEIGHTS_LOC
+	};
+
+	/// @name Constructors/Destructor
+	/// @{
+	SkinModelPatch(const ModelPatch* mpatch_);
+	/// @}
+
+	/// @name Accessors
+	/// @{
+	SkinMesh& getSkinMesh()
+	{
+		return *skinMesh;
+	}
+
+	const SkinMesh& getSkinMesh() const
+	{
+		return *skinMesh;
 	}
+	/// @}
 
+	/// @name Implementations of ModelPatchBase virtuals
+	/// @{
 	const MeshBase& getMeshBase() const
 	{
 		return *skinMesh;
@@ -96,10 +122,12 @@ public:
 	{
 		return mpatch->getMaterial();
 	}
+	/// @}
 
 private:
 	boost::scoped_ptr<SkinMesh> skinMesh;
 	const ModelPatch* mpatch;
+	Vao tfVao;
 };
 
 
@@ -108,17 +136,6 @@ class SkinPatchNode: public SceneNode, public Movable, public Renderable,
 	public Spatial
 {
 public:
-	/// See TfHwSkinningGeneric.glsl for the locations
-	enum TfShaderProgAttribLoc
-	{
-		POSITION_LOC,
-		NORMAL_LOC,
-		TANGENT_LOC,
-		VERT_WEIGHT_BONES_NUM_LOC,
-		VERT_WEIGHT_BONE_IDS_LOC,
-		VERT_WEIGHT_WEIGHTS_LOC
-	};
-
 	/// @name Constructors/Destructor
 	/// @{
 	SkinPatchNode(const ModelPatch* modelPatch_,
@@ -172,169 +189,190 @@ private:
 
 
 /// A skin scene node
-class SkinNode: public SceneNode
+class SkinNode: public SceneNode, public Movable
 {
-	public:
-		template<typename T>
-		class Types
-		{
-			public:
-				typedef std::vector<T> Container;
-				typedef typename Container::iterator Iterator;
-				typedef typename Container::const_iterator ConstIterator;
-				typedef boost::iterator_range<Iterator> MutableRange;
-				typedef boost::iterator_range<ConstIterator> ConstRange;
-		};
-
-		SkinNode(Scene& scene, ulong flags, SceneNode* parent);
-		~SkinNode();
-
-		/// @name Accessors
-		/// @{
-		Types<Vec3>::ConstRange getHeads() const
-		{
-			return Types<Vec3>::ConstRange(heads.begin(), heads.end());
-		}
-		Types<Vec3>::MutableRange getHeads()
-		{
-			return Types<Vec3>::MutableRange(heads.begin(), heads.end());
-		}
-
-		Types<Vec3>::ConstRange getTails() const
-		{
-			return Types<Vec3>::ConstRange(tails.begin(), tails.end());
-		}
-		Types<Vec3>::MutableRange getTails()
-		{
-			return Types<Vec3>::MutableRange(tails.begin(), tails.end());
-		}
-
-		Types<Mat3>::ConstRange getBoneRotations() const
-		{
-			return Types<Mat3>::ConstRange(boneRotations.begin(),
-				boneRotations.end());
-		}
-		Types<Mat3>::MutableRange getBoneRotations()
-		{
-			return Types<Mat3>::MutableRange(boneRotations.begin(),
-				boneRotations.end());
-		}
-
-		Types<Vec3>::ConstRange getBoneTranslations() const
-		{
-			return Types<Vec3>::ConstRange(boneTranslations.begin(),
-				boneTranslations.end());
-		}
-		Types<Vec3>::MutableRange getBoneTranslations()
-		{
-			return Types<Vec3>::MutableRange(boneTranslations.begin(),
-				boneTranslations.end());
-		}
-
-		Types<SkinPatchNode*>::ConstRange getPatchNodes() const
-		{
-			return Types<SkinPatchNode*>::ConstRange(patches.begin(),
-				patches.end());
-		}
-		Types<SkinPatchNode*>::MutableRange getPatchNodes()
-		{
-			return Types<SkinPatchNode*>::MutableRange(patches.begin(),
-				patches.end());
-		}
-
-		const CollisionShape*
-			getVisibilityCollisionShapeWorldSpace() const
-		{
-			return &visibilityShapeWSpace;
-		}
-
-		const Skin& getSkin() const
-		{
-			return *skin;
-		}
-
-		float getStep() const
-		{
-			return step;
-		}
-		float& getStep()
-		{
-			return step;
-		}
-		void setStep(const float x)
-		{
-			step = x;
-		}
-
-		float getFrame() const
-		{
-			return frame;
-		}
-		float& getFrame()
-		{
-			return frame;
-		}
-		void setFrame(const float x)
-		{
-			frame = x;
-		}
-
-		void setAnimation(const SkelAnim& anim_)
-		{
-			anim = &anim_;
-		}
-		const SkelAnim* getAnimation() const
-		{
-			return anim;
-		}
-		/// @}
-
-		void init(const char* filename);
-
-		/// Update boundingShapeWSpace from bone tails (not heads as well
-		/// cause its faster that way). The tails come from the previous frame
-		void moveUpdate();
-
-		/// Update the animation stuff
-		void frameUpdate(float prevUpdateTime, float crntTime);
-
-	private:
-		SkinResourcePointer skin; ///< The resource
-		std::vector<SkinPatchNode*> patches;
-		Obb visibilityShapeWSpace;
-
-		/// @name Animation stuff
-		/// @{
-		float step;
-		float frame;
-		const SkelAnim* anim; ///< The active skeleton animation
-		/// @}
-
-		/// @name Bone data
-		/// @{
-		std::vector<Vec3> heads;
-		std::vector<Vec3> tails;
-		std::vector<Mat3> boneRotations;
-		std::vector<Vec3> boneTranslations;
-		/// @}
-
-		/// Interpolate
-		/// @param[in] animation Animation
-		/// @param[in] frame Frame
-		/// @param[out] translations Translations vector
-		/// @param[out] rotations Rotations vector
-		static void interpolate(const SkelAnim& animation, float frame,
-			std::vector<Vec3>& translations, std::vector<Mat3>& rotations);
-
-		/// Calculate the global pose
-		static void updateBoneTransforms(const Skeleton& skel,
-			std::vector<Vec3>& translations, std::vector<Mat3>& rotations);
-
-		/// Deform the heads and tails
-		static void deformHeadsTails(const Skeleton& skeleton,
-		    const std::vector<Vec3>& boneTranslations,
-		    const std::vector<Mat3>& boneRotations,
-		    std::vector<Vec3>& heads, std::vector<Vec3>& tails);
+public:
+	template<typename T>
+	struct Types
+	{
+		typedef std::vector<T> Container;
+		typedef typename Container::iterator Iterator;
+		typedef typename Container::const_iterator ConstIterator;
+		typedef boost::iterator_range<Iterator> MutableRange;
+		typedef boost::iterator_range<ConstIterator> ConstRange;
+	};
+
+	typedef boost::ptr_vector<SkinPatchNode> PatchesContainer;
+	typedef boost::iterator_range<PatchesContainer::iterator>
+		PatchesMutableRange;
+	typedef boost::iterator_range<PatchesContainer::const_iterator>
+		PatchesConstRange;
+
+	/// @name Constructors/Destructor
+	/// @{
+	SkinNode(const char* skinFname,
+		const char* name, Scene* scene, // SceneNode
+		uint movableFlags, Movable* movParent); // Movable
+
+	~SkinNode();
+	/// @}
+
+	/// @name SceneNode virtuals
+	/// @{
+
+	/// Override SceneNode::getMovable()
+	Movable* getMovable()
+	{
+		return this;
+	}
+
+	/// Update the animation stuff
+	void frameUpdate(float prevUpdateTime, float crntTime, int frame);
+	/// @}
+
+	/// @name Movable virtuals
+	/// @{
+
+	/// Update boundingShapeWSpace from bone tails (not heads as well
+	/// cause its faster that way). The tails come from the previous frame
+	void movableUpdate();
+	/// @}
+
+	/// @name Accessors
+	/// @{
+	Types<Vec3>::ConstRange getHeads() const
+	{
+		return Types<Vec3>::ConstRange(heads.begin(), heads.end());
+	}
+	Types<Vec3>::MutableRange getHeads()
+	{
+		return Types<Vec3>::MutableRange(heads.begin(), heads.end());
+	}
+
+	Types<Vec3>::ConstRange getTails() const
+	{
+		return Types<Vec3>::ConstRange(tails.begin(), tails.end());
+	}
+	Types<Vec3>::MutableRange getTails()
+	{
+		return Types<Vec3>::MutableRange(tails.begin(), tails.end());
+	}
+
+	Types<Mat3>::ConstRange getBoneRotations() const
+	{
+		return Types<Mat3>::ConstRange(boneRotations.begin(),
+			boneRotations.end());
+	}
+	Types<Mat3>::MutableRange getBoneRotations()
+	{
+		return Types<Mat3>::MutableRange(boneRotations.begin(),
+			boneRotations.end());
+	}
+
+	Types<Vec3>::ConstRange getBoneTranslations() const
+	{
+		return Types<Vec3>::ConstRange(boneTranslations.begin(),
+			boneTranslations.end());
+	}
+	Types<Vec3>::MutableRange getBoneTranslations()
+	{
+		return Types<Vec3>::MutableRange(boneTranslations.begin(),
+			boneTranslations.end());
+	}
+
+	PatchesConstRange getPatchNodes() const
+	{
+		return PatchesConstRange(patches.begin(), patches.end());
+	}
+	PatchesMutableRange getPatchNodes()
+	{
+		return PatchesMutableRange(patches.begin(), patches.end());
+	}
+
+	const CollisionShape*
+		getVisibilityCollisionShapeWorldSpace() const
+	{
+		return &visibilityShapeWSpace;
+	}
+
+	const Skin& getSkin() const
+	{
+		return *skin;
+	}
+
+	float getStep() const
+	{
+		return step;
+	}
+	float& getStep()
+	{
+		return step;
+	}
+	void setStep(const float x)
+	{
+		step = x;
+	}
+
+	float getFrame() const
+	{
+		return frame;
+	}
+	float& getFrame()
+	{
+		return frame;
+	}
+	void setFrame(const float x)
+	{
+		frame = x;
+	}
+
+	void setAnimation(const SkelAnim& anim_)
+	{
+		anim = &anim_;
+	}
+	const SkelAnim* getAnimation() const
+	{
+		return anim;
+	}
+	/// @}
+
+private:
+	SkinResourcePointer skin; ///< The resource
+	PatchesContainer patches;
+	Obb visibilityShapeWSpace;
+
+	/// @name Animation stuff
+	/// @{
+	float step;
+	float frame;
+	const SkelAnim* anim; ///< The active skeleton animation
+	/// @}
+
+	/// @name Bone data
+	/// @{
+	std::vector<Vec3> heads;
+	std::vector<Vec3> tails;
+	std::vector<Mat3> boneRotations;
+	std::vector<Vec3> boneTranslations;
+	/// @}
+
+	/// Interpolate
+	/// @param[in] animation Animation
+	/// @param[in] frame Frame
+	/// @param[out] translations Translations vector
+	/// @param[out] rotations Rotations vector
+	static void interpolate(const SkelAnim& animation, float frame,
+		std::vector<Vec3>& translations, std::vector<Mat3>& rotations);
+
+	/// Calculate the global pose
+	static void updateBoneTransforms(const Skeleton& skel,
+		std::vector<Vec3>& translations, std::vector<Mat3>& rotations);
+
+	/// Deform the heads and tails
+	static void deformHeadsTails(const Skeleton& skeleton,
+		const std::vector<Vec3>& boneTranslations,
+		const std::vector<Mat3>& boneRotations,
+		std::vector<Vec3>& heads, std::vector<Vec3>& tails);
 };