ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

keyframe.cpp

Go to the documentation of this file.
00001 /* ResidualVM - A 3D game interpreter
00002  *
00003  * ResidualVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/endian.h"
00024 
00025 #include "engines/grim/debug.h"
00026 #include "engines/grim/keyframe.h"
00027 #include "engines/grim/textsplit.h"
00028 #include "engines/grim/resource.h"
00029 #include "engines/grim/model.h"
00030 
00031 namespace Grim {
00032 
00033 KeyframeAnim::KeyframeAnim(const Common::String &fname, Common::SeekableReadStream *data) :
00034         Object(), _fname(fname) {
00035 
00036     uint32 tag = data->readUint32BE();
00037     if (tag == MKTAG('F','Y','E','K'))
00038         loadBinary(data);
00039     else {
00040         data->seek(0, SEEK_SET);
00041         TextSplitter ts(fname, data);
00042         loadText(ts);
00043     }
00044 }
00045 
00046 void KeyframeAnim::loadBinary(Common::SeekableReadStream *data) {
00047     // First four bytes are the FYEK Keyframe identifier code
00048     // Next 36 bytes are the filename
00049     Debug::debug(Debug::Keyframes, "Loading Keyframe '%s'.", _fname.c_str());
00050     // Next four bytes are the flags
00051     data->seek(40, SEEK_SET);
00052     _flags = data->readUint32LE();
00053     // Next four bytes are a duplicate of _numJoints (?)
00054     // Next four bytes are the type
00055     data->readUint32LE();
00056     _type = data->readUint32LE();
00057     // Next four bytes are the frames per second
00058     // The fps value seems to be ignored and causes the animation the first time manny
00059     // enters the kitchen of the Blue Casket to go out of sync. So we force it to 15.
00060 //  _fps = data->readFloatLE();
00061     _fps = 15.;
00062     // Next four bytes are the number of frames
00063     data->seek(56, SEEK_SET);
00064     _numFrames = data->readUint32LE();
00065     // Next four bytes are the number of joints
00066     _numJoints = data->readUint32LE();
00067     // Next four bytes are unknown (?)
00068     // Next four bytes are the number of markers
00069     data->readUint32LE();
00070     _numMarkers = data->readUint32LE();
00071     _markers = new Marker[_numMarkers];
00072     data->seek(72, SEEK_SET);
00073     for (int i = 0; i < _numMarkers; i++) {
00074         _markers[i].frame = data->readFloatLE();
00075     }
00076 
00077     data->seek(104, SEEK_SET);
00078     for (int i = 0; i < _numMarkers; i++)
00079         _markers[i].val = data->readUint32LE();
00080 
00081     _nodes = new KeyframeNode *[_numJoints];
00082     // The first 136 bytes are for the header, this was originally
00083     // listed as 180 bytes since the first operation is usually a
00084     // "null" key, however ma_card_hold.key showed that this is
00085     // not always the case so we should not skip this operation
00086     data->seek(136, SEEK_SET);
00087     for (int i = 0; i < _numJoints; i++) {
00088         _nodes[i] = nullptr;
00089         int nodeNum;
00090         // The first 32 bytes (of a keyframe) are the name handle
00091         char nameHandle[32];
00092         data->read(nameHandle, 32);
00093         // If the name handle is entirely null (like ma_rest.key)
00094         // then we shouldn't try to set the name
00095         if (nameHandle[0] == 0)
00096             memcpy(nameHandle, "(null)", 7);
00097 
00098         // The next four bytes are the node number identifier
00099         nodeNum = data->readUint32LE();
00100 
00101         // Because of the issue above ma_card_hold.key used to crash
00102         // at this part without checking to make sure nodeNum is a
00103         // valid number, we'll leave this in just in case something
00104         // else is still wrong but it should now load correctly in
00105         // all cases
00106         if (nodeNum >= _numJoints) {
00107             Debug::warning(Debug::Keyframes, "A node number was greater than the maximum number of nodes (%d/%d)", nodeNum, _numJoints);
00108             return;
00109         }
00110         if (_nodes[nodeNum]) {
00111             // Null node. Usually 7, 13 and 27 are null nodes.
00112             data->seek(8, SEEK_CUR);
00113             continue;
00114         }
00115         _nodes[nodeNum] = new KeyframeNode();
00116         _nodes[nodeNum]->loadBinary(data, nameHandle);
00117     }
00118 }
00119 
00120 void KeyframeAnim::loadText(TextSplitter &ts) {
00121     ts.expectString("section: header");
00122     ts.scanString("flags %x", 1, &_flags);
00123     ts.scanString("type %x", 1, &_type);
00124     ts.scanString("frames %d", 1, &_numFrames);
00125     ts.scanString("fps %f", 1, &_fps);
00126     ts.scanString("joints %d", 1, &_numJoints);
00127 
00128     if (scumm_stricmp(ts.getCurrentLine(), "section: markers") == 0) {
00129         ts.nextLine();
00130         ts.scanString("markers %d", 1, &_numMarkers);
00131         _markers = new Marker[_numMarkers];
00132         for (int i = 0; i < _numMarkers; i++)
00133             ts.scanString("%f %d", 2, &_markers[i].frame, &_markers[i].val);
00134     } else {
00135         _numMarkers = 0;
00136         _markers = nullptr;
00137     }
00138 
00139     ts.expectString("section: keyframe nodes");
00140     int numNodes;
00141     ts.scanString("nodes %d", 1, &numNodes);
00142     _nodes = new KeyframeNode *[_numJoints];
00143     for (int i = 0; i < _numJoints; i++)
00144         _nodes[i] = nullptr;
00145     for (int i = 0; i < numNodes; i++) {
00146         int which;
00147         ts.scanString("node %d", 1, &which);
00148         _nodes[which] = new KeyframeNode;
00149         _nodes[which]->loadText(ts);
00150     }
00151 }
00152 
00153 KeyframeAnim::~KeyframeAnim() {
00154     for (int i = 0; i < _numJoints; i++)
00155         delete _nodes[i];
00156     delete[] _nodes;
00157     delete[] _markers;
00158     g_resourceloader->uncacheKeyframe(this);
00159 }
00160 
00161 bool KeyframeAnim::isNodeAnimated(ModelNode *nodes, int num, float time, bool tagged) const {
00162     // Without this sending the bread down the tube in "mo" often crashes,
00163     // because it goes outside the bounds of the array of the nodes.
00164     if (num >= _numJoints)
00165         return false;
00166 
00167     float frame = time * _fps;
00168 
00169     if (frame > _numFrames)
00170         frame = _numFrames;
00171 
00172     if (_nodes[num] && tagged == ((_type & nodes[num]._type) != 0)) {
00173         return _nodes[num]->_numEntries != 0;
00174     } else {
00175         return false;
00176     }
00177 }
00178 
00179 void KeyframeAnim::animate(ModelNode *nodes, int num, float time, float fade, bool tagged) const {
00180     // Without this sending the bread down the tube in "mo" often crashes,
00181     // because it goes outside the bounds of the array of the nodes.
00182     if (num >= _numJoints)
00183         return;
00184 
00185     float frame = time * _fps;
00186 
00187     if (frame > _numFrames)
00188         frame = _numFrames;
00189 
00190     if (_nodes[num] && tagged == ((_type & nodes[num]._type) != 0)) {
00191         _nodes[num]->animate(nodes[num], frame, fade, (_flags & 256) == 0);
00192     }
00193 }
00194 
00195 int KeyframeAnim::getMarker(float startTime, float stopTime) const {
00196     if (!_markers)
00197         return 0;
00198 
00199     startTime *= _fps;
00200     stopTime *= _fps ;
00201 
00202     for (int i = 0; i < _numMarkers; ++i) {
00203         Marker &m = _markers[i];
00204         if (m.frame >= startTime && m.frame < stopTime) {
00205             return m.val;
00206         }
00207     }
00208     return 0;
00209 }
00210 
00211 void KeyframeAnim::KeyframeEntry::loadBinary(Common::SeekableReadStream *data) {
00212     _frame = data->readFloatLE();
00213     _flags = data->readUint32LE();
00214     _pos.readFromStream(data);
00215     _pitch = data->readFloatLE();
00216     _yaw = data->readFloatLE();
00217     _roll = data->readFloatLE();
00218     _dpos.readFromStream(data);
00219     _dpitch = data->readFloatLE();
00220     _dyaw = data->readFloatLE();
00221     _droll = data->readFloatLE();
00222 }
00223 
00224 void KeyframeAnim::KeyframeNode::loadBinary(Common::SeekableReadStream *data, char *meshName) {
00225     memcpy(_meshName, meshName, 32);
00226 
00227     _numEntries = data->readUint32LE();
00228     data->seek(4, SEEK_CUR);
00229     _entries = new KeyframeEntry[_numEntries];
00230     for (int i = 0; i < _numEntries; i++) {
00231         _entries[i].loadBinary(data);
00232     }
00233 }
00234 
00235 void KeyframeAnim::KeyframeNode::loadText(TextSplitter &ts) {
00236     ts.scanString("mesh name %s", 1, _meshName);
00237     ts.scanString("entries %d", 1, &_numEntries);
00238     _entries = new KeyframeEntry[_numEntries];
00239     for (int i = 0; i < _numEntries; i++) {
00240         int which;
00241         unsigned flags;
00242         float frame, x, y, z, p, yaw, r, dx, dy, dz, dp, dyaw, dr;
00243         ts.scanString(" %d: %f %x %f %f %f %f %f %f", 9, &which, &frame, &flags, &x, &y, &z, &p, &yaw, &r);
00244         ts.scanString(" %f %f %f %f %f %f", 6, &dx, &dy, &dz, &dp, &dyaw, &dr);
00245         _entries[which]._frame = frame;
00246         _entries[which]._flags = (int)flags;
00247         _entries[which]._pos = Math::Vector3d(x, y, z);
00248         _entries[which]._dpos = Math::Vector3d(dx, dy, dz);
00249         _entries[which]._pitch = p;
00250         _entries[which]._yaw = yaw;
00251         _entries[which]._roll = r;
00252         _entries[which]._dpitch = dp;
00253         _entries[which]._dyaw = dyaw;
00254         _entries[which]._droll = dr;
00255     }
00256 }
00257 
00258 KeyframeAnim::KeyframeNode::~KeyframeNode() {
00259     delete[] _entries;
00260 }
00261 
00262 void KeyframeAnim::KeyframeNode::animate(ModelNode &node, float frame, float fade, bool useDelta) const {
00263     if (_numEntries == 0)
00264         return;
00265 
00266     // Do a binary search for the nearest previous frame
00267     // Loop invariant: entries_[low].frame_ <= frame < entries_[high].frame_
00268     int low = 0, high = _numEntries;
00269     while (high > low + 1) {
00270         int mid = (low + high) / 2;
00271         if (_entries[mid]._frame <= frame)
00272             low = mid;
00273         else
00274             high = mid;
00275     }
00276 
00277     float dt = frame - _entries[low]._frame;
00278     Math::Vector3d pos = _entries[low]._pos;
00279     Math::Angle pitch = _entries[low]._pitch;
00280     Math::Angle yaw = _entries[low]._yaw;
00281     Math::Angle roll = _entries[low]._roll;
00282 
00289     if (useDelta) {
00290         pos += dt * _entries[low]._dpos;
00291         pitch += dt * _entries[low]._dpitch;
00292         yaw += dt * _entries[low]._dyaw;
00293         roll += dt * _entries[low]._droll;
00294     }
00295 
00296     node._animPos += (pos - node._pos) * fade;
00297 
00298     Math::Quaternion rotQuat = Math::Quaternion::fromEuler(yaw, pitch, roll, Math::EO_ZXY);
00299     rotQuat = node._animRot * node._rot.inverse() * rotQuat;
00300     node._animRot = node._animRot.slerpQuat(rotQuat, fade);
00301 }
00302 
00303 } // end of namespace Grim


Generated on Sat Feb 16 2019 05:00:55 for ResidualVM by doxygen 1.7.1
curved edge   curved edge