scummvm/keyframe.cpp

207 lines
6.3 KiB
C++
Raw Normal View History

2003-08-15 18:00:22 +00:00
// Residual - Virtual machine to run LucasArts' 3D adventure games
// Copyright (C) 2003 The ScummVM-Residual Team (www.scummvm.org)
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "keyframe.h"
#include "debug.h"
#include "bits.h"
#include "textsplit.h"
2003-08-15 18:00:22 +00:00
#include <cstring>
KeyframeAnim::KeyframeAnim(const char *filename, const char *data, int len) :
Resource(filename) {
if (len >= 4 && std::memcmp(data, "FYEK", 4) == 0)
loadBinary(data, len);
else {
TextSplitter ts(data, len);
loadText(ts);
}
}
void KeyframeAnim::loadBinary(const char *data, int len) {
flags_ = get_LE_uint32(data + 40);
type_ = get_LE_uint32(data + 48);
fps_ = get_float(data + 52);
numFrames_ = get_LE_uint32(data + 56);
numJoints_ = get_LE_uint32(data + 60);
numMarkers_ = get_LE_uint32(data + 68);
markers_ = new Marker[numMarkers_];
for (int i = 0; i < numMarkers_; i++) {
markers_[i].frame_ = get_float(data + 72 + 4 * i);
markers_[i].val_ = get_LE_uint32(data + 104 + 4 * i);
}
nodes_ = new KeyframeNode*[numJoints_];
for (int i = 0; i < numJoints_; i++)
nodes_[i] = NULL;
const char *dataEnd = data + len;
data += 180;
while (data < dataEnd) {
int nodeNum = get_LE_uint32(data + 32);
nodes_[nodeNum] = new KeyframeNode;
nodes_[nodeNum]->loadBinary(data);
}
}
void KeyframeAnim::loadText(TextSplitter &ts) {
ts.expectString("section: header");
ts.scanString("flags %i", 1, &flags_);
ts.scanString("type %i", 1, &type_);
ts.scanString("frames %d", 1, &numFrames_);
ts.scanString("fps %f", 1, &fps_);
ts.scanString("joints %d", 1, &numJoints_);
if (std::strcmp(ts.currentLine(), "section: markers") == 0) {
ts.nextLine();
ts.scanString("markers %d", 1, &numMarkers_);
markers_ = new Marker[numMarkers_];
for (int i = 0; i < numMarkers_; i++)
ts.scanString("%f %d", 2, &markers_[i].frame_, &markers_[i].val_);
}
else {
numMarkers_ = 0;
markers_ = NULL;
}
ts.expectString("section: keyframe nodes");
int numNodes;
ts.scanString("nodes %d", 1, &numNodes);
nodes_ = new KeyframeNode*[numJoints_];
for (int i = 0; i < numJoints_; i++)
nodes_[i] = NULL;
for (int i = 0; i < numNodes; i++) {
int which;
ts.scanString("node %d", 1, &which);
nodes_[which] = new KeyframeNode;
nodes_[which]->loadText(ts);
}
}
KeyframeAnim::~KeyframeAnim() {
for (int i = 0; i < numJoints_; i++)
delete nodes_[i];
delete[] markers_;
}
void KeyframeAnim::animate(Model::HierNode *nodes, float time,
int priority1, int priority2) const {
float frame = time * fps_;
if (frame > numFrames_)
frame = numFrames_;
for (int i = 0; i < numJoints_; i++)
if (nodes_[i] != NULL)
nodes_[i]->animate(nodes[i], frame,
((type_ & nodes[i].type_) != 0 ?
priority2 : priority1));
}
void KeyframeAnim::KeyframeEntry::loadBinary(const char *&data) {
frame_ = get_float(data);
flags_ = get_LE_uint32(data + 4);
pos_ = get_vector3d(data + 8);
pitch_ = get_float(data + 20);
yaw_ = get_float(data + 24);
roll_ = get_float(data + 28);
dpos_ = get_vector3d(data + 32);
dpitch_ = get_float(data + 44);
dyaw_ = get_float(data + 48);
droll_ = get_float(data + 52);
data += 56;
}
void KeyframeAnim::KeyframeNode::loadBinary(const char *&data) {
std::memcpy(meshName_, data, 32);
numEntries_ = get_LE_uint32(data + 36);
data += 44;
entries_ = new KeyframeEntry[numEntries_];
for (int i = 0; i < numEntries_; i++)
entries_[i].loadBinary(data);
}
void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) {
ts.scanString("mesh name %s", 1, meshName_);
ts.scanString("entries %d", 1, &numEntries_);
entries_ = new KeyframeEntry[numEntries_];
for (int i = 0; i < numEntries_; i++) {
int which, flags;
float frame, x, y, z, p, yaw, r, dx, dy, dz, dp, dyaw, dr;
ts.scanString(" %d: %f %i %f %f %f %f %f %f", 9, &which, &frame, &flags,
&x, &y, &z, &p, &yaw, &r);
ts.scanString(" %f %f %f %f %f %f", 6, &dx, &dy, &dz, &dp, &dyaw, &dr);
entries_[which].frame_ = frame;
entries_[which].flags_ = flags;
entries_[which].pos_ = Vector3d(x, y, z);
entries_[which].dpos_ = Vector3d(dx, dy, dz);
entries_[which].pitch_ = p;
entries_[which].yaw_ = yaw;
entries_[which].roll_ = r;
entries_[which].dpitch_ = dp;
entries_[which].dyaw_ = dyaw;
entries_[which].droll_ = dr;
}
}
KeyframeAnim::KeyframeNode::~KeyframeNode() {
delete[] entries_;
}
void KeyframeAnim::KeyframeNode::animate(Model::HierNode &node,
float frame, int priority) const {
if (numEntries_ == 0)
return;
if (priority < node.priority_)
return;
// Do a binary search for the nearest previous frame
// Loop invariant: entries_[low].frame_ <= frame < entries_[high].frame_
int low = 0, high = numEntries_;
while (high > low + 1) {
int mid = (low + high) / 2;
if (entries_[mid].frame_ <= frame)
low = mid;
else
high = mid;
}
float dt = frame - entries_[low].frame_;
Vector3d pos = entries_[low].pos_ + dt * entries_[low].dpos_;
float pitch = entries_[low].pitch_ + dt * entries_[low].dpitch_;
float yaw = entries_[low].yaw_ + dt * entries_[low].dyaw_;
float roll = entries_[low].roll_ + dt * entries_[low].droll_;
if (pitch > 180)
pitch -= 360;
if (yaw > 180)
yaw -= 360;
if (roll > 180)
roll -= 360;
if (priority > node.priority_) {
node.priority_ = priority;
node.totalWeight_ = 1;
node.animPos_ = pos;
node.animPitch_ = pitch;
node.animYaw_ = yaw;
node.animRoll_ = roll;
}
else { // priority == node.priority_
node.totalWeight_++;
node.animPos_ += pos;
node.animPitch_ += pitch;
node.animYaw_ += yaw;
node.animRoll_ += roll;
}
}