Coverage Report

Created: 2026-10-28 07:46

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
15.6k
size_t Write(IOStream *stream, const T &v) {
66
15.6k
    return stream->Write(&v, sizeof(T), 1);
67
15.6k
}
unsigned long Assimp::Write<bool>(Assimp::IOStream*, bool const&)
Line
Count
Source
65
2.06k
size_t Write(IOStream *stream, const T &v) {
66
2.06k
    return stream->Write(&v, sizeof(T), 1);
67
2.06k
}
unsigned long Assimp::Write<int>(Assimp::IOStream*, int const&)
Line
Count
Source
65
12.9k
size_t Write(IOStream *stream, const T &v) {
66
12.9k
    return stream->Write(&v, sizeof(T), 1);
67
12.9k
}
unsigned long Assimp::Write<unsigned long>(Assimp::IOStream*, unsigned long const&)
Line
Count
Source
65
615
size_t Write(IOStream *stream, const T &v) {
66
615
    return stream->Write(&v, sizeof(T), 1);
67
615
}
68
69
// -----------------------------------------------------------------------------------
70
// Serialize an aiString
71
template <>
72
149k
inline size_t Write<aiString>(IOStream *stream, const aiString &s) {
73
149k
    const size_t s2 = (uint32_t)s.length;
74
149k
    stream->Write(&s, 4, 1);
75
149k
    stream->Write(s.data, s2, 1);
76
77
149k
    return s2 + 4;
78
149k
}
79
80
// -----------------------------------------------------------------------------------
81
// Serialize an unsigned int as uint32_t
82
template <>
83
602k
inline size_t Write<unsigned int>(IOStream *stream, const unsigned int &w) {
84
602k
    const uint32_t t = (uint32_t)w;
85
602k
    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
602k
    stream->Write(&t, 4, 1);
91
92
602k
    return 4;
93
602k
}
94
95
// -----------------------------------------------------------------------------------
96
// Serialize an unsigned int as uint16_t
97
template <>
98
2.00M
inline size_t Write<uint16_t>(IOStream *stream, const uint16_t &w) {
99
2.00M
    static_assert(sizeof(uint16_t) == 2, "sizeof(uint16_t)==2");
100
2.00M
    stream->Write(&w, 2, 1);
101
102
2.00M
    return 2;
103
2.00M
}
104
105
// -----------------------------------------------------------------------------------
106
// Serialize a float
107
template <>
108
6.15M
inline size_t Write<float>(IOStream *stream, const float &f) {
109
6.15M
    static_assert(sizeof(float) == 4, "sizeof(float)==4");
110
6.15M
    stream->Write(&f, 4, 1);
111
112
6.15M
    return 4;
113
6.15M
}
114
115
// -----------------------------------------------------------------------------------
116
// Serialize a double
117
template <>
118
29.3k
inline size_t Write<double>(IOStream *stream, const double &f) {
119
29.3k
    static_assert(sizeof(double) == 8, "sizeof(double)==8");
120
29.3k
    stream->Write(&f, 8, 1);
121
122
29.3k
    return 8;
123
29.3k
}
124
125
// -----------------------------------------------------------------------------------
126
// Serialize a vec3
127
template <>
128
1.42M
inline size_t Write<aiVector3D>(IOStream *stream, const aiVector3D &v) {
129
1.42M
    size_t t = Write<ai_real>(stream, v.x);
130
1.42M
    t += Write<float>(stream, v.y);
131
1.42M
    t += Write<float>(stream, v.z);
132
133
1.42M
    return t;
134
1.42M
}
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
40.8k
inline size_t Write<aiColor4D>(IOStream *stream, const aiColor4D &v) {
151
40.8k
    size_t t = Write<ai_real>(stream, v.r);
152
40.8k
    t += Write<float>(stream, v.g);
153
40.8k
    t += Write<float>(stream, v.b);
154
40.8k
    t += Write<float>(stream, v.a);
155
156
40.8k
    return t;
157
40.8k
}
158
159
// -----------------------------------------------------------------------------------
160
// Serialize a quaternion
161
template <>
162
18.9k
inline size_t Write<aiQuaternion>(IOStream *stream, const aiQuaternion &v) {
163
18.9k
    size_t t = Write<ai_real>(stream, v.w);
164
18.9k
    t += Write<float>(stream, v.x);
165
18.9k
    t += Write<float>(stream, v.y);
166
18.9k
    t += Write<float>(stream, v.z);
167
18.9k
    ai_assert(t == 16);
168
169
18.9k
    return t;
170
18.9k
}
171
172
// -----------------------------------------------------------------------------------
173
// Serialize a vertex weight
174
template <>
175
33.2k
inline size_t Write<aiVertexWeight>(IOStream *stream, const aiVertexWeight &v) {
176
33.2k
    size_t t = Write<unsigned int>(stream, v.mVertexId);
177
178
33.2k
    return t + Write<float>(stream, v.mWeight);
179
33.2k
}
180
181
constexpr size_t MatrixSize = 64;
182
183
// -----------------------------------------------------------------------------------
184
// Serialize a mat4x4
185
template <>
186
100k
inline size_t Write<aiMatrix4x4>(IOStream *stream, const aiMatrix4x4 &m) {
187
501k
    for (unsigned int i = 0; i < 4; ++i) {
188
2.00M
        for (unsigned int i2 = 0; i2 < 4; ++i2) {
189
1.60M
            Write<ai_real>(stream, m[i][i2]);
190
1.60M
        }
191
400k
    }
192
193
100k
    return MatrixSize;
194
100k
}
195
196
// -----------------------------------------------------------------------------------
197
// Serialize an aiVectorKey
198
template <>
199
9.72k
inline size_t Write<aiVectorKey>(IOStream *stream, const aiVectorKey &v) {
200
9.72k
    const size_t t = Write<double>(stream, v.mTime);
201
9.72k
    return t + Write<aiVector3D>(stream, v.mValue);
202
9.72k
}
203
204
// -----------------------------------------------------------------------------------
205
// Serialize an aiQuatKey
206
template <>
207
18.9k
inline size_t Write<aiQuatKey>(IOStream *stream, const aiQuatKey &v) {
208
18.9k
    const size_t t = Write<double>(stream, v.mTime);
209
18.9k
    return t + Write<aiQuaternion>(stream, v.mValue);
210
18.9k
}
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
56.4k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
56.4k
    size_t n = 0;
