2011年3月6日 星期日

BoneEdit - 骨骼

c:骨骼的功能操作過程如下:

路人:那個骨骼資料從那裡來的啊?
c:自己先定義一個文字檔,填入編號、位置、相依關係等...
  主要是得到骨架的資料,載入工具後,就可以自由調整了.
路人:這種事用3DMAX來做,再導出骨骼不是更好?
c:沒錯,那這個工具也不用寫了,直接寫3DMAX外掛就好啦.
  反正不是為工作而做的,從無到有弄個編輯器也是有其中的樂趣嘛.
路人:嗯,那就抓些重點出來吧.
c:先看一些定義吧.

typedef struct
{
    float r,g,b;
} gColor;

struct gBone//一根骨骼
{
    long id;     // BONE ID
    char name[32];    // BONE NAME
    gBone *parent;     // POINTER TO PARENT BONE
    int childCnt;//子骨骼數量 // COUNT OF CHILD BONES
    gBone *children;//指向的子骨骼位置 // POINTER TO CHILDREN
    int childrenidx;//指向的子骨骼位置索引
    gMatrix matrix;//矩形的值
    Vector pos;
    Vector rot;
    Vector startrot;//初始旋轉量
    Vector startpos;//初始位置
    Vector wpos;//真實的世界位置
    int length;//長
    int width; //寬
    gColor color;
};
最重要的變數就是matrix了,模型的頂點都要乘上這個值,這樣模型就會跟著骨骼動作了,
它的定義前幾篇就有貼出來了,這裡我再貼一次.
typedef struct
{
    float m[16];
} gMatrix;

class CBone
{
public:
    CBone();
    virtual ~CBone();
    bool LoadBone( char *filename );//載入文字檔
    bool LoadBone_b( char *filename );//載入位元檔
    bool SaveBone_b( char *filename );//存入位元檔
    void Draw( GLenum mode );
    void Destroy( void );
    bool flag( void ){ if( Bone ) return true; else return false; }
    void AllColor( float r, float g, float b );//設定所有骨骼顏色
    void Color( int id, float r, float g, float b );//設定骨骼顏色
    void ComputeWPos( void );//算出每個骨骼的世界位置
    Vector CameraPoint;
    Vector CameraView;
    int BoneCount, Selectid;
    bool Show; //是否顥示出來
    gBone *Bone;
protected:
    void DrawSkeleton( gBone *rootBone, GLenum mode );
    void BoneBox( gBone *curBone );
    void CWPos( gBone *rootBone );//算出每個骨骼的世界位置
};

matrix怎麼算出來的呢?呼叫DrawSkeleton就可得到

void CBone::Draw( GLenum mode )
{
    if( !Bone )
        return;
    glPushMatrix();
    glTranslatef(Bone->startpos[0]+Bone->pos[0],
                 Bone->startpos[1]+Bone->pos[1],
                 Bone->startpos[2]+Bone->pos[2]);
    glRotatef(Bone->rot[2], 0.0f, 0.0f, 1.0f);
    glRotatef(Bone->rot[1], 0.0f, 1.0f, 0.0f);
    glRotatef(Bone->rot[0], 1.0f, 0.0f, 0.0f);
    DrawSkeleton(Bone,mode);
    glPopMatrix();
}

DrawSkeleton是個遞迴函式,因為骨頭是一層一層的

void CBone::DrawSkeleton( gBone *rootBone, GLenum mode )
{
    int loop;
    gBone *curBone;
    curBone = rootBone->children;
    for (loop = 0; loop < rootBone->childCnt; loop++)
    {
        glPushMatrix();
        glTranslatef(curBone->startpos[0]+curBone->pos[0],
                     curBone->startpos[1]+curBone->pos[1],
                     curBone->startpos[2]+curBone->pos[2]);
        glRotatef(curBone->rot[2], 0.0f, 0.0f, 1.0f);
        glRotatef(curBone->rot[1], 0.0f, 1.0f, 0.0f);
        glRotatef(curBone->rot[0], 1.0f, 0.0f, 0.0f);
        if( mode == GL_SELECT )
        {
            if( Show )
            {
                glLoadName(curBone->id);
                BoneBox( curBone );
            }
        }
        else
        {
            if( Show )
                BoneBox( curBone );
        }
        glGetFloatv(GL_MODELVIEW_MATRIX,curBone->matrix.m);///在這裡取得的矩陣就可以給模型用了
if (curBone->childCnt > 0)
   DrawSkeleton( curBone, mode );
glPopMatrix();
        curBone++;
    }
}

沒有留言:

張貼留言