1 /*
2 Open Asset Import Library (assimp)
3 ----------------------------------------------------------------------
4
5 Copyright (c) 2006-2015, assimp team
6 All rights reserved.
7
8 Redistribution and use of this software in source and binary forms,
9 with or without modification, are permitted provided that the
10 following conditions are met:
11
12 * Redistributions of source code must retain the above
13 copyright notice, this list of conditions and the
14 following disclaimer.
15
16 * Redistributions in binary form must reproduce the above
17 copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
20
21 * Neither the name of the assimp team, nor the names of its
22 contributors may be used to endorse or promote products
23 derived from this software without specific prior
24 written permission of the assimp team.
25
26 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
29 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
30 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
31 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
33 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
34 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
35 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
36 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
38 ----------------------------------------------------------------------
39 */
40
41 /** @file FBXMeshGeometry.cpp
42 * @brief Assimp::FBX::MeshGeometry implementation
43 */
44
45 #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER
46
47 #include <functional>
48
49 #include "FBXParser.h"
50 #include "FBXDocument.h"
51 #include "FBXImporter.h"
52 #include "FBXImportSettings.h"
53 #include "FBXDocumentUtil.h"
54 #include <boost/foreach.hpp>
55
56
57 namespace Assimp {
58 namespace FBX {
59
60 using namespace Util;
61
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 BOOST_FOREACH(const Connection* con, conns) {
70 const Skin* const sk = ProcessSimpleConnection<Skin>(*con, false, "Skin -> Geometry", element);
71 if(sk) {
72 skin = sk;
73 break;
74 }
75 }
76 }
77
78
79 // ------------------------------------------------------------------------------------------------
~Geometry()80 Geometry::~Geometry()
81 {
82
83 }
84
85
86
87 // ------------------------------------------------------------------------------------------------
MeshGeometry(uint64_t id,const Element & element,const std::string & name,const Document & doc)88 MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
89 : Geometry(id, element,name, doc)
90 {
91 const Scope* sc = element.Compound();
92 if (!sc) {
93 DOMError("failed to read Geometry object (class: Mesh), no data scope found");
94 }
95
96 // must have Mesh elements:
97 const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element);
98 const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element);
99
100 // optional Mesh elements:
101 const ElementCollection& Layer = sc->GetCollection("Layer");
102
103 std::vector<aiVector3D> tempVerts;
104 ParseVectorDataArray(tempVerts,Vertices);
105
106 if(tempVerts.empty()) {
107 FBXImporter::LogWarn("encountered mesh with no vertices");
108 return;
109 }
110
111 std::vector<int> tempFaces;
112 ParseVectorDataArray(tempFaces,PolygonVertexIndex);
113
114 if(tempFaces.empty()) {
115 FBXImporter::LogWarn("encountered mesh with no faces");
116 return;
117 }
118
119 vertices.reserve(tempFaces.size());
120 faces.reserve(tempFaces.size() / 3);
121
122 mapping_offsets.resize(tempVerts.size());
123 mapping_counts.resize(tempVerts.size(),0);
124 mappings.resize(tempFaces.size());
125
126 const size_t vertex_count = tempVerts.size();
127
128 // generate output vertices, computing an adjacency table to
129 // preserve the mapping from fbx indices to *this* indexing.
130 unsigned int count = 0;
131 BOOST_FOREACH(int index, tempFaces) {
132 const int absi = index < 0 ? (-index - 1) : index;
133 if(static_cast<size_t>(absi) >= vertex_count) {
134 DOMError("polygon vertex index out of range",&PolygonVertexIndex);
135 }
136
137 vertices.push_back(tempVerts[absi]);
138 ++count;
139
140 ++mapping_counts[absi];
141
142 if (index < 0) {
143 faces.push_back(count);
144 count = 0;
145 }
146 }
147
148 unsigned int cursor = 0;
149 for (size_t i = 0, e = tempVerts.size(); i < e; ++i) {
150 mapping_offsets[i] = cursor;
151 cursor += mapping_counts[i];
152
153 mapping_counts[i] = 0;
154 }
155
156 cursor = 0;
157 BOOST_FOREACH(int index, tempFaces) {
158 const int absi = index < 0 ? (-index - 1) : index;
159 mappings[mapping_offsets[absi] + mapping_counts[absi]++] = cursor++;
160 }
161
162 // if settings.readAllLayers is true:
163 // * read all layers, try to load as many vertex channels as possible
164 // if settings.readAllLayers is false:
165 // * read only the layer with index 0, but warn about any further layers
166 for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) {
167 const TokenList& tokens = (*it).second->Tokens();
168
169 const char* err;
170 const int index = ParseTokenAsInt(*tokens[0], err);
171 if(err) {
172 DOMError(err,&element);
173 }
174
175 if(doc.Settings().readAllLayers || index == 0) {
176 const Scope& layer = GetRequiredScope(*(*it).second);
177 ReadLayer(layer);
178 }
179 else {
180 FBXImporter::LogWarn("ignoring additional geometry layers");
181 }
182 }
183 }
184
185
186 // ------------------------------------------------------------------------------------------------
~MeshGeometry()187 MeshGeometry::~MeshGeometry()
188 {
189
190 }
191
192
193
194 // ------------------------------------------------------------------------------------------------
ReadLayer(const Scope & layer)195 void MeshGeometry::ReadLayer(const Scope& layer)
196 {
197 const ElementCollection& LayerElement = layer.GetCollection("LayerElement");
198 for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) {
199 const Scope& elayer = GetRequiredScope(*(*eit).second);
200
201 ReadLayerElement(elayer);
202 }
203 }
204
205
206 // ------------------------------------------------------------------------------------------------
ReadLayerElement(const Scope & layerElement)207 void MeshGeometry::ReadLayerElement(const Scope& layerElement)
208 {
209 const Element& Type = GetRequiredElement(layerElement,"Type");
210 const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex");
211
212 const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0));
213 const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0));
214
215 const Scope& top = GetRequiredScope(element);
216 const ElementCollection candidates = top.GetCollection(type);
217
218 for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) {
219 const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0));
220 if(index == typedIndex) {
221 ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second));
222 return;
223 }
224 }
225
226 FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ")
227 << type << ", index: " << typedIndex);
228 }
229
230
231 // ------------------------------------------------------------------------------------------------
ReadVertexData(const std::string & type,int index,const Scope & source)232 void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source)
233 {
234 const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken(
235 GetRequiredElement(source,"MappingInformationType"),0)
236 );
237
238 const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken(
239 GetRequiredElement(source,"ReferenceInformationType"),0)
240 );
241
242 if (type == "LayerElementUV") {
243 if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) {
244 FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ")
245 << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" );
246 return;
247 }
248
249 const Element* Name = source["Name"];
250 uvNames[index] = "";
251 if(Name) {
252 uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0));
253 }
254
255 ReadVertexDataUV(uvs[index],source,
256 MappingInformationType,
257 ReferenceInformationType
258 );
259 }
260 else if (type == "LayerElementMaterial") {
261 if (materials.size() > 0) {
262 FBXImporter::LogError("ignoring additional material layer");
263 return;
264 }
265
266 std::vector<int> temp_materials;
267
268 ReadVertexDataMaterials(temp_materials,source,
269 MappingInformationType,
270 ReferenceInformationType
271 );
272
273 // sometimes, there will be only negative entries. Drop the material
274 // layer in such a case (I guess it means a default material should
275 // be used). This is what the converter would do anyway, and it
276 // avoids loosing the material if there are more material layers
277 // coming of which at least one contains actual data (did observe
278 // that with one test file).
279 const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),std::bind2nd(std::less<int>(),0));
280 if(count_neg == temp_materials.size()) {
281 FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)");
282 return;
283 }
284
285 std::swap(temp_materials, materials);
286 }
287 else if (type == "LayerElementNormal") {
288 if (normals.size() > 0) {
289 FBXImporter::LogError("ignoring additional normal layer");
290 return;
291 }
292
293 ReadVertexDataNormals(normals,source,
294 MappingInformationType,
295 ReferenceInformationType
296 );
297 }
298 else if (type == "LayerElementTangent") {
299 if (tangents.size() > 0) {
300 FBXImporter::LogError("ignoring additional tangent layer");
301 return;
302 }
303
304 ReadVertexDataTangents(tangents,source,
305 MappingInformationType,
306 ReferenceInformationType
307 );
308 }
309 else if (type == "LayerElementBinormal") {
310 if (binormals.size() > 0) {
311 FBXImporter::LogError("ignoring additional binormal layer");
312 return;
313 }
314
315 ReadVertexDataBinormals(binormals,source,
316 MappingInformationType,
317 ReferenceInformationType
318 );
319 }
320 else if (type == "LayerElementColor") {
321 if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) {
322 FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ")
323 << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" );
324 return;
325 }
326
327 ReadVertexDataColors(colors[index],source,
328 MappingInformationType,
329 ReferenceInformationType
330 );
331 }
332 }
333
334
335 // ------------------------------------------------------------------------------------------------
336 // Lengthy utility function to read and resolve a FBX vertex data array - that is, the
337 // output is in polygon vertex order. This logic is used for reading normals, UVs, colors,
338 // tangents ..
339 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)340 void ResolveVertexDataArray(std::vector<T>& data_out, const Scope& source,
341 const std::string& MappingInformationType,
342 const std::string& ReferenceInformationType,
343 const char* dataElementName,
344 const char* indexDataElementName,
345 size_t vertex_count,
346 const std::vector<unsigned int>& mapping_counts,
347 const std::vector<unsigned int>& mapping_offsets,
348 const std::vector<unsigned int>& mappings)
349 {
350 std::vector<T> tempUV;
351 ParseVectorDataArray(tempUV,GetRequiredElement(source,dataElementName));
352
353 // handle permutations of Mapping and Reference type - it would be nice to
354 // deal with this more elegantly and with less redundancy, but right
355 // now it seems unavoidable.
356 if (MappingInformationType == "ByVertice" && ReferenceInformationType == "Direct") {
357 data_out.resize(vertex_count);
358 for (size_t i = 0, e = tempUV.size(); i < e; ++i) {
359
360 const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
361 for (unsigned int j = istart; j < iend; ++j) {
362 data_out[mappings[j]] = tempUV[i];
363 }
364 }
365 }
366 else if (MappingInformationType == "ByVertice" && ReferenceInformationType == "IndexToDirect") {
367 data_out.resize(vertex_count);
368
369 std::vector<int> uvIndices;
370 ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
371
372 for (size_t i = 0, e = uvIndices.size(); i < e; ++i) {
373
374 const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i];
375 for (unsigned int j = istart; j < iend; ++j) {
376 if(static_cast<size_t>(uvIndices[i]) >= tempUV.size()) {
377 DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
378 }
379 data_out[mappings[j]] = tempUV[uvIndices[i]];
380 }
381 }
382 }
383 else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "Direct") {
384 if (tempUV.size() != vertex_count) {
385 FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
386 << tempUV.size() << ", expected " << vertex_count
387 );
388 return;
389 }
390
391 data_out.swap(tempUV);
392 }
393 else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "IndexToDirect") {
394 data_out.resize(vertex_count);
395
396 std::vector<int> uvIndices;
397 ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName));
398
399 if (uvIndices.size() != vertex_count) {
400 FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping");
401 return;
402 }
403
404 unsigned int next = 0;
405 BOOST_FOREACH(int i, uvIndices) {
406 if(static_cast<size_t>(i) >= tempUV.size()) {
407 DOMError("index out of range",&GetRequiredElement(source,indexDataElementName));
408 }
409
410 data_out[next++] = tempUV[i];
411 }
412 }
413 else {
414 FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ")
415 << MappingInformationType << "," << ReferenceInformationType);
416 }
417 }
418
419 // ------------------------------------------------------------------------------------------------
ReadVertexDataNormals(std::vector<aiVector3D> & normals_out,const Scope & source,const std::string & MappingInformationType,const std::string & ReferenceInformationType)420 void MeshGeometry::ReadVertexDataNormals(std::vector<aiVector3D>& normals_out, const Scope& source,
421 const std::string& MappingInformationType,
422 const std::string& ReferenceInformationType)
423 {
424 ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType,
425 "Normals",
426 "NormalsIndex",
427 vertices.size(),
428 mapping_counts,
429 mapping_offsets,
430 mappings);
431 }
432
433
434 // ------------------------------------------------------------------------------------------------
ReadVertexDataUV(std::vector<aiVector2D> & uv_out,const Scope & source,const std::string & MappingInformationType,const std::string & ReferenceInformationType)435 void MeshGeometry::ReadVertexDataUV(std::vector<aiVector2D>& uv_out, const Scope& source,
436 const std::string& MappingInformationType,
437 const std::string& ReferenceInformationType)
438 {
439 ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType,
440 "UV",
441 "UVIndex",
442 vertices.size(),
443 mapping_counts,
444 mapping_offsets,
445 mappings);
446 }
447
448
449 // ------------------------------------------------------------------------------------------------
ReadVertexDataColors(std::vector<aiColor4D> & colors_out,const Scope & source,const std::string & MappingInformationType,const std::string & ReferenceInformationType)450 void MeshGeometry::ReadVertexDataColors(std::vector<aiColor4D>& colors_out, const Scope& source,
451 const std::string& MappingInformationType,
452 const std::string& ReferenceInformationType)
453 {
454 ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType,
455 "Colors",
456 "ColorIndex",
457 vertices.size(),
458 mapping_counts,
459 mapping_offsets,
460 mappings);
461 }
462
463
464 // ------------------------------------------------------------------------------------------------
ReadVertexDataTangents(std::vector<aiVector3D> & tangents_out,const Scope & source,const std::string & MappingInformationType,const std::string & ReferenceInformationType)465 void MeshGeometry::ReadVertexDataTangents(std::vector<aiVector3D>& tangents_out, const Scope& source,
466 const std::string& MappingInformationType,
467 const std::string& ReferenceInformationType)
468 {
469 const char * str = source.Elements().count( "Tangents" ) > 0 ? "Tangents" : "Tangent";
470 ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType,
471 str,
472 "TangentIndex",
473 vertices.size(),
474 mapping_counts,
475 mapping_offsets,
476 mappings);
477 }
478
479
480 // ------------------------------------------------------------------------------------------------
ReadVertexDataBinormals(std::vector<aiVector3D> & binormals_out,const Scope & source,const std::string & MappingInformationType,const std::string & ReferenceInformationType)481 void MeshGeometry::ReadVertexDataBinormals(std::vector<aiVector3D>& binormals_out, const Scope& source,
482 const std::string& MappingInformationType,
483 const std::string& ReferenceInformationType)
484 {
485 const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal";
486 ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType,
487 str,
488 "BinormalIndex",
489 vertices.size(),
490 mapping_counts,
491 mapping_offsets,
492 mappings);
493 }
494
495
496 // ------------------------------------------------------------------------------------------------
ReadVertexDataMaterials(std::vector<int> & materials_out,const Scope & source,const std::string & MappingInformationType,const std::string & ReferenceInformationType)497 void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, const Scope& source,
498 const std::string& MappingInformationType,
499 const std::string& ReferenceInformationType)
500 {
501 const size_t face_count = faces.size();
502 ai_assert(face_count);
503
504 // materials are handled separately. First of all, they are assigned per-face
505 // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
506 // has a slightly different meaning for materials.
507 ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials"));
508
509 if (MappingInformationType == "AllSame") {
510 // easy - same material for all faces
511 if (materials_out.empty()) {
512 FBXImporter::LogError(Formatter::format("expected material index, ignoring"));
513 return;
514 }
515 else if (materials_out.size() > 1) {
516 FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one"));
517 materials_out.clear();
518 }
519
520 materials.assign(vertices.size(),materials_out[0]);
521 }
522 else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
523 materials.resize(face_count);
524
525 if(materials_out.size() != face_count) {
526 FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ")
527 << materials_out.size() << ", expected " << face_count
528 );
529 return;
530 }
531 }
532 else {
533 FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ")
534 << MappingInformationType << "," << ReferenceInformationType);
535 }
536 }
537
538 } // !FBX
539 } // !Assimp
540
541 #endif
542
543