Coverage Report

Created: 2025-11-11 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/assimp/code/AssetLib/FBX/FBXMeshGeometry.cpp
Line
Count
Source
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
42
/** @file  FBXMeshGeometry.cpp
43
 *  @brief Assimp::FBX::MeshGeometry implementation
44
 */
45
46
#ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
47
48
#include <functional>
49
50
#include "FBXMeshGeometry.h"
51
#include "FBXDocument.h"
52
#include "FBXImporter.h"
53
#include "FBXImportSettings.h"
54
#include "FBXDocumentUtil.h"
55
56
namespace Assimp {
57
namespace FBX {
58
59
using namespace Util;
60
61
// ------------------------------------------------------------------------------------------------
62
Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) :
63
0
        Object(id, element, name), skin() {
64
0
    const std::vector<const Connection*> &conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer");
65
0
    for(const Connection* con : conns) {
66
0
        const Skin* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
67
0
        if(sk) {
68
0
            skin = sk;
69
0
        }
70
0
        const BlendShape* const bsp = ProcessSimpleConnection<BlendShape>(*con, false, "BlendShape -> Geometry", element);
71
0
        if (bsp) {
72
0
            auto pr = blendShapes.insert(bsp);
73
0
            if (!pr.second) {
74
0
                FBXImporter::LogWarn("there is the same blendShape id ", bsp->ID());
75
0
            }
76
0
        }
77
0
    }
78
0
}
79
80
// ------------------------------------------------------------------------------------------------
81
0
const std::unordered_set<const BlendShape*>& Geometry::GetBlendShapes() const {
82
0
    return blendShapes;
83
0
}
84
85
// ------------------------------------------------------------------------------------------------
86
0
const Skin* Geometry::DeformerSkin() const {
87
0
    return skin;
88
0
}
89
90
// ------------------------------------------------------------------------------------------------
91
MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
92
0
: Geometry(id, element,name, doc)
93
0
{
94
0
    const Scope* sc = element.Compound();
95
0
    if (!sc) {
96
0
        DOMError("failed to read Geometry object (class: Mesh), no data scope found");
97
0
    }
98
99
    // must have Mesh elements:
100
0
    const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element);
101
0
    const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element);
102
103
    // optional Mesh elements:
104
0
    const ElementCollection& Layer = sc->GetCollection("Layer");
105
106
0
    std::vector<aiVector3D> tempVerts;
107
0
    ParseVectorDataArray(tempVerts,Vertices);
108
109
0
    if(tempVerts.empty()) {
110
0
        FBXImporter::LogWarn("encountered mesh with no vertices");
111
0
    }
112
113
0
    std::vector<int> tempFaces;
114
0
    ParseVectorDataArray(tempFaces,PolygonVertexIndex);
115
116
0
    if(tempFaces.empty()) {
117
0
        FBXImporter::LogWarn("encountered mesh with no faces");
118
0
    }
119
120
0
    m_vertices.reserve(tempFaces.size());
121
0
    m_faces.reserve(tempFaces.size() / 3);
122
123
0
    m_mapping_offsets.resize(tempVerts.size());
124
0
    m_mapping_counts.resize(tempVerts.size(),0);
125
0
    m_mappings.resize(tempFaces.size());
126
127
0
    const size_t vertex_count = tempVerts.size();
128
129
    // generate output vertices, computing an adjacency table to
130
    // preserve the mapping from fbx indices to *this* indexing.
131
0
    unsigned int count = 0;
132
0
    for(int index : tempFaces) {
133
0
        const int absi = index < 0 ? (-index - 1) : index;
134
0
        if(static_cast<size_t>(absi) >= vertex_count) {
135
0
            DOMError("polygon vertex index out of range",&PolygonVertexIndex);
136
0
        }
137
138
0
        m_vertices.push_back(tempVerts[absi]);
139
0
        ++count;
140
141
0
        ++m_mapping_counts[absi];
142
143
0
        if (index < 0) {
144
0
            m_faces.push_back(count);
145
0
            count = 0;
146
0
        }
147
0
    }
148
149
0
    unsigned int cursor = 0;
