游戏动画引擎 -(Crapell Game Engine Design - animation)

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

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

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

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

      BVHSkeleton动画文件格式为.bvhTpose文件格式.bvh0帧动画),游戏动画格式为.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 Site
	vector<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.z
		vec3  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*4

const 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 Site
        if((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不为0
		mat4& 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来匹配offset
			joint->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不为0
		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);
			}
			else
			{
				motion = m_motions + (offsetF+i*3+PosChannelNum);
			}

			//先旋转 后offset
			matLocal = 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 scale
			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];

				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 逐帧动画 有可能目前关键帧数为0
	for (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];//GetMixFrameFromID
		frame->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;  //具有位移时才需要重新计算CalKeyFrame
					break;
				}
				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];//id2index
	memset(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];//id2index
	memset(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();

	//根据末端骨骼名获取所在链、EndEffector
	IKBone*  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; //拓扑排序

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值