Coverage Report

Created: 2025-06-22 07:30

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