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