• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

PredatorCZ / HavokLib / 89

06 Nov 2025 01:09PM UTC coverage: 63.837% (-3.2%) from 67.014%
89

push

github

PredatorCZ
s

3354 of 5254 relevant lines covered (63.84%)

129082.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.34
/source/hka_spline_decompressor.cpp
1
/*  Havok Format Library
2
    Copyright(C) 2016-2023 Lukas Cone
3

4
    This program is free software : you can redistribute it and / or modify
5
    it under the terms of the GNU General Public License as published by
6
    the Free Software Foundation, either version 3 of the License, or
7
    (at your option) any later version.
8

9
    This program is distributed in the hope that it will be useful,
10
    but WITHOUT ANY WARRANTY; without even the implied warranty of
11
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12
    GNU General Public License for more details.
13

14
    You should have received a copy of the GNU General Public License
15
    along with this program.If not, see <https://www.gnu.org/licenses/>.
16
*/
17

18
#include "hka_spline_decompressor.hpp"
19
#include <cmath>
20
#include <cstring>
21

22
Vector4A16 Read32Quat(const char *&buffer) {
756,576✔
23
  constexpr uint64 rMask = (1 << 10) - 1;
756,576✔
24
  constexpr float rFrac = GetFraction(10);
756,576✔
25
  constexpr float fPI = 3.14159265f;
756,576✔
26
  constexpr float fPI2 = 0.5f * fPI;
756,576✔
27
  constexpr float fPI4 = 0.5f * fPI2;
756,576✔
28
  constexpr float phiFrac = fPI2 / 511.f;
756,576✔
29

30
  const uint32 cVal = *reinterpret_cast<const uint32 *>(buffer);
756,576✔
31

32
  float R = static_cast<float>((cVal >> 18) & rMask) * rFrac;
756,576✔
33
  R = 1.0f - (R * R);
756,576✔
34

35
  const float phiTheta = static_cast<float>((cVal & 0x3FFFF));
756,576✔
36

37
  float phi = floorf(sqrtf(phiTheta));
756,576✔
38
  float theta = 0;
756,576✔
39

40
  if (phi > 0.0f) {
756,576✔
41
    theta = fPI4 * (phiTheta - (phi * phi)) / phi;
669,744✔
42
    phi = phiFrac * phi;
669,744✔
43
  }
44

45
  const float magnitude = sqrtf(1.0f - R * R);
756,576✔
46
  const float sPhi = sinf(phi);
756,576✔
47
  const float cPhi = cosf(phi);
756,576✔
48
  const float sTheta = sinf(theta);
756,576✔
49
  const float cTheta = cosf(theta);
756,576✔
50

51
  const Vector4A16 retVal0 = Vector4A16(sPhi, sPhi, cPhi, R) *
756,576✔
52
                             Vector4A16(cTheta, sTheta, 1.f, 1.f) *
1,513,152✔
53
                             Vector4A16(magnitude, magnitude, magnitude, 1.f);
1,513,152✔
54
  const IVector4A16 signMask(0x10000000, 0x20000000, 0x40000000, 0x80000000);
756,576✔
55
  const auto blendMask =
56
      _mm_cmpeq_epi32((IVector4A16(cVal) & signMask)._data, signMask._data);
756,576✔
57

58
  const auto retVal =
59
      _mm_blendv_ps(retVal0._data, (-retVal0)._data,
756,576✔
60
                    reinterpret_cast<const __m128 &>(blendMask));
756,576✔
61

62
  buffer += 4;
756,576✔
63
  return retVal;
756,576✔
64
}
65

66
Vector4A16 Read40Quat(const char *&buffer) {
869,184✔
67
  constexpr float fractal = 0.000345436f;
869,184✔
68
  const Vector4A16 fract(fractal, fractal, fractal, 0);
869,184✔
69

70
  const uint64 cVal0 = *reinterpret_cast<const uint64 *>(buffer);
869,184✔
71
  const uint32 cVal1 = cVal0 >> 24;
869,184✔
72
  const auto tmpVal = (UIVector4A16(cVal0, cVal0, cVal1, 0) *
869,184✔
73
                       UIVector4A16(1 << 20, 1 << 8, 1 << 20, 0)) >>
1,738,368✔
74
                      20;
869,184✔
75
  const auto tmpVal1 = IVector4A16(tmpVal) - (1 << 11) - 1;
869,184✔
76
  const auto tmpVal2 = (Vector4A16(tmpVal1) * fract).QComputeElement();
869,184✔
77
  const size_t resultShift = (cVal0 >> 36) & 3;
869,184✔
78
  const Vector4A16 wmul(1.f, 1.f, 1.f, (cVal0 >> 38) & 1 ? -1.f : 1.f);
869,184✔
79
  const auto retVal = (wmul * tmpVal2)._data;
869,184✔
80
  buffer += 5;
869,184✔
81

82
  switch (resultShift) {
869,184✔
83
  case 0:
37,296✔
84
    return _mm_shuffle_ps(retVal, retVal, _MM_SHUFFLE(2, 1, 0, 3));
37,296✔
85
  case 1:
15,696✔
86
    return _mm_shuffle_ps(retVal, retVal, _MM_SHUFFLE(2, 1, 3, 0));
15,696✔
87
  case 2:
38,592✔
88
    return _mm_shuffle_ps(retVal, retVal, _MM_SHUFFLE(2, 3, 1, 0));
38,592✔
89
  default:
777,600✔
90
    return retVal;
777,600✔
91
  }
92
}
93

94
Vector4A16 Read48Quat(const char *&buffer) {
749,520✔
95
  constexpr uint64 mask = (1 << 15) - 1;
749,520✔
96
  constexpr float fractal = 0.000043161f;
749,520✔
97
  const Vector4A16 fract(fractal, fractal, fractal, 0);
749,520✔
98
  const SVector cVal = *reinterpret_cast<const SVector *>(buffer);
749,520✔
99

100
  const char resultShift = ((cVal.Y >> 14) & 2) | ((cVal.X >> 15) & 1);
749,520✔
101
  const bool rSign = (cVal.Z >> 15) != 0;
749,520✔
102
  const IVector4A16 retVal0(cVal.X, cVal.Y, cVal.Z, 0.f);
749,520✔
103
  auto retVal1 = Vector4A16((retVal0 & mask) - (mask >> 1)) * fract;
749,520✔
104
  retVal1.QComputeElement();
749,520✔
105
  const Vector4A16 wmul(1.f, 1.f, 1.f, rSign ? -1.f : 1.f);
749,520✔
106
  const auto retVal = (wmul * retVal1)._data;
749,520✔
107
  buffer += 6;
749,520✔
108

109
  switch (resultShift) {
749,520✔
110
  case 0:
36,144✔
111
    return _mm_shuffle_ps(retVal, retVal, _MM_SHUFFLE(2, 1, 0, 3));
36,144✔
112
  case 1:
15,120✔
113
    return _mm_shuffle_ps(retVal, retVal, _MM_SHUFFLE(2, 1, 3, 0));
15,120✔
114
  case 2:
42,192✔
115
    return _mm_shuffle_ps(retVal, retVal, _MM_SHUFFLE(2, 3, 1, 0));
42,192✔
116
  default:
656,064✔
117
    return retVal;
656,064✔
118
  }
119
}
120

121
Vector4A16 ReadQuat(QuantizationType qType, const char *&buffer) {
2,375,280✔
122
  switch (qType) {
2,375,280✔
123
  case QT_32bit:
756,576✔
124
    return Read32Quat(buffer);
756,576✔
125
  case QT_40bit:
869,184✔
126
    return Read40Quat(buffer);
869,184✔
127
  case QT_48bit:
749,520✔
128
    return Read48Quat(buffer);
749,520✔
129
  case QT_Uncompressed: {
×
130
    Vector4A16 retVal = *reinterpret_cast<const Vector4 *>(buffer);
×
131
    buffer += 16;
×
132
    return retVal;
×
133
  }
134
  default:
×
135
    return {0.0f, 0.0f, 0.0f, 1.0f};
×
136
  }
137
}
138

139
// Algorithm A2.1 The NURBS Book 2nd edition, page 68
140
int FindKnotSpan(int degree, float value, int cPointsSize,
1,684,800✔
141
                 std::span<uint8> &knots) {
142
  if (value >= knots[cPointsSize])
1,684,800✔
143
    return cPointsSize - 1;
×
144

145
  int low = degree;
1,684,800✔
146
  int high = cPointsSize;
1,684,800✔
147
  int mid = (low + high) / 2;
1,684,800✔
148

149
  while (value < knots[mid] || value >= knots[mid + 1]) {
7,668,648✔
150
    if (value < knots[mid])
5,983,848✔
151
      high = mid;
3,229,704✔
152
    else
153
      low = mid;
2,754,144✔
154

155
    mid = (low + high) / 2;
5,983,848✔
156
  }
157

158
  return mid;
1,684,800✔
159
}
160

161
// Basis_ITS1, GetPoint_NR1, TIME-EFFICIENT NURBS CURVE EVALUATION ALGORITHMS,
162
// pages 64 & 65
163
template <class C>
164
C GetSinglePoint(int knotSpanIndex, int degree, float frame,
1,833,624✔
165
                 std::span<uint8> &knots, std::vector<C> &cPoints) {
166
  float N[5] = {1.0f};
1,833,624✔
167

168
  for (int i = 1; i <= degree; i++)
4,591,584✔
169
    for (int j = i - 1; j >= 0; j--) {
6,733,224✔
170
      float A = (frame - knots[knotSpanIndex - j]) /
3,975,264✔
171
                (knots[knotSpanIndex + i - j] - knots[knotSpanIndex - j]);
3,975,264✔
172
      float tmp = N[j] * A;
3,975,264✔
173
      N[j + 1] += N[j] - tmp;
3,975,264✔
174
      N[j] = tmp;
3,975,264✔
175
    }
176

177
  C retVal;
1,576,800✔
178
  memset(static_cast<void *>(&retVal), 0, sizeof(C));
1,833,624✔
179

180
  for (int i = 0; i <= degree; i++)
6,425,208✔
181
    retVal += cPoints[knotSpanIndex - i] * N[i];
4,591,584✔
182

183
  return retVal;
1,833,624✔
184
}
185

256,824✔
186
Vector4A16 SplineDynamicTrackQuat::GetValue(float localFrame) {
187
  int knotSpan =
256,824✔
188
      FindKnotSpan(degree, localFrame, static_cast<int>(track.size()), knots);
189
  return GetSinglePoint(knotSpan, degree, localFrame, knots, track);
556,848✔
190
}
650,448✔
191

350,424✔
192
Vector SplineDynamicTrackVector::GetValue(float localFrame) {
350,424✔
193
  Vector out;
350,424✔
194
  int knotSpan = -1;
350,424✔
195

350,424✔
196
  int cSize = static_cast<int>(tracks[0].size());
197

198
  if (cSize == 1)
199
    out.X = tracks[0][0];
256,824✔
200
  else {
201
    knotSpan = FindKnotSpan(degree, localFrame, cSize, knots);
813,672✔
202
    out.X = GetSinglePoint(knotSpan, degree, localFrame, knots, tracks[0]);
556,848✔
203
  }
204

256,824✔
205
  cSize = static_cast<int>(tracks[1].size());
206

1,576,800✔
207
  if (cSize == 1)
208
    out.Y = tracks[1][0];
1,576,800✔
209
  else {
210
    if (knotSpan < 0)
4,034,736✔
211
      knotSpan = FindKnotSpan(degree, localFrame, cSize, knots);
6,082,776✔
212

3,624,840✔
213
    out.Y = GetSinglePoint(knotSpan, degree, localFrame, knots, tracks[1]);
3,624,840✔
214
  }
3,624,840✔
215

3,624,840✔
216
  cSize = static_cast<int>(tracks[2].size());
3,624,840✔
217

218
  if (cSize == 1)
219
    out.Z = tracks[2][0];
1,576,800✔
220
  else {
1,576,800✔
221
    if (knotSpan < 0)
222
      knotSpan = FindKnotSpan(degree, localFrame, cSize, knots);
5,611,536✔
223

4,034,736✔
224
    out.Z = GetSinglePoint(knotSpan, degree, localFrame, knots, tracks[2]);
225
  }
1,576,800✔
226

227
  return out;
228
}
1,576,800✔
229

230
void ApplyPadding(char *&buffer, int alignment = 4) {
1,576,800✔
231
  const size_t iterPos = reinterpret_cast<intptr_t>(buffer);
1,576,800✔
232
  const size_t result = iterPos & (alignment - 1);
233

234
  if (!result)
108,000✔
235
    return;
108,000✔
236

108,000✔
237
  buffer += alignment - result;
238
}
108,000✔
239

240
struct TrackBBOX {
108,000✔
241
  float min, max;
21,600✔
242
};
243

86,400✔
244
void TransformSplineBlock::Assign(char *buffer, size_t numTracks,
86,400✔
245
                                  size_t numFloatTractks) {
246
  auto trackStart = reinterpret_cast<TransformMask *>(buffer);
247
  buffer += sizeof(TransformMask) * numTracks + numFloatTractks;
108,000✔
248
  ApplyPadding(buffer);
249
  int cTrack = 0;
108,000✔
250
  masks = {trackStart, numTracks};
45,576✔
251
  tracks.resize(numTracks);
252

62,424✔
253
  for (auto &m : masks) {
×
254
    auto MakeTrack = [&](QuantizationType qtype, float defVal,
255
                         auto subTypes) -> TransformTrack::TrackType<Vector> {
62,424✔
256
      using stype = decltype(subTypes);
257
      const bool useSpline =
258
          m.GetSubTrackType(static_cast<TransformType>(stype::X)) ==
108,000✔
259
              STT_DYNAMIC ||
260
          m.GetSubTrackType(static_cast<TransformType>(stype::Y)) ==
108,000✔
261
              STT_DYNAMIC ||
×
262
          m.GetSubTrackType(static_cast<TransformType>(stype::Z)) ==
263
              STT_DYNAMIC;
108,000✔
264

21,600✔
265
      if (useSpline) {
266
        auto sTrack = std::make_unique<SplineDynamicTrackVector>();
108,000✔
267
        uint16 numItems = *reinterpret_cast<const uint16 *>(buffer++);
268
        buffer++;
269
        sTrack->degree = *reinterpret_cast<const uint8 *>(buffer++);
108,000✔
270
        const size_t bufferSkip = numItems + sTrack->degree + 2;
271
        sTrack->knots = {reinterpret_cast<uint8 *>(buffer), bufferSkip};
272
        buffer += bufferSkip;
125,568✔
273
        ApplyPadding(buffer);
125,568✔
274

125,568✔
275
        TrackBBOX extremes[3] = {};
276

125,568✔
277
        auto MakeSubTrack = [&](auto type, size_t id) {
27,072✔
278
          const auto ttype =
279
              m.GetSubTrackType(static_cast<TransformType>(type));
98,496✔
280

281
          if (ttype == STT_DYNAMIC) {
282
            extremes[id] = *reinterpret_cast<const TrackBBOX *>(buffer);
283
            buffer += 8;
284
            sTrack->tracks[id].resize(numItems + 1);
285
          } else if (ttype == STT_STATIC) {
286
            sTrack->tracks[id].push_back(
1,008✔
287
                *reinterpret_cast<const float *>(buffer));
288
            buffer += 4;
1,008✔
289
          } else {
1,008✔
290
            sTrack->tracks[id].push_back(defVal);
1,008✔
291
          }
1,008✔
292
        };
1,008✔
293

1,008✔
294
        MakeSubTrack(stype::X, 0);
295
        MakeSubTrack(stype::Y, 1);
94,752✔
296
        MakeSubTrack(stype::Z, 2);
187,488✔
297

298
        auto UnpackPoints8 = [&](auto type, size_t id, size_t sid) {
299
          constexpr float fractal = 1.0f / 255.0f;
187,488✔
300
          const auto ttype =
187,488✔
301
              m.GetSubTrackType(static_cast<TransformType>(type));
183,456✔
302
          if (ttype == STT_DYNAMIC) {
183,456✔
303
            float dVar =
370,944✔
304
                static_cast<float>(*reinterpret_cast<const uint8 *>(buffer++)) *
183,456✔
305
                fractal;
306
            sTrack->tracks[id][sid] =
307
                extremes[id].min + (extremes[id].max - extremes[id].min) * dVar;
187,488✔
308
          }
4,896✔
309
        };
211,968✔
310

4,896✔
311
        auto UnpackPoints16 = [&](auto type, size_t id, size_t sid) {
4,896✔
312
          constexpr float fractal = 1.0f / 0xffff;
4,896✔
313
          const auto ttype =
4,896✔
314
              m.GetSubTrackType(static_cast<TransformType>(type));
4,896✔
315
          if (ttype == STT_DYNAMIC) {
4,896✔
316
            float dVar = static_cast<float>(
317
                             *reinterpret_cast<const uint16 *>(buffer++)) *
4,896✔
318
                         fractal;
319
            sTrack->tracks[id][sid] =
64,656✔
320
                extremes[id].min + (extremes[id].max - extremes[id].min) * dVar;
321
            buffer++;
14,688✔
322
          }
323
        };
14,688✔
324

11,664✔
325
        if (qtype == QT_8bit) {
11,664✔
326
          for (int t = 0; t <= numItems; t++) {
11,664✔
327
            UnpackPoints8(stype::X, 0, t);
3,024✔
328
            UnpackPoints8(stype::Y, 1, t);
1,008✔
329
            UnpackPoints8(stype::Z, 2, t);
330
          }
1,008✔
331
        } else {
332
          for (int t = 0; t <= numItems; t++) {
2,016✔
333
            UnpackPoints16(stype::X, 0, t);
334
            UnpackPoints16(stype::Y, 1, t);
335
            UnpackPoints16(stype::Z, 2, t);
336
          }
4,896✔
337
        }
4,896✔
338

4,896✔
339
        ApplyPadding(buffer);
340
        return sTrack;
519,984✔
341
      }
181,440✔
342

343
      auto sTrack = std::make_unique<SplineStaticTrack<Vector>>();
181,440✔
344
      auto MakeSubTrack = [&](auto type, size_t id) {
181,440✔
345
        const auto ttype = m.GetSubTrackType(static_cast<TransformType>(type));
152,208✔
346

152,208✔
347
        if (ttype == STT_STATIC) {
348
          sTrack->item[id] = *reinterpret_cast<const float *>(buffer);
152,208✔
349
          buffer += 4;
152,208✔
350
        } else {
351
          sTrack->item[id] = defVal;
352
        }
353
      };
1,315,584✔
354

360,288✔
355
      MakeSubTrack(stype::X, 0);
356
      MakeSubTrack(stype::Y, 1);
360,288✔
357
      MakeSubTrack(stype::Z, 2);
360,288✔
358

295,056✔
359
      return sTrack;
295,056✔
360
    };
361

295,056✔
362
    tracks[cTrack].pos =
295,056✔
363
        MakeTrack(m.GetPosQuantizationType(), 0.f, TransformTypePos{});
295,056✔
364

365
    if (m.GetSubTrackType(ttRotation) == STT_DYNAMIC) {
366
      auto rTrack = new SplineDynamicTrackQuat();
367
      tracks[cTrack].rotation = TransformTrack::TrackType<Vector4A16>(rTrack);
4,896✔
368
      uint16 numItems = *reinterpret_cast<const uint16 *>(buffer++);
61,200✔
369
      buffer++;
60,480✔
370
      rTrack->degree = *reinterpret_cast<const uint8 *>(buffer++);
60,480✔
371
      const size_t bufferSkip = numItems + rTrack->degree + 2;
60,480✔
372
      rTrack->knots = {reinterpret_cast<uint8 *>(buffer), bufferSkip};
373
      buffer += bufferSkip;
374

124,272✔
375
      QuantizationType quantType = m.GetRotQuantizationType();
120,096✔
376

120,096✔
377
      if (quantType == QT_48bit || quantType == QT_16bitQuat)
120,096✔
378
        ApplyPadding(buffer, 2);
379
      else if (quantType == QT_32bit || quantType == QT_Uncompressed)
380
        ApplyPadding(buffer);
381

4,896✔
382
      rTrack->track.resize(numItems + 1);
4,896✔
383

4,896✔
384
      for (int t = 0; t <= numItems; t++) {
385
        rTrack->track[t] = ReadQuat(quantType, (const char *&)buffer);
182,592✔
386
      }
1,952,928✔
387
    } else {
547,776✔
388
      auto rTrack = std::make_unique<SplineStaticTrack<Vector4A16>>();
389

547,776✔
390
      if (m.GetSubTrackType(ttRotation) == STT_STATIC) {
127,008✔
391
        rTrack->item =
127,008✔
392
            ReadQuat(m.GetRotQuantizationType(), (const char *&)buffer);
393
      } else {
420,768✔
394
        rTrack->item.W = 1.0f;
395
      }
396

397
      tracks[cTrack].rotation = std::move(rTrack);
182,592✔
398
    }
182,592✔
399

182,592✔
400
    ApplyPadding(buffer);
401

182,592✔
402
    tracks[cTrack].scale =
276,336✔
403
        MakeTrack(m.GetScaleQuantizationType(), 1.f, TransformTypeScale{});
93,744✔
404

405
    cTrack++;
406
  }
93,744✔
407
}
93,744✔
408

92,736✔
409
void hkaSplineDecompressor::Assign(
92,736✔
410
    hkaSplineCompressedAnimationInternalInterface *input) {
186,480✔
411
  auto blockOffsets = input->GetBlockOffsets();
92,736✔
412
  char *data = input->GetData();
413
  blocks.resize(blockOffsets.size());
414
  int cBlock = 0;
93,744✔
415

1,872✔
416
  for (auto &b : blocks) {
103,104✔
417
    b.Assign(data + *(blockOffsets.data() + cBlock),
1,872✔
418
             input->GetNumOfTransformTracks(), input->GetNumOfFloatTracks());
1,872✔
419
    cBlock++;
1,872✔
420
  }
1,872✔
421
}
1,872✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc