如何加载《天龙八部》Skeleton
说明:本文章纯属学习之用,如有商业之用,与本人无关。网络游戏《天龙八部》采用的是Ogre3d作为其客户端渲染引擎,他们对之做了许多自定义的修改,在这里作为学习只用,特别说明一下其自定义的Skeleton的格式,以及如何加载的办法。
天龙八部加入了这样一个区段:
SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME
0x4120
(
16672
)
在加入了这个区段的内容之后,其格式大概就是这样一个样子:
enum
SkeletonChunkID {
SKELETON_HEADER = 0x1000 ,
// char* version : Version number check
SKELETON_BONE = 0x2000 ,
// Repeating section defining each bone in the system.
// Bones are assigned indexes automatically based on their order of declaration
// starting with 0.
// char* name : name of the bone
// unsigned short handle : handle of the bone, should be contiguous & start at 0
// Vector3 position : position of this bone relative to parent
// Quaternion orientation : orientation of this bone relative to parent
// Vector3 scale : scale of this bone relative to parent
SKELETON_BONE_PARENT = 0x3000 ,
// Record of the parent of a single bone, used to build the node tree
// Repeating section, listed in Bone Index order, one per Bone
// unsigned short handle : child bone
// unsigned short parentHandle : parent bone
SKELETON_ANIMATION = 0x4000 ,
// A single animation for this skeleton
// char* name : Name of the animation
// float length : Length of the animation in seconds
SKELETON_ANIMATION_TRACK = 0x4100 ,
// A single animation track (relates to a single bone)
// Repeating section (within SKELETON_ANIMATION)
// unsigned short boneIndex : Index of bone to apply to
SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110 ,
// A single keyframe within the track
// Repeating section
// float time : The time position (seconds)
// Quaternion rotate : Rotation to apply at this keyframe
// Vector3 translate : Translation to apply at this keyframe
// Vector3 scale : Scale to apply at this keyframe
SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME = 0x4120 ,
// A multiple keyframe within the track
// Repeating section
// float length : Length of the animation in seconds
// float flags : Length of the animation in seconds
// float time : The time position (seconds)
// Quaternion rotate : Rotation to apply at this keyframe
// Vector3 translate : Translation to apply at this keyframe
SKELETON_ANIMATION_LINK = 0x5000
// Link to another skeleton, to re-use its animations
// char* skeletonName : name of skeleton to get animations from
// float scale : scale to apply to trans/scale keys
};
SKELETON_HEADER = 0x1000 ,
// char* version : Version number check
SKELETON_BONE = 0x2000 ,
// Repeating section defining each bone in the system.
// Bones are assigned indexes automatically based on their order of declaration
// starting with 0.
// char* name : name of the bone
// unsigned short handle : handle of the bone, should be contiguous & start at 0
// Vector3 position : position of this bone relative to parent
// Quaternion orientation : orientation of this bone relative to parent
// Vector3 scale : scale of this bone relative to parent
SKELETON_BONE_PARENT = 0x3000 ,
// Record of the parent of a single bone, used to build the node tree
// Repeating section, listed in Bone Index order, one per Bone
// unsigned short handle : child bone
// unsigned short parentHandle : parent bone
SKELETON_ANIMATION = 0x4000 ,
// A single animation for this skeleton
// char* name : Name of the animation
// float length : Length of the animation in seconds
SKELETON_ANIMATION_TRACK = 0x4100 ,
// A single animation track (relates to a single bone)
// Repeating section (within SKELETON_ANIMATION)
// unsigned short boneIndex : Index of bone to apply to
SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110 ,
// A single keyframe within the track
// Repeating section
// float time : The time position (seconds)
// Quaternion rotate : Rotation to apply at this keyframe
// Vector3 translate : Translation to apply at this keyframe
// Vector3 scale : Scale to apply at this keyframe
SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME = 0x4120 ,
// A multiple keyframe within the track
// Repeating section
// float length : Length of the animation in seconds
// float flags : Length of the animation in seconds
// float time : The time position (seconds)
// Quaternion rotate : Rotation to apply at this keyframe
// Vector3 translate : Translation to apply at this keyframe
SKELETON_ANIMATION_LINK = 0x5000
// Link to another skeleton, to re-use its animations
// char* skeletonName : name of skeleton to get animations from
// float scale : scale to apply to trans/scale keys
};
然后我们打开OgreSkeletonSerializer.cpp找到SkeletonSerializer::readAnimationTrack的实现,然后替换为下面的代码:
void
SkeletonSerializer::readAnimationTrack(DataStreamPtr
&
stream, Animation
*
anim,
Skeleton * pSkel)
{
// unsigned short boneIndex : Index of bone to apply to
unsigned short boneHandle;
readShorts(stream, & boneHandle, 1 );
// Find bone
Bone * targetBone = pSkel -> getBone(boneHandle);
// Create track
NodeAnimationTrack * pTrack = anim -> createNodeTrack(boneHandle, targetBone);
// Keep looking for nested keyframes
if ( ! stream -> eof())
{
unsigned short streamID = readChunk(stream);
while ( (streamID == SKELETON_ANIMATION_TRACK_KEYFRAME || streamID == SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME)
&& ! stream -> eof())
{
if (streamID == SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME)
{
// TLBB 新增了此部分
unsigned short len;
unsigned short flags;
readShorts(stream, & len, 1 );
readShorts(stream, & flags, 1 );
int count = (mCurrentstreamLen - 4 - 4 ) / 4 ;
if (len != count / 8 )
{
len = len;
}
float time;
for ( int i = 0 ; i < len; i += 1 )
{
readFloats(stream, & time, 1 );
TransformKeyFrame * kf = pTrack -> createNodeKeyFrame(time);
Quaternion rot = Quaternion::IDENTITY;
if (flags & 1 )
{
readObject(stream, rot);
}
kf -> setRotation(rot);
Vector3 trans = Vector3::ZERO;
if (flags & 2 )
{
readObject(stream, trans);
}
kf -> setTranslate(trans);
}
}
else
{
readKeyFrame(stream, pTrack, pSkel);
}
if ( ! stream -> eof())
{
// Get next stream
streamID = readChunk(stream);
}
}
if ( ! stream -> eof())
{
// Backpedal back to start of this stream if we've found a non-keyframe
stream -> skip( - STREAM_OVERHEAD_SIZE);
}
}
}
保存然后重新编译Ogre就OK了,理论上就是可以加载其骨骼动画了。
Skeleton * pSkel)
{
// unsigned short boneIndex : Index of bone to apply to
unsigned short boneHandle;
readShorts(stream, & boneHandle, 1 );
// Find bone
Bone * targetBone = pSkel -> getBone(boneHandle);
// Create track
NodeAnimationTrack * pTrack = anim -> createNodeTrack(boneHandle, targetBone);
// Keep looking for nested keyframes
if ( ! stream -> eof())
{
unsigned short streamID = readChunk(stream);
while ( (streamID == SKELETON_ANIMATION_TRACK_KEYFRAME || streamID == SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME)
&& ! stream -> eof())
{
if (streamID == SKELETON_ANIMATION_TRACK_MULTI_KEYFRAME)
{
// TLBB 新增了此部分
unsigned short len;
unsigned short flags;
readShorts(stream, & len, 1 );
readShorts(stream, & flags, 1 );
int count = (mCurrentstreamLen - 4 - 4 ) / 4 ;
if (len != count / 8 )
{
len = len;
}
float time;
for ( int i = 0 ; i < len; i += 1 )
{
readFloats(stream, & time, 1 );
TransformKeyFrame * kf = pTrack -> createNodeKeyFrame(time);
Quaternion rot = Quaternion::IDENTITY;
if (flags & 1 )
{
readObject(stream, rot);
}
kf -> setRotation(rot);
Vector3 trans = Vector3::ZERO;
if (flags & 2 )
{
readObject(stream, trans);
}
kf -> setTranslate(trans);
}
}
else
{
readKeyFrame(stream, pTrack, pSkel);
}
if ( ! stream -> eof())
{
// Get next stream
streamID = readChunk(stream);
}
}
if ( ! stream -> eof())
{
// Backpedal back to start of this stream if we've found a non-keyframe
stream -> skip( - STREAM_OVERHEAD_SIZE);
}
}
}
另外,其骨骼文件名,模型文件名都是用的中文,而VS2005对中文路径名的支持是有一个bug的,解决此问题参见下面这篇文章:
http://www.cppblog.com/tx7do/archive/2008/12/09/68897.html