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