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