9#include "glm/gtc/quaternion.hpp"
21 std::map<int16, std::pair<GameFile*, GameFile*>>
animfiles;
34 return static_cast<T
>(v1 * (1.0f - r) + v2 * r);
46inline T
interpolateHermite(
const float r,
const T& v1,
const T& v2,
const T& in,
const T& outVal)
49 float h1 = 2.0f * r * r * r - 3.0f * r * r + 1.0f;
50 float h2 = -2.0f * r * r * r + 3.0f * r * r;
51 float h3 = r * r * r - 2.0f * r * r + r;
52 float h4 = r * r * r - r * r;
55 return static_cast<T
>(v1 * h1 + v2 * h2 + in * h3 + outVal * h4);
67inline T
interpolateBezier(
const float r,
const T& v1,
const T& v2,
const T& in,
const T& outVal)
69 const float InverseFactor = (1.0f - r);
70 const float FactorTimesTwo = r * r;
71 const float InverseFactorTimesTwo = InverseFactor * InverseFactor;
73 float h1 = InverseFactorTimesTwo * InverseFactor;
74 float h2 = 3.0f * r * InverseFactorTimesTwo;
75 float h3 = 3.0f * FactorTimesTwo * InverseFactor;
76 float h4 = FactorTimesTwo * r;
79 return static_cast<T
>(v1 * h1 + v2 * h2 + in * h3 + outVal * h4);
86 return glm::slerp(v1, v2, r);
111 static const T&
conv(
const T& t)
131 static_cast<float>(t.
w < 0 ? t.
w + 32768 : t.
w - 32767) / 32767.0f,
132 static_cast<float>(t.
x < 0 ? t.
x + 32768 : t.
x - 32767) / 32767.0f,
133 static_cast<float>(t.
y < 0 ? t.
y + 32768 : t.
y - 32767) / 32767.0f,
134 static_cast<float>(t.
z < 0 ? t.
z + 32768 : t.
z - 32767) / 32767.0f);
143 static float conv(
const short t)
159template <
class T,
class D=T,
class Conv=Identity<T>>
175 return ((
data[anim].size()) > 0);
192 if (
data[anim].size() > 1 &&
times[anim].size() > 1)
196 const size_t max_time =
times[anim][
times[anim].size() - 1];
201 pos =
times[anim].size() - 1;
205 return data[anim][pos];
207 return interpolate<T>(r,
data[anim][pos],
data[anim][pos]);
211 return interpolateHermite<T>(r,
data[anim][pos],
data[anim][pos],
in[anim][pos],
out[anim][pos]);
216 return interpolateBezier<T>(r,
data[anim][pos],
data[anim][pos],
in[anim][pos],
out[anim][pos]);
219 return data[anim][pos];
223 for (
size_t i = 0; i <
times[anim].size() - 1; i++)
225 if (time >=
times[anim][i] && time <
times[anim][i + 1])
231 const size_t t1 =
times[anim][pos];
232 const size_t t2 =
times[anim][pos + 1];
233 r = (time - t1) /
static_cast<float>(t2 - t1);
236 return data[anim][pos];
238 return interpolate<T>(r,
data[anim][pos],
data[anim][pos + 1]);
242 return interpolateHermite<T>(r,
data[anim][pos],
data[anim][pos + 1],
in[anim][pos],
248 return interpolateBezier<T>(r,
data[anim][pos],
data[anim][pos + 1],
in[anim][pos],
out[anim][pos]);
251 return data[anim][pos];
257 if (
data[anim].size() == 0)
260 return data[anim][0];
278 for (
size_t j = 0; j < b.
nTimes; j++)
283 const unsigned int* ptimes =
reinterpret_cast<unsigned int*
>(f->
getBuffer() + pHeadTimes->
ofsEntrys);
284 for (
size_t i = 0; i < pHeadTimes->
nEntrys; i++)
285 times[j].push_back(ptimes[i]);
289 for (
size_t j = 0; j < b.
nKeys; j++)
299 for (
size_t i = 0; i < pHeadKeys->
nEntrys; i++)
300 data[j].push_back(Conv::conv(keys[i]));
304 for (
size_t i = 0; i < pHeadKeys->
nEntrys; i++)
306 data[j].push_back(Conv::conv(keys[i * 3]));
307 in[j].push_back(Conv::conv(keys[i * 3 + 1]));
308 out[j].push_back(Conv::conv(keys[i * 3 + 2]));
330 for (
size_t j = 0; j < b.
nTimes; j++)
337 auto it = modelData.
animfiles.find(animIdIt->second);
340 GameFile* animfile = it->second.first;
341 GameFile* skelfile = it->second.second;
342 if (!animfile || !skelfile)
344 LOG_WARNING <<
"Animation data loading: null file pointer for animation index" << j;
349 LOG_WARNING <<
"Animation data loading: setChunk(SKB1) failed for" << skelfile->
fullname();
352 unsigned char* skelBuf = skelfile->
getBuffer();
353 unsigned char* animBuf = animfile->
getBuffer();
355 if (!skelBuf || !animBuf)
357 if ((!skelBuf && !skelfile->
isEof()) || (!animBuf && !animfile->
isEof()))
359 LOG_WARNING <<
"Animation data loading: null buffer despite file not being EOF - skelBuf:" << (
void*)skelBuf
360 <<
"animBuf:" << (
void*)animBuf
361 <<
"skelEof:" << skelfile->
isEof()
362 <<
"animEof:" << animfile->
isEof()
370 LOG_WARNING <<
"Animation data loading: header offset" << headerOffset <<
"out of bounds for" << skelfile->
fullname();
376 LOG_WARNING <<
"Animation data loading: invalid header data - ofsEntrys:" << pHeadTimes->
ofsEntrys
377 <<
"nEntrys:" << pHeadTimes->
nEntrys
378 <<
"animSize:" << animfile->
getSize()
382 const size_t dataSize =
static_cast<size_t>(pHeadTimes->
nEntrys) *
sizeof(
uint32);
385 LOG_WARNING <<
"Animation data loading: data size" << dataSize <<
"at offset" << pHeadTimes->
ofsEntrys
386 <<
"exceeds file size" << animfile->
getSize() <<
"for" << animfile->
fullname();
399 const size_t dataSize =
static_cast<size_t>(pHeadTimes->
nEntrys) *
sizeof(
uint32);
405 for (
size_t i = 0; i < pHeadTimes->
nEntrys; i++)
406 times[j].push_back(ptimes[i]);
410 for (
size_t j = 0; j < b.
nKeys; j++)
417 auto it = modelData.
animfiles.find(animIdIt->second);
420 GameFile* animfile = it->second.first;
421 GameFile* skelfile = it->second.second;
422 if (!animfile || !skelfile)
424 LOG_WARNING <<
"Keyframe data loading: null file pointer for animation index" << j;
432 unsigned char* skelBuf = skelfile->
getBuffer();
433 unsigned char* animBuf = animfile->
getBuffer();
435 if (!skelBuf || !animBuf)
437 if ((!skelBuf && !skelfile->
isEof()) || (!animBuf && !animfile->
isEof()))
439 LOG_WARNING <<
"Keyframe data loading: null buffer despite file not being EOF - skelBuf:" << (
void*)skelBuf
440 <<
"animBuf:" << (
void*)animBuf
441 <<
"skelEof:" << skelfile->
isEof()
442 <<
"animEof:" << animfile->
isEof()
450 LOG_WARNING <<
"Keyframe data loading: header offset" << headerOffset <<
"out of bounds for" << skelfile->
fullname();
456 LOG_WARNING <<
"Keyframe data loading: invalid header data - ofsEntrys:" << pHeadKeys->
ofsEntrys
457 <<
"nEntrys:" << pHeadKeys->
nEntrys
458 <<
"animSize:" << animfile->
getSize()
463 const size_t dataSize =
static_cast<size_t>(pHeadKeys->
nEntrys) * multiplier *
sizeof(D);
466 LOG_WARNING <<
"Keyframe data loading: data size" << dataSize <<
"at offset" << pHeadKeys->
ofsEntrys
467 <<
"exceeds file size" << animfile->
getSize() <<
"for" << animfile->
fullname();
470 keys =
reinterpret_cast<D*
>(animBuf + pHeadKeys->
ofsEntrys);
481 const size_t dataSize =
static_cast<size_t>(pHeadKeys->
nEntrys) * multiplier *
sizeof(D);
491 for (
size_t i = 0; i < pHeadKeys->
nEntrys; i++)
492 data[j].push_back(Conv::conv(keys[i]));
496 for (
size_t i = 0; i < pHeadKeys->
nEntrys; i++)
498 data[j].push_back(Conv::conv(keys[i * 3]));
499 in[j].push_back(Conv::conv(keys[i * 3 + 1]));
500 out[j].push_back(Conv::conv(keys[i * 3 + 2]));
512 outVal <<
" <type>" << v.
type <<
"</type>" << std::endl;
513 outVal <<
" <seq>" << v.
seq <<
"</seq>" << std::endl;
514 outVal <<
" <anims>" << std::endl;
515 for (
size_t j = 0; j < v.
sizes; j++)
525 if (v.
uses((
unsigned int)j))
527 outVal <<
" <anim id=\"" << j <<
"\" size=\"" << v.
data[j].size() <<
"\">" << std::endl;
528 for (
size_t k = 0; k < v.
data[j].size(); k++)
532 outVal <<
" </anim>" << std::endl;
535 outVal <<
" </anims>" << std::endl;
544float randfloat(
float lower,
float upper);
545int randint(
int lower,
int upper);
T interpolateBezier(const float r, const T &v1, const T &v2, const T &in, const T &outVal)
Bezier spline interpolation between two values.
T interpolateHermite(const float r, const T &v1, const T &v2, const T &in, const T &outVal)
Hermite spline interpolation between two values.
float randfloat(float lower, float upper)
glm::fquat interpolate< glm::fquat >(const float r, const glm::fquat &v1, const glm::fquat &v2)
T interpolate(const float r, const T &v1, const T &v2)
Linearly interpolate between two values.
size_t globalTime
Global clock for global-sequence animations.
std::pair< size_t, size_t > AnimRange
A (start, end) frame range for an animation.
Interpolations
Interpolation modes used by animated values in M2 models.
@ INTERPOLATION_LINEAR
Linear interpolation.
@ INTERPOLATION_NONE
No interpolation (step).
@ INTERPOLATION_HERMITE
Hermite spline.
@ INTERPOLATION_BEZIER
Bezier spline.
int randint(int lower, int upper)
Animated< float, short, ShortToFloat > AnimatedShort
Generic animated value class that reads keyframe data from M2 files.
std::vector< T > data[MAX_ANIMATED]
friend std::ostream & operator<<(std::ostream &outVal, const Animated &v)
T getValue(ssize_t anim, size_t time)
std::vector< uint32 > globals
std::vector< size_t > times[MAX_ANIMATED]
void init(AnimationBlock &b, GameFile *f, std::vector< uint32 > &gs)
void init(AnimationBlock &b, GameFile &f, const modelAnimData &modelData)
std::vector< T > out[MAX_ANIMATED]
std::vector< T > in[MAX_ANIMATED]
bool uses(ssize_t anim) const
Abstract base class representing a file within the game data archive.
size_t getSize()
Total size of the file in bytes.
unsigned char * getBuffer() const
Pointer to the start of the internal buffer.
const std::string & fullname() const
bool setChunk(std::string chunkName, bool resetToStart=true)
Switch the active read window to the named chunk.
bool isEof()
True if the read pointer has reached the end of the file.
Identity conversion functor — returns its argument unchanged.
static const T & conv(const T &t)
Passthrough conversion.
Converts a packed 16-bit quaternion to a 32-bit float quaternion.
static const glm::fquat conv(const PACK_QUATERNION t)
Convert packed 16-bit quaternion to glm::fquat.
Converts opacity values stored as int16 to normalised float [0, 1].
static float conv(const short t)
Convert a short opacity value to float.
Holds per-model animation metadata: index-to-id mapping, external anim files, and global sequences.
std::vector< uint32 > globalSequences
Global sequence durations.
std::map< int16, std::pair< GameFile *, GameFile * > > animfiles
Maps anim ID to (anim file, skel file) pair.
std::map< uint, int16 > animIndexToAnimId
Maps animation index to animation ID.
Sub-block header for animated values in M2 models.
Packed 16-bit quaternion as stored in WoW 2.0+ M2 files.
int16 w
Packed quaternion components.