This repository has been archived on 2023-10-28. You can view files and clone it, but cannot push or open issues or pull requests.
frostbite/snowlib/AnimDecoder.cpp
2020-02-06 00:23:46 +00:00

155 lines
5.9 KiB
C++

#include "AnimDecoder.h"
#include "AnimData.h"
#include "DataUtil.h"
AnimData* AnimDecoder::decode(const unsigned char* data, int length)
{
// Decode BGDA
int endIndex = length;
AnimData* animData = new AnimData();
animData->NumBones = DataUtil::getLEInt(data, 0);
animData->Offset4Val = DataUtil::getLEInt(data, 4);
animData->Offset14Val = DataUtil::getLEInt(data, 0x14);
animData->Offset18Val = DataUtil::getLEInt(data, 0x18);
int offset8Val = DataUtil::getLEInt(data, 8);
int bindingPoseOffset = DataUtil::getLEInt(data, 0x0C);
animData->bindingPose = new glm::vec3[animData->NumBones];
for (int i = 0; i < animData->NumBones; ++i)
{
animData->bindingPose[i].x = -DataUtil::getLEShort(data, bindingPoseOffset + i * 8 + 0) / 64.0f;
animData->bindingPose[i].y = -DataUtil::getLEShort(data, bindingPoseOffset + i * 8 + 2) / 64.0f;
animData->bindingPose[i].z = -DataUtil::getLEShort(data, bindingPoseOffset + i * 8 + 4) / 64.0f;
}
// Skeleton structure
int offset10Val = DataUtil::getLEInt(data, 0x10);
animData->skeletonDef = new int[animData->NumBones];
for (int i = 0; i < animData->NumBones; ++i)
{
animData->skeletonDef[i] = data[offset10Val + i];
}
AnimPose* curPose = new AnimPose[animData->NumBones];
for (int boneNum = 0; boneNum < animData->NumBones; ++boneNum)
{
AnimPose& pose = curPose[boneNum];
pose.BoneNum = boneNum;
pose.FrameNum = 0;
int frameOff = offset8Val + boneNum * 0x0e;
pose.Position.x = DataUtil::getLEShort(data, frameOff) / 64.0f;
pose.Position.y = DataUtil::getLEShort(data, frameOff + 2) / 64.0f;
pose.Position.z = DataUtil::getLEShort(data, frameOff + 4) / 64.0f;
pose.Rotation.w = DataUtil::getLEShort(data, frameOff + 6) / 4096.0f;
pose.Rotation.x = DataUtil::getLEShort(data, frameOff + 8) / 4096.0f;
pose.Rotation.y = DataUtil::getLEShort(data, frameOff + 0x0A) / 4096.0f;
pose.Rotation.z = DataUtil::getLEShort(data, frameOff + 0x0C) / 4096.0f;
pose.Velocity.x = pose.Velocity.y = pose.Velocity.z = 0.0f;
pose.AngularVelocity.w = pose.AngularVelocity.x = pose.AngularVelocity.y = pose.AngularVelocity.z = 0.0f;
// This may give us duplicate frame zero poses, but that's ok.
animData->MeshPoses.push_back(pose);
}
int* curAngVelFrame = (int*)calloc(animData->NumBones, sizeof(int));
int* curVelFrame = (int*)calloc(animData->NumBones, sizeof(int));
animData->NumFrames = 1;
int totalFrame = 0;
int otherOff = offset8Val + animData->NumBones * 0x0e;
AnimPose* pose = nullptr;
while (otherOff < endIndex) {
int count = data[otherOff++];
unsigned char byte2 = data[otherOff++];
int boneNum = byte2 & 0x3f;
if (boneNum == 0x3f) break;
totalFrame += count;
if (pose == nullptr || pose->FrameNum != totalFrame || pose->BoneNum != boneNum)
{
if (pose != nullptr)
{
animData->MeshPoses.push_back(*pose);
}
delete pose;
pose = new AnimPose();
pose->FrameNum = totalFrame;
pose->BoneNum = boneNum;
pose->Position = curPose[boneNum].Position;
pose->Rotation = curPose[boneNum].Rotation;
pose->AngularVelocity = curPose[boneNum].AngularVelocity;
pose->Velocity = curPose[boneNum].Velocity;
}
// bit 7 specifies whether to read 4 (set) or 3 elements following
// bit 6 specifies whether they are shorts or bytes (set).
if ((byte2 & 0x80) == 0x80) {
int w, x, y, z;
if ((byte2 & 0x40) == 0x40) {
w = (char)data[otherOff++];
x = (char)data[otherOff++];
y = (char)data[otherOff++];
z = (char)data[otherOff++];
} else {
w = DataUtil::getLEShort(data, otherOff);
x = DataUtil::getLEShort(data, otherOff+2);
y = DataUtil::getLEShort(data, otherOff+4);
z = DataUtil::getLEShort(data, otherOff+6);
otherOff += 8;
}
glm::quat angVel(w, x, y, z);
glm::quat prevAngVel = pose->AngularVelocity;
float coeff = (totalFrame - curAngVelFrame[boneNum]) / 131072.0f;
glm::quat angDelta = prevAngVel * coeff;
pose->Rotation = pose->Rotation + angDelta;
pose->FrameNum = totalFrame;
pose->AngularVelocity = angVel;
curPose[boneNum].Rotation = pose->Rotation;
curPose[boneNum].AngularVelocity = pose->AngularVelocity;
curAngVelFrame[boneNum] = totalFrame;
}
else
{
int x, y, z;
if ((byte2 & 0x40) == 0x40) {
x = (char)data[otherOff++];
y = (char)data[otherOff++];
z = (char)data[otherOff++];
} else {
x = DataUtil::getLEShort(data, otherOff);
y = DataUtil::getLEShort(data, otherOff + 2);
z = DataUtil::getLEShort(data, otherOff + 4);
otherOff += 6;
}
glm::vec3 vel(x, y, z);
glm::vec3 prevVel = pose->Velocity;
float coeff = (totalFrame - curVelFrame[boneNum]) / 512.0f;
glm::vec3 posDelta = prevVel * coeff;
pose->Position = pose->Position + posDelta;
pose->FrameNum = totalFrame;
pose->Velocity = vel;
curPose[boneNum].Position = pose->Position;
curPose[boneNum].Velocity = pose->Velocity;
curVelFrame[boneNum] = totalFrame;
}
}
animData->MeshPoses.push_back(*pose);
delete pose;
free(curAngVelFrame);
free(curVelFrame);
animData->NumFrames = totalFrame+1;
animData->BuildPerFramePoses();
animData->BuildPerFrameFKPoses();
return animData;
}