I want to draw a stickman based on skeletal animation. Basically the stickman should be able to reproduce human-like motion such as walking,... I have made a code under the following logic: there are different type of joints in the stickman: -parent joints -child joints when a parent joint moves the child joint is bound to move as well. An analogy would be that if you rotate your elbow joint(i.e parent joint) the wrist joint(child joint) will always move as well. I have a non-working code that is suppose to draw the left leg's joints .
package org.example.pointtest;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import javax.microedition.khronos.opengles.GL11;
public class SimplePoint
{
public FloatBuffer hipVertexBuffer;
public FloatBuffer kneeVertexBuffer;
public FloatBuffer ankleVertexBuffer;
float[]hip={1.75f,-2.75f,0.0f};//0 hip
float[]knee={1.75f,-6.75f,0.0f};//1 knee
float[]ankle={1.75f,-10.75f,0.0f};//2 ankle
float[][]leftleg={hip,knee,ankle};
FloatBuffer []VertexBuffers={kneeVertexBuffer,ankleVertexBuffer};
FloatBuffer[]CompleteVertexBuffers={hipVertexBuffer,kneeVertexBuffer};
public float distance2D(float[]origin,float[]extremity)
{
float a=extremity[0]-origin[0];
float b=extremity[1]-origin[1];
float c=extremity[2]-origin[2];
float[] d={a,b,c};
return d[1];
}
public SimplePoint()
{
float []hippoint=
{1.75f,-2.75f,0.0f};//0 hip
float[]kneepoint=
{1.75f,-6.75f,0.0f};//1 knee
float[]anklepoint=
{1.75f,-10.75f,0.0f};//2 ankle
ByteBuffer vbb = ByteBuffer.allocateDirect(1*3*4);
vbb.order(ByteOrder.nativeOrder());
hipVertexBuffer=vbb.asFloatBuffer();
hipVertexBuffer.put(hippoint);
hipVertexBuffer.position(0);
ByteBuffer kbb = ByteBuffer.allocateDirect(1*3*4);
kbb.order(ByteOrder.nativeOrder());
kneeVertexBuffer=kbb.asFloatBuffer();
kneeVertexBuffer.put(kneepoint);
kneeVertexBuffer.position(0);
ByteBuffer abb = ByteBuffer.allocateDirect(1*3*4);
abb.order(ByteOrder.nativeOrder());
ankleVertexBuffer=abb.asFloatBuffer();
ankleVertexBuffer.put(anklepoint);
ankleVertexBuffer.position(0);
}
public void draw(GL10 gl)
{
/*gl.glPushMatrix();
gl.glTranslatef(0, 1, 0);
gl.glDrawArrays(GL10.GL_POINTS, 2, 1);
gl.glPopMatrix();
*/
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,hipVertexBuffer);// root joint transformation matrix(supposition)
gl.glColor4f(1f, 0f, 0f,开发者_JAVA百科 1f);
gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
gl.glPushMatrix();
int i=0;
while(leftleg[i]!=leftleg[leftleg.length-1])
{
if (leftleg[i]!=leftleg[leftleg.length-1])
{
gl.glTranslatef(0, distance2D(hip, knee), 0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,VertexBuffers[i]);
gl.glDrawArrays(GL10.GL_POINTS, 0, 1);
gl.glMultMatrixf(CompleteVertexBuffers[i]);
gl.glPushMatrix();
}
if(leftleg[i]==leftleg[leftleg.length-1])
{
gl.glPopMatrix();
}
i++;
}
}
}
What you describe is called Forward Kinematics. There is plenty of info about it on the net.
What you basically need to do is specify the next bone in the skeletal structure as being relative to the parent bone. In practice this means that each "bone" will start at 0,0 and stretch to another point (where the next bone will attach).
You can then multiply up from the root all these "local" transforms into a "world" transform to get your final positions.
This, for an example, is the header file from my 3D bone animation system:
#ifndef THE__MESH_SKELETON_NODE_H_
#define THE__MESH_SKELETON_NODE_H_
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
#include "String/String.h"
#include "Utils/Crc.h"
#include "Maths/Matrix4x4.h"
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
class XMLElement;
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
enum MeshSkeletonNodeFlags
{
MSN_Root = 0x00000001,
MSN_Dirty = 0x00000002,
MSN_NoRender = 0x00000004, // Not entirely sure about this one as it buggers up my render table.
};
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
class MeshSkeletonNode
{
protected:
MathsLib::Matrix4x4* mpRestToLocalTransform;
MathsLib::Matrix4x4* mpLocalTransform;
MathsLib::Matrix4x4* mpWorldTransform;
unsigned int mFlags;
CRCVAL mBoneCRC;
StringT mBoneName;
MeshSkeletonNode* mpParent;
MeshSkeletonNode* mpChild;
MeshSkeletonNode* mpNextSibling;
MeshSkeletonNode* mpPrevSibling;
bool UpdateWorldTransform( MathsLib::Matrix4x4* pParentWorld );
public:
MeshSkeletonNode();
~MeshSkeletonNode();
//bool Load( XMLElement* pBone, MeshSkeletonNode* pParent, MeshSkeletonNode* pPrevSibling,
// MathsLib::Matrix4x4* pRestToLocal, MathsLib::Matrix4x4* pLocal, MathsLib::Matrix4x4* pWorld );
unsigned int GetFlags();
void SetFlags( unsigned int flags );
void ClearFlags( unsigned int flags );
bool UpdateWorldTransform();
MathsLib::Matrix4x4* GetRestToLocalTransform();
MathsLib::Matrix4x4* GetLocalTransform();
MathsLib::Matrix4x4* GetWorldTransform();
void SetLocalTransform( MathsLib::Matrix4x4* pTransform );
bool AttachSiblingNode( MeshSkeletonNode* pNode );
bool DetachSiblingNode( MeshSkeletonNode* pNode );
bool AttachChildNode( MeshSkeletonNode* pNode );
bool DetachChildNode( MeshSkeletonNode* pNode );
MeshSkeletonNode*& Child();
MeshSkeletonNode*& Parent();
MeshSkeletonNode*& NextSibling();
MeshSkeletonNode*& PrevSibling();
MeshSkeletonNode* FindNodeByName( StringT& boneName );
MeshSkeletonNode* FindNodeByCRC( CRCVAL crc );
CRCVAL GetBoneCRC();
const StringT& GetBoneName();
};
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline unsigned int MeshSkeletonNode::GetFlags()
{
return mFlags;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline void MeshSkeletonNode::SetFlags( unsigned int flags )
{
mFlags |= flags;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline void MeshSkeletonNode::ClearFlags( unsigned int flags )
{
mFlags &= ~flags;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MathsLib::Matrix4x4* MeshSkeletonNode::GetRestToLocalTransform()
{
return mpRestToLocalTransform;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MathsLib::Matrix4x4* MeshSkeletonNode::GetLocalTransform()
{
return mpLocalTransform;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MathsLib::Matrix4x4* MeshSkeletonNode::GetWorldTransform()
{
return mpWorldTransform;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline void MeshSkeletonNode::SetLocalTransform( MathsLib::Matrix4x4* pTransform )
{
*mpLocalTransform = *pTransform;
SetFlags( MSN_Dirty );
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::Child()
{
return mpChild;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::Parent()
{
return mpParent;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::NextSibling()
{
return mpNextSibling;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline MeshSkeletonNode*& MeshSkeletonNode::PrevSibling()
{
return mpPrevSibling;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline CRCVAL MeshSkeletonNode::GetBoneCRC()
{
return mBoneCRC;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
inline const StringT& MeshSkeletonNode::GetBoneName()
{
return mBoneName;
}
/*+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+----+*/
#endif
Edit: To give you a better idea you would define a root bone (an instance of a class). You would then add n (where n is a value between 0 and infinity) child bones to that root bone. These bones would have a transformation that represents its offset from this root position. You would then define a further n bones that have a local transformation of an offset from one of these child bones. One you have defined that then you'll find that any given bone's world transformation is the matrix multiplication of the parent's world transform and the bone's local transform. You should then be able to propagate the transforms to the root node to the leaves via the bones in between (this is the forward part of the forward kinematics). Once you have this working then what you need to do is define a set of animation key frames that define the transformations applied to the "rest" state you defined when building the bones. Each transformation remains relative to the parent's bone to allow the transforms to propagate properly. In this way you can define that the wrist bone yaws 20 degrees to the right and whatever the parent's bone positions the wrist will yaw relative to the bones above it in the hierarchy.
精彩评论