226
1.57M
    for (unsigned int i = 0; i < size; i++)
227
1.51M
        n += Write<T>(stream, in[i]);
228
229
56.4k
    return n;
230
56.4k
}
unsigned long Assimp::WriteArray<aiVector3t<float> >(Assimp::IOStream*, aiVector3t<float> const*, unsigned int)
Line
Count
Source
224
47.6k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
47.6k
    size_t n = 0;
226
1.46M
    for (unsigned int i = 0; i < size; i++)
227
1.41M
        n += Write<T>(stream, in[i]);
228
229
47.6k
    return n;
230
47.6k
}
unsigned long Assimp::WriteArray<aiColor4t<float> >(Assimp::IOStream*, aiColor4t<float> const*, unsigned int)
Line
Count
Source
224
5.34k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
5.34k
    size_t n = 0;
226
46.1k
    for (unsigned int i = 0; i < size; i++)
227
40.8k
        n += Write<T>(stream, in[i]);
228
229
5.34k
    return n;
230
5.34k
}
unsigned long Assimp::WriteArray<aiVertexWeight>(Assimp::IOStream*, aiVertexWeight const*, unsigned int)
Line
Count
Source
224
956
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
956
    size_t n = 0;
226
34.2k
    for (unsigned int i = 0; i < size; i++)
227
33.2k
        n += Write<T>(stream, in[i]);
228
229
956
    return n;
230
956
}
unsigned long Assimp::WriteArray<aiVectorKey>(Assimp::IOStream*, aiVectorKey const*, unsigned int)
Line
Count
Source
224
1.67k
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
1.67k
    size_t n = 0;
226
11.3k
    for (unsigned int i = 0; i < size; i++)
227
9.72k
        n += Write<T>(stream, in[i]);
228
229
1.67k
    return n;
230
1.67k
}
unsigned long Assimp::WriteArray<aiQuatKey>(Assimp::IOStream*, aiQuatKey const*, unsigned int)
Line
Count
Source
224
836
inline size_t WriteArray(IOStream *stream, const T *in, unsigned int size) {
225
836
    size_t n = 0;
226
19.7k
    for (unsigned int i = 0; i < size; i++)
227
18.9k
        n += Write<T>(stream, in[i]);
228
229
836
    return n;
230
836
}
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
149k
            buffer(nullptr),
246
149k
            magic(magic),
247
149k
            container(container),
248
149k
            cur_size(0),
249
149k
            cursor(0),
250
149k
            initial(initial) {
251
        // empty
252
149k
    }
253
254
149k
    ~AssbinChunkWriter() override {
255
149k
        if (container) {
256
149k
            container->Write(&magic, sizeof(uint32_t), 1);
257
149k
            container->Write(&cursor, sizeof(uint32_t), 1);
258
149k
            container->Write(buffer, 1, cursor);
259
149k
        }
260
149k
        if (buffer) delete[] buffer;
261
149k
    }
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
9.56M
    size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override {
286
9.56M
        pSize *= pCount;
287
9.56M
        if (cursor + pSize > cur_size) {
288
156k
            Grow(cursor + pSize);
289
156k
        }
290
291
9.56M
        memcpy(buffer + cursor, pvBuffer, pSize);
292
9.56M
        cursor += pSize;
293
294
9.56M
        return pCount;
295
9.56M
    }
296
297
private:
298
    // -------------------------------------------------------------------
299
156k
    void Grow(size_t need = 0) {
300
156k
        size_t new_size = std::max(initial, std::max(need, cur_size + (cur_size >> 1)));
301
302
156k
        const uint8_t *const old = buffer;
303
156k
        buffer = new uint8_t[new_size];
304
305
156k
        if (old) {
306
6.99k
            memcpy(buffer, old, cur_size);
307
6.99k
            delete[] old;
308
6.99k
        }
309
310
156k
        cur_size = new_size;
311
156k
    }
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
99.2k
    void WriteBinaryNode(IOStream *container, const aiNode *node) {
334
99.2k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODE);
335
336
99.2k
        unsigned int nb_metadata = (node->mMetaData != nullptr ? node->mMetaData->mNumProperties : 0);
337
338
99.2k
        Write<aiString>(&chunk, node->mName);
339
99.2k
        Write<aiMatrix4x4>(&chunk, node->mTransformation);
340
99.2k
        Write<unsigned int>(&chunk, node->mNumChildren);
341
99.2k
        Write<unsigned int>(&chunk, node->mNumMeshes);
342
99.2k
        Write<unsigned int>(&chunk, nb_metadata);
343
344
119k
        for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
345
20.7k
            Write<unsigned int>(&chunk, node->mMeshes[i]);
346
20.7k
        }
347
348
196k
        for (unsigned int i = 0; i < node->mNumChildren; ++i) {
349
97.4k
            WriteBinaryNode(&chunk, node->mChildren[i]);
350
97.4k
        }
351
352
120k
        for (unsigned int i = 0; i < nb_metadata; ++i) {
353
21.2k
            const aiString &key = node->mMetaData->mKeys[i];
354
21.2k
            aiMetadataType type = node->mMetaData->mValues[i].mType;
355
21.2k
            void *value = node->mMetaData->mValues[i].mData;
356
357
21.2k
            Write<aiString>(&chunk, key);
358
21.2k
            Write<uint16_t>(&chunk, (uint16_t)type);
359
360
21.2k
            switch (type) {
361
2.06k
            case AI_BOOL:
362
2.06k
                Write<bool>(&chunk, *((bool *)value));
363
2.06k
                break;
364
12.9k
            case AI_INT32:
365
12.9k
                Write<int32_t>(&chunk, *((int32_t *)value));
366
12.9k
                break;
367
615
            case AI_UINT64:
368
615
                Write<uint64_t>(&chunk, *((uint64_t *)value));
369
615
                break;
370
706
            case AI_FLOAT:
371
706
                Write<float>(&chunk, *((float *)value));
372
706
                break;
373
0
            case AI_DOUBLE:
374
0
                Write<double>(&chunk, *((double *)value));
375
0
                break;
376
2.79k
            case AI_AISTRING:
377
2.79k
                Write<aiString>(&chunk, *((aiString *)value));
378
2.79k
                break;
379
2.04k
            case AI_AIVECTOR3D:
380
2.04k
                Write<aiVector3D>(&chunk, *((aiVector3D *)value));
381
2.04k
                break;
382
#ifdef SWIG
383
                case FORCE_32BIT:
384
#endif // SWIG
385
0
            default:
386
0
                break;
387
21.2k
            }
388
21.2k
        }
389
99.2k
    }
390
391
    // -----------------------------------------------------------------------------------
392
211
    void WriteBinaryTexture(IOStream *container, const aiTexture *tex) {
393
211
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AITEXTURE);
394
395
211
        Write<unsigned int>(&chunk, tex->mWidth);
396
211
        Write<unsigned int>(&chunk, tex->mHeight);
397
        // Write the texture format, but don't include the null terminator.
398
211
        chunk.Write(tex->achFormatHint, sizeof(char), HINTMAXTEXTURELEN - 1);
399
400
211
        if (!shortened) {
401
211
            if (!tex->mHeight) {
402
91
                chunk.Write(tex->pcData, 1, tex->mWidth);
403
120
            } else {
404
120
                chunk.Write(tex->pcData, 1, tex->mWidth * tex->mHeight * 4);
405
120
            }
406
211
        }
407
211
    }
408
409
    // -----------------------------------------------------------------------------------
410
956
    void WriteBinaryBone(IOStream *container, const aiBone *b) {
411
956
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIBONE);
412
413
956
        Write<aiString>(&chunk, b->mName);
414
956
        Write<unsigned int>(&chunk, b->mNumWeights);
415
956
        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
956
        if (shortened) {
420
0
            WriteBounds(&chunk, b->mWeights, b->mNumWeights);
421
0
        } // else write as usual
422
956
        else
423
956
            WriteArray<aiVertexWeight>(&chunk, b->mWeights, b->mNumWeights);
424
956
    }
425
426
    // -----------------------------------------------------------------------------------
427
20.7k
    void WriteBinaryMesh(IOStream *container, const aiMesh *mesh) {
428
20.7k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMESH);
429
430
20.7k
        Write<unsigned int>(&chunk, mesh->mPrimitiveTypes);
431
20.7k
        Write<unsigned int>(&chunk, mesh->mNumVertices);
432
20.7k
        Write<unsigned int>(&chunk, mesh->mNumFaces);
433
20.7k
        Write<unsigned int>(&chunk, mesh->mNumBones);
434
20.7k
        Write<unsigned int>(&chunk, mesh->mMaterialIndex);
435
436
        // first of all, write bits for all existent vertex components
437
20.7k
        unsigned int c = 0;
438
20.7k
        if (mesh->mVertices) {
439
20.7k
            c |= ASSBIN_MESH_HAS_POSITIONS;
440
20.7k
        }
441
20.7k
        if (mesh->mNormals) {
442
17.0k
            c |= ASSBIN_MESH_HAS_NORMALS;
443
17.0k
        }
444
20.7k
        if (mesh->mTangents && mesh->mBitangents) {
445
3.15k
            c |= ASSBIN_MESH_HAS_TANGENTS_AND_BITANGENTS;
446
3.15k
        }
447
24.2k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
448
24.2k
            if (!mesh->mTextureCoords[n]) {
449
20.7k
                break;
450
20.7k
            }
451
3.55k
            c |= ASSBIN_MESH_HAS_TEXCOORD(n);
452
3.55k
        }
453
26.0k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
454
26.0k
            if (!mesh->mColors[n]) {
455
20.7k
                break;
456
20.7k
            }
457
5.34k
            c |= ASSBIN_MESH_HAS_COLOR(n);
458
5.34k
        }
459
20.7k
        Write<unsigned int>(&chunk, c);
460
461
20.7k
        aiVector3D minVec, maxVec;
462
20.7k
        if (mesh->mVertices) {
463
20.7k
            if (shortened) {
464
0
                WriteBounds(&chunk, mesh->mVertices, mesh->mNumVertices);
465
0
            } // else write as usual
466
20.7k
            else
467
20.7k
                WriteArray<aiVector3D>(&chunk, mesh->mVertices, mesh->mNumVertices);
468
20.7k
        }
469
20.7k
        if (mesh->mNormals) {
470
17.0k
            if (shortened) {
471
0
                WriteBounds(&chunk, mesh->mNormals, mesh->mNumVertices);
472
0
            } // else write as usual
473
17.0k
            else
474
17.0k
                WriteArray<aiVector3D>(&chunk, mesh->mNormals, mesh->mNumVertices);
475
17.0k
        }
476
20.7k
        if (mesh->mTangents && mesh->mBitangents) {
477
3.15k
            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
3.15k
            else {
482
3.15k
                WriteArray<aiVector3D>(&chunk, mesh->mTangents, mesh->mNumVertices);
483
3.15k
                WriteArray<aiVector3D>(&chunk, mesh->mBitangents, mesh->mNumVertices);
484
3.15k
            }
485
3.15k
        }
486
26.0k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_COLOR_SETS; ++n) {
487
26.0k
            if (!mesh->mColors[n])
488
20.7k
                break;
489
490
5.34k
            if (shortened) {
491
0
                WriteBounds(&chunk, mesh->mColors[n], mesh->mNumVertices);
492
0
            } // else write as usual
493
5.34k
            else
494
5.34k
                WriteArray<aiColor4D>(&chunk, mesh->mColors[n], mesh->mNumVertices);
495
5.34k
        }
496
24.2k
        for (unsigned int n = 0; n < AI_MAX_NUMBER_OF_TEXTURECOORDS; ++n) {
497
24.2k
            if (!mesh->mTextureCoords[n])
498
20.7k
                break;
499
500
            // write number of UV components
501
3.55k
            Write<unsigned int>(&chunk, mesh->mNumUVComponents[n]);
502
503
3.55k
            if (shortened) {
504
0
                WriteBounds(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
505
0
            } // else write as usual
506
3.55k
            else
507
3.55k
                WriteArray<aiVector3D>(&chunk, mesh->mTextureCoords[n], mesh->mNumVertices);
508
3.55k
        }
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
20.7k
        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
20.7k
        {
533
            // if there are less than 2^16 vertices, we can simply use 16 bit integers ...
534
555k
            for (unsigned int i = 0; i < mesh->mNumFaces; ++i) {
535
534k
                const aiFace &f = mesh->mFaces[i];
536
537
534k
                static_assert(AI_MAX_FACE_INDICES <= 0xffff, "AI_MAX_FACE_INDICES <= 0xffff");
538
534k
                Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mNumIndices));
539
540
1.98M
                for (unsigned int a = 0; a < f.mNumIndices; ++a) {
541
1.44M
                    if (mesh->mNumVertices < (1u << 16)) {
542
1.44M
                        Write<uint16_t>(&chunk, static_cast<uint16_t>(f.mIndices[a]));
543
1.44M
                    } else {
544
0
                        Write<unsigned int>(&chunk, f.mIndices[a]);
545
0
                    }
546
1.44M
                }
547
534k
            }
548
20.7k
        }
549
550
        // write bones
551
20.7k
        if (mesh->mNumBones) {
552
1.46k
            for (unsigned int a = 0; a < mesh->mNumBones; ++a) {
553
956
                const aiBone *b = mesh->mBones[a];
554
956
                WriteBinaryBone(&chunk, b);
555
956
            }
556
506
        }
557
20.7k
    }
558
559
    // -----------------------------------------------------------------------------------
560
23.6k
    void WriteBinaryMaterialProperty(IOStream *container, const aiMaterialProperty *prop) {
561
23.6k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIALPROPERTY);
562
563
23.6k
        Write<aiString>(&chunk, prop->mKey);
564
23.6k
        Write<unsigned int>(&chunk, prop->mSemantic);
565
23.6k
        Write<unsigned int>(&chunk, prop->mIndex);
566
567
23.6k
        Write<unsigned int>(&chunk, prop->mDataLength);
568
23.6k
        Write<unsigned int>(&chunk, (unsigned int)prop->mType);
569
23.6k
        chunk.Write(prop->mData, 1, prop->mDataLength);
570
23.6k
    }
571
572
    // -----------------------------------------------------------------------------------
573
2.00k
    void WriteBinaryMaterial(IOStream *container, const aiMaterial *mat) {
574
2.00k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIMATERIAL);
575
576
2.00k
        Write<unsigned int>(&chunk, mat->mNumProperties);
577
25.6k
        for (unsigned int i = 0; i < mat->mNumProperties; ++i) {
578
23.6k
            WriteBinaryMaterialProperty(&chunk, mat->mProperties[i]);
579
23.6k
        }
580
2.00k
    }
581
582
    // -----------------------------------------------------------------------------------
583
836
    void WriteBinaryNodeAnim(IOStream *container, const aiNodeAnim *nd) {
584
836
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AINODEANIM);
585
586
836
        Write<aiString>(&chunk, nd->mNodeName);
587
836
        Write<unsigned int>(&chunk, nd->mNumPositionKeys);
588
836
        Write<unsigned int>(&chunk, nd->mNumRotationKeys);
589
836
        Write<unsigned int>(&chunk, nd->mNumScalingKeys);
590
836
        Write<unsigned int>(&chunk, nd->mPreState);
591
836
        Write<unsigned int>(&chunk, nd->mPostState);
592
593
836
        if (nd->mPositionKeys) {
594
836
            if (shortened) {
595
0
                WriteBounds(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
596
597
0
            } // else write as usual
598
836
            else
599
836
                WriteArray<aiVectorKey>(&chunk, nd->mPositionKeys, nd->mNumPositionKeys);
600
836
        }
601
836
        if (nd->mRotationKeys) {
602
836
            if (shortened) {
603
0
                WriteBounds(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
604
605
0
            } // else write as usual
606
836
            else
607
836
                WriteArray<aiQuatKey>(&chunk, nd->mRotationKeys, nd->mNumRotationKeys);
608
836
        }
609
836
        if (nd->mScalingKeys) {
610
836
            if (shortened) {
611
0
                WriteBounds(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
612
613
0
            } // else write as usual
614
836
            else
615
836
                WriteArray<aiVectorKey>(&chunk, nd->mScalingKeys, nd->mNumScalingKeys);
616
836
        }
617
836
    }
618
619
    // -----------------------------------------------------------------------------------
620
332
    void WriteBinaryAnim(IOStream *container, const aiAnimation *anim) {
621
332
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AIANIMATION);
622
623
332
        Write<aiString>(&chunk, anim->mName);
624
332
        Write<double>(&chunk, anim->mDuration);
625
332
        Write<double>(&chunk, anim->mTicksPerSecond);
626
332
        Write<unsigned int>(&chunk, anim->mNumChannels);
627
628
1.16k
        for (unsigned int a = 0; a < anim->mNumChannels; ++a) {
629
836
            const aiNodeAnim *nd = anim->mChannels[a];
630
836
            WriteBinaryNodeAnim(&chunk, nd);
631
836
        }
632
332
    }
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
104
    void WriteBinaryCamera(IOStream *container, const aiCamera *cam) {
663
104
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AICAMERA);
664
665
104
        Write<aiString>(&chunk, cam->mName);
666
104
        Write<aiVector3D>(&chunk, cam->mPosition);
667
104
        Write<aiVector3D>(&chunk, cam->mLookAt);
668
104
        Write<aiVector3D>(&chunk, cam->mUp);
669
104
        Write<float>(&chunk, cam->mHorizontalFOV);
670
104
        Write<float>(&chunk, cam->mClipPlaneNear);
671
104
        Write<float>(&chunk, cam->mClipPlaneFar);
672
104
        Write<float>(&chunk, cam->mAspect);
673
104
    }
674
675
    // -----------------------------------------------------------------------------------
676
1.81k
    void WriteBinaryScene(IOStream *container, const aiScene *scene) {
677
1.81k
        AssbinChunkWriter chunk(container, ASSBIN_CHUNK_AISCENE);
678
679
        // basic scene information
680
1.81k
        Write<unsigned int>(&chunk, scene->mFlags);
681
1.81k
        Write<unsigned int>(&chunk, scene->mNumMeshes);
682
1.81k
        Write<unsigned int>(&chunk, scene->mNumMaterials);
683
1.81k
        Write<unsigned int>(&chunk, scene->mNumAnimations);
684
1.81k
        Write<unsigned int>(&chunk, scene->mNumTextures);
685
1.81k
        Write<unsigned int>(&chunk, scene->mNumLights);
686
1.81k
        Write<unsigned int>(&chunk, scene->mNumCameras);
687
688
        // write node graph
689
1.81k
        WriteBinaryNode(&chunk, scene->mRootNode);
690
691
        // write all meshes
692
22.5k
        for (unsigned int i = 0; i < scene->mNumMeshes; ++i) {
693
20.7k
            const aiMesh *mesh = scene->mMeshes[i];
694
20.7k
            WriteBinaryMesh(&chunk, mesh);
695
20.7k
        }
696
697
        // write materials
698
3.82k
        for (unsigned int i = 0; i < scene->mNumMaterials; ++i) {
699
2.00k
            const aiMaterial *mat = scene->mMaterials[i];
700
2.00k
            WriteBinaryMaterial(&chunk, mat);
701
2.00k
        }
702
703
        // write all animations
704
2.15k
        for (unsigned int i = 0; i < scene->mNumAnimations; ++i) {
705
332
            const aiAnimation *anim = scene->mAnimations[i];
706
332
            WriteBinaryAnim(&chunk, anim);
707
332
        }
708
709
        // write all textures
710
2.03k
        for (unsigned int i = 0; i < scene->mNumTextures; ++i) {
711
211
            const aiTexture *mesh = scene->mTextures[i];
712
211
            WriteBinaryTexture(&chunk, mesh);
713
211
        }
714
715
        // write lights
716
1.81k
        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
1.92k
        for (unsigned int i = 0; i < scene->mNumCameras; ++i) {
723
104
            const aiCamera *cam = scene->mCameras[i];
724
104
            WriteBinaryCamera(&chunk, cam);
725
104
        }
726
1.81k
    }
727
728
public:
729
    AssbinFileWriter(bool shortened, bool compressed) :
730
1.81k
            shortened(shortened), compressed(compressed) {
731
1.81k
    }
732
733
    // -----------------------------------------------------------------------------------
734
    // Write a binary model dump
735
1.81k
    void WriteBinaryDump(const char *pFile, const char *cmd, IOSystem *pIOSystem, const aiScene *pScene) {
736
1.81k
        IOStream *out = pIOSystem->Open(pFile, "wb");
737
1.81k
        if (!out)
738
0
            throw std::runtime_error("Unable to open output file " + std::string(pFile) + '\n');
739
740
1.81k
        auto CloseIOStream = [&]() {
741
1.81k
            if (out) {
742
1.81k
                pIOSystem->Close(out);
743
1.81k
                out = nullptr; // Ensure this is only done once.
744
1.81k
            }
745
1.81k
        };
746
747
1.81k
        try {
748
1.81k
            time_t tt = time(nullptr);
749
#if _WIN32
750
            tm *p = gmtime(&tt);
751
#else
752
1.81k
            struct tm now;
753
1.81k
            tm *p = gmtime_r(&tt, &now);
754
1.81k
#endif
755
756
            // header
757
1.81k
            char s[64];
758
1.81k
            memset(s, 0, 64);
759
#if _MSC_VER >= 1400
760
            sprintf_s(s, "ASSIMP.binary-dump.%s", asctime(p));
761
#else
762
1.81k
            ai_snprintf(s, 64, "ASSIMP.binary-dump.%s", asctime(p));
763
1.81k
#endif
764
1.81k
            out->Write(s, 44, 1);
765
            // == 44 bytes
766
767
1.81k
            Write<unsigned int>(out, ASSBIN_VERSION_MAJOR);
768
1.81k
            Write<unsigned int>(out, ASSBIN_VERSION_MINOR);
769
1.81k
            Write<unsigned int>(out, aiGetVersionRevision());
770
1.81k
            Write<unsigned int>(out, aiGetCompileFlags());
771
1.81k
            Write<uint16_t>(out, shortened);
772
1.81k
            Write<uint16_t>(out, compressed);
773
            // ==  20 bytes
774
775
1.81k
            char buff[256] = { 0 };
776
1.81k
            ai_snprintf(buff, 256, "%s", pFile);
777
1.81k
            out->Write(buff, sizeof(char), 256);
778
779
1.81k
            memset(buff, 0, sizeof(buff));
780
1.81k
            ai_snprintf(buff, 128, "%s", cmd);
781
1.81k
            out->Write(buff, sizeof(char), 128);
782
783
            // leave 64 bytes free for future extensions
784
1.81k
            memset(buff, 0xcd, 64);
785
1.81k
            out->Write(buff, sizeof(char), 64);
786
            // == 435 bytes
787
788
            // ==== total header size: 512 bytes
789
1.81k
            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
1.81k
            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
1.81k
            } else {
812
1.81k
                WriteBinaryScene(out, pScene);
813
1.81k
            }
814
815
1.81k
            CloseIOStream();
816
1.81k
        } catch (...) {
817
0
            CloseIOStream();
818
0
            throw;
819
0
        }
820
1.81k
    }
821
};
822
823
void DumpSceneToAssbin(
824
        const char *pFile, const char *cmd, IOSystem *pIOSystem,
825
1.81k
        const aiScene *pScene, bool shortened, bool compressed) {
826
1.81k
    AssbinFileWriter fileWriter(shortened, compressed);
827
1.81k
    fileWriter.WriteBinaryDump(pFile, cmd, pIOSystem, pScene);
828
1.81k
}
829
#if _MSC_VER
830
#pragma warning(pop)
831
#endif // _MSC_VER
832
833
} // end of namespace Assimp