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