FBX人物动画——导出骨骼动画树

导出骨骼动画树

对FBX人物动画文件的骨骼动画节点树中每一个节点的动画帧进行导出时,导出数据为骨骼local space中的offset,传统的导出方式:

out.position[frame] = in.position[frame];
out.rotation[frame] = in.rotation[frame];
out.scale[frame] = in.scale[frame];

但这样在实际应用在导出FBX人物动画场景下可能会出现严重的问题(没出问题是因为你没用Scaling动画)

这里有2点需要注意:

1.父节点坐标系的Scaling变化。

如下图,A是B的父节点,那么A节点的坐标系进行Scaling变化时,B是否要变化呢?如果不变,那么可以想象海贼王路飞的伸缩手臂,当只有锁骨部分无限拉长,且其他部位纹丝不动,测试,它是无法像动漫里一样拉伸的。所以当父节点(骨骼绑定在节点上,可以理解为骨骼继承了节点)出现Scale变化时,是需要引起子节点的变化,即A节点Scaling变化,那么所有的子节点都要进行Scaling变化,那么如何变化呢?当然是通过程序运算而来=-=。FBX骨骼动画文件存储的KeyFrame数据都是相对于当前骨骼的局部坐标系,我们需要根据当前节点的Scale作为子节点的全局缩放,一路递归运算…

在这里插入图片描述

FBX骨骼动画文件的KeyFrame信息:
在这里插入图片描述

2.单位问题(1英寸=0.0254米)。

FBX的keyFrame数据导出时,英寸和米的单位换算也会造成动画G掉。当你使用FBX SDK提供的方法将英寸单位转为米后,移动1英寸和移动0.0254米是一样,但Scaling 1英寸和Scaling 0.0254米是一样的嘛?不是的。所以确定好动画KeyFrame的单位变化量也是一件至关重要的事情。

3.解决方式

综上,对于人物动画的导出,解决方式应该具体针对父节点坐标系的Scaling变化需要引起子节点坐标系的Scaling变化,体现在Position和Scale。以及需要注意单位变化量。话不多说,上代码。

代码

void export(TrackDesc* in, TrackDesc* out, uint32 frame, Vector3 globalScale = Vector3::UNIT_SCALE)
{
	size_t childCount = in->GetChildrenCount();
	if (childCount == 0)
		return;
	// 英寸的单位变化量
	const float UNIT_CONVERSION = 0.0254f;
	for (size_t i = 0; i < childCount; ++i)
	{
		TrackDesc* outChild = out->GetChild(i);
		TrackDesc* inChild = in->GetChild(i);
		// 局部坐标系位移应用全局缩放
		outChild->positions[frame] = inChild->positions[frame] * globalScale;
		outChild->rotations[frame] = inChild->rotations[frame];
		// 计算当前节点的局部缩放
		Vector3 localScale = inChild->scalings[frame] * globalScale;
		outChild->scalings[frame] = localScale / UNIT_CONVERSION;
		// 当前节点的局部坐标系为子节点的全局坐标系
		export(inChild, outChild, frame, localScale);
	}
}

得到的是应用了全局缩放后的骨骼局部坐标系下的KeyFrame信息.

补充部分

需要将导出后的骨骼局部坐标系的Track数据应用到实际角色动画中,还需进行一步处理:计算KeyFrame数据位于当前骨骼的父骨骼节点坐标系下的Offset数据。因为动画Track中当前骨骼的Offset TransForm是拿当前骨骼在Global Coordinate(父骨骼节点坐标系下)下的Offset算的,而不是Local Coordinate下的Offset,即KeyFrame数据。

			Vector3		bonePos		= targetBone->getPosition();
			Quaternion	boneQuat	= targetBone->getOrientation();
			Vector3		boneScale	= targetBone->getScale();

			for (uint32 j = 0; j < frameCount; ++j)
			{
				Vector3		&framePos = targetTrack->positions[j];
				Quaternion	&frameOri = targetTrack->rotations[j];
				Vector3		&frameSca = targetTrack->scalings[j];

				framePos = framePos - bonePos;
				frameOri = boneQuat.Inverse() * frameOri;
				frameOri.Normalize();
				frameSca = frameSca / boneScale;
			}
			trackStack.push_back(targetTrack);

如:framePos - bonePos放在坐标系下,可以理解为在将当前骨骼在父骨骼下的Pos移到父骨骼的原点部分,然后应用framePos作为父骨骼的Offset,旋转和缩放同理。整个过程就是子骨骼Local Coordinate到Global Coordinate的一个转换,通过动画KeyFrame和Bone的World Information(可由骨骼的Pos,Quat,Scale计算出骨骼的World Matrix)计算出动画KeyFrame在父骨骼下的World Information,在导出动画时候计算出这些数据,方便后续Animation Track的直接应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值