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