Coverage Report

Created: 2026-09-14 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/Assbin/AssbinFileWriter.cpp
Line
Count
Source
1
/*
2
Open Asset Import Library (assimp)
3
----------------------------------------------------------------------
4
5
Copyright (c) 2006-2026, assimp team
6
7
All rights reserved.
8
9
Redistribution and use of this software in source and binary forms,
10
with or without modification, are permitted provided that the
11
following conditions are met:
12
13
* Redistributions of source code must retain the above
14
  copyright notice, this list of conditions and the
15
  following disclaimer.
16
17
* Redistributions in binary form must reproduce the above
18
  copyright notice, this list of conditions and the
19
  following disclaimer in the documentation and/or other
20
  materials provided with the distribution.
21
22
* Neither the name of the assimp team, nor the names of its
23
  contributors may be used to endorse or promote products
24
  derived from this software without specific prior
25
  written permission of the assimp team.
26
27
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39
----------------------------------------------------------------------
40
*/
41
/** @file  AssbinFileWriter.cpp
42
 *  @brief Implementation of Assbin file writer.
43
 */
44
45
#include "AssbinFileWriter.h"
46
#include "Common/assbin_chunks.h"
47
#include "PostProcessing/ProcessHelper.h"
48
49
#include <assimp/Exceptional.h>
50
#include <assimp/version.h>
51
#include <assimp/IOStream.hpp>
52
53
#include "zlib.h"
54
55
#include <ctime>
56
57
#if _MSC_VER
58
#pragma warning(push)
59
#pragma warning(disable : 4706)
60
#endif // _MSC_VER
61
62
namespace Assimp {
63
64
template <typename T>
65
25.8k
size_t Write(IOStream *stream, const T &v) {
66
25.8k
    return stream->Write(&v, sizeof(T), 1);
67
25.8k
}
unsigned long Assimp::Write<bool>(Assimp::IOStream*, bool const&)
Line
Count
Source
65
2.38k
size_t Write(IOStream *stream, const T &v) {
66
2.38k
    return stream->Write(&v, sizeof(T), 1);
67
2.38k
}
unsigned long Assimp::Write<int>(Assimp::IOStream*, int const&)
Line
Count
Source
65
22.8k
size_t Write(IOStream *stream, const T &v) {
66
22.8k
    return stream->Write(&v, sizeof(T), 1);
67
22.8k
}
unsigned long Assimp::Write<unsigned long>(Assimp::IOStream*, unsigned long const&)
Line
Count
Source
65
589
size_t Write(IOStream *stream, const T &v) {
66
589
    return stream->Write(&v, sizeof(T), 1);
67
589
}
68
69
// -----------------------------------------------------------------------------------
70
// Serialize an aiString
71
template <>
72
231k
inline size_t Write<aiString>(IOStream *stream, const aiString &s) {
73
231k
    const size_t s2 = (uint32_t)s.length;
74
231k
    stream->Write(&s, 4, 1);
75
231k
    stream->Write(s.data, s2, 1);
76
77
231k
    return s2 + 4;
78
231k
}
79
80
// -----------------------------------------------------------------------------------
81
// Serialize an unsigned int as uint32_t
82
template <>
83
948k
inline size_t Write<unsigned int>(IOStream *stream, const unsigned int &w) {
84
948k
    const uint32_t t = (uint32_t)w;
85
948k
    if (w > t) {
86
        // this shouldn't happen, integers in Assimp data structures never exceed 2^32
87
0
        throw DeadlyExportError("loss of data due to 64 -> 32 bit integer conversion");
88
0
    }
89
90
948k
    stream->Write(&t, 4, 1);
91
92
948k
    return 4;
93
948k
}
94
95
// -----------------------------------------------------------------------------------
96
// Serialize an unsigned int as uint16_t
97
template <>
98
2.54M
inline size_t Write<uint16_t>(IOStream *stream, const uint16_t &w) {
99
2.54M
    static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t)==2");
100
2.54M
    stream->Write(&w, 2, 1);
101
102
2.54M
    return 2;
103
2.54M
}
104
105
// -----------------------------------------------------------------------------------
106
// Serialize a float
107
template <>
108
7.38M
inline size_t Write<float>(IOStream *stream, const float &f) {
109
7.38M
    static_assert(sizeof(float) == 4, "sizeof(float)==4");
110
7.38M
    stream->Write(&f, 4, 1);
111
112
7.38M
    return 4;
113
7.38M
}
114
115
// -----------------------------------------------------------------------------------
116
// Serialize a double
117
template <>
118
66.1k
inline size_t Write<double>(IOStream *stream, const double &f) {
119
66.1k
    static_assert(sizeof(double) == 8, "sizeof(double)==8");
120
66.1k
    stream->Write(&f, 8, 1);
121
122
66.1k
    return 8;
123
66.1k
}
124
125
// -----------------------------------------------------------------------------------
126
// Serialize a vec3
127
template <>
128
1.56M
inline size_t Write<aiVector3D>(IOStream *stream, const aiVector3D &v) {
129
1.56M
    size_t t = Write<ai_real>(stream, v.x);
130
1.56M
    t += Write<float>(stream, v.y);
131
1.56M
    t += Write<float>(stream, v.z);
132
133
1.56M
    return t;
134
1.56M
}
135
136
// -----------------------------------------------------------------------------------
137
// Serialize a color value
138
template <>
139
0
inline size_t Write<aiColor3D>(IOStream *stream, const aiColor3D &v) {
140
0
    size_t t = Write<ai_real>(stream, v.r);
141
0
    t += Write<float>(stream, v.g);
142
0
    t += Write<float>(stream, v.b);
143
144
0
    return t;
145
0
}
146
147
// -----------------------------------------------------------------------------------
148
// Serialize a color value
149
template <>
150
44.4k
inline size_t Write<aiColor4D>(IOStream *stream, const aiColor4D &v) {
151
44.4k
    size_t t = Write<ai_real>(stream, v.r);
152
44.4k
    t += Write<float>(stream, v.g);
153
44.4k
    t += Write<float>(stream, v.b);
154
44.4k
    t += Write<float>(stream, v.a);
155
156
44.4k
    return t;
157
44.4k
}
158
159
// -----------------------------------------------------------------------------------
160
// Serialize a quaternion
161
template <>
162
46.3k
inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) {
163
46.3k
    size_t t = Write<ai_real>(stream, v.w);
164
46.3k
    t += Write<float>(stream, v.x);
165
46.3k
    t += Write<float>(stream, v.y);
166
46.3k
    t += Write<float>(stream, v.z);
167
46.3k
    ai_assert(t == 16);
168
169
46.3k
    return t;
170
46.3k
}
171
172
// -----------------------------------------------------------------------------------
173
// Serialize a vertex weight
174
template <>
175
64.7k
inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) {
176
64.7k
    size_t t = Write<unsigned int>(stream, v.mVertexId);
177
178
64.7k
    return t + Write<float>(stream, v.mWeight);
179
64.7k
}
180
181
constexpr size_t MatrixSize = 64;
182
183
// -----------------------------------------------------------------------------------
184
// Serialize a mat4x4
185
template <>
186
141k
inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) {
187
705k
    for (unsigned int i = 0; i < 4; ++i) {
188
2.82M
        for (unsigned int i2 = 0; i2 < 4; ++i2) {
189
2.25M
            Write<ai_real>(stream, m[i][i2]);
190
2.25M
        }
191
564k
    }
192
193
141k
    return MatrixSize;
194
141k
}
195
196
// -----------------------------------------------------------------------------------
197
// Serialize an aiVectorKey
198
template <>
199
18.4k
inline size_t Write<aiVectorKey>(IOStream *stream, const aiVectorKey &v) {
200
18.4k
    const size_t t = Write<double>(stream, v.mTime);
201
18.4k
    return t + Write<aiVector3D>(stream, v.mValue);
202
18.4k
}
203
204
// -----------------------------------------------------------------------------------
205
// Serialize an aiQuatKey
206
template <>
207
46.3k
inline size_t Write<aiQuatKey>(IOStream *stream, const aiQuatKey &v) {
208
46.3k
    const size_t t = Write<double>(stream, v.mTime);
209
46.3k
    return t + Write<aiQuaternion>(stream, v.mValue);
210
46.3k
}
211
212
template <typename T>
213
0
inline size_t WriteBounds(IOStream *stream, const T *in, unsigned int size) {
214
0
    T minc, maxc;
215
0
    ArrayBounds(in, size, minc, maxc);
216
217
0
    const size_t t = Write<T>(stream, minc);
218
0
    return t + Write<T>(stream, maxc);
219
0
}
Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiVector3t<float> >(Assimp::IOStream*, aiVector3t<float> const*, unsigned int)
Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiColor4t<float> >(Assimp::IOStream*, aiColor4t<float> const*, unsigned int)
Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiVertexWeight>(Assimp::IOStream*, aiVertexWeight const*, unsigned int)
Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiVectorKey>(Assimp::IOStream*, aiVectorKey const*, unsigned int)
Unexecuted instantiation: unsigned long Assimp::WriteBounds<aiQuatKey>(Assimp::IOStream*, aiQuatKey const*, unsigned int)
220
221
// We use this to write out non-byte arrays so that we write using the specializations.
222
// This way we avoid writing out extra bytes that potentially come from struct alignment.
223
template <typename T>
224
91.9k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
91.9k
    size_t n = 0;
