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