150
0
    for (size_t i = 0, e = tempVerts.size(); i < e; ++i) {
151
0
        m_mapping_offsets[i] = cursor;
152
0
        cursor += m_mapping_counts[i];
153
154
0
        m_mapping_counts[i] = 0;
155
0
    }
156
157
0
    cursor = 0;
158
0
    for(int index : tempFaces) {
159
0
        const int absi = index < 0 ? (-index - 1) : index;
160
0
        m_mappings[m_mapping_offsets[absi] + m_mapping_counts[absi]++] = cursor++;
161
0
    }
162
163
    // if settings.readAllLayers is true:
164
    //  * read all layers, try to load as many vertex channels as possible
165
    // if settings.readAllLayers is false:
166
    //  * read only the layer with index 0, but warn about any further layers
167
0
    for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
168
0
        const TokenList& tokens = (*it).second->Tokens();
169
170
0
        const char* err;
171
0
        const int index = ParseTokenAsInt(*tokens[0], err);
172
0
        if(err) {
173
0
            DOMError(err,&element);
174
0
        }
175
176
0
        if(doc.Settings().readAllLayers || index == 0) {
177
0
            const Scope& layer = GetRequiredScope(*(*it).second);
178
0
            ReadLayer(layer);
179
0
        } else {
180
0
            FBXImporter::LogWarn("ignoring additional geometry layers");
181
0
        }
182
0
    }
183
0
}
184
185
// ------------------------------------------------------------------------------------------------
186
0
const std::vector<aiVector3D>& MeshGeometry::GetVertices() const {
187
0
    return m_vertices;
188
0
}
189
190
// ------------------------------------------------------------------------------------------------
191
0
const std::vector<aiVector3D>& MeshGeometry::GetNormals() const {
192
0
    return m_normals;
193
0
}
194
195
// ------------------------------------------------------------------------------------------------
196
0
const std::vector<aiVector3D>& MeshGeometry::GetTangents() const {
197
0
    return m_tangents;
198
0
}
199
200
// ------------------------------------------------------------------------------------------------
201
0
const std::vector<aiVector3D>& MeshGeometry::GetBinormals() const {
202
0
    return m_binormals;
203
0
}
204
205
// ------------------------------------------------------------------------------------------------
206
0
const std::vector<unsigned int>& MeshGeometry::GetFaceIndexCounts() const {
207
0
    return m_faces;
208
0
}
209
210
// ------------------------------------------------------------------------------------------------
211
0
const std::vector<aiVector2D>& MeshGeometry::GetTextureCoords( unsigned int index ) const {
212
0
    static const std::vector<aiVector2D> empty;
213
0
    return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? empty : m_uvs[ index ];
214
0
}
215
216
0
std::string MeshGeometry::GetTextureCoordChannelName( unsigned int index ) const {
217
0
    return index >= AI_MAX_NUMBER_OF_TEXTURECOORDS ? "" : m_uvNames[ index ];
218
0
}
219
220
0
const std::vector<aiColor4D>& MeshGeometry::GetVertexColors( unsigned int index ) const {
221
0
    static const std::vector<aiColor4D> empty;
222
0
    return index >= AI_MAX_NUMBER_OF_COLOR_SETS ? empty : m_colors[ index ];
223
0
}
224
225
0
const MatIndexArray& MeshGeometry::GetMaterialIndices() const {
226
0
    return m_materials;
227
0
}
228
// ------------------------------------------------------------------------------------------------
229
0
const unsigned int* MeshGeometry::ToOutputVertexIndex( unsigned int in_index, unsigned int& count ) const {
230
0
    if ( in_index >= m_mapping_counts.size() ) {
231
0
        return nullptr;
232
0
    }
233
234
0
    ai_assert( m_mapping_counts.size() == m_mapping_offsets.size() );
235
0
    count = m_mapping_counts[ in_index ];
236
237
0
    ai_assert( m_mapping_offsets[ in_index ] + count <= m_mappings.size() );
238
239
0
    return &m_mappings[ m_mapping_offsets[ in_index ] ];
240
0
}
241
242
// ------------------------------------------------------------------------------------------------
243
0
unsigned int MeshGeometry::FaceForVertexIndex( unsigned int in_index ) const {
244
0
    ai_assert( in_index < m_vertices.size() );
245
246
    // in the current conversion pattern this will only be needed if
247
    // weights are present, so no need to always pre-compute this table
248
0
    if ( m_facesVertexStartIndices.empty() ) {
249
0
        m_facesVertexStartIndices.resize( m_faces.size() + 1, 0 );
250
251
0
        std::partial_sum( m_faces.begin(), m_faces.end(), m_facesVertexStartIndices.begin() + 1 );
252
0
        m_facesVertexStartIndices.pop_back();
253
0
    }
254
255
0
    ai_assert( m_facesVertexStartIndices.size() == m_faces.size() );
256
0
    const std::vector<unsigned int>::iterator it = std::upper_bound(
257
0
        m_facesVertexStartIndices.begin(),
258
0
        m_facesVertexStartIndices.end(),
259
0
        in_index
260
0
        );
261
262
0
    return static_cast< unsigned int >( std::distance( m_facesVertexStartIndices.begin(), it - 1 ) );
263
0
}
264
265
// ------------------------------------------------------------------------------------------------
266
void MeshGeometry::ReadLayer(const Scope& layer)
267
0
{
268
0
    const ElementCollection& LayerElement = layer.GetCollection("LayerElement");
269
0
    for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
270
0
        const Scope& elayer = GetRequiredScope(*(*eit).second);
271
272
0
        ReadLayerElement(elayer);
273
0
    }
274
0
}
275
276
277
// ------------------------------------------------------------------------------------------------
278
void MeshGeometry::ReadLayerElement(const Scope& layerElement)
279
0
{
280
0
    const Element& Type = GetRequiredElement(layerElement,"Type");
281
0
    const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex");
282
283
0
    const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0));
284
0
    const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0));
285
286
0
    const Scope& top = GetRequiredScope(element);
287
0
    const ElementCollection candidates = top.GetCollection(type);
288
289
0
    for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) {
290
0
        const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0));
291
0
        if(index == typedIndex) {
292
0
            ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second));
293
0
            return;
294
0
        }
295
0
    }
296
297
0
    FBXImporter::LogError("failed to resolve vertex layer element: ",
298
0
        type, ", index: ", typedIndex);
299
0
}
300
301
// ------------------------------------------------------------------------------------------------
302
void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source)
303
0
{
304
0
    const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken(
305
0
        GetRequiredElement(source,"MappingInformationType"),0)
306
0
    );
307
308
0
    const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
309
0
        GetRequiredElement(source,"ReferenceInformationType"),0)
310
0
    );
311
312
0
    if (type == "LayerElementUV") {
313
0
        if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
314
0
            FBXImporter::LogError("ignoring UV layer, maximum number of UV channels exceeded: ",
315
0
                index, " (limit is ", AI_MAX_NUMBER_OF_TEXTURECOORDS, ")" );
316
0
            return;
317
0
        }
318
319
0
        const Element* Name = source["Name"];
320
0
        m_uvNames[index] = std::string();
321
0
        if(Name) {
322
0
            m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
323
0
        }
324
325
0
        ReadVertexDataUV(m_uvs[index],source,
326
0
            MappingInformationType,
327
0
            ReferenceInformationType
328
0
        );
329
0
    }
330
0
    else if (type == "LayerElementMaterial") {
331
0
        if (m_materials.size() > 0) {
332
0
            FBXImporter::LogError("ignoring additional material layer");
333
0
            return;
334
0
        }
335
336
0
        std::vector<int> temp_materials;
337
338
0
        ReadVertexDataMaterials(temp_materials,source,
339
0
            MappingInformationType,
340
0
            ReferenceInformationType
341
0
        );
342
343
        // sometimes, there will be only negative entries. Drop the material
344
        // layer in such a case (I guess it means a default material should
345
        // be used). This is what the converter would do anyway, and it
346
        // avoids losing the material if there are more material layers
347
        // coming of which at least one contains actual data (did observe
348
        // that with one test file).
349
0
        const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),[](int n) { return n < 0; });
350
0
        if(count_neg == temp_materials.size()) {
351
0
            FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)");
352
0
            return;
353
0
        }
354
355
0
        std::swap(temp_materials, m_materials);
356
0
    }
357
0
    else if (type == "LayerElementNormal") {
358
0
        if (m_normals.size() > 0) {
359
0
            FBXImporter::LogError("ignoring additional normal layer");
360
0
            return;
361
0
        }
362
363
0
        ReadVertexDataNormals(m_normals,source,
364
0
            MappingInformationType,
365
0
            ReferenceInformationType
366
0
        );
367
0
    }
368
0
    else if (type == "LayerElementTangent") {
369
0
        if (m_tangents.size() > 0) {
370
0
            FBXImporter::LogError("ignoring additional tangent layer");
371
0
            return;
372
0
        }
373
374
0
        ReadVertexDataTangents(m_tangents,source,
375
0
            MappingInformationType,
376
0
            ReferenceInformationType
377
0
        );
378
0
    }
379
0
    else if (type == "LayerElementBinormal") {
380
0
        if (m_binormals.size() > 0) {
381
0
            FBXImporter::LogError("ignoring additional binormal layer");
382
0
            return;
383
0
        }
384
385
0
        ReadVertexDataBinormals(m_binormals,source,
386
0
            MappingInformationType,
387
0
            ReferenceInformationType
388
0
        );
389
0
    }
390
0
    else if (type == "LayerElementColor") {
391
0
        if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) {
392
0
            FBXImporter::LogError("ignoring vertex color layer, maximum number of color sets exceeded: ",
393
0
                index, " (limit is ", AI_MAX_NUMBER_OF_COLOR_SETS, ")" );
394
0
            return;
395
0
        }
396
397
0
        ReadVertexDataColors(m_colors[index],source,
398
0
            MappingInformationType,
399
0
            ReferenceInformationType
400
0
        );
401
0
    }
402
0
}
403
404
// ------------------------------------------------------------------------------------------------
405
// Lengthy utility function to read and resolve a FBX vertex data array - that is, the
406
// output is in polygon vertex order. This logic is used for reading normals, UVs, colors,
407
// tangents ..
408
template <typename T>
409
void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
410
    const std::string& MappingInformationType,
411
    const std::string& ReferenceInformationType,
412
    const char* dataElementName,
413
    const char* indexDataElementName,
414
    size_t vertex_count,
415
    const std::vector<unsigned int>& mapping_counts,
416
    const std::vector<unsigned int>& mapping_offsets,
417
    const std::vector<unsigned int>& mappings)
418
0
{
419
0
    bool isDirect = ReferenceInformationType == "Direct";
420
0
    bool isIndexToDirect = ReferenceInformationType == "IndexToDirect";
421
0
    const bool hasDataElement = HasElement(source, dataElementName);
422
0
    const bool hasIndexDataElement = HasElement(source, indexDataElementName);
423
424
    // fall-back to direct data if there is no index data element
425
0
    if (isIndexToDirect && !hasIndexDataElement) {
426
0
        isDirect = true;
427
0
        isIndexToDirect = false;
428
0
    }
429
430
    // handle permutations of Mapping and Reference type - it would be nice to
431
    // deal with this more elegantly and with less redundancy, but right
432
    // now it seems unavoidable.
433
0
    if (MappingInformationType == "ByVertice" && isDirect) {
434
0
        if (!hasDataElement) {
435
0
            FBXImporter::LogWarn("missing data element: ", dataElementName);
436
0
            return;
437
0
        }
438
0
        std::vector<T> tempData;
439
0
        ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
440
441
0
        if (tempData.size() != mapping_offsets.size()) {
442
0
            FBXImporter::LogError("length of input data unexpected for ByVertice mapping: ",
443
0
                                  tempData.size(), ", expected ", mapping_offsets.size());
444
0
            return;
445
0
        }
446
447
0
        data_out.resize(vertex_count);
448
0
        for (size_t i = 0, e = tempData.size(); i < e; ++i) {
449
0
            const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
450
0
            for (unsigned int j = istart; j < iend; ++j) {
451
0
                data_out[mappings[j]] = tempData[i];
452
0
            }
453
0
        }
454
0
    }
455
0
    else if (MappingInformationType == "ByVertice" && isIndexToDirect) {
456
0
    std::vector<T> tempData;
457
0
        if (!hasDataElement || !hasIndexDataElement) {
458
0
            if (!hasDataElement)
459
0
                FBXImporter::LogWarn("missing data element: ", dataElementName);
460
0
            if (!hasIndexDataElement)
461
0
                FBXImporter::LogWarn("missing index data element: ", indexDataElementName);
462
0
            return;
463
0
        }
464
465
0
        ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
466
467
0
        std::vector<int> uvIndices;
468
0
        ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
469
470
0
        if (uvIndices.size() != mapping_offsets.size()) {
471
0
            FBXImporter::LogError("length of input data unexpected for ByVertice mapping: ",
472
0
                                  uvIndices.size(), ", expected ", mapping_offsets.size());
473
0
            return;
474
0
        }
475
476
0
        data_out.resize(vertex_count);
477
478
0
        for (size_t i = 0, e = uvIndices.size(); i < e; ++i) {
479
480
0
            const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
481
0
            for (unsigned int j = istart; j < iend; ++j) {
482
0
        if (static_cast<size_t>(uvIndices[i]) >= tempData.size()) {
483
0
                    DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
484
0
                }
485
0
        data_out[mappings[j]] = tempData[uvIndices[i]];
486
0
            }
487
0
        }
488
0
    }
489
0
    else if (MappingInformationType == "ByPolygonVertex" && isDirect) {
490
0
        if (!hasDataElement) {
491
0
            FBXImporter::LogWarn("missing data element: ", dataElementName);
492
0
            return;
493
0
        }
494
495
0
    std::vector<T> tempData;
496
0
    ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
497
498
0
    if (tempData.size() != vertex_count) {
499
0
            FBXImporter::LogError("length of input data unexpected for ByPolygon mapping: ",
500
0
        tempData.size(), ", expected ", vertex_count
501
0
            );
502
0
            return;
503
0
        }
504
505
0
    data_out.swap(tempData);
506
0
    }
507
0
    else if (MappingInformationType == "ByPolygonVertex" && isIndexToDirect) {
508
0
    std::vector<T> tempData;
509
0
        if (!hasDataElement || !hasIndexDataElement) {
510
0
            if (!hasDataElement)
511
0
                FBXImporter::LogWarn("missing data element: ", dataElementName);
512
0
            if (!hasIndexDataElement)
513
0
                FBXImporter::LogWarn("missing index data element: ", indexDataElementName);
514
0
            return;
515
0
        }
516
0
        ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName));
517
518
0
        std::vector<int> uvIndices;
519
0
        ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
520
521
0
        if (uvIndices.size() > vertex_count) {
522
0
            FBXImporter::LogWarn("trimming length of input array for ByPolygonVertex mapping: ",
523
0
                                          uvIndices.size(), ", expected ", vertex_count);
524
0
            uvIndices.resize(vertex_count);
525
0
        }
526
527
0
        if (uvIndices.size() != vertex_count) {
528
0
            FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping: ",
529
0
                                  uvIndices.size(), ", expected ", vertex_count);
530
0
            return;
531
0
        }
532
533
0
        data_out.resize(vertex_count);
534
535
0
        const T empty;
536
0
        unsigned int next = 0;
537
0
        for(int i : uvIndices) {
538
0
            if ( -1 == i ) {
539
0
                data_out[ next++ ] = empty;
540
0
                continue;
541
0
            }
542
0
            if (static_cast<size_t>(i) >= tempData.size()) {
543
0
                DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
544
0
            }
545
546
0
      data_out[next++] = tempData[i];
547
0
        }
548
0
    }
549
0
    else {
550
0
        FBXImporter::LogError("ignoring vertex data channel, access type not implemented: ",
551
0
            MappingInformationType, ",", ReferenceInformationType);
552
0
    }
