Coverage Report

Created: 2025-06-22 07:30

/src/assimp/code/AssetLib/FBX/FBXExportNode.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
#ifndef ASSIMP_BUILD_NO_EXPORT
41
#ifndef ASSIMP_BUILD_NO_FBX_EXPORTER
42
43
#include "FBXExportNode.h"
44
#include "FBXCommon.h"
45
46
#include <assimp/StreamWriter.h> // StreamWriterLE
47
#include <assimp/Exceptional.h> // DeadlyExportError
48
#include <assimp/ai_assert.h>
49
#include <assimp/StringUtils.h> // ai_snprintf
50
51
#include <string>
52
#include <ostream>
53
#include <sstream> // ostringstream
54
#include <memory> // shared_ptr
55
56
namespace Assimp {
57
58
// AddP70<type> helpers... there's no usable pattern here,
59
// so all are defined as separate functions.
60
// Even "animatable" properties are often completely different
61
// from the standard (nonanimated) property definition,
62
// so they are specified with an 'A' suffix.
63
64
42.7k
void FBX::Node::AddP70int(const std::string& cur_name, int32_t value) {
65
42.7k
    FBX::Node n("P");
66
42.7k
    n.AddProperties(cur_name, "int", "Integer", "", value);
67
42.7k
    AddChild(n);
68
42.7k
}
69
70
47.9k
void FBX::Node::AddP70bool(const std::string& cur_name, bool value) {
71
47.9k
    FBX::Node n("P");
72
47.9k
    n.AddProperties(cur_name, "bool", "", "", int32_t(value));
73
47.9k
    AddChild(n);
74
47.9k
}
75
76
7.01k
void FBX::Node::AddP70double(const std::string &cur_name, double value) {    FBX::Node n("P");
77
7.01k
    n.AddProperties(cur_name, "double", "Number", "", value);
78
7.01k
    AddChild(n);
79
7.01k
}
80
81
2.15k
void FBX::Node::AddP70numberA(const std::string &cur_name, double value) {
82
2.15k
    FBX::Node n("P");
83
2.15k
    n.AddProperties(cur_name, "Number", "", "A", value);
84
2.15k
    AddChild(n);
85
2.15k
}
86
87
void FBX::Node::AddP70color(
88
1.11k
        const std::string &cur_name, double r, double g, double b) {
89
1.11k
    FBX::Node n("P");
90
1.11k
    n.AddProperties(cur_name, "ColorRGB", "Color", "", r, g, b);
91
1.11k
    AddChild(n);
92
1.11k
}
93
94
void FBX::Node::AddP70colorA(
95
2.16k
        const std::string &cur_name, double r, double g, double b) {
96
2.16k
    FBX::Node n("P");
97
2.16k
    n.AddProperties(cur_name, "Color", "", "A", r, g, b);
98
2.16k
    AddChild(n);
99
2.16k
}
100
101
void FBX::Node::AddP70vector(
102
5.46k
        const std::string &cur_name, double x, double y, double z) {
103
5.46k
    FBX::Node n("P");
104
5.46k
    n.AddProperties(cur_name, "Vector3D", "Vector", "", x, y, z);
105
5.46k
    AddChild(n);
106
5.46k
}
107
108
void FBX::Node::AddP70vectorA(
109
87
        const std::string &cur_name, double x, double y, double z) {
110
87
    FBX::Node n("P");
111
87
    n.AddProperties(cur_name, "Vector", "", "A", x, y, z);
112
87
    AddChild(n);
113
87
}
114
115
void FBX::Node::AddP70string(
116
763
        const std::string &cur_name, const std::string &value) {
117
763
    FBX::Node n("P");
118
763
    n.AddProperties(cur_name, "KString", "", "", value);
119
763
    AddChild(n);
120
763
}
121
122
void FBX::Node::AddP70enum(
123
43.5k
        const std::string &cur_name, int32_t value) {
124
43.5k
    FBX::Node n("P");
125
43.5k
    n.AddProperties(cur_name, "enum", "", "", value);
126
43.5k
    AddChild(n);
127
43.5k
}
128
129
void FBX::Node::AddP70time(
130
763
        const std::string &cur_name, int64_t value) {
131
763
    FBX::Node n("P");
132
763
    n.AddProperties(cur_name, "KTime", "Time", "", value);
133
763
    AddChild(n);
134
763
}
135
136
137
// public member functions for writing nodes to stream
138
139
void FBX::Node::Dump(
140
        const std::shared_ptr<Assimp::IOStream> &outfile,
141
1.08k
        bool binary, int indent) {
142
1.08k
    if (binary) {
143
1.08k
        Assimp::StreamWriterLE outstream(outfile);
144
1.08k
        DumpBinary(outstream);
145
1.08k
    } else {
146
0
        std::ostringstream ss;
147
0
        DumpAscii(ss, indent);
148
0
        std::string s = ss.str();
149
0
        outfile->Write(s.c_str(), s.size(), 1);
150
0
    }
151
1.08k
}
152
153
void FBX::Node::Dump(
154
    Assimp::StreamWriterLE &outstream,
155
    bool binary, int indent
156
145k
) {
157
145k
    if (binary) {
158
145k
        DumpBinary(outstream);
159
145k
    } else {
160
0
        std::ostringstream ss;
161
0
        DumpAscii(ss, indent);
162
0
        outstream.PutString(ss.str());
163
0
    }
164
145k
}
165
166
167
// public member functions for low-level writing
168
169
void FBX::Node::Begin(
170
    Assimp::StreamWriterLE &s,
171
    bool binary, int indent
172
1.83k
) {
173
1.83k
    if (binary) {
174
1.83k
        BeginBinary(s);
175
1.83k
    } else {
176
        // assume we're at the correct place to start already
177
0
        (void)indent;
178
0
        std::ostringstream ss;
179
0
        BeginAscii(ss, indent);
180
0
        s.PutString(ss.str());
181
0
    }
182
1.83k
}
183
184
void FBX::Node::DumpProperties(
185
    Assimp::StreamWriterLE& s,
186
    bool binary, int indent
187
1.03k
) {
188
1.03k
    if (binary) {
189
1.03k
        DumpPropertiesBinary(s);
190
1.03k
    } else {
191
0
        std::ostringstream ss;
192
0
        DumpPropertiesAscii(ss, indent);
193
0
        s.PutString(ss.str());
194
0
    }
195
1.03k
}
196
197
void FBX::Node::EndProperties(
198
    Assimp::StreamWriterLE &s,
199
    bool binary, int indent
200
1.30k
) {
201
1.30k
    EndProperties(s, binary, indent, properties.size());
202
1.30k
}
203
204
void FBX::Node::EndProperties(
205
    Assimp::StreamWriterLE &s,
206
    bool binary, int indent,
207
    size_t num_properties
208
1.57k
) {
209
1.57k
    if (binary) {
210
1.57k
        EndPropertiesBinary(s, num_properties);
211
1.57k
    } else {
212
        // nothing to do
213
0
        (void)indent;
214
0
    }
215
1.57k
}
216
217
void FBX::Node::BeginChildren(
218
    Assimp::StreamWriterLE &s,
219
    bool binary, int indent
220
1.83k
) {
221
1.83k
    if (binary) {
222
        // nothing to do
223
1.83k
    } else {
224
0
        std::ostringstream ss;
225
0
        BeginChildrenAscii(ss, indent);
226
0
        s.PutString(ss.str());
227
0
    }
228
1.83k
}
229
230
void FBX::Node::DumpChildren(
231
    Assimp::StreamWriterLE& s,
232
    bool binary, int indent
233
0
) {
234
0
    if (binary) {
235
0
        DumpChildrenBinary(s);
236
0
    } else {
237
0
        std::ostringstream ss;
238
0
        DumpChildrenAscii(ss, indent);
239
0
        if (ss.tellp() > 0)
240
0
            s.PutString(ss.str());
241
0
    }
242
0
}
243
244
void FBX::Node::End(
245
    Assimp::StreamWriterLE &s,
246
    bool binary, int indent,
247
    bool has_children
248
1.83k
) {
249
1.83k
    if (binary) {
250
1.83k
        EndBinary(s, has_children);
251
1.83k
    } else {
252
0
        std::ostringstream ss;
253
0
        EndAscii(ss, indent, has_children);
254
0
        if (ss.tellp() > 0)
255
0
            s.PutString(ss.str());
256
0
    }
257
1.83k
}
258
259
260
// public member functions for writing to binary fbx
261
262
void FBX::Node::DumpBinary(Assimp::StreamWriterLE &s)
263
556k
{
264
    // write header section (with placeholders for some things)
265
556k
    BeginBinary(s);
266
267
    // write properties
268
556k
    DumpPropertiesBinary(s);
269
270
    // go back and fill in property related placeholders
271
556k
    EndPropertiesBinary(s, properties.size());
272
273
    // write children
274
556k
    DumpChildrenBinary(s);
275
276
    // finish, filling in end offset placeholder
277
556k
    EndBinary(s, force_has_children || !children.empty());
278
556k
}
279
280
281
// public member functions for writing to ascii fbx
282
283
void FBX::Node::DumpAscii(std::ostream &s, int indent)
284
0
{
285
    // write name
286
0
    BeginAscii(s, indent);
287
288
    // write properties
289
0
    DumpPropertiesAscii(s, indent);
290
291
0
    if (force_has_children || !children.empty()) {
292
        // begin children (with a '{')
293
0
        BeginChildrenAscii(s, indent + 1);
294
        // write children
295
0
        DumpChildrenAscii(s, indent + 1);
296
0
    }
297
298
    // finish (also closing the children bracket '}')
299
0
    EndAscii(s, indent, force_has_children || !children.empty());
300
0
}
301
302
303
// private member functions for low-level writing to fbx
304
305
void FBX::Node::BeginBinary(Assimp::StreamWriterLE &s)
306
559k
{
307
    // remember start pos so we can come back and write the end pos
308
559k
    this->start_pos = s.Tell();
309
310
    // placeholders for end pos and property section info
311
559k
    s.PutU8(0); // end pos
312
559k
    s.PutU8(0); // number of properties
313
559k
    s.PutU8(0); // total property section length
314
315
    // node name
316
559k
    s.PutU1(uint8_t(name.size())); // length of node name
317
559k
    s.PutString(name); // node name as raw bytes
318
319
    // property data comes after here
320
559k
    this->property_start = s.Tell();
321
559k
}
322
323
void FBX::Node::DumpPropertiesBinary(Assimp::StreamWriterLE& s)
324
557k
{
325
1.44M
    for (auto &p : properties) {
326
1.44M
        p.DumpBinary(s);
327
1.44M
    }
328
557k
}
329
330
void FBX::Node::EndPropertiesBinary(
331
    Assimp::StreamWriterLE &s,
332
    size_t num_properties
333
559k
) {
334
559k
    if (num_properties == 0) { return; }
335
513k
    size_t pos = s.Tell();
336
513k
    ai_assert(pos > property_start);
337
513k
    size_t property_section_size = pos - property_start;
338
513k
    s.Seek(start_pos + 8); // 8 bytes of uint64_t of end_pos
339
513k
    s.PutU8(num_properties);
340
513k
    s.PutU8(property_section_size);
341
513k
    s.Seek(pos);
342
513k
}
343
344
void FBX::Node::DumpChildrenBinary(Assimp::StreamWriterLE& s)
345
556k
{
346
556k
    for (FBX::Node& child : children) {
347
409k
        child.DumpBinary(s);
348
409k
    }
349
556k
}
350
351
void FBX::Node::EndBinary(
352
    Assimp::StreamWriterLE &s,
353
    bool has_children
354
559k
) {
355
    // if there were children, add a null record
356
559k
    if (has_children) { s.PutString(Assimp::FBX::NULL_RECORD_STRING); }
357
358
    // now go back and write initial pos
359
559k
    this->end_pos = s.Tell();
360
559k
    s.Seek(start_pos);
361
559k
    s.PutU8(end_pos);
362
559k
    s.Seek(end_pos);
363
559k
}
364
365
366
void FBX::Node::BeginAscii(std::ostream& s, int indent)
367
0
{
368
0
    s << '\n';
369
0
    for (int i = 0; i < indent; ++i) { s << '\t'; }
370
0
    s << name << ": ";
371
0
}
372
373
void FBX::Node::DumpPropertiesAscii(std::ostream &s, int indent)
374
0
{
375
0
    for (size_t i = 0; i < properties.size(); ++i) {
376
0
        if (i > 0) { s << ", "; }
377
0
        properties[i].DumpAscii(s, indent);
378
0
    }
379
0
}
380
381
void FBX::Node::BeginChildrenAscii(std::ostream& s, int indent)
382
0
{
383
    // only call this if there are actually children
384
0
    s << " {";
385
0
    (void)indent;
386
0
}
387
388
void FBX::Node::DumpChildrenAscii(std::ostream& s, int indent)
389
0
{
390
    // children will need a lot of padding and corralling
391
0
    if (children.size() || force_has_children) {
392
0
        for (size_t i = 0; i < children.size(); ++i) {
393
            // no compression in ascii files, so skip this node if it exists
394
0
            if (children[i].name == "EncryptionType") { continue; }
395
            // the child can dump itself
396
0
            children[i].DumpAscii(s, indent);
397
0
        }
398
0
    }
399
0
}
400
401
0
void FBX::Node::EndAscii(std::ostream& s, int indent, bool has_children) {
402
0
    if (!has_children) { return; } // nothing to do
403
0
    s << '\n';
404
0
    for (int i = 0; i < indent; ++i) { s << '\t'; }
405
0
    s << "}";
406
0
}
407
408
// private helpers for static member functions
409
410
// ascii property node from vector of doubles
411
void FBX::Node::WritePropertyNodeAscii(
412
        const std::string& name,
413
        const std::vector<double>& v,
414
        Assimp::StreamWriterLE& s,
415
0
        int indent){
416
0
    char buffer[32];
417
0
    FBX::Node node(name);
418
0
    node.Begin(s, false, indent);
419
0
    std::string vsize = ai_to_string(v.size());
420
    // *<size> {
421
0
    s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
422
    // indent + 1
423
0
    for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
424
    // a: value,value,value,...
425
0
    s.PutString("a: ");
426
0
    int count = 0;
427
0
    for (size_t i = 0; i < v.size(); ++i) {
428
0
        if (i > 0) { s.PutChar(','); }
429
0
        int len = ai_snprintf(buffer, sizeof(buffer), "%f", v[i]);
430
0
        count += len;
431
0
        if (count > 2048) { s.PutChar('\n'); count = 0; }
432
0
        if (len < 0 || len > 31) {
433
            // this should never happen
434
0
            throw DeadlyExportError("failed to convert double to string");
435
0
        }
436
0
        for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
437
0
    }
438
    // }
439
0
    s.PutChar('\n');
440
0
    for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
441
0
    s.PutChar('}'); s.PutChar(' ');
442
0
    node.End(s, false, indent, false);
443
0
}
444
445
// ascii property node from vector of int32_t
446
void FBX::Node::WritePropertyNodeAscii(
447
    const std::string& name,
448
    const std::vector<int32_t>& v,
449
    Assimp::StreamWriterLE& s,
450
    int indent
451
0
){
452
0
    char buffer[32];
453
0
    FBX::Node node(name);
454
0
    node.Begin(s, false, indent);
455
0
    std::string vsize = ai_to_string(v.size());
456
    // *<size> {
457
0
    s.PutChar('*'); s.PutString(vsize); s.PutString(" {\n");
458
    // indent + 1
459
0
    for (int i = 0; i < indent + 1; ++i) { s.PutChar('\t'); }
460
    // a: value,value,value,...
461
0
    s.PutString("a: ");
462
0
    int count = 0;
463
0
    for (size_t i = 0; i < v.size(); ++i) {
464
0
        if (i > 0) { s.PutChar(','); }
465
0
        int len = ai_snprintf(buffer, sizeof(buffer), "%d", v[i]);
466
0
        count += len;
467
0
        if (count > 2048) { s.PutChar('\n'); count = 0; }
468
0
        if (len < 0 || len > 31) {
469
            // this should never happen
470
0
            throw DeadlyExportError("failed to convert double to string");
471
0
        }
472
0
        for (int j = 0; j < len; ++j) { s.PutChar(buffer[j]); }
473
0
    }
474
    // }
475
0
    s.PutChar('\n');
476
0
    for (int i = 0; i < indent; ++i) { s.PutChar('\t'); }
477
0
    s.PutChar('}'); s.PutChar(' ');
478
0
    node.End(s, false, indent, false);
479
0
}
480
481
// binary property node from vector of doubles
482
// TODO: optional zip compression!
483
void FBX::Node::WritePropertyNodeBinary(
484
    const std::string& name,
485
    const std::vector<double>& v,
486
    Assimp::StreamWriterLE& s
487
1.03k
){
488
1.03k
    FBX::Node node(name);
489
1.03k
    node.BeginBinary(s);
490
1.03k
    s.PutU1('d');
491
1.03k
    s.PutU4(uint32_t(v.size())); // number of elements
492
1.03k
    s.PutU4(0); // no encoding (1 would be zip-compressed)
493
1.03k
    s.PutU4(uint32_t(v.size()) * 8); // data size
494
6.19M
    for (auto it = v.begin(); it != v.end(); ++it) { s.PutF8(*it); }
495
1.03k
    node.EndPropertiesBinary(s, 1);
496
1.03k
    node.EndBinary(s, false);
497
1.03k
}
498
499
// binary property node from vector of int32_t
500
// TODO: optional zip compression!
501
void FBX::Node::WritePropertyNodeBinary(
502
    const std::string& name,
503
    const std::vector<int32_t>& v,
504
    Assimp::StreamWriterLE& s
505
472
){
506
472
    FBX::Node node(name);
507
472
    node.BeginBinary(s);
508
472
    s.PutU1('i');
509
472
    s.PutU4(uint32_t(v.size())); // number of elements
510
472
    s.PutU4(0); // no encoding (1 would be zip-compressed)
511
472
    s.PutU4(uint32_t(v.size()) * 4); // data size
512
949k
    for (auto it = v.begin(); it != v.end(); ++it) { s.PutI4(*it); }
513
472
    node.EndPropertiesBinary(s, 1);
514
472
    node.EndBinary(s, false);
515
472
}
516
517
// public static member functions
518
519
// convenience function to create and write a property node,
520
// holding a single property which is an array of values.
521
// does not copy the data, so is efficient for large arrays.
522
void FBX::Node::WritePropertyNode(
523
    const std::string& name,
524
    const std::vector<double>& v,
525
    Assimp::StreamWriterLE& s,
526
    bool binary, int indent
527
1.03k
){
528
1.03k
    if (binary) {
529
1.03k
        FBX::Node::WritePropertyNodeBinary(name, v, s);
530
1.03k
    } else {
531
0
        FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
532
0
    }
533
1.03k
}
534
535
// convenience function to create and write a property node,
536
// holding a single property which is an array of values.
537
// does not copy the data, so is efficient for large arrays.
538
void FBX::Node::WritePropertyNode(
539
    const std::string& name,
540
    const std::vector<int32_t>& v,
541
    Assimp::StreamWriterLE& s,
542
    bool binary, int indent
543
472
){
544
472
    if (binary) {
545
472
        FBX::Node::WritePropertyNodeBinary(name, v, s);
546
472
    } else {
547
0
        FBX::Node::WritePropertyNodeAscii(name, v, s, indent);
548
0
    }
549
472
}
550
551
} // namespace Assimp
552
553
#endif // ASSIMP_BUILD_NO_FBX_EXPORTER
554
#endif // ASSIMP_BUILD_NO_EXPORT