226
1.80M
    for (unsigned int i = 0; i < size; i++)
227
1.71M
        n += Write<T>(stream, in[i]);
228
229
91.9k
    return n;
230
91.9k
}
unsigned long Assimp::WriteArray<aiVector3t<float> >(Assimp::IOStream*, aiVector3t<float> const*, unsigned int)
Line
Count
Source
224
76.7k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
76.7k
    size_t n = 0;
226
1.61M
    for (unsigned int i = 0; i < size; i++)
227
1.54M
        n += Write<T>(stream, in[i]);
228
229
76.7k
    return n;
230
76.7k
}
unsigned long Assimp::WriteArray<aiColor4t<float> >(Assimp::IOStream*, aiColor4t<float> const*, unsigned int)
Line
Count
Source
224
5.83k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
5.83k
    size_t n = 0;
226
50.3k
    for (unsigned int i = 0; i < size; i++)
227
44.4k
        n += Write<T>(stream, in[i]);
228
229
5.83k
    return n;
230
5.83k
}
unsigned long Assimp::WriteArray<aiVertexWeight>(Assimp::IOStream*, aiVertexWeight const*, unsigned int)
Line
Count
Source
224
2.43k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
2.43k
    size_t n = 0;
226
67.1k
    for (unsigned int i = 0; i < size; i++)
227
64.7k
        n += Write<T>(stream, in[i]);
228
229
2.43k
    return n;
230
2.43k
}
unsigned long Assimp::WriteArray<aiVectorKey>(Assimp::IOStream*, aiVectorKey const*, unsigned int)
Line
Count
Source
224
4.62k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
4.62k
    size_t n = 0;
226
23.0k
    for (unsigned int i = 0; i < size; i++)
227
18.4k
        n += Write<T>(stream, in[i]);
228
229
4.62k
    return n;
230
4.62k
}
unsigned long Assimp::WriteArray<aiQuatKey>(Assimp::IOStream*, aiQuatKey const*, unsigned int)
Line
Count
Source
224
2.31k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
2.31k
    size_t n = 0;
226
48.6k
    for (unsigned int i = 0; i < size; i++)
227
46.3k
        n += Write<T>(stream, in[i]);
228
229
2.31k
    return n;
230
2.31k
}
231
232
// ----------------------------------------------------------------------------------
233
/** @class  AssbinChunkWriter
234
 *  @brief  Chunk writer mechanism for the .assbin file structure
235
 *
236
 *  This is a standard in-memory IOStream (most of the code is based on BlobIOStream),
237
 *  the difference being that this takes another IOStream as a "container" in the
238
 *  constructor, and when it is destroyed, it appends the magic number, the chunk size,
239
 *  and the chunk contents to the container stream. This allows relatively easy chunk
240
 *  chunk construction, even recursively.
241
 */
242
class AssbinChunkWriter final : public IOStream {
243
public:
244
    AssbinChunkWriter(IOStream *container, uint32_t magic, size_t initial = 4096) :
245
230k
            buffer(nullptr),
246
230k
            magic(magic),
247
230k
            container(container),
248
230k
            cur_size(0),
249
230k
            cursor(0),
250
230k
            initial(initial) {
251
        // empty
252
230k
    }
253
254
230k
    ~AssbinChunkWriter() override {
255
230k
        if (container) {
256
230k
            container->Write(&magic, sizeof(uint32_t), 1);
257
230k
            container->Write(&cursor, sizeof(uint32_t), 1);
258
230k
            container->Write(buffer, 1, cursor);
259
230k
        }
260
230k
        if (buffer) delete[] buffer;
261
230k
    }
262
263
0
    void *GetBufferPointer() { return buffer; }
264
265
0
    size_t Read(void * /*pvBuffer*/, size_t /*pSize*/, size_t /*pCount*/) override {
266
0
        return 0;
267
0
    }
268
269
0
    aiReturn Seek(size_t /*pOffset*/, aiOrigin /*pOrigin*/) override {
270
0
        return aiReturn_FAILURE;
271
0
    }
272
273
0
    size_t Tell() const override {
274
0
        return cursor;
275
0
    }
276
277
0
    void Flush() override {
278
        // not implemented
279
0
    }
280
281
0
    size_t FileSize() const override {
282
0
        return cursor;
283
0
    }
284
285
12.1M
    size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override {
286
12.1M
        pSize *= pCount;
287
12.1M
        if (cursor + pSize > cur_size) {
288
238k
            Grow(cursor + pSize);
289
238k
        }
290
291
12.1M
        memcpy(buffer + cursor, pvBuffer, pSize);
292
12.1M
        cursor += pSize;
293
294
12.1M
        return pCount;
295
12.1M
    }
296
297
private:
298
    // -------------------------------------------------------------------
299
238k
    void Grow(size_t need = 0) {
300
238k
        size_t new_size = std::max(initial, std::max(need, cur_size + (cur_size >> 1)));
301
302
238k
        const uint8_t *const old = buffer;
303
238k
        buffer = new uint8_t[new_size];
304
305
238k
        if (old) {
306
8.50k
            memcpy(buffer, old, cur_size);
307
8.50k
            delete[] old;
308
8.50k
        }
309
310
238k
        cur_size = new_size;
311
238k
    }
312
313
private:
314
    uint8_t *buffer;
315
    uint32_t magic;
316
    IOStream *container;
317
    size_t cur_size, cursor, initial;
318
};
319
320
// ----------------------------------------------------------------------------------
321
/** @class  AssbinFileWriter
322
 *  @brief  Assbin file writer class
323
 *
324
 *  This class writes an .assbin file, and is responsible for the file layout.
325
 */
326
class AssbinFileWriter {
327
private:
328
    bool shortened;
329
    bool compressed;
330
331
protected:
332
    // -----------------------------------------------------------------------------------
333
138k
    void WriteBinaryNode(IOStream *container, const aiNode *node) {
334
138k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODE);
335
336
138k
        unsigned int nb_metadata = (node->mMetaData != nullptr ? node->mMetaData->mNumProperties : 0);
337
338
138k
        Write<aiString>(&chunk, node->mName);
339
138k
        Write<aiMatrix4x4>(&chunk, node->mTransformation);
340
138k
        Write<unsigned int>(&chunk, node->mNumChildren);
341
138k
        Write<unsigned int>(&chunk, node->mNumMeshes);
342
138k
        Write<unsigned int>(&chunk, nb_metadata);
343
344
166k
        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
345
27.7k
            Write<unsigned int>(&chunk, node->mMeshes[i]);
346
27.7k
        }
347
348
272k
        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
349
133k
            WriteBinaryNode(&chunk, node->mChildren[i]);
350
133k
        }
351
352
173k
        for (unsigned int i = 0; i < nb_metadata; ++i) {
353
34.5k
            const aiString &key = node->mMetaData->mKeys[i];
354
34.5k
            aiMetadataType type = node->mMetaData->mValues[i].mType;
355
34.5k
            void *value = node->mMetaData->mValues[i].mData;
356
357
34.5k
            Write<aiString>(&chunk, key);
358
34.5k
            Write<uint16_t>(&chunk, (uint16_t)type);
359
360
34.5k
            switch (type) {
361
2.38k
            case AI_BOOL:
362
2.38k
                Write<bool>(&chunk, *((bool *)value));
363
2.38k
                break;
364
22.8k
            case AI_INT32:
365
22.8k
                Write<int32_t>(&chunk, *((int32_t *)value));
366
22.8k
                break;
367
589
            case AI_UINT64:
368
589
                Write<uint64_t>(&chunk, *((uint64_t *)value));
369
589
                break;
370
895
            case AI_FLOAT:
371
895
                Write<float>(&chunk, *((float *)value));
372
895
                break;
373
0
            case AI_DOUBLE:
374
0
                Write<double>(&chunk, *((double *)value));
375
0
                break;
376
4.32k
            case AI_AISTRING:
377
4.32k
                Write<aiString>(&chunk, *((aiString *)value));
378
4.32k
                break;
379
3.55k
            case AI_AIVECTOR3D:
380
3.55k
                Write<aiVector3D>(&chunk, *((aiVector3D *)value));
381
3.55k
                break;
382
#ifdef SWIG
383
                case FORCE_32BIT:
384
#endif // SWIG
385
0
            default:
386
0
                break;
387
34.5k
            }
388
34.5k
        }
389
138k
    }
390
391
    // -----------------------------------------------------------------------------------
392
348
    void WriteBinaryTexture(IOStream *container, const aiTexture *tex) {
393
348
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AITEXTURE);
394
395
348
        Write<unsigned int>(&chunk, tex->mWidth);
396
348
        Write<unsigned int>(&chunk, tex->mHeight);
397
        // Write the texture format, but don't include the null terminator.
398
348
        chunk.Write(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1);
399
400
348
        if (!shortened) {
401
348
            if (!tex->mHeight) {
402
71
                chunk.Write(tex->pcData, 1, tex->mWidth);
403
277
            } else {
404
277
                chunk.Write(tex->pcData, 1, tex->mWidth * tex->mHeight * 4);
405
277
            }
406
348
        }
407
348
    }
408
409
    // -----------------------------------------------------------------------------------
410
2.43k
    void WriteBinaryBone(IOStream *container, const aiBone *b) {
411
2.43k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIBONE);
412
413
2.43k
        Write<aiString>(&chunk, b->mName);
414
2.43k
        Write<unsigned int>(&chunk, b->mNumWeights);
415
2.43k
        Write<aiMatrix4x4>(&chunk, b->mOffsetMatrix);
416
417
        // for the moment we write dumb min/max values for the bones, too.
418
        // maybe I'll add a better, hash-like solution later
419
2.43k
        if (shortened) {
420
0
            WriteBounds(&chunk, b->mWeights, b->mNumWeights);
421
0
        } // else write as usual
422
2.43k
        else
423
2.43k
            WriteArray<aiVertexWeight>(&chunk, b->mWeights, b->mNumWeights);
424
2.43k
    }
425
426
    // -----------------------------------------------------------------------------------
427
27.6k
    void WriteBinaryMesh(IOStream *container, const aiMesh *mesh) {
428
27.6k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMESH);
429
430
27.6k
        Write<unsigned int>(&chunk, mesh->mPrimitiveTypes);
431
27.6k
        Write<unsigned int>(&chunk, mesh->mNumVertices);
432
27.6k
        Write<unsigned int>(&chunk, mesh->mNumFaces);
433
27.6k
        Write<unsigned int>(&chunk, mesh->mNumBones);
434
27.6k
        Write<unsigned int>(&chunk, mesh->mMaterialIndex);
435
436
        // first of all, write bits for all existent vertex components
437
27.6k
        unsigned int c = 0;
438
27.6k
        if (mesh->mVertices) {
439
27.6k
            c |= ASSBIN_MESH_HAS_POSITIONS;
440
27.6k
        }
441
27.6k
        if (mesh->mNormals) {
442
26.8k
            c |= ASSBIN_MESH_HAS_NORMALS;
443
26.8k
        }
444
27.6k
        if (mesh->mTangents && mesh->mBitangents) {
445
7.35k
            c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
446
7.35k
        }
447
35.1k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
448
35.1k
            if (!mesh->mTextureCoords[n]) {
449
27.6k
                break;
450
27.6k
            }
451
7.51k
            c |= ASSBIN_MESH_HAS_TEXCOORD(n);
452
7.51k
        }
453
33.4k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
454
33.4k
            if (!mesh->mColors[n]) {
455
27.6k
                break;
456
27.6k
            }
457
5.83k
            c |= ASSBIN_MESH_HAS_COLOR(n);
458
5.83k
        }
459
27.6k
        Write<unsigned int>(&chunk, c);
460
461
27.6k
        aiVector3D minVec, maxVec;
462
27.6k
        if (mesh->mVertices) {
463
27.6k
            if (shortened) {
464
0
                WriteBounds(&chunk, mesh->mVertices, mesh->mNumVertices);
465
0
            } // else write as usual
466
27.6k
            else
467
27.6k
                WriteArray<aiVector3D>(&chunk, mesh->mVertices, mesh->mNumVertices);
468
27.6k
        }
469
27.6k
        if (mesh->mNormals) {
470
26.8k
            if (shortened) {
471
0
                WriteBounds(&chunk, mesh->mNormals, mesh->mNumVertices);
472
0
            } // else write as usual
473
26.8k
            else
474
26.8k
                WriteArray<aiVector3D>(&chunk, mesh->mNormals, mesh->mNumVertices);
475
26.8k
        }
476
27.6k
        if (mesh->mTangents && mesh->mBitangents) {
477
7.35k
            if (shortened) {
478
0
                WriteBounds(&chunk, mesh->mTangents, mesh->mNumVertices);
479
0
                WriteBounds(&chunk, mesh->mBitangents, mesh->mNumVertices);
480
0
            } // else write as usual
481
7.35k
            else {
482
7.35k
                WriteArray<aiVector3D>(&chunk, mesh->mTangents, mesh->mNumVertices);
483
7.35k
                WriteArray<aiVector3D>(&chunk, mesh->mBitangents, mesh->mNumVertices);
484
7.35k
            }
485
7.35k
        }
486
33.4k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
487
33.4k
            if (!mesh->mColors[n])
488
27.6k
                break;
489
490
5.83k
            if (shortened) {
491
0
                WriteBounds(&chunk, mesh->mColors[n], mesh->mNumVertices);
492
0
            } // else write as usual
493
5.83k
            else
494
5.83k
                WriteArray<aiColor4D>(&chunk, mesh->mColors[n], mesh->mNumVertices);
495
5.83k
        }
496
35.1k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
497
35.1k
            if (!mesh->mTextureCoords[n])
498
27.6k
                break;
499
500
            // write number of UV components
501
7.51k
            Write<unsigned int>(&chunk, mesh->mNumUVComponents[n]);
502
503
7.51k
            if (shortened) {
504
0
                WriteBounds(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
505
0
            } // else write as usual
506
7.51k
            else
507
7.51k
                WriteArray<aiVector3D>(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
508
7.51k
        }
509
510
        // write faces. There are no floating-point calculations involved
511
        // in these, so we can write a simple hash over the face data
512
        // to the dump file. We generate a single 32 Bit hash for 512 faces
513
        // using Assimp's standard hashing function.
514
27.6k
        if (shortened) {
515
0
            unsigned int processed = 0;
516
0
            for (unsigned int job; (job = std::min(mesh->mNumFaces - processed, 512u)); processed += job) {
517
0
                uint32_t hash = 0;
518
0
                for (unsigned int a = 0; a < job; ++a) {
519
520
0
                    const aiFace &f = mesh->mFaces[processed + a];
521
0
                    uint32_t tmp = f.mNumIndices;
522
0
                    hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash);
523
0
                    for (unsigned int i = 0; i < f.mNumIndices; ++i) {
524
0
                        static_assert(AI_MAX_VERTICES <= 0xffffffff, "AI_MAX_VERTICES <= 0xffffffff");
525
0
                        tmp = static_cast<uint32_t>(f.mIndices[i]);
526
0
                        hash = SuperFastHash(reinterpret_cast<const char *>(&tmp), sizeof tmp, hash);
527
0
                    }
528
0
                }
529
0
                Write<unsigned int>(&chunk, hash);
530
0
            }
531
0
        } else // else write as usual
532
27.6k
        {
533
            // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
534
689k
            for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
535
661k
                const aiFace &f = mesh->mFaces[i];
536
537
661k
                static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
538
661k
                Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mNumIndices));
539
540
2.50M
                for (unsigned int a = 0; a < f.mNumIndices; ++a) {
541
1.83M
                    if (mesh->mNumVertices < (1u << 16)) {
542
1.83M
                        Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mIndices[a]));
543
1.83M
                    } else {
544
0
                        Write<unsigned int>(&chunk, f.mIndices[a]);
545
0
                    }
546
1.83M
                }
547
661k
            }
548
27.6k
        }
549
550
        // write bones
551
27.6k
        if (mesh->mNumBones) {
552
3.28k
            for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
553
2.43k
                const aiBone *b = mesh->mBones[a];
554
2.43k
                WriteBinaryBone(&chunk, b);
555
2.43k
            }
556
842
        }
557
27.6k
    }
558
559
    // -----------------------------------------------------------------------------------
560
48.2k
    void WriteBinaryMaterialProperty(IOStream *container, const aiMaterialProperty *prop) {
561
48.2k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIALPROPERTY);
562
563
48.2k
        Write<aiString>(&chunk, prop->mKey);
564
48.2k
        Write<unsigned int>(&chunk, prop->mSemantic);
565
48.2k
        Write<unsigned int>(&chunk, prop->mIndex);
566
567
48.2k
        Write<unsigned int>(&chunk, prop->mDataLength);
568
48.2k
        Write<unsigned int>(&chunk, (unsigned int)prop->mType);
569
48.2k
        chunk.Write(prop->mData, 1, prop->mDataLength);
570
48.2k
    }
571
572
    // -----------------------------------------------------------------------------------
573
4.86k
    void WriteBinaryMaterial(IOStream *container, const aiMaterial *mat) {
574
4.86k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIAL);
575
576
4.86k
        Write<unsigned int>(&chunk, mat->mNumProperties);
577
53.1k
        for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
578
48.2k
            WriteBinaryMaterialProperty(&chunk, mat->mProperties[i]);
579
48.2k
        }
580
4.86k
    }
581
582
    // -----------------------------------------------------------------------------------
583
2.31k
    void WriteBinaryNodeAnim(IOStream *container, const aiNodeAnim *nd) {
584
2.31k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODEANIM);
585
586
2.31k
        Write<aiString>(&chunk, nd->mNodeName);
587
2.31k
        Write<unsigned int>(&chunk, nd->mNumPositionKeys);
588
2.31k
        Write<unsigned int>(&chunk, nd->mNumRotationKeys);
589
2.31k
        Write<unsigned int>(&chunk, nd->mNumScalingKeys);
590
2.31k
        Write<unsigned int>(&chunk, nd->mPreState);
591
2.31k
        Write<unsigned int>(&chunk, nd->mPostState);
592
593
2.31k
        if (nd->mPositionKeys) {
594
2.31k
            if (shortened) {
595
0
                WriteBounds(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
596
597
0
            } // else write as usual
598
2.31k
            else
599
2.31k
                WriteArray<aiVectorKey>(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
600
2.31k
        }
601
2.31k
        if (nd->mRotationKeys) {
602
2.31k
            if (shortened) {
603
0
                WriteBounds(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
604
605
0
            } // else write as usual
606
2.31k
            else
607
2.31k
                WriteArray<aiQuatKey>(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
608
2.31k
        }
609
2.31k
        if (nd->mScalingKeys) {
610
2.31k
            if (shortened) {
611
0
                WriteBounds(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
612
613
0
            } // else write as usual
614
2.31k
            else
615
2.31k
                WriteArray<aiVectorKey>(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
616
2.31k
        }
617
2.31k
    }
618
619
    // -----------------------------------------------------------------------------------
620
678
    void WriteBinaryAnim(IOStream *container, const aiAnimation *anim) {
621
678
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIANIMATION);
622
623
678
        Write<aiString>(&chunk, anim->mName);
624
678
        Write<double>(&chunk, anim->mDuration);
625
678
        Write<double>(&chunk, anim->mTicksPerSecond);
626
678
        Write<unsigned int>(&chunk, anim->mNumChannels);
627
628
2.98k
        for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
629
2.31k
            const aiNodeAnim *nd = anim->mChannels[a];
630
2.31k
            WriteBinaryNodeAnim(&chunk, nd);
631
2.31k
        }
632
678
    }
633
634
    // -----------------------------------------------------------------------------------
635
0
    void WriteBinaryLight(IOStream *container, const aiLight *l) {
636
0
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AILIGHT);
637
638
0
        Write<aiString>(&chunk, l->mName);
639
0
        Write<unsigned int>(&chunk, l->mType);
640
641
0
        Write<aiVector3D>(&chunk, l->mPosition);
642
0
        Write<aiVector3D>(&chunk, l->mDirection);
643
0
        Write<aiVector3D>(&chunk, l->mUp);
644
645
0
        if (l->mType != aiLightSource_DIRECTIONAL) {
646
0
            Write<float>(&chunk, l->mAttenuationConstant);
647
0
            Write<float>(&chunk, l->mAttenuationLinear);
648
0
            Write<float>(&chunk, l->mAttenuationQuadratic);
649
0
        }
650
651
0
        Write<aiColor3D>(&chunk, l->mColorDiffuse);
652
0
        Write<aiColor3D>(&chunk, l->mColorSpecular);
653
0
        Write<aiColor3D>(&chunk, l->mColorAmbient);
654
655
0
        if (l->mType == aiLightSource_SPOT) {
656
0
            Write<float>(&chunk, l->mAngleInnerCone);
657
0
            Write<float>(&chunk, l->mAngleOuterCone);
658
0
        }
659
0
    }
660
661
    // -----------------------------------------------------------------------------------
662
133
    void WriteBinaryCamera(IOStream *container, const aiCamera *cam) {
663
133
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AICAMERA);
664
665
133
        Write<aiString>(&chunk, cam->mName);
666
133
        Write<aiVector3D>(&chunk, cam->mPosition);
667
133
        Write<aiVector3D>(&chunk, cam->mLookAt);
668
133
        Write<aiVector3D>(&chunk, cam->mUp);
669
133
        Write<float>(&chunk, cam->mHorizontalFOV);
670
133
        Write<float>(&chunk, cam->mClipPlaneNear);
671
133
        Write<float>(&chunk, cam->mClipPlaneFar);
672
133
        Write<float>(&chunk, cam->mAspect);
673
133
    }
674
675
    // -----------------------------------------------------------------------------------
676
4.85k
    void WriteBinaryScene(IOStream *container, const aiScene *scene) {
677
4.85k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AISCENE);
678
679
        // basic scene information
680
4.85k
        Write<unsigned int>(&chunk, scene->mFlags);
681
4.85k
        Write<unsigned int>(&chunk, scene->mNumMeshes);
682
4.85k
        Write<unsigned int>(&chunk, scene->mNumMaterials);
683
4.85k
        Write<unsigned int>(&chunk, scene->mNumAnimations);
684
4.85k
        Write<unsigned int>(&chunk, scene->mNumTextures);
685
4.85k
        Write<unsigned int>(&chunk, scene->mNumLights);
686
4.85k
        Write<unsigned int>(&chunk, scene->mNumCameras);
687
688
        // write node graph
689
4.85k
        WriteBinaryNode(&chunk, scene->mRootNode);
690
691
        // write all meshes
692
32.5k
        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
693
27.6k
            const aiMesh *mesh = scene->mMeshes[i];
694
27.6k
            WriteBinaryMesh(&chunk, mesh);
695
27.6k
        }
696
697
        // write materials
698
9.71k
        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
699
4.86k
            const aiMaterial *mat = scene->mMaterials[i];
700
4.86k
            WriteBinaryMaterial(&chunk, mat);
701
4.86k
        }
702
703
        // write all animations
704
5.53k
        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
705
678
            const aiAnimation *anim = scene->mAnimations[i];
706
678
            WriteBinaryAnim(&chunk, anim);
707
678
        }
708
709
        // write all textures
710
5.20k
        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
711
348
            const aiTexture *mesh = scene->mTextures[i];
712
348
            WriteBinaryTexture(&chunk, mesh);
713
348
        }
714
715
        // write lights
716
4.85k
        for (unsigned int i = 0; i < scene->mNumLights; ++i) {
717
0
            const aiLight *l = scene->mLights[i];
718
0
            WriteBinaryLight(&chunk, l);
719
0
        }
720
721
        // write cameras
722
4.98k
        for (unsigned int i = 0; i < scene->mNumCameras; ++i) {
723
133
            const aiCamera *cam = scene->mCameras[i];
724
133
            WriteBinaryCamera(&chunk, cam);
725
133
        }
726
4.85k
    }
727
728
public:
729
    AssbinFileWriter(bool shortened, bool compressed) :
730
4.85k
            shortened(shortened), compressed(compressed) {
731
4.85k
    }
732
733
    // -----------------------------------------------------------------------------------
734
    // Write a binary model dump
735
4.85k
    void WriteBinaryDump(const char *pFile, const char *cmd, IOSystem *pIOSystem, const aiScene *pScene) {
736
4.85k
        IOStream *out = pIOSystem->Open(pFile, "wb");
737
4.85k
        if (!out)
738
0
            throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
739
740
4.85k
        auto CloseIOStream = [&]() {
741
4.85k
            if (out) {
742
4.85k
                pIOSystem->Close(out);
743
4.85k
                out = nullptr; // Ensure this is only done once.
744
4.85k
            }
745
4.85k
        };
746
747
4.85k
        try {
748
4.85k
            time_t tt = time(nullptr);
749
#if _WIN32
750
            tm *p = gmtime(&tt);
751
#else
752
4.85k
            struct tm now;
753
4.85k
            tm *p = gmtime_r(&tt, &now);
754
4.85k
#endif
755
756
            // header
757
4.85k
            char s[64];
758
4.85k
            memset(s, 0, 64);
759
#if _MSC_VER >= 1400
760
            sprintf_s(s, "ASSIMP.binary-dump.%s", asctime(p));
761
#else
762
4.85k
            ai_snprintf(s, 64, "ASSIMP.binary-dump.%s", asctime(p));
763
4.85k
#endif
764
4.85k
            out->Write(s, 44, 1);
765
            // == 44 bytes
766
767
4.85k
            Write<unsigned int>(out, ASSBIN_VERSION_MAJOR);
768
4.85k
            Write<unsigned int>(out, ASSBIN_VERSION_MINOR);
769
4.85k
            Write<unsigned int>(out, aiGetVersionRevision());
770
4.85k
            Write<unsigned int>(out, aiGetCompileFlags());
771
4.85k
            Write<uint16_t>(out, shortened);
772
4.85k
            Write<uint16_t>(out, compressed);
773
            // ==  20 bytes
774
775
4.85k
            char buff[256] = { 0 };
776
4.85k
            ai_snprintf(buff, 256, "%s", pFile);
777
4.85k
            out->Write(buff, sizeof(char), 256);
778
779
4.85k
            memset(buff, 0, sizeof(buff));
780
4.85k
            ai_snprintf(buff, 128, "%s", cmd);
781
4.85k
            out->Write(buff, sizeof(char), 128);
782
783
            // leave 64 bytes free for future extensions
784
4.85k
            memset(buff, 0xcd, 64);
785
4.85k
            out->Write(buff, sizeof(char), 64);
786
            // == 435 bytes
787
788
            // ==== total header size: 512 bytes
789
4.85k
            ai_assert(out->Tell() == ASSBIN_HEADER_LENGTH);
790
791
            // Up to here the data is uncompressed. For compressed files, the rest
792
            // is compressed using standard DEFLATE from zlib.
793
4.85k
            if (compressed) {
794
0
                AssbinChunkWriter uncompressedStream(nullptr, 0);
795
0
                WriteBinaryScene(&uncompressedStream, pScene);
796
797
0
                uLongf uncompressedSize = static_cast<uLongf>(uncompressedStream.Tell());
798
0
                uLongf compressedSize = (uLongf)compressBound(uncompressedSize);
799
0
                uint8_t *compressedBuffer = new uint8_t[compressedSize];
800
801
0
                int res = compress2(compressedBuffer, &compressedSize, (const Bytef *)uncompressedStream.GetBufferPointer(), uncompressedSize, 9);
802
0
                if (res != Z_OK) {
803
0
                    delete[] compressedBuffer;
804
0
                    throw DeadlyExportError("Compression failed.");
805
0
                }
806
807
0
                out->Write(&uncompressedSize, sizeof(uint32_t), 1);
808
0
                out->Write(compressedBuffer, sizeof(char), compressedSize);
809
810
0
                delete[] compressedBuffer;
811
4.85k
            } else {
812
4.85k
                WriteBinaryScene(out, pScene);
813
4.85k
            }
814
815
4.85k
            CloseIOStream();
816
4.85k
        } catch (...) {
817
0
            CloseIOStream();
818
0
            throw;
819
0
        }
820
4.85k
    }
821
};
822
823
void DumpSceneToAssbin(
824
        const char *pFile, const char *cmd, IOSystem *pIOSystem,
825
4.85k
        const aiScene *pScene, bool shortened, bool compressed) {
826
4.85k
    AssbinFileWriter fileWriter(shortened, compressed);
827
4.85k
    fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
828
4.85k
}
829
#if _MSC_VER
830
#pragma warning(pop)
831
#endif // _MSC_VER
832
833
} // end of namespace Assimp