553
0
}
Unexecuted instantiation: void Assimp::FBX::ResolveVertexDataArray<aiVector3t<float> >(std::__1::vector<aiVector3t<float>, std::__1::allocator<aiVector3t<float> > >&, Assimp::FBX::Scope const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*, char const*, unsigned long, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
Unexecuted instantiation: void Assimp::FBX::ResolveVertexDataArray<aiVector2t<float> >(std::__1::vector<aiVector2t<float>, std::__1::allocator<aiVector2t<float> > >&, Assimp::FBX::Scope const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*, char const*, unsigned long, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
Unexecuted instantiation: void Assimp::FBX::ResolveVertexDataArray<aiColor4t<float> >(std::__1::vector<aiColor4t<float>, std::__1::allocator<aiColor4t<float> > >&, Assimp::FBX::Scope const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, char const*, char const*, unsigned long, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&, std::__1::vector<unsigned int, std::__1::allocator<unsigned int> > const&)
554
555
// ------------------------------------------------------------------------------------------------
556
void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, const Scope& source,
557
    const std::string& MappingInformationType,
558
    const std::string& ReferenceInformationType)
559
0
{
560
0
    ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType,
561
0
        "Normals",
562
0
        "NormalsIndex",
563
0
        m_vertices.size(),
564
0
        m_mapping_counts,
565
0
        m_mapping_offsets,
566
0
        m_mappings);
567
0
}
568
569
// ------------------------------------------------------------------------------------------------
570
void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope& source,
571
    const std::string& MappingInformationType,
572
    const std::string& ReferenceInformationType)
573
0
{
574
0
    ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType,
575
0
        "UV",
576
0
        "UVIndex",
577
0
        m_vertices.size(),
578
0
        m_mapping_counts,
579
0
        m_mapping_offsets,
580
0
        m_mappings);
581
0
}
582
583
// ------------------------------------------------------------------------------------------------
584
void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, const Scope& source,
585
    const std::string& MappingInformationType,
586
    const std::string& ReferenceInformationType)
587
0
{
588
0
    ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType,
589
0
        "Colors",
590
0
        "ColorIndex",
591
0
        m_vertices.size(),
592
0
        m_mapping_counts,
593
0
        m_mapping_offsets,
594
0
        m_mappings);
595
0
}
596
597
// ------------------------------------------------------------------------------------------------
598
static const char *TangentIndexToken = "TangentIndex";
599
static const char *TangentsIndexToken = "TangentsIndex";
600
601
void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
602
    const std::string& MappingInformationType,
603
    const std::string& ReferenceInformationType)
604
0
{
605
0
    const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
606
0
    const char * strIdx = source.Elements().count( "Tangents" ) > 0 ? TangentsIndexToken : TangentIndexToken;
607
0
    ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
608
0
        str,
609
0
        strIdx,
610
0
        m_vertices.size(),
611
0
        m_mapping_counts,
612
0
        m_mapping_offsets,
613
0
        m_mappings);
614
0
}
615
616
// ------------------------------------------------------------------------------------------------
617
static const char * BinormalIndexToken = "BinormalIndex";
618
static const char * BinormalsIndexToken = "BinormalsIndex";
619
620
void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_out, const Scope& source,
621
    const std::string& MappingInformationType,
622
    const std::string& ReferenceInformationType)
623
0
{
624
0
    const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal";
625
0
    const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken : BinormalIndexToken;
626
0
    ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
627
0
        str,
628
0
        strIdx,
629
0
        m_vertices.size(),
630
0
        m_mapping_counts,
631
0
        m_mapping_offsets,
632
0
        m_mappings);
633
0
}
634
635
636
// ------------------------------------------------------------------------------------------------
637
void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, const Scope& source,
638
    const std::string& MappingInformationType,
639
    const std::string& ReferenceInformationType)
640
0
{
641
0
    const size_t face_count = m_faces.size();
642
0
    if( 0 == face_count )
643
0
    {
644
0
        return;
645
0
    }
646
647
0
    if (source["Materials"]) {
648
        // materials are handled separately. First of all, they are assigned per-face
649
        // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
650
        // has a slightly different meaning for materials.
651
0
        ParseVectorDataArray(materials_out, GetRequiredElement(source, "Materials"));
652
0
    }
653
654
0
    if (MappingInformationType == "AllSame") {
655
        // easy - same material for all faces
656
0
        if (materials_out.empty()) {
657
0
            FBXImporter::LogError("expected material index, ignoring");
658
0
            return;
659
0
        } else if (materials_out.size() > 1) {
660
0
            FBXImporter::LogWarn("expected only a single material index, ignoring all except the first one");
661
0
            materials_out.clear();
662
0
        }
663
664
0
        materials_out.resize(m_vertices.size());
665
0
        std::fill(materials_out.begin(), materials_out.end(), materials_out.at(0));
666
0
    } else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
667
0
        materials_out.resize(face_count);
668
669
0
        if(materials_out.size() != face_count) {
670
0
            FBXImporter::LogError("length of input data unexpected for ByPolygon mapping: ",
671
0
                materials_out.size(), ", expected ", face_count
672
0
            );
673
0
            return;
674
0
        }
675
0
    } else {
676
0
        FBXImporter::LogError("ignoring material assignments, access type not implemented: ",
677
0
            MappingInformationType, ",", ReferenceInformationType);
678
0
    }
679
0
}
680
// ------------------------------------------------------------------------------------------------
681
ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
682
0
: Geometry(id, element, name, doc) {
683
0
    const Scope *sc = element.Compound();
684
0
    if (nullptr == sc) {
685
0
        DOMError("failed to read Geometry object (class: Shape), no data scope found");
686
0
    }
687
0
    const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
688
0
    const Element& Vertices = GetRequiredElement(*sc, "Vertices", &element);
689
0
    ParseVectorDataArray(m_indices, Indexes);
690
0
    ParseVectorDataArray(m_vertices, Vertices);
691
692
0
    if ((*sc)["Normals"]) {
693
0
        const Element& Normals = GetRequiredElement(*sc, "Normals", &element);
694
0
        ParseVectorDataArray(m_normals, Normals);
695
0
    }
696
0
}
697
698
// ------------------------------------------------------------------------------------------------
699
0
ShapeGeometry::~ShapeGeometry() = default;
700
// ------------------------------------------------------------------------------------------------
701
0
const std::vector<aiVector3D>& ShapeGeometry::GetVertices() const {
702
0
    return m_vertices;
703
0
}
704
// ------------------------------------------------------------------------------------------------
705
0
const std::vector<aiVector3D>& ShapeGeometry::GetNormals() const {
706
0
    return m_normals;
707
0
}
708
// ------------------------------------------------------------------------------------------------
709
0
const std::vector<unsigned int>& ShapeGeometry::GetIndices() const {
710
0
    return m_indices;
711
0
}
712
// ------------------------------------------------------------------------------------------------
713
LineGeometry::LineGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
714
0
    : Geometry(id, element, name, doc)
715
0
{
716
0
    const Scope* sc = element.Compound();
717
0
    if (!sc) {
718
0
        DOMError("failed to read Geometry object (class: Line), no data scope found");
719
0
    }
720
0
    const Element& Points = GetRequiredElement(*sc, "Points", &element);
721
0
    const Element& PointsIndex = GetRequiredElement(*sc, "PointsIndex", &element);
722
0
    ParseVectorDataArray(m_vertices, Points);
723
0
    ParseVectorDataArray(m_indices, PointsIndex);
724
0
}
725
726
// ------------------------------------------------------------------------------------------------
727
0
LineGeometry::~LineGeometry() = default;
728
// ------------------------------------------------------------------------------------------------
729
0
const std::vector<aiVector3D>& LineGeometry::GetVertices() const {
730
0
    return m_vertices;
731
0
}
732
// ------------------------------------------------------------------------------------------------
733
0
const std::vector<int>& LineGeometry::GetIndices() const {
734
0
    return m_indices;
735
0
}
736
} // !FBX
737
} // !Assimp
738
#endif
739