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

PredatorCZ / HavokLib / 90

07 Nov 2025 09:36AM UTC coverage: 62.461% (-1.4%) from 63.837%
90

push

github

PredatorCZ
add support for scene data

556 of 1153 new or added lines in 22 files covered. (48.22%)

205 existing lines in 35 files now uncovered.

2777 of 4446 relevant lines covered (62.46%)

124997.44 hits per line

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

86.03
/source/hka_wavelet_decompressor.cpp
1
/*  Havok Format Library
2
    Copyright(C) 2016-2025 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_wavelet_decompressor.hpp"
19
#include <cassert>
20

21
static const Vector4A16 BOTTOM_ROW[] = {
22
    {1.0f, 1.0f, 1.0f, 1.0f},         {0.5f, 0.5f, 0.5f, 0.5f},
23
    {-0.25f, -0.25f, -0.25f, -0.25f}, {-0.25f, 0.25f, 0.75f, 0.75f},
24
    {0.0f, 0.0f, 0.0f, 0.0f},         {-0.25f, -0.125f, 0.0f, 0.0f},
25
    {-0.25f, 0.75f, -0.25f, -0.25f},  {0.0f, -0.125f, -0.25f, 0.75f}};
26

27
static const Vector4A16 TOP_ROW[] = {
28
    {1.0f, 1.0f, 1.0f, 1.0f},          {-0.5f, -0.25f, 0.0f, 0.25f},
29
    {-0.5f, 0.0625f, 0.625f, 0.1875f}, {0.0f, -0.0625f, -0.125f, -0.1875f},
30
    {-0.5f, 0.625f, -0.25f, -0.125f},  {0.0f, -0.125f, -0.25f, 0.75f},
31
    {0.0f, 0.0f, 0.0f, -0.125f},       {0.0f, 0.0f, 0.0f, 0.0f}};
32

33
static void UpdateReflect(float factor, float *outData, const float *inData,
53,760✔
34
                          int frame) {
35
  const int prevFrame = frame - 1;
53,760✔
36

37
  int v5 = 1;
38
  if (frame - 1 >= 4) {
53,760✔
39
    const float *v06 = inData;
40
    float *v07 = outData;
41
    uint32 v7 = ((uint32)(frame - 5) >> 2) + 1;
53,760✔
42
    v5 = 4 * v7 + 1;
53,760✔
43
    do {
44
      --v7;
349,440✔
45

46
      v07[1] = (v06[0] + v06[1]) * factor + v07[1];
349,440✔
47
      v07[2] = (v06[1] + v06[2]) * factor + v07[2];
349,440✔
48
      v07[3] = (v06[2] + v06[3]) * factor + v07[3];
349,440✔
49
      v07[4] = (v06[3] + v06[4]) * factor + v07[4];
349,440✔
50

51
      v06 += 4;
349,440✔
52
      v07 += 4;
349,440✔
53
    } while (v7);
349,440✔
54
  }
55
  if (v5 < frame) {
53,760✔
56
    float *v11 = outData + v5;
53,760✔
57
    int v12 = frame - v5;
53,760✔
58

59
    do {
60
      *v11 = (v11[prevFrame] + v11[frame]) * factor + *v11;
161,280✔
61
      v11++;
161,280✔
62
      --v12;
161,280✔
63
    } while (v12);
161,280✔
64
  }
65

66
  outData[0] = factor * 2 * inData[0] + outData[0];
53,760✔
67
}
53,760✔
68

69
static void PredictReflect(float factor, const float *inData, float *outData,
53,760✔
70
                           int frame) {
71
  int prevFrame = frame - 1;
53,760✔
72
  int v7 = 0;
73

74
  if (prevFrame >= 4) {
53,760✔
75
    const float *v06 = inData;
76
    float *v07 = outData;
77
    uint32 v10 = ((uint32)(frame - 5) >> 2) + 1;
53,760✔
78

79
    v7 = 4 * v10;
53,760✔
80
    do {
81
      --v10;
349,440✔
82

83
      v07[0] = (v06[0] + v06[1]) * factor + v07[0];
349,440✔
84
      v07[1] = (v06[1] + v06[2]) * factor + v07[1];
349,440✔
85
      v07[2] = (v06[2] + v06[3]) * factor + v07[2];
349,440✔
86
      v07[3] = (v06[3] + v06[4]) * factor + v07[3];
349,440✔
87

88
      v06 += 4;
349,440✔
89
      v07 += 4;
349,440✔
90
    } while (v10);
349,440✔
91
  }
92

93
  if (v7 < prevFrame) {
53,760✔
94
    float *v14 = outData + v7;
53,760✔
95
    int v15 = prevFrame - v7;
53,760✔
96

97
    do {
98
      *v14 = (v14[-prevFrame] + v14[-frame]) * factor + *v14;
161,280✔
99
      v14++;
161,280✔
100
      --v15;
161,280✔
101
    } while (v15);
161,280✔
102
  }
103

104
  outData[prevFrame] = inData[prevFrame] * factor * 2 + outData[prevFrame];
53,760✔
105
}
53,760✔
106

107
static void MergeOddEven(const float *inData, float *outData, int numFrames) {
53,760✔
108
  std::vector<float> buffer(inData, inData + numFrames);
53,760✔
109
  float *bufferPtr = buffer.data();
110

111
  const int v6 = numFrames >> 1;
53,760✔
112
  int v7 = 0;
113
  if (v6 >= 4) {
53,760✔
114
    uint32 v9 = ((uint32)(v6 - 4) >> 2) + 1;
53,760✔
115

116
    float *v01 = bufferPtr + v6;
53,760✔
117
    float *v02 = bufferPtr;
118
    float *v03 = outData;
119

120
    v7 = 4 * v9;
53,760✔
121
    do {
122
      --v9;
403,200✔
123

124
      v03[0] = v02[0];
403,200✔
125
      v03[1] = v01[0];
403,200✔
126
      v03[2] = v02[1];
403,200✔
127
      v03[3] = v01[1];
403,200✔
128
      v03[4] = v02[2];
403,200✔
129
      v03[5] = v01[2];
403,200✔
130
      v03[6] = v02[3];
403,200✔
131
      v03[7] = v01[3];
403,200✔
132

133
      v01 += 4;
403,200✔
134
      v02 += 4;
403,200✔
135
      v03 += 8;
403,200✔
136
    } while (v9);
403,200✔
137
  }
138
  if (v7 < v6) {
53,760✔
139
    float *v12 = &bufferPtr[v7 + v6];
×
140
    do {
141
      outData[2 * v7] = bufferPtr[v7];
×
142
      ++v7;
×
143
      outData[2 * v7 - 1] = *v12++;
×
144
    } while (v7 < v6);
×
145
  }
146
}
53,760✔
147

148
static void TransformWavelet(float *values, size_t blockSize) {
188,160✔
149
  Vector4A16 top(values[0]);
188,160✔
150
  Vector4A16 bottom(top);
188,160✔
151

152
  for (uint32 i = 1; i < 8; i++) {
1,505,280✔
153
    top = TOP_ROW[i] * values[i] + top;
1,317,120✔
154
    bottom = BOTTOM_ROW[i] * values[i] + bottom;
155
  }
156

157
  memcpy(values, &top, 16);
158
  memcpy(values + 4, &bottom, 16);
188,160✔
159

160
  for (size_t v7 = 8; v7 < blockSize;) {
241,920✔
161
    UpdateReflect(-0.25, values, values + v7, v7);
53,760✔
162
    PredictReflect(0.5, values, values + v7, v7);
53,760✔
163
    v7 *= 2;
53,760✔
164
    MergeOddEven(values, values, v7);
53,760✔
165
  }
166
}
188,160✔
167

168
static size_t Decompress(float offset, float scale, char *buffer, char bitWidth,
188,160✔
169
                         size_t numIndicies, std::vector<float> &coeficients) {
170
  const size_t actualNumItems = coeficients.size();
171
  size_t currentItem = 0;
172
  size_t currentBit = 0;
173
  int dataCurrentBit = 0;
174
  int *cBuffer = reinterpret_cast<int *>(buffer);
175
  uint32 *dataBuffer = reinterpret_cast<uint32 *>(buffer + numIndicies);
188,160✔
176
  const int bitMask = (1 << bitWidth) - 1;
188,160✔
177
  const float fractal = 1.0f / bitMask;
188,160✔
178
  int numCoeficients = 0;
179

180
  while (currentItem < actualNumItems) {
3,306,240✔
181
    if (!(*cBuffer & (1 << currentBit))) {
3,118,080✔
182
      uint32 curNumber = (*dataBuffer >> dataCurrentBit) & bitMask;
2,357,952✔
183
      dataCurrentBit += bitWidth;
2,357,952✔
184

185
      if (dataCurrentBit > 31) {
2,357,952✔
186
        dataCurrentBit -= 32;
829,952✔
187
        dataBuffer++;
829,952✔
188

189
        if (dataCurrentBit) {
829,952✔
190
          const uint32 subNumber = (*dataBuffer & ((1 << dataCurrentBit) - 1))
597,888✔
191
                                   << (bitWidth - dataCurrentBit);
597,888✔
192
          curNumber |= subNumber;
597,888✔
193
        }
194
      }
195

196
      float dequantized =
2,357,952✔
197
          (offset + static_cast<float>(curNumber) * scale * fractal);
2,357,952✔
198
      coeficients[currentItem] = dequantized;
2,357,952✔
199
      numCoeficients++;
2,357,952✔
200
    }
201

202
    currentBit++;
3,118,080✔
203

204
    if (currentBit > 31) {
3,118,080✔
205
      cBuffer++;
53,760✔
206
      currentBit = 0;
207
    }
208

209
    currentItem++;
3,118,080✔
210
  }
211

212
  numCoeficients = (numCoeficients * bitWidth + 7) >> 3;
188,160✔
213
  return numIndicies + numCoeficients;
188,160✔
214
}
215

216
void hkaWaveletDecompressor::Assign(
128✔
217
    hkaWaveletCompressedAnimationInternalInterface *input) {
218
  char *buffer = const_cast<char *>(input->GetData());
128✔
219
  hkaAnimation *anim = dynamic_cast<hkaAnimation *>(input);
128✔
220
  const size_t numTracks = anim->GetNumOfTransformTracks();
128✔
221
  const size_t numFloatTracks = anim->GetNumOfFloatTracks();
128✔
222
  const size_t numDynTracks = input->GetNumDynamicTracks();
128✔
223
  const size_t numFrames = input->GetNumOfPoses();
128✔
224

225
  uint8 *bitWidthStart =
226
      reinterpret_cast<uint8 *>(buffer + input->GetBitWidthOffset());
128✔
227
  StaticMask *masksStart =
228
      reinterpret_cast<StaticMask *>(buffer + input->GetStaticMaskOffset());
128✔
229
  float *offsetsStart =
230
      reinterpret_cast<float *>(buffer + input->GetOffsetsOffset());
128✔
231
  float *scalesStart =
232
      reinterpret_cast<float *>(buffer + input->GetScalesOffset());
128✔
233

234
  bitWidths = {bitWidthStart, numDynTracks};
128✔
235
  masks = {masksStart, numTracks + numFloatTracks};
128✔
236
  offsets = {offsetsStart, numDynTracks};
128✔
237
  scales = {scalesStart, numDynTracks};
128✔
238

239
  tracks.reserve(numTracks);
128✔
240
  floats.reserve(numFloatTracks);
128✔
241

242
  float *staticBuffer =
243
      reinterpret_cast<float *>(buffer + input->GetStaticDataOffset());
128✔
244

245
  buffer += input->GetQuantizedDataOffset();
128✔
246

247
  std::vector<DynamicTrack<float>> dynamicTracks;
128✔
248
  dynamicTracks.resize(numDynTracks);
128✔
249

250
  const size_t blockSize = input->GetBlockSize();
128✔
251
  const size_t numBlocks =
128✔
252
      static_cast<size_t>(ceilf(numFrames / static_cast<float>(blockSize)));
128✔
253
  const size_t numPreserved = input->GetNumPreserved();
128✔
254

255
  for (size_t b = 0; b < numBlocks; b++) {
1,024✔
256
    size_t numItems = blockSize * (b + 1);
896✔
257

258
    if (numItems > numFrames)
259
      numItems = numFrames % blockSize;
260
    else
261
      numItems = blockSize;
262

263
    for (size_t p = 0; p < numDynTracks; p++) {
189,056✔
264
      auto &tck = dynamicTracks[p];
265

266
      for (size_t pr = 0; pr < numPreserved; pr++) {
188,160✔
UNCOV
267
        tck.items.push_back(*reinterpret_cast<float *>(buffer));
×
UNCOV
268
        buffer += 4;
×
269
      }
270

271
      const size_t numIndicies = (blockSize + 7) >> 3;
188,160✔
272
      std::vector<float> coeficients(numIndicies * 8);
188,160✔
273

274
      buffer += Decompress(offsets[p], scales[p], buffer, bitWidths[p],
188,160✔
275
                           numIndicies, coeficients);
276
      TransformWavelet(coeficients.data(), blockSize);
188,160✔
277

278
      tck.items.insert(tck.items.end(), coeficients.begin(), coeficients.end());
188,160✔
279
    }
280
  }
281

282
  size_t curDynTrack = 0;
128✔
283
  for (size_t p = 0; p < numTracks; p++) {
12,032✔
284
    StaticMask &mask = masks[p];
285
    MasterTrack *mtPtr = new MasterTrack(mask);
11,904✔
286
    tracks.emplace_back(mtPtr);
11,904✔
287

288
    auto CopyDynamic = [&](StaticMask::MaskType msk, size_t id,
39,552✔
289
                           DynamicTrack<Vector4A16> *tck) {
290
      if (mask.UseSubTrack(msk)) {
39,552✔
291
        auto cTrack = dynamicTracks[curDynTrack++].items.begin();
26,880✔
292
        for (auto &c : tck->items) {
2,741,760✔
293
          c[id] = *cTrack++;
2,714,880✔
294
        }
295
      }
296
    };
11,904✔
297

298
    switch (mask.GetPosTrackType()) {
11,904✔
299
    case TT_STATIC: {
11,264✔
300
      StaticTrack<Vector4A16> *tck =
301
          static_cast<StaticTrack<Vector4A16> *>(mtPtr->pos.get());
302
      tck->track = reinterpret_cast<Vector &>(*staticBuffer);
11,264✔
303
      staticBuffer += 3;
11,264✔
304
      break;
11,264✔
305
    }
306
    case TT_DYNAMIC: {
384✔
307
      DynamicTrack<Vector4A16> *tck =
308
          static_cast<DynamicTrack<Vector4A16> *>(mtPtr->pos.get());
309

310
      Vector staticValues;
311

312
      if (!mask.UseSubTrack(StaticMask::posX)) {
384✔
313
        staticValues.X = *staticBuffer;
314
        staticBuffer++;
315
      }
316
      if (!mask.UseSubTrack(StaticMask::posY)) {
384✔
317
        staticValues.Y = *staticBuffer;
128✔
318
        staticBuffer++;
128✔
319
      }
320
      if (!mask.UseSubTrack(StaticMask::posZ)) {
384✔
321
        staticValues.Z = *staticBuffer;
322
        staticBuffer++;
323
      }
324

UNCOV
325
      if (mask.UseSubTrack(StaticMask::posX) ||
×
326
          mask.UseSubTrack(StaticMask::posY) ||
384✔
327
          mask.UseSubTrack(StaticMask::posZ)) {
328
        tck->items.insert(tck->items.begin(), numFrames, staticValues);
384✔
329
      }
330

331
      CopyDynamic(StaticMask::posX, 0, tck);
384✔
332
      CopyDynamic(StaticMask::posY, 1, tck);
384✔
333
      CopyDynamic(StaticMask::posZ, 2, tck);
384✔
334

335
      break;
336
    }
337
    default:
338
      break;
339
    }
340

341
    switch (mask.GetRotTrackType()) {
11,904✔
342
    case TT_STATIC: {
2,304✔
343
      StaticTrack<Vector4A16> *tck =
344
          static_cast<StaticTrack<Vector4A16> *>(mtPtr->rot.get());
345
      memcpy(tck->track._arr, staticBuffer, sizeof(Vector4A16));
2,304✔
346
      staticBuffer += 4;
2,304✔
347
      break;
2,304✔
348
    }
349
    case TT_DYNAMIC: {
9,600✔
350
      DynamicTrack<Vector4A16> *tck =
351
          static_cast<DynamicTrack<Vector4A16> *>(mtPtr->rot.get());
352

353
      Vector4A16 staticValues;
354

355
      if (!mask.UseSubTrack(StaticMask::rotX)) {
9,600✔
356
        staticValues.X = *staticBuffer;
1,280✔
357
        staticBuffer++;
1,280✔
358
      }
359
      if (!mask.UseSubTrack(StaticMask::rotY)) {
9,600✔
360
        staticValues.Y = *staticBuffer;
1,536✔
361
        staticBuffer++;
1,536✔
362
      }
363
      if (!mask.UseSubTrack(StaticMask::rotZ)) {
9,600✔
364
        staticValues.Z = *staticBuffer;
640✔
365
        staticBuffer++;
640✔
366
      }
367
      if (!mask.UseSubTrack(StaticMask::rotW)) {
9,600✔
368
        staticValues.W = *staticBuffer;
9,088✔
369
        staticBuffer++;
9,088✔
370
      }
371

372
      if (mask.UseSubTrack(StaticMask::rotX) ||
1,280✔
373
          mask.UseSubTrack(StaticMask::rotY) ||
1,280✔
374
          mask.UseSubTrack(StaticMask::rotZ) ||
9,600✔
375
          mask.UseSubTrack(StaticMask::rotW)) {
376
        tck->items.insert(tck->items.begin(), numFrames, staticValues);
9,600✔
377
      }
378

379
      CopyDynamic(StaticMask::rotX, 0, tck);
9,600✔
380
      CopyDynamic(StaticMask::rotY, 1, tck);
9,600✔
381
      CopyDynamic(StaticMask::rotZ, 2, tck);
9,600✔
382
      CopyDynamic(StaticMask::rotW, 3, tck);
9,600✔
383

384
      for (auto &t : tck->items) {
979,200✔
385
        if (t.W == 2.0f || t.W == -2.0f) {
969,600✔
386
          float basis = t.W * 0.5f;
904,960✔
387
          t.W = 0.f;
904,960✔
388
          t.QComputeElement();
389
          t.W *= basis;
904,960✔
390
        }
391
      }
392
      break;
393
    }
394
    default:
395
      break;
396
    }
397

398
    switch (mask.GetScaleTrackType()) {
11,904✔
UNCOV
399
    case TT_STATIC: {
×
400
      StaticTrack<Vector4A16> *tck =
401
          static_cast<StaticTrack<Vector4A16> *>(mtPtr->scale.get());
UNCOV
402
      tck->track = reinterpret_cast<Vector &>(*staticBuffer);
×
403
      staticBuffer += 3;
404
      break;
×
405
    }
406
    case TT_DYNAMIC: {
×
407
      DynamicTrack<Vector4A16> *tck =
408
          static_cast<DynamicTrack<Vector4A16> *>(mtPtr->scale.get());
409

410
      Vector staticValues;
411

412
      if (!mask.UseSubTrack(StaticMask::scaleX)) {
×
413
        staticValues.X = *staticBuffer;
414
        staticBuffer++;
415
      }
UNCOV
416
      if (!mask.UseSubTrack(StaticMask::scaleY)) {
×
417
        staticValues.Y = *staticBuffer;
418
        staticBuffer++;
419
      }
UNCOV
420
      if (!mask.UseSubTrack(StaticMask::scaleZ)) {
×
421
        staticValues.Z = *staticBuffer;
422
        staticBuffer++;
423
      }
424

UNCOV
425
      if (mask.UseSubTrack(StaticMask::scaleX) ||
×
UNCOV
426
          mask.UseSubTrack(StaticMask::scaleY) ||
×
427
          mask.UseSubTrack(StaticMask::scaleZ)) {
428
        tck->items.insert(tck->items.begin(), numFrames, staticValues);
×
429
      }
430

UNCOV
431
      CopyDynamic(StaticMask::scaleX, 0, tck);
×
UNCOV
432
      CopyDynamic(StaticMask::scaleY, 1, tck);
×
433
      CopyDynamic(StaticMask::scaleZ, 2, tck);
×
434

435
      break;
436
    }
437
    default:
438
      break;
439
    }
440

441
    for (size_t f = numTracks; f < numFloatTracks + numTracks; f++) {
11,904✔
442
      StaticMask &m = masks[f];
443

444
      switch (m.GetPosTrackType()) {
×
UNCOV
445
      case TT_IDENTITY:
×
446
        floats.emplace_back(new StaticTrack<float>);
×
447
        break;
×
448
      case TT_STATIC:
×
449
        floats.emplace_back(new StaticTrack<float>(*staticBuffer++));
×
450
        break;
×
451
      case TT_DYNAMIC: {
×
452
        auto cFloatTrack = new DynamicTrack<float>;
×
453
        floats.emplace_back(cFloatTrack);
×
454
        cFloatTrack->items.swap(dynamicTracks[curDynTrack++].items);
×
455
        break;
456
      }
457
      }
458
    }
459
  }
460
}
128✔
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