动画重定向和逆向运动学:

Biovision Hierarchy, 是BioVision公司推出的人体动作捕捉文件格式,天然具有重定向功能。首先实现BVHSkeleton类来加载bvh文件(包含骨架和动画),与渲染引擎中的RendSys::Skeleton相互转化即可方便的实现游戏的动画重定向功能。

体型Tpose数据用只有骨架没有动画的BVHSkeleton来表示。

重定向过程BVHSkeleton1 + Tpose1 + Tpose2=> BVHSkeleton2 源动画+源姿态+目标姿态 产出目标动画。

BVHSkeleton动画文件格式为.bvh,Tpose文件格式为.bvh(0帧动画),游戏动画格式为.bone。

逆向运动学动画:使用FABRIK算法,不需要计算旋转,只需要计算线性位置,快速并且变化光滑,不会存在断点或者突变。

SkinFit类用来动态调整蒙皮和动画。

//========================================================
//  @Date:     2016.05
//  @File:     Include/Render/BVHSkeleton.h
//  @Brief:     动画重定向
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#pragma once#ifndef _BVH_H_
#define _BVH_H_#include "Math/Mathlib.h"
#include "Render/Texture.h"//3Dmax Biped骨骼要在绑定模型之前导入BVH骨骼,才会匹配BVH骨骼大小,然后可以导出bip文件
//3Dmax Biped骨骼在绑定模型之后导入bip文件,不会改变骨骼大小(3Dmax内部做了动画重定向)#include <vector>
#include <string>
using namespace std;enum ChannelType
{Xposition,Yposition,Zposition,Xrotation,Yrotation,Zrotation,
};
const char* ChannelTypeToString(int enumeration);
bool StringToChannelType(const char* str,int& type);struct RayCollideRes;
namespace RendSys
{class Skeleton;class MixSkeleton;class Mesh;class MovieClip;
}struct Channel;struct BJoint
{BJoint();string            name;       int               index;      BJoint*           parent;     vector<BJoint*>   children;   vec3              offset;     //根节点初始位置及相对位置bool              isSite;     //是否是末节点vec3              site;       //末端位置 末端节点才有 End Sitevector<Channel*>  channels;   //通道vector<mat4>      keyMotion;  //offset*trans*rot,先旋转后平移mat4              TposeRot;   //mat3即可mat4              TposeRotInv;bool              bMount;     //不随父骨骼旋转 比如 脚印 挂载点mat4              matFinal;   //vector<mat4> ?
};struct Channel
{ChannelType      type;       int              index;
};//骨架结构 不固定 名字也不统一
//0Hips
//| 1Spine
//  |   2Spine1
//      |   3Neck
//          |   4Head
//          5LeftShoulder
//          |   6LeftArm
//              |   7LeftForeArm
//                  |   8LeftHand
//          9RightShoulder
//          |   10RightArm
//              |   11RightForeArm
//                  |   12RightHand
//  13LeftUpLeg
//  |   14LeftLeg
//      |   15LeftFoot
//          |   16LeftToeBase
//  17RightUpLeg
//  |   18RightLeg
//      |   19RightFoot
//          |   20RightToeBase
//
//                                  ●head
//                                  |
//                                  ●neck
//                   leftCollar●   |    ●rightCollar
//                            ╱╲  |   ╱╲
//                          ╱    ╲| ╱    ╲
//               leftUpArm●        ●chest   ●rightUpArm
//                      ╱          |           ╲
//                    ╱            |             ╲
//        leftLowAnn●              |               ●rightLowArm
//                 ╱               |                ╲
//       leftHand●                 |                  ●rightHand
//                                  ●1hips
//                                 ╱╲
//                               ╱    ╲
//                    leftUpLeg●        ●rightUpLeg
//                           ╱            ╲
//                         ╱                ╲
//             leftLowLeg●                   ●rightLowLeg
//                      ╱                     ╲
//                    ╱                         ╲
//           leftFoot●                           ●rightFoot//Biovision Hierarchy, 是BioVision公司推出的人体动作捕捉文件格式//捕捉类型:
//局部捕捉
//全身捕捉
//多人捕捉
//多人混合捕捉//捕捉方法:
//光学捕捉   一般是多目相机
//惯性捕捉   传感器
//外骨骼捕捉 机械旋转typedef vector<int> BChain;class BVHSkeleton
{
public:BVHSkeleton();~BVHSkeleton();void Free();//从文件加载,LoadFromFile("human2Tpose.bvh") bool LoadFromFile(const char* fileName);void SaveToFile(const char *fileName);void operator=(BVHSkeleton& bvh);//从3dsmax导出的游戏动画生成bvh姿态,可以预计算保存到文件 GenTposeFromSkeleton("humanTpose.bone") ->SaveToFile("humanTpose.bvh")bool GenTposeFromSkeleton(RendSys::Skeleton* skeleton);//从3dsmax导出的游戏动画生成bvh动画, GenAnimFromSkeleton("humanRun.bone","humanTpose.bvh") 可以预计算保存到文件bool GenAnimFromSkeleton(RendSys::Skeleton* anim,BVHSkeleton* Tpose);//逐帧拷贝到游戏动画void CopyToSkeleton   (RendSys::Skeleton* anim);//需要事先K好帧void CopyToSkeleton2  (RendSys::Skeleton* anim);//单帧实时拷贝void CopyToMixSkeleton(RendSys::MixSkeleton* anim,float weight=1);BJoint*   IntersectTray(RayCollideRes& res);void Render();//void RecursRender(Joint_ *part);void Update();void Forward();void CalKeyFrame(int frame=-1);//缩放TPose 骨架void ScaleJoints(float scale);//获取int     GetJointNum() const;BJoint* GetJoint(int index) const;BJoint* GetJoint(const char* name) const;int    GetChannelNum() const;//int    GetFrameNum() const;float  GetFps() const;//int    GetChainNum() const;vector<int>& GetChain(int index);//从srcBVH中抽取动画void   AnimRetargetingFrom(BVHSkeleton& srcBVH,float scale=1);//将动画赋给目标姿态void   AnimRetargetingWith(BVHSkeleton& dstTpose,float scale=1);void   ResizeMotion(int frameNum, int channelNum);float  GetMotion(int frame, int c) const;void   SetMotion(int frame, int c, float value);//猜测匹配的骨骼: id顺序一致时无需此函数 不一致时说明骨架结构不同int    GuessJoint(const char* name);void   Inversekinematic();
private:int              m_channelNum;    //通道总数vector<Channel*> m_channels;      //所有通道vector<BJoint*>  m_sortJoints;    //所有关节 拓扑排序vector<BChain>   m_chains;        //骨骼链 从root到每个leaf 用于雅克比int            m_curFrame;int            m_frameNum;        //帧数float          m_ifps;            //频率float*         m_motions;         //运动数据 一般只含旋转数据 骨骼长度在pose里  也可以包括旋转后的平移比如伸缩臂private:BVHSkeleton(BVHSkeleton&);
};//一般的游戏, skin直接在max中预先调整, 重定向的tpose也是预生成的,无需SkinFit类
//对于可以捏制体型的游戏, 才需要用到SkinFit
//SkinFit用来修改bonemovie中的蒙皮skin,以及重定向Skeleton
class SkinFit
{
public:SkinFit();~SkinFit();void Free();bool Init(BVHSkeleton* tpose);bool LoadFromFile(const char* fileName);void SaveToFile(const char *fileName);void Reset();//叠加修改void Add(SkinFit& delta);//修改蒙皮void FitSkin(RendSys::MovieClip* movie);void FitSkin(RendSys::Mesh* mesh);//修改姿态void FitTpose(BVHSkeleton* tpose);struct BoneFit{int   boneID;vec3  skinScale;  //skinScale.xy影响skin肥瘦, skinScale.z影响skin长短 //有时候骨骼初始方向为boneScale.x 不是boneScale.zvec3  boneScale;  //只用到了boneScale.z, boneScale.z和skinScale.z通常缩放系数不同mat4  rot;        //boneScale.z 和 rot(相对于体模式tpose)共同影响bvh中的骨骼偏移offset};int      m_boneNum;BoneFit* m_boneFits;
};#endif
//========================================================
//  @Date:     2016.05
//  @File:     SourceLib/Render/BVHSkeleton.cpp
//  @Brief:     动画重定向
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#include "General/Pch.h"
#include "General/File.h"
#include "General/Timer.h"
#include "Render/Curve.h"
#include "Render/RendDriver.h"
#include "Render/BVHSkeleton.h"
#include "Render/MC_MovieClip.h"
#include "Render/MC_Misc.h"
#include <vector>
#include <string>
#include "General/Pce.h"
#include "Math/MathLibAdvance.h"
#include "General/StringUtil.h"
#include "Math/MathLibIncDbg.h"using namespace RendSys;
#define BUFFER_LENGTH 1024*4const char* ChannelTypeToString(int enumeration)
{switch(enumeration){case Xposition: return "Xposition";case Yposition: return "Yposition";case Zposition: return "Zposition";case Xrotation: return "Xrotation";case Yrotation: return "Yrotation";case Zrotation: return "Zrotation";}return "";
}
bool StringToChannelType(const char* str,int& type)
{if      (stricmp(str,"Xposition")==0){ type = Xposition; return true;}else if (stricmp(str,"Yposition")==0){ type = Yposition; return true;}else if (stricmp(str,"Zposition")==0){ type = Zposition; return true;}else if (stricmp(str,"Xrotation")==0){ type = Xrotation; return true;}else if (stricmp(str,"Yrotation")==0){ type = Yrotation; return true;}else if (stricmp(str,"Zrotation")==0){ type = Zrotation; return true;}type = Xposition;return false;
}BJoint::BJoint()
:parent(NULL)
,bMount(false)
{}BVHSkeleton::BVHSkeleton()
{m_channelNum = 0;m_frameNum = 0;m_ifps    = 0;m_motions = NULL;m_curFrame = 0;
}
BVHSkeleton::~BVHSkeleton()
{Free();
}
void  BVHSkeleton::Free()
{for(int i = 0; i != m_channels.size(); ++i)delete m_channels[i];m_channels.clear();for(int i = 0; i != m_sortJoints.size(); ++i)delete m_sortJoints[i];m_sortJoints.clear();SafeDeleteArray(m_motions);m_chains.clear();m_channelNum = 0;m_frameNum   = 0;m_ifps       = 0;
}
bool  BVHSkeleton::LoadFromFile(const char* fileName)
{Free();File   file;char   token[256];vector<BJoint*> jointStack;BJoint* joint    = NULL;BJoint* newJoint = NULL;if(file.Fopen(fileName,"rt") == false)return false; file.SetSplitChar("   ,[]\n\x0a ()=:;|\x0d");while(1){if(file.IsEnd())return false;file.ReadBlock(token,256);//关节的开始if(strcmp(token, "{") == 0){//当前关节添加到栈中jointStack.push_back(joint);joint = newJoint;continue;}//关节的结束if(strcmp(token, "}") == 0){//释放栈中当前关节joint = jointStack.back();jointStack.pop_back();//if(joint->isSite==false)//  joint = joint->parent; //末端节点退出两次处理不同continue;}//判断关节if((strcmp(token, "ROOT") == 0) || strcmp(token, "JOINT") == 0){//创建关节newJoint = new BJoint();newJoint->index = m_sortJoints.size();newJoint->parent = joint;newJoint->isSite = false;m_sortJoints.push_back(newJoint);if(joint)joint->children.push_back(newJoint);//获取关节名file.SetSplitChar("\n");file.ReadBlock(token,256);file.SetSplitChar("    ,[]\n\x0a ()=:;|\x0d");newJoint->name = token;if (StriStr(newJoint->name.c_str(),"footstep")){newJoint->bMount = true;}continue;}//末端 End Siteif((strcmp(token, "End") == 0)){newJoint = joint;// End Site 并不新建骨骼joint->isSite = true;file.NextLine();continue;}//偏移量或者末端位置if((strcmp(token, "OFFSET") == 0)){//坐标的偏移量if(joint->isSite){file.ReadFloatArray(&joint->site.x,3);}else{file.ReadFloatArray(&joint->offset.x,3);}continue;}//关节的通道if(strcmp(token, "CHANNELS") == 0){//通道数量int num = file.ReadInt();joint->channels.resize(num);for(int i = 0; i != num; ++i){//创建通道Channel* channel = new Channel;channel->index = m_channels.size();m_channels.push_back(channel);joint->channels[i] = channel;//获取通道类别file.ReadBlock(token,256);int type;bool res = StringToChannelType(token,type);channel->type = (ChannelType)type;}}//运动数据段前停止if(strcmp(token, "MOTION") == 0)break;}//topology sorted//遍历骨骼链BChain chain;vector<int> invChain;for(int f = 1; f != m_sortJoints.size(); ++f){if(m_sortJoints[f]->isSite){BJoint* joint = m_sortJoints[f];invChain.push_back(joint->index);while (joint->parent){joint = joint->parent;invChain.push_back(joint->index);}//逆序while(invChain.size() != 0){chain.push_back(invChain.back());invChain.pop_back();}m_chains.push_back(chain);chain.clear();}}//编辑运动file.ReadBlock(token,256);if(strcmp(token, "Frames") != 0)return false;m_frameNum = file.ReadInt();//"Frame Time"file.ReadBlock(token,256);if(strcmp(token, "Frame") != 0)return false;file.ReadBlock(token,256);if(strcmp(token, "Time") != 0)return false;m_ifps = file.ReadFloat();m_channelNum = m_channels.size();//数据获取m_motions = new float[m_frameNum * m_channelNum];for(int f = 0; f < m_frameNum; ++f){Channel** channel = &m_channels[0];for(int j = 0; j < m_channelNum; ++j){switch((*channel)->type){case(Xposition):case(Yposition):case(Zposition):m_motions[f * m_channelNum + j] = file.ReadFloat();break;case(Zrotation):case(Yrotation):case(Xrotation):m_motions[f * m_channelNum + j] = file.ReadFloat()*DEG2RAD;break;}channel++;}}//读取bvh 扩展file.ReadBlock(token,256);if(strcmp(token, "EXTROT") == 0){for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];file.ReadFloatArray(joint->TposeRot.mat,16);joint->TposeRotInv = joint->TposeRot;joint->TposeRotInv.Inverse();}}file.Fclose();CalKeyFrame();return true;
}static void WriteJoint(File& outfile, BJoint *joint,string space)
{
#define SPACE outfile.Fprintf(space.c_str());if (joint->parent==NULL){SPACE outfile.Fprintf("ROOT %s\n",joint->name.c_str());}else{SPACE outfile.Fprintf("JOINT %s\n",joint->name.c_str());}SPACE outfile.Fprintf("{\n");string space_ = space;space += "  ";float* a = joint->offset;SPACE outfile.Fprintf("OFFSET %f %f %f\n",a[0],a[1],a[2]);SPACE outfile.Fprintf("CHANNELS %d ",int(joint->channels.size()));for(int i = 0; i != (joint->channels).size(); ++i){/*SPACE */outfile.Fprintf(ChannelTypeToString(joint->channels[i]->type));/*SPACE*/ outfile.Fprintf(" ");}outfile.Fprintf("\n");if(joint->children.size() != 0){for(int i = 0; i != joint->children.size(); ++i)WriteJoint(outfile, joint->children[i],space);}if(joint->isSite == true){SPACE outfile.Fprintf("End Site\n");SPACE outfile.Fprintf("{\n");a = joint->site;SPACE outfile.Fprintf("OFFSET %f %f %f\n",a[0],a[1],a[2]);SPACE outfile.Fprintf("}\n");}outfile.Fprintf(space_.c_str()); outfile.Fprintf("}\n");
}void  BVHSkeleton::SaveToFile(const char *fileName)
{BJoint *joint = GetJoint(0);if (joint==NULL){return;}File outfile;if(outfile.Fopen(fileName,"wt")==false){return;}outfile.Fprintf("HIERARCHY\n");WriteJoint(outfile, joint,"");outfile.Fprintf("MOTION\n");outfile.Fprintf("Frames:    %d\n",m_frameNum);outfile.Fprintf("Frame Time:    %f\n",m_ifps);for(int f = 0; f != m_frameNum; ++f){int start = f*m_channelNum;Channel** channel = &m_channels[0];for(int c = 0; c != m_channelNum; ++c){switch((*channel)->type){case(Xposition):case(Yposition):case(Zposition):outfile.Fprintf("    %f",m_motions[start + c]);break;case(Zrotation):case(Yrotation):case(Xrotation):outfile.Fprintf("    %f",m_motions[start + c]*RAD2DEG);break;}channel++;}outfile.Fprintf("\n");}//保存bvh 扩展outfile.Fprintf("EXTROT\n");for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];outfile.WriteFloatArray(joint->TposeRot.mat,16);outfile.Fprintf("\n");}outfile.Fclose();return ;
}void  BVHSkeleton::Render()
{if (m_sortJoints.size()==0 || m_frameNum==0){return ;}G_RendDriver->DisableRendState(RS_TEXTURE_2D);G_RendDriver->SetLineWidth(1);mat4 temp;mat4 model;for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];BJoint* parentJoint = joint->parent;if (parentJoint/* && parentJoint->flag==1*/){if (joint->bMount==false){float length = (parentJoint->matFinal.GetTranslate() - joint->matFinal.GetTranslate()).Length();temp.FromScale(vec3(length,1, 1));model = parentJoint->matFinal * temp;G_RendDriver->PushMatrix();G_RendDriver->MultMatrix(model);G_RendDriver->Color4f(0, 0, 1, 0.5f);Skeleton::EditRendIdentityBone();G_RendDriver->Color3f(0.0f, 1.0f, 0.0f);G_RendDriver->RendBegin(RS_LINES);G_RendDriver->Vertex3f(0, 0, 0);G_RendDriver->Vertex3f(1,0,0);G_RendDriver->RendEnd();G_RendDriver->SetPointSize(2);G_RendDriver->Color3f(1.0f, 0.0f, 0.0f);G_RendDriver->RendBegin(RS_POINTS);G_RendDriver->Vertex3f(0, 0, 0);G_RendDriver->Vertex3f(1,0,0);G_RendDriver->RendEnd();G_RendDriver->PopMatrix();}else{temp.FromScale(vec3(1,1, 1));model = joint->matFinal * temp;G_RendDriver->PushMatrix();G_RendDriver->MultMatrix(model);G_RendDriver->Color4f(0, 0, 1, 0.5f);Skeleton::EditRendIdentityBone();G_RendDriver->Color3f(0.0f, 1.0f, 0.0f);G_RendDriver->RendBegin(RS_LINES);G_RendDriver->Vertex3f(0, 0, 0);G_RendDriver->Vertex3f(1,0,0);G_RendDriver->RendEnd();G_RendDriver->SetPointSize(2);G_RendDriver->Color3f(1.0f, 0.0f, 0.0f);G_RendDriver->RendBegin(RS_POINTS);G_RendDriver->Vertex3f(0, 0, 0);G_RendDriver->Vertex3f(1,0,0);G_RendDriver->RendEnd();G_RendDriver->PopMatrix();}}}
}void  BVHSkeleton::Update()
{//return;if (m_sortJoints.size()==0 || m_frameNum==0){return;}static float frame = 1;frame += G_Timer->GetStepTime()/m_ifps;if(frame >= m_frameNum)frame = 0;m_curFrame = frame;Forward();
}BJoint* BVHSkeleton::IntersectTray(RayCollideRes& res)
{if (m_sortJoints.size()==0){return NULL;}vec3 a1, b1, a2, b2;float s, t;vec3 resPos1, resPos2;b1 = res.vStart;b2 = res.vEnd;BJoint* nearJoint = NULL;float  nearDist = 1;for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];BJoint* parentJoint = joint->parent;if (parentJoint/* && parentJoint->flag==1*/){if (joint->bMount==false){a1 = parentJoint->matFinal.GetTranslate();a2 = joint->matFinal.GetTranslate();}else{a1 = joint->matFinal.GetTranslate();a2 = joint->matFinal * vec3(1,0,0);}ClosestSegmentSegment(a1, a2, b1, b2, s, t, resPos1, resPos2);float dist = (resPos1-resPos2).Length();if (dist < nearDist){nearJoint = joint;nearDist  = dist;}}}return nearJoint;
}void  BVHSkeleton::Forward()
{//int Size = m_sortJoints.size();BJoint** ppJoint = &m_sortJoints[0];for (int i=0;i<Size;++i,++ppJoint){BJoint* joint = *ppJoint;//root  :先绕原点旋转再平移到root关键帧位置  keyMotion包含位置 offset为0//子骨骼:先绕父骨骼起点旋转再平移到父骨骼的末端 keyMotion一般只含旋转 offset不为0mat4& mat = joint->keyMotion[m_curFrame]; if (joint->parent){//joint->matFinal = joint->parent->matFinal * mat;Mat4xMat4(joint->matFinal,joint->parent->matFinal,mat);}else{joint->matFinal = mat;}}//!!!ppJoint = &m_sortJoints[0];for (int i=0;i<Size;++i,++ppJoint){BJoint* joint = *ppJoint;joint->matFinal *= joint->TposeRot;}
}void  BVHSkeleton::operator=(BVHSkeleton& bvh)
{Free();m_channelNum = bvh.m_channelNum;  m_channels.resize(m_channelNum);for (int i=0;i<m_channelNum;i++){m_channels[i] = new Channel;*m_channels[i] = *bvh.m_channels[i];}m_sortJoints.resize(bvh.m_sortJoints.size());for (int i=0;i<m_sortJoints.size();i++){BJoint* dst = new BJoint;m_sortJoints[i] = dst;BJoint* src = bvh.m_sortJoints[i];dst->name           = src->name        ;dst->index          = src->index       ;dst->offset         = src->offset      ;dst->isSite         = src->isSite      ;dst->site           = src->site        ;dst->keyMotion      = src->keyMotion   ;dst->matFinal       = src->matFinal    ;dst->TposeRot       = src->TposeRot    ;dst->TposeRotInv    = src->TposeRotInv ;dst->bMount         = src->bMount;for (int j=0;j<src->channels.size();j++){dst->channels.push_back(m_channels[src->channels[j]->index]);}   }//单独循环 m_sortJoints全部new出来后执行for (int i=0;i<m_sortJoints.size();i++){BJoint* dst = m_sortJoints[i];BJoint* src = bvh.m_sortJoints[i];dst->parent = (src->parent==NULL)? NULL:m_sortJoints[src->parent->index];     for (int j=0;j<src->children.size();j++){dst->children.push_back(m_sortJoints[src->children[j]->index]);}  }/*vector<BChain>   */m_chains = bvh.m_chains;      m_curFrame = bvh.m_curFrame ;m_frameNum = bvh.m_frameNum ;        m_ifps     = bvh.m_ifps     ;        m_motions = new float[m_frameNum * m_channelNum];   memcpy(m_motions,bvh.m_motions,4*m_frameNum * m_channelNum);CalKeyFrame();
}bool  BVHSkeleton::GenTposeFromSkeleton(RendSys::Skeleton* skeleton)
{if (skeleton==NULL||skeleton->m_root==NULL){return false;}Free();#define PosChannelNum 6//要求 bip01 必须且只能挂接在 一个dummy01上m_channelNum = skeleton->m_boneNum*3 + PosChannelNum; //只有dummy01 root具有平移  其它没有活塞关节或定向关节m_frameNum   = 1;m_ifps       = 0.03f;//pose->m_root->m_frameLine.;m_motions    = new float[m_channelNum*m_frameNum];//通道数量m_channels.resize(m_channelNum);m_sortJoints.resize(skeleton->m_boneNum);BJoint* jointRoot = new BJoint;vector<BJoint*> jointStack;jointStack.push_back(jointRoot);vector<Bone*> boneStack;boneStack.push_back(skeleton->m_root);MixSkeleton mixSkeleton;mixSkeleton.SetSkeleton(skeleton);mixSkeleton.SetCurTimeFrame(0);mixSkeleton.Advance(0,true);mat4 matInverse;while(boneStack.size()>0){BJoint* joint = jointStack.back();jointStack.pop_back();Bone* bone = boneStack.back();boneStack.pop_back();joint->isSite = (bone->subBoneNum>0)?false:true;joint->name   = bone->boneName.c_str();if (StriStr(joint->name.c_str(),"footstep")){joint->bMount = true;}//pose 中已经拓扑排序joint->index  = skeleton->m_id2indexs[bone->boneID];m_sortJoints[joint->index] = joint;//注意 dummy01和bip01 都有旋转和平移属性//坐标的偏移量if (  joint->index==0//dummy01||joint->index==1//bip01){joint->offset = vec3();}else{RendSys::Frame* frame = mixSkeleton.GetMixFrameFromIndex(bone->boneID-1);//bone->m_frameLine.m_keyFrames[0].m_matrix.GetTranslate()RendSys::Frame* parentFrame = mixSkeleton.GetMixFrameFromIndex(bone->parentBoneID-1);//bone->parentBone->m_frameLine.m_keyFrames[0]joint->offset = frame->m_matrix.GetTranslate() - parentFrame->m_matrix.GetTranslate();//matInverse = parentFrame->m_matrix;//matInverse.Inverse();//joint->offset = matInverse*frame->m_matrix.GetTranslate();//不一定非要轴对齐 但是要用相同的Tpose来匹配offsetjoint->TposeRot = frame->m_matrix;joint->TposeRot.SetTranslate(vec3());joint->TposeRotInv = joint->TposeRot;joint->TposeRotInv.Inverse();//Inverse4() 无影响int a = 0;}if(joint->isSite){joint->site = vec3();//?}if (  joint->index==0//dummy01||joint->index==1//bip01){//创建通道joint->channels.resize(6);Channel* channel;int offset = joint->index*6;channel = new Channel; channel->type = Xposition; channel->index = offset+0; joint->channels[0] = channel; m_channels[offset+0] = channel;channel = new Channel; channel->type = Yposition; channel->index = offset+1; joint->channels[1] = channel; m_channels[offset+1] = channel;channel = new Channel; channel->type = Zposition; channel->index = offset+2; joint->channels[2] = channel; m_channels[offset+2] = channel;channel = new Channel; channel->type = Zrotation; channel->index = offset+3; joint->channels[3] = channel; m_channels[offset+3] = channel;channel = new Channel; channel->type = Xrotation; channel->index = offset+4; joint->channels[4] = channel; m_channels[offset+4] = channel;channel = new Channel; channel->type = Yrotation; channel->index = offset+5; joint->channels[5] = channel; m_channels[offset+5] = channel;}else{//创建通道joint->channels.resize(3);Channel* channel;int offset = joint->index*3 + PosChannelNum;channel = new Channel; channel->type = Zrotation; channel->index = offset+0; joint->channels[0] = channel; m_channels[offset+0] = channel;channel = new Channel; channel->type = Xrotation; channel->index = offset+1; joint->channels[1] = channel; m_channels[offset+1] = channel;channel = new Channel; channel->type = Yrotation; channel->index = offset+2; joint->channels[2] = channel; m_channels[offset+2] = channel;}Bone* subBone = bone->subBoneHead;for (int i=0;i<bone->subBoneNum;++i){//正向添加BJoint* newJoint = new BJoint;newJoint->parent = joint;joint->children.push_back(newJoint);//入栈jointStack.push_back(newJoint);boneStack.push_back(subBone);subBone = subBone->next;//debug//newJoint->name   = subBone->boneName.c_str();}}topology sorted//m_sortJoints.clear();//vector<BJoint*> jointStack;//jointStack.push_back(root);//while(jointStack.size()>0)//{//    BJoint* joint = jointStack.back();//   jointStack.pop_back();//    joint->index = m_sortJoints.size();//   m_sortJoints.push_back(joint);//    //for (int i=0;i<joint->children.size();++i)// for (int i=int(joint->children.size())-1;i>=0;--i)//    {//     jointStack.push_back(joint->children[i]);//  }//}//Tpose 没有动作 只有一个空白帧memset(m_motions,0,m_channelNum*m_frameNum*4);并不需要motions数据 以下只是让渲染体模式时位置正确//int f=0;//{// int offsetF = m_channelNum*f;//    for (int i=0;i<m_sortJoints.size();++i)// {//     BJoint* joint  = m_sortJoints[i];          //      BJoint* parentJoint  = joint->parent;//     int index = i;//mixSkeleton->m_skeleton->GetBoneIndex(bone->boneID);//        int id = i+1;//       if (index>=skeleton->m_boneNum)//        {//         break;//        }//     RendSys::Frame* frame = mixSkeleton.GetMixFrameFromIndex(index);//     RendSys::Frame* parentFrame = NULL;//      if (parentJoint)//          parentFrame = mixSkeleton.GetMixFrameFromIndex(parentJoint->index);//       float*  motion = NULL;//       if (  joint->index==0//dummy01//           )//     {//         motion = m_motions + offsetF;//       }//     else if (joint->index==1//bip01//          )//     {//         motion = m_motions + (offsetF+6);//      }//     if (  joint->index==0//dummy01//           )//     {//         //dummy01有位移//          motion[0] = frame->m_matrix.mat[12];//          motion[1] = frame->m_matrix.mat[13];//          motion[2] = frame->m_matrix.mat[14];//      }//     else if ( joint->index==1//bip01//         )//     {//         //bip01有位移//            motion[0] = frame->m_matrix.mat[12] - parentFrame->m_matrix.mat[12];//           motion[1] = frame->m_matrix.mat[13] - parentFrame->m_matrix.mat[13];//           motion[2] = frame->m_matrix.mat[14] - parentFrame->m_matrix.mat[14];//       }// }//}CalKeyFrame();return true;
}bool  BVHSkeleton::GenAnimFromSkeleton(RendSys::Skeleton* skeleton,BVHSkeleton* Tpose)
{if (skeleton==NULL||skeleton->m_root==NULL){return false;}Free();(*this) = *Tpose;ResizeMotion(skeleton->GetMaxTimeFrame(),m_channelNum);//要求 bip01 必须且只能挂接在 一个dummy01上//注意 dummy01和bip01 都有旋转和平移属性//bvh是逐帧捕捉动画, 同时欧拉角插值可能产生错误结果//RendSys::Skeleton是关键帧动画,可以插值,各骨骼关键帧可以不同MixSkeleton mixSkeleton;mixSkeleton.SetSkeleton(skeleton);vec3 dir;vec3 ang;mat4 matLocal;mat4 matInverse;mat4 mat;for (int f=0;f<m_frameNum;++f){mixSkeleton.SetCurTimeFrame(f);mixSkeleton.Advance(0,true);int offsetF = m_channelNum*f;root:先绕原点旋转再平移到root关键帧位置  keyMotion包含位置 offset为0子骨骼:先绕父骨骼起点旋转再平移到父骨骼的末端 keyMotion一般只含旋转 offset不为0for (int i=0;i<m_sortJoints.size();++i){BJoint* joint  = m_sortJoints[i];          BJoint* parentJoint  = joint->parent;int index = i;//mixSkeleton->m_skeleton->GetBoneIndex(bone->boneID);int id = i+1;if (index>=skeleton->m_boneNum){break;}RendSys::Frame* frame = mixSkeleton.GetMixFrameFromIndex(index);RendSys::Frame* parentFrame = NULL;if (parentJoint)parentFrame = mixSkeleton.GetMixFrameFromIndex(parentJoint->index);float*  motion = NULL;if (  joint->index==0//dummy01){motion = m_motions + offsetF;}else if (joint->index==1//bip01){motion = m_motions + (offsetF+6);}else{motion = m_motions + (offsetF+i*3+PosChannelNum);}//先旋转 后offsetmatLocal = frame->m_matrix*joint->TposeRotInv;                      //右乘 注意乘法顺序if (parentJoint){matInverse  = parentFrame->m_matrix * parentJoint->TposeRotInv; //右乘matInverse.Inverse();matLocal = matInverse*matLocal;                                 //左乘}//matLocal.SetTranslate(vec3());//参见CalKeyFrame 注意旋转顺序  ang = matLocal.GetEulerAngRadYXZ(); //no scaleif (  joint->index==0//dummy01){//dummy01有位移motion[0] = frame->m_matrix.mat[12];motion[1] = frame->m_matrix.mat[13];motion[2] = frame->m_matrix.mat[14];motion[3] = ang.z;motion[4] = ang.x;motion[5] = ang.y;}else if ( joint->index==1//bip01){//bip01有位移motion[0] = frame->m_matrix.mat[12] - parentFrame->m_matrix.mat[12];motion[1] = frame->m_matrix.mat[13] - parentFrame->m_matrix.mat[13];motion[2] = frame->m_matrix.mat[14] - parentFrame->m_matrix.mat[14];motion[3] = ang.z;motion[4] = ang.x;motion[5] = ang.y;}else{motion[0] = ang.z;motion[1] = ang.x;motion[2] = ang.y;}//mat4 check;//if (parentJoint)//{//   dir = joint->offset;//  dir.Normalize();//  mat.FromToDir(vec3(1,0,0),dir);//   check = joint->parent->matFinal * mat;//}//else//{// check = mat;//}//if (check!=joint->matFinal)//{//  int a = 0;//}}}CalKeyFrame();return true;
}void  BVHSkeleton::CopyToSkeleton(RendSys::Skeleton* skeleton)
{int JointSize = m_sortJoints.size();if (JointSize==0 || m_frameNum==0){return;}//resize 逐帧动画 有可能目前关键帧数为0for (int i=0;i<JointSize;++i){int index = i;//mixSkeleton->m_skeleton->GetBoneIndex(bone->boneID);int id = i+1;if (index>=skeleton->m_boneNum){break;}//一次计算后缓存在内存RendSys::Bone* bone = skeleton->GetBone(id);if (bone == NULL)continue;bone->m_frameLine.ResizeKeyFrame(m_frameNum);}for(int f=0;f<m_frameNum;f++){m_curFrame = f;Forward();for (int i=0;i<JointSize;++i){BJoint* joint = m_sortJoints[i];int index = i;//mixSkeleton->m_skeleton->GetBoneIndex(bone->boneID);int id = i+1;if (index>=skeleton->m_boneNum){break;}//逐帧动画RendSys::Bone* bone = skeleton->GetBone(id);if (bone == NULL)continue;RendSys::Frame* frame = bone->m_frameLine.GetKeyFrameFromIndex(f);if (frame) {frame->m_timeFrame = f;frame->m_matrix = joint->matFinal;frame->CalFromMatrix();}}}
}void  BVHSkeleton::CopyToSkeleton2(RendSys::Skeleton* skeleton)
{int JointSize = m_sortJoints.size();if (JointSize==0 || m_frameNum==0){return;}//需要事先K好帧for(int f=0;f<m_frameNum;f++){m_curFrame = f;Forward();for (int i=0;i<JointSize;++i){BJoint* joint = m_sortJoints[i];int index = i;//mixSkeleton->m_skeleton->GetBoneIndex(bone->boneID);int id = i+1;if (index>=skeleton->m_boneNum){break;}//逐帧判断关键帧RendSys::Bone* bone = skeleton->GetBone(id);if (bone == NULL)continue;RendSys::Frame* frame = bone->m_frameLine.GetKeyFrameFromTime(f);if (frame) {frame->m_timeFrame = f;frame->m_matrix = joint->matFinal;frame->CalFromMatrix();}}}
}void  BVHSkeleton::CopyToMixSkeleton(RendSys::MixSkeleton* mixSkeleton,float weight/*=1*/)
{int JointSize = m_sortJoints.size();if (JointSize==0 || m_frameNum==0){return ;}for (int i=0;i<JointSize;++i){BJoint* bone = m_sortJoints[i];int index = i;//mixSkeleton->m_skeleton->GetBoneIndex(bone->boneID);if (index>=mixSkeleton->m_skeleton->m_boneNum){break;}RendSys::Frame* frame = &mixSkeleton->m_mixFrameArr[index];//GetMixFrameFromIDframe->m_matrix = bone->matFinal;frame->CalFromMatrix();}
}void  BVHSkeleton::AnimRetargetingFrom(BVHSkeleton& srcBVH,float scale)
{if (m_channelNum!=srcBVH.m_channelNum){return;}//重定向this->ResizeMotion(srcBVH.GetFrameNum(), srcBVH.GetChannelNum());for(int frame = 0; frame != srcBVH.GetFrameNum(); ++frame){//? 通道数量不一致??for(int c = 0; c != m_channelNum; ++c){switch(m_channels[c]->type){case(Xposition):case(Yposition):case(Zposition)://骨架缩小scale倍, root骨骼位移也要缩小scale倍this->SetMotion(frame, c, srcBVH.GetMotion(frame, c) * scale);  //具有位移时才需要重新计算CalKeyFrame  一般只有root节点带位移break;default:this->SetMotion(frame, c, srcBVH.GetMotion(frame, c));break;}}}CalKeyFrame();
}void  BVHSkeleton::AnimRetargetingWith(BVHSkeleton& dstTpose,float scale/*=1*/)
{if (m_channelNum!=dstTpose.m_channelNum){return;}for (int i=0;i<m_sortJoints.size();i++){BJoint* dst = m_sortJoints[i];BJoint* src = dstTpose.m_sortJoints[i];//dst->name           = src->name        ;//dst->index          = src->index       ;dst->offset         = src->offset      ;dst->isSite         = src->isSite      ;dst->site           = src->site        ;dst->keyMotion      = src->keyMotion   ;dst->matFinal       = src->matFinal    ;dst->TposeRot       = src->TposeRot    ;dst->TposeRotInv    = src->TposeRotInv    ;}CalKeyFrame();
}int   BVHSkeleton::GuessJoint(const char* name)
{//!没有做id重排,所以注意导出动作时要保证骨骼顺序一致//                                  ●Bip01 HeadNub//                                  |//                                  ●Bip01 Head//                                  |//                                  |Neck  //                   L Clavicle●---●---●R Clavicle//                            ╱    |     ╲//                          ╱      |       ╲//              L UpperArm●        |         ●R UpperArm//                      ╱          |           ╲//                    ╱            ●Spine1      ╲//         L Forearm●              |               ●R Forearm//                 ╱               |                ╲//       L Hand  ●                 |                  ●R Hand//        L... ╱|╲                ●Spine          ╱|╲ R Finger0、1、2//        L... | | |              ╱|╲              | | | R Finger01、11、21//        L... ☉☉☉           ╱  |  ╲            ☉☉☉R Finger0Nub、1Nub、2Nub//                            ╱    ●Pelvis   //                          ╱      ●Bip01 ╲//                 L Thigh●     ╱  .        ●R Thigh//                       ╱    ╱    .          ╲//                     ╱    ●Dummy01            ╲//                   ╱              .              ╲//           L Calf ●               .               ●R Calf//                 ╱                .                ╲//          L Foot●                ●Footsteps        ●R Foot//          L Toe0|                                    |R Toe0//       L Toe0Nub☉                                   ☉R Toe0Nub////                                  ●head//                                  |//                                  ●neck  //                   leftCollar●   |    ●rightCollar//                            ╱╲  |   ╱╲//                          ╱    ╲| ╱    ╲//               leftUpArm●        ●chest   ●rightUpArm//                      ╱          |           ╲//                    ╱            |             ╲//        leftLowAnn●              |               ●rightLowArm//                 ╱               |                ╲//       leftHand●                 |                  ●rightHand//                                  ●hips//                                 ╱╲//                               ╱    ╲//                    leftUpLeg●        ●rightUpLeg//                           ╱            ╲//                         ╱                ╲//             leftLowLeg●                   ●rightLowLeg//                      ╱                     ╲//                    ╱                         ╲//           leftFoot●                           ●rightFoot//忽略Bip01 和Bip02的区别for (int i=0;i<m_sortJoints.size();++i){if (m_sortJoints[i]->name==name){return i;}}return 0;
}void  BVHSkeleton::CalKeyFrame(int frame)
{if (m_sortJoints.size()==0 || m_frameNum==0){return;}mat4 matOffset;mat4 trans;mat3 rotY;mat3 rotX;mat3 rotZ;float sign = 1;//-1 左手坐标系mat4 mat;vec3 transPos;int begin = 0;int end   = m_frameNum-1;if (frame != -1){begin = frame;end   = frame;}for (int f=begin;f<=end;++f){float* mov = &m_motions[f*m_channelNum];for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];if (frame == -1){joint->keyMotion.resize(m_frameNum);}transPos.x = 0;transPos.y = 0;transPos.z = 0;int ChannelNum = joint->channels.size();for (int c=0;c<ChannelNum;++c){float  value = *mov++;switch(joint->channels[c]->type){case(Xposition):transPos.x = value;break;case(Yposition):transPos.y = value;break;case(Zposition):transPos.z = value;break;case(Zrotation):rotZ.FromRotateZ(sign * value);break;case(Yrotation):rotY.FromRotateY(sign * value);break;case(Xrotation):rotX.FromRotateX(sign * value);break;}}trans.SetTranslate(transPos);matOffset.SetTranslate(joint->offset);//骨骼动画使用的是欧拉角旋转//renderdriver 默认的是固定角旋转//joint->keyMotion[f] = matOffset * trans * (rotY * rotX * rotZ);// 固定角旋转和欧拉角旋转矩阵相乘顺序正好相反joint->keyMotion[f] = matOffset * trans * (rotZ * rotX * rotY);  // 先旋转后平移}}
}void  BVHSkeleton::ScaleJoints(float scale)
{if (m_sortJoints.size()==0 || m_frameNum==0){return ;}for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];joint->offset *= scale;}for (int f=0;f<m_frameNum;++f){float* mov = &m_motions[f*m_channelNum];for (int i=0;i<m_sortJoints.size();++i){BJoint* joint = m_sortJoints[i];int ChannelNum = joint->channels.size();for (int c=0;c<ChannelNum;++c){switch(joint->channels[c]->type){case(Xposition):case(Yposition):case(Zposition):(*mov) *= scale;  //具有位移时才需要重新计算CalKeyFramebreak;}mov++;}}}   CalKeyFrame();
}void  BVHSkeleton::ResizeMotion(int frameNum, int channelNum)
{if (frameNum<=0 || channelNum<=0){return;}m_frameNum = frameNum;SafeDeleteArray(m_motions);m_motions = new float[frameNum*channelNum];memset(m_motions,0,frameNum*channelNum*4);
}
void  BVHSkeleton::SetMotion(int frame, int c, float value)
{if (frame >= m_frameNum){return;}if (c >= m_channelNum){return;}m_motions[frame*m_channelNum + c] = value;
}float BVHSkeleton::GetMotion(int frame, int c) const
{if (frame >= m_frameNum){return 0;}if (c >= m_channelNum){return 0;}return m_motions[frame*m_channelNum + c];
}
float BVHSkeleton::GetFps() const
{return m_ifps;
}int   BVHSkeleton::GetFrameNum() const
{return m_frameNum;
}int   BVHSkeleton::GetChannelNum() const
{return m_channels.size();
}int   BVHSkeleton::GetJointNum() const
{return m_sortJoints.size();
}
int   BVHSkeleton::GetChainNum() const
{return  m_chains.size();
}
BJoint* BVHSkeleton::GetJoint(const char* name) const
{for(int i = 1; i != m_sortJoints.size(); ++i){if((m_sortJoints[i]->name) == name){return m_sortJoints[i];}}return NULL;
}
BJoint* BVHSkeleton::GetJoint(int index) const
{if (index >= m_sortJoints.size()){return NULL;}return m_sortJoints[index];
}BChain& BVHSkeleton::GetChain(int index)
{if (index >= m_chains.size()){return m_chains[0];}return m_chains[index];
}//==================^_^==================^_^==================^_^==================^_^SkinFit::SkinFit()
:m_boneFits(NULL)
{}SkinFit::~SkinFit()
{Free();
}void SkinFit::Free()
{SafeDeleteArray(m_boneFits);
}bool SkinFit::LoadFromFile(const char* filename)
{Free();File file;if (file.Fopen(filename,"rt")){if(strcmp("SkinFit1.0",file.ReadString())!=0)return false;m_boneNum = file.ReadInt();m_boneFits = new BoneFit[m_boneNum];vec3 rot;for (int i=0;i<m_boneNum;i++){m_boneFits[i].boneID = file.ReadInt();file.ReadFloatArray(&m_boneFits[i].skinScale.x,3);file.ReadFloatArray(&m_boneFits[i].boneScale.x,3);file.ReadFloatArray(&rot.x,3);m_boneFits[i].rot.FromEulerAngRadXZY(rot);}}return true;
}void SkinFit::SaveToFile(const char* filename)
{File file;if (file.Fopen(filename,"wt")){file.WriteString("SkinFit1.0");                   file.Fprintf("\n");file.WriteInt(m_boneNum);                        file.Fprintf("    ");vec3 rot;for (int i=0;i<m_boneNum;i++)                         {file.WriteInt(m_boneFits[i].boneID);          file.Fprintf("    [");file.WriteFloatArray(&m_boneFits[i].skinScale.x,3);    file.Fprintf("]   [");file.WriteFloatArray(&m_boneFits[i].boneScale.x,3);    file.Fprintf("]   [");rot = m_boneFits[i].rot.GetEulerAngRadXZY();file.WriteFloatArray(&rot.x,3);               file.Fprintf("]\n");}}
}bool SkinFit::Init(BVHSkeleton* tpose)
{Free();m_boneNum = tpose->GetJointNum();m_boneFits = new BoneFit[m_boneNum];for (int i=0;i<m_boneNum;i++){m_boneFits[i].boneID = i+1;//tpose->GetJoint(i).index+1;}Reset();return true;
}void SkinFit::Reset()
{vec3 identity (1,1,1);for (int i=0;i<m_boneNum;i++){m_boneFits[i].skinScale = identity;m_boneFits[i].boneScale = identity;m_boneFits[i].rot.Identity();// = vec3();}
}void SkinFit::Add(SkinFit& delta)
{if(m_boneNum != delta.m_boneNum)return;for (int i=0;i<m_boneNum;i++){m_boneFits[i].skinScale *= delta.m_boneFits[i].skinScale;m_boneFits[i].rot *= delta.m_boneFits[i].rot;m_boneFits[i].boneScale *= delta.m_boneFits[i].boneScale;}
}void SkinFit::FitSkin(RendSys::Mesh* mesh)
{if (mesh==NULL||mesh->m_vertexWeights==NULL){return;}BoneFit* fits[128];//id2indexmemset(fits,0,128*4);for (int i=0;i<m_boneNum;i++)                        {int boneID = m_boneFits[i].boneID;fits[boneID] = &m_boneFits[i];}for (int i=0;i<mesh->m_vVertexNum;i++)                         {RendSys::Mesh::VertexWeight* weight = &mesh->m_vertexWeights[i];for (int w=0;w<weight->boneNum;w++)                       {RendSys::Mesh::BoneWeight* bw = &weight->boneWeight[w];BoneFit* fit = fits[bw->boneId];if (fit){//可能骨骼不匹配 避免crash//if (abs(fit->scale.LengthSq()-1) > _EPSILON)//{//}bw->x *= fit->skinScale.x;bw->y *= fit->skinScale.y;bw->z *= fit->skinScale.z;}}}
}void SkinFit::FitSkin(RendSys::MovieClip* movie)
{MovieThrough thtorgh(movie);RendSys::MovieClip* subMovie = thtorgh.GetFirst();while (subMovie){RendSys::MC_Skeleton* skeletonMovie = dynamic_cast<MC_Skeleton*>(subMovie);if (skeletonMovie){RendSys::Mesh* mesh = skeletonMovie->GetMesh();FitSkin(mesh);}FitSkin(subMovie);subMovie = thtorgh.GetNext();}
}void SkinFit::FitTpose(BVHSkeleton* tpose)
{if (tpose==NULL){return;}BoneFit* fits[128];//id2indexmemset(fits,0,128*4);for (int i=0;i<m_boneNum;i++)                         {int boneID = m_boneFits[i].boneID;fits[boneID] = &m_boneFits[i];}for (int i=0;i<tpose->GetJointNum();i++)                       {BJoint* joint = tpose->GetJoint(i);int boneID = i+1;BoneFit* fit = fits[boneID];if (fit){//可能骨骼不匹配 避免crash//if (abs(fit->scale.z-1) > _EPSILON)//{//}//joint->offset *= fit->scale.z;    //自身X缩放:自身offset发生变化? 沿着自身纵轴,应该是缩放子骨骼更好些? 有些具有多个子骨骼?//joint->offset *= fit->skinScale.x;joint->offset *= fit->boneScale.x;//自身旋转: 自身rot发生变,化子骨骼的offset发生变化,子骨骼旋转不变for(int j=0;j<joint->children.size();j++){BJoint* child = joint->children[j];child->offset = fit->rot*child->offset;}joint->TposeRot    = fit->rot * joint->TposeRot;         //旋转不够准确 ?//joint->TposeRot  = joint->TposeRot * fit->rot;joint->TposeRotInv = joint->TposeRot;joint->TposeRotInv.Inverse();}}tpose->CalKeyFrame();tpose->Forward();
}//==================^_^==================^_^==================^_^==================^_^static BVHSkeleton* m_BVH;
void TestInitBVH()
{{//man_small_Tpose 是 man_big_dongzuo 骨架的0.5倍大小BVHSkeleton tpose;//tpose.LoadFromFile("data/test/kinematics/man_small_Tpose.bvh");//tpose.LoadFromFile("data/test/kinematics/man_small_Tpose_rightarm.bvh");//保留右肩上提 右臂过长的体型特点tpose.LoadFromFile("data/test/kinematics/man_small_Tpose_humpback.bvh");//保留驼背的体型特点//man_small_Tpose2缩小到0.1倍大小tpose.ScaleJoints(0.2f);tpose.SaveToFile("data/test/kinematics/man_small_Tpose2.bvh");}{BVHSkeleton srcBVH;srcBVH.LoadFromFile("data/test/kinematics/man_big_dongzuo.bvh");BVHSkeleton res;res.LoadFromFile("data/test/kinematics/man_small_Tpose2.bvh");//man_small_Tpose2已经缩小到0.1倍大小res.AnimRetargetingFrom(srcBVH,0.1f);res.SaveToFile("data/test/kinematics/man_small_dongzuoresult.bvh");}m_BVH = new BVHSkeleton;m_BVH->LoadFromFile("data/test/kinematics/man_small_dongzuoresult.bvh");//m_BVH->LoadFromFile("data/test/kinematics/test01.bvh");{//预生成tpose//Skeleton    commonTposeSk;        commonTposeSk.LoadFromFile("data/test/kinematics/commonman_tpose.bone");//BVHSkeleton commonTposeBVH;//commonTposeBVH.GenTposeFromSkeleton(&commonTposeSk);//commonTposeBVH.SaveToFile("data/test/kinematics/commonman_tpose.bvh");//预生成bvh anim//Skeleton    commonAnimSk;         commonAnimSk.LoadFromFile("data/test/kinematics/commonman_run.bone");//BVHSkeleton commonTposeBVH;       commonTposeBVH.LoadFromFile("data/test/kinematics/commonman_tpose.bvh");//BVHSkeleton commonAnimBVH;//commonAnimBVH.GenAnimFromSkeleton(&commonAnimSk,&commonTposeBVH);//commonAnimBVH.SaveToFile("data/test/kinematics/commonman_run.bvh");}{实时重定向//BVHSkeleton commonTposeBVH;       commonTposeBVH.LoadFromFile("data/test/kinematics/commonman_tpose.bvh");//BVHSkeleton coolTposeBVH;         commonTposeBVH.LoadFromFile("data/test/kinematics/coolman_tpose.bvh");//BVHSkeleton commonAnimBVH;        commonAnimBVH.LoadFromFile("data/test/kinematics/commonman_run.bvh");//BVHSkeleton coolAnimBVH;//coolAnimBVH = commonAnimBVH;//coolAnimBVH.AnimRetargetingWith(coolTposeBVH);//coolAnimBVH = coolTposeBVH;//coolAnimBVH.AnimRetargetingFrom(coolTposeBVH);//Skeleton coolAnimSK;//coolAnimBVH.CopyToSkeleton(&coolAnimSK);太多 直接使用不预先保存coolAnimSK.SaveToFile("data/test/kinematics/coolman_result.bone");}
}void TestRendBVH()
{m_BVH->Update();m_BVH->Render();}
//========================================================
//  @Date:     2016.05
//  @File:     Include/Render/FABRIKSkeleton.h
//  @Brief:     逆向运动学
//  @Author:     LouLei
//  @Email:  twopointfive@163.com
//  @Copyright (Crapell) - All Rights Reserved
//========================================================#pragma once
//#ifndef  __ReproductMesh__H__
//#define  __ReproductMesh__H__#include "Math/Mathlib.h"
#include <vector>//正向动力学(FK)和反向动力学(IK Inverse kinematics)。
//反向动力学中比较流行的方法则是 Cyclic Coordinate Descent(CCD)和 Forward And Backward Reaching Inverse Kinematics (FABRIK)。
//几何分析与雅可比矩阵的方法,计算起来比较复杂,用的不是太多。//目标可以到达的范围: 最远为各臂长之和,最短为各臂长排列正负号之和的最小绝对值//CCD的具体实现:
//1从骨骼链上最深的子骨节开始相对于(父坐标系)原点进行旋转,使它指向效应点。
//2将这个骨节的父节点针对于(祖坐标系)原点进行旋转,使父骨节的原点到新旋转的子骨节端点的连线指向效应点。
//3对每个骨节进行1~2步骤的处理。
//上述步骤进行多次循环从而得到更加稳定的值。//FABRIK:不需要计算旋转,只需要计算线性位置,快速并且变化光滑,不会存在断点或者突变。FABRIK的详细理论可以参看Andreas Aristidou的论文,从收敛等各个角度详细阐述了FABRIK的特性。
//给定四个点,假定原点P1限制不能移动,移动P2、P3、P4使得最终P4到达目标点
//1forward   将P4移动到目标点P4',然后连接目标点和P3,在这条线上取距离为d3得到P3', 重复这个过程直到得到P1',P1'偏移了固定点。
//2backward  将P1移回到固定点,然后连接固定点和P2,在这条线上取距离为d2得到P2',重复这个过程直到得到P4',P4'偏移了目标点,但是比第一步前更近了
//3迭代到 足够近
//分叉骨骼链:比如手掌
//第一步还是forward 只不过这个时候不是到达start起点,而是从每一个end effector到达距离他最近的sub-base节点。对于有多个end effector的情况,这样做会得到多个sub-base的新位置,我们取所有sub-base新位置的中心点作为最终的sub-base的新位置。
//如果这个sub-base又连接到另外一个sub-base上面了,那么我们重复上面的过程。如果sub-base连接到了起点上面,我们实际上就最终到达了起始点。
//第二部还是backward,这个时候我们从起点出发,然后遇到sub-base的时候我们就把每个支路作为单独的网络分别求解,过程和上面没有任何不同!//FABRIK 注视动画(眼睛前方添加骨骼bonenub  bonenub目标点在头部单位圆上转动 )using namespace  std;//目标点
class EndEffector
{
public:EndEffector();void Render();
public:vec3  pos;mat4  matFinal;
};//一个joint对应一个matrix, bone(link)比joint少一个
class IKBone
{
public:IKBone();void Render();//newStartPos 子骨骼的起点 即父骨骼的终点 多个子骨骼起点必须相同??IKBone* AddBone(const vec3& pos,const char* name="",int boneID=-1,const mat4* mat=NULL);void   Set(const vec3& startTpose,const vec3& endTpose,const char* name="",int boneID=-1,const mat4* matTpose=NULL);public:char  name[64];int   boneID;   vec3  startPos;vec3  endPos;EndEffector endEffector;float length;    //骨骼长度  末端骨骼无长度float pistonLen; //可伸缩活塞长度 reaching时可以拉长活塞mat4  matFinal;mat4  matLocal;bool  tposeMatSetted;mat4  matTpose;vec3  startTpose;vec3  endTpose;IKBone* parent;vector<IKBone*> children;int   backCount;//逆向计数//关节约束enum ConstraintType{None    ,Fixed   ,//固定 0自由度 比如锁骨Cone    ,//锥角FourDir ,//四方向度数Hinge   ,//绕轴旋转,限制了另外两个角的自由度Slider  ,};ConstraintType constraintType;float constraintCone;   //锥角vec4  constraintFourDir;//限制四方向度数float constraintTwist;  //限制扭曲
};namespace RendSys
{class Skeleton;class MixSkeleton;
}//一根骨骼链只能在末端添加子骨骼,在链的中间A处添加子骨骼C是不允许的,
//变通的方法:给A的父骨骼额外添加一个分叉子骨骼B(B是A的兄弟 让B与A始终重合),再在新的分叉末端B添加C
class IKSkeleton
{
public:IKSkeleton();~IKSkeleton();void Free();bool GenFromSkeleton(RendSys::Skeleton* pose);bool SetRoot(IKBone * root);void Solve();void Render();//根据末端骨骼名获取所在链、EndEffectorIKBone*  GetBone(const char* name);void CopyToSkeleton   (RendSys::Skeleton* anim,int keyFrame);void CopyToMixSkeleton(RendSys::MixSkeleton* anim,float weight=1);void SortBones();void CalBoneMatrix();//关节约束void ConstraintCone(vec3& point,IKBone* parent,IKBone* bone);void ConstraintFourDir(vec3& point,IKBone* parent,IKBone* bone);void ConstraintHinge(vec3& point,IKBone* parent,IKBone* bone);void Backward(); void Forward();  private:public:vec3           m_origin;     //固定点或起始点IKBone*         m_root;       //m_sortJoints[0]vector<IKBone*> m_sortJoints; //拓扑排序};

游戏动画引擎 -(Crapell Game Engine Design - animation)相关推荐

  1. 【3D服装级设计/游戏动画引擎】Marvelous Designer软件

    MD可以满足设计师对服装细节和品质的苛求,并且和OBJ文件以及其它3D软件也可以很好的兼容,所以MD不仅是各大服装设计师的必备软件,更是EA,Konami等全球顶尖的游戏工作室的最爱. 附 Blend ...

  2. Delphi程序员的“Unity3D”:开源跨平台游戏开发引擎Castle Game Engine(CGE)

    官方网站:https://castle-engine.io/features.php Delphi牛人开发的3D/2D开源游戏引擎支持: 跨平台(Linux,Windows,Mac OS X,Free ...

  3. 【转】游戏引擎剖析(Game Engine Anatomy 101)

    游戏引擎剖析(Game Engine Anatomy 101) 原文作者:Jake Simpson 译者: 向海 Email:GameWorldChina@myway.com 英文原版下载 : 第1部 ...

  4. [转载]游戏相关引擎荟萃2

    [转载]游戏相关引擎荟萃2 Name Language Platform License Graphics Sound Networking Scripting Other features Plus ...

  5. 如何将unity3d动画嵌入html,在Unity3D中使用精灵动画引擎制作动画的两种方法

    7月28日消息,如今的游戏玩家对于游戏角色的动作要求越来越高,给开发者提出了众多的要求,工作量也相应上升.那么如何才能简单快速地制作角色动画以提升效率呢?下面就和大家分享两个在Unity3D中使用精灵 ...

  6. 如何学好游戏3D引擎编程(摘抄)

    网上看到,觉得很好,因此收藏一下,侵删. <如何学好游戏3D引擎编程> 此篇文章献给那些为了游戏编程不怕困难的热血青年,它的神秘要我永远不间断的去挑战自我,超越自我,这样才能攀登到游戏技术 ...

  7. 常见3D游戏物理引擎总结

    1.  Havok: 老牌的君王,支持功能如下: http://www.havok.com ·         Collision Detection - including Continuous P ...

  8. Unity游戏动画 从入门到住院 1

    http://www.gameres.com/674582.html Unity3D游戏美术全攻略:从入门到精通 发布者: 小篱 | 发布时间: 2016-8-8 13:40| 评论数: 1 文/拉撒 ...

  9. unity游戏动画 从入门到住院 二 模型导入

    第一篇:unity游戏动画 从入门到住院 一 导出设置 好的,现在我们已经导出了一个干净或者不干净的FBX,总之他是可用的.至于导出如何设置请看前文链接--既然导出来了我们如何在unity中使用它呢? ...

最新文章

  1. iOS开发8:使用Tool Bar切换视图
  2. 谷歌时间晶体登上Nature,诺奖得主重大猜想成为现实
  3. 八周二次课(5月14日)
  4. 嵌入式Linux系统编程学习之二十四消息队列
  5. 吃了核辐射食物怎么办_我们经常吃的猪肉,相当一部分是核辐射照过的,会对身体有害吗...
  6. RadComboBox的用法
  7. ContextLoaderListener和Spring MVC中的DispatcherServlet加载内容的区别
  8. 文荣:7月24日阿里云上海峰会网络大神
  9. laravel 中Predis使用手册
  10. 打印机质量测试软件,打印机断针测试软件
  11. php测试宽带速度慢,别被运营商骗了! 手把手教你学会测自家网速
  12. 7月,带你阅读图灵原创图书以及上榜新书
  13. 看网易的lofter,预测轻博客的未来
  14. 科技部等6部门发文,推动AI场景创新;『精益副业』教程序员优雅做副业;『可扩展系统』设计全教程;人物动作数据集;前沿论文 | ShowMeAI资讯日报
  15. 输入框添加Emoje表情demo
  16. nyoj 239 月老的难题 【二分匹配之匈牙利】
  17. 忽悠自由主义_所有教育工作者都应该知道的16种自由主义
  18. 长沙市21中2021年高考成绩查询,长沙几大高中名校2020年高考成绩放榜了,这样的成绩你满意吗...
  19. python使用pika操作rabbitmq总结(一)
  20. No module named _lzma

热门文章

  1. iphone11怎么安装字体
  2. CEGUI 的HelloWord
  3. 软件开发之版本管理Versioning
  4. A/D | D/A 转换原理计算
  5. 英语发音规则---th
  6. 请听题!如何实现只用1个VN5640A搭建含2个交换机的车载以太网网络?|VN5640A新功能
  7. 数据结构通过链表实现班级同学通讯录
  8. 企业号 php 考勤,使用微信企业号实现微信考勤和办公
  9. 用“AI核弹”饱和攻击的英伟达,如何赢下AI计算新赛场?
  10. 相同点安卓和iosui的相同点_iOS和Android的UI设计规范有啥区别?