nuclear@0: /* nuclear@0: Open Asset Import Library (assimp) nuclear@0: ---------------------------------------------------------------------- nuclear@0: nuclear@0: Copyright (c) 2006-2012, assimp team nuclear@0: All rights reserved. nuclear@0: nuclear@0: Redistribution and use of this software in source and binary forms, nuclear@0: with or without modification, are permitted provided that the nuclear@0: following conditions are met: nuclear@0: nuclear@0: * Redistributions of source code must retain the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer. nuclear@0: nuclear@0: * Redistributions in binary form must reproduce the above nuclear@0: copyright notice, this list of conditions and the nuclear@0: following disclaimer in the documentation and/or other nuclear@0: materials provided with the distribution. nuclear@0: nuclear@0: * Neither the name of the assimp team, nor the names of its nuclear@0: contributors may be used to endorse or promote products nuclear@0: derived from this software without specific prior nuclear@0: written permission of the assimp team. nuclear@0: nuclear@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS nuclear@0: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT nuclear@0: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR nuclear@0: A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT nuclear@0: OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, nuclear@0: SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT nuclear@0: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, nuclear@0: DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY nuclear@0: THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT nuclear@0: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE nuclear@0: OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. nuclear@0: nuclear@0: ---------------------------------------------------------------------- nuclear@0: */ nuclear@0: nuclear@0: /** @file FBXMeshGeometry.cpp nuclear@0: * @brief Assimp::FBX::MeshGeometry implementation nuclear@0: */ nuclear@0: #include "AssimpPCH.h" nuclear@0: nuclear@0: #ifndef ASSIMP_BUILD_NO_FBX_IMPORTER nuclear@0: nuclear@0: #include nuclear@0: nuclear@0: #include "FBXParser.h" nuclear@0: #include "FBXDocument.h" nuclear@0: #include "FBXImporter.h" nuclear@0: #include "FBXImportSettings.h" nuclear@0: #include "FBXDocumentUtil.h" nuclear@0: nuclear@0: nuclear@0: namespace Assimp { nuclear@0: namespace FBX { nuclear@0: nuclear@0: using namespace Util; nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Geometry::Geometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) nuclear@0: : Object(id, element,name) nuclear@0: , skin() nuclear@0: { nuclear@0: const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID(),"Deformer"); nuclear@0: BOOST_FOREACH(const Connection* con, conns) { nuclear@0: const Skin* const sk = ProcessSimpleConnection(*con, false, "Skin -> Geometry", element); nuclear@0: if(sk) { nuclear@0: skin = sk; nuclear@0: break; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: Geometry::~Geometry() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc) nuclear@0: : Geometry(id, element,name, doc) nuclear@0: { nuclear@0: const Scope* sc = element.Compound(); nuclear@0: if (!sc) { nuclear@0: DOMError("failed to read Geometry object (class: Mesh), no data scope found"); nuclear@0: } nuclear@0: nuclear@0: // must have Mesh elements: nuclear@0: const Element& Vertices = GetRequiredElement(*sc,"Vertices",&element); nuclear@0: const Element& PolygonVertexIndex = GetRequiredElement(*sc,"PolygonVertexIndex",&element); nuclear@0: nuclear@0: // optional Mesh elements: nuclear@0: const ElementCollection& Layer = sc->GetCollection("Layer"); nuclear@0: nuclear@0: std::vector tempVerts; nuclear@0: ParseVectorDataArray(tempVerts,Vertices); nuclear@0: nuclear@0: if(tempVerts.empty()) { nuclear@0: FBXImporter::LogWarn("encountered mesh with no vertices"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: std::vector tempFaces; nuclear@0: ParseVectorDataArray(tempFaces,PolygonVertexIndex); nuclear@0: nuclear@0: if(tempFaces.empty()) { nuclear@0: FBXImporter::LogWarn("encountered mesh with no faces"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: vertices.reserve(tempFaces.size()); nuclear@0: faces.reserve(tempFaces.size() / 3); nuclear@0: nuclear@0: mapping_offsets.resize(tempVerts.size()); nuclear@0: mapping_counts.resize(tempVerts.size(),0); nuclear@0: mappings.resize(tempFaces.size()); nuclear@0: nuclear@0: const size_t vertex_count = tempVerts.size(); nuclear@0: nuclear@0: // generate output vertices, computing an adjacency table to nuclear@0: // preserve the mapping from fbx indices to *this* indexing. nuclear@0: unsigned int count = 0; nuclear@0: BOOST_FOREACH(int index, tempFaces) { nuclear@0: const int absi = index < 0 ? (-index - 1) : index; nuclear@0: if(static_cast(absi) >= vertex_count) { nuclear@0: DOMError("polygon vertex index out of range",&PolygonVertexIndex); nuclear@0: } nuclear@0: nuclear@0: vertices.push_back(tempVerts[absi]); nuclear@0: ++count; nuclear@0: nuclear@0: ++mapping_counts[absi]; nuclear@0: nuclear@0: if (index < 0) { nuclear@0: faces.push_back(count); nuclear@0: count = 0; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: unsigned int cursor = 0; nuclear@0: for (size_t i = 0, e = tempVerts.size(); i < e; ++i) { nuclear@0: mapping_offsets[i] = cursor; nuclear@0: cursor += mapping_counts[i]; nuclear@0: nuclear@0: mapping_counts[i] = 0; nuclear@0: } nuclear@0: nuclear@0: cursor = 0; nuclear@0: BOOST_FOREACH(int index, tempFaces) { nuclear@0: const int absi = index < 0 ? (-index - 1) : index; nuclear@0: mappings[mapping_offsets[absi] + mapping_counts[absi]++] = cursor++; nuclear@0: } nuclear@0: nuclear@0: // if settings.readAllLayers is true: nuclear@0: // * read all layers, try to load as many vertex channels as possible nuclear@0: // if settings.readAllLayers is false: nuclear@0: // * read only the layer with index 0, but warn about any further layers nuclear@0: for (ElementMap::const_iterator it = Layer.first; it != Layer.second; ++it) { nuclear@0: const TokenList& tokens = (*it).second->Tokens(); nuclear@0: nuclear@0: const char* err; nuclear@0: const int index = ParseTokenAsInt(*tokens[0], err); nuclear@0: if(err) { nuclear@0: DOMError(err,&element); nuclear@0: } nuclear@0: nuclear@0: if(doc.Settings().readAllLayers || index == 0) { nuclear@0: const Scope& layer = GetRequiredScope(*(*it).second); nuclear@0: ReadLayer(layer); nuclear@0: } nuclear@0: else { nuclear@0: FBXImporter::LogWarn("ignoring additional geometry layers"); nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: MeshGeometry::~MeshGeometry() nuclear@0: { nuclear@0: nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadLayer(const Scope& layer) nuclear@0: { nuclear@0: const ElementCollection& LayerElement = layer.GetCollection("LayerElement"); nuclear@0: for (ElementMap::const_iterator eit = LayerElement.first; eit != LayerElement.second; ++eit) { nuclear@0: const Scope& elayer = GetRequiredScope(*(*eit).second); nuclear@0: nuclear@0: ReadLayerElement(elayer); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadLayerElement(const Scope& layerElement) nuclear@0: { nuclear@0: const Element& Type = GetRequiredElement(layerElement,"Type"); nuclear@0: const Element& TypedIndex = GetRequiredElement(layerElement,"TypedIndex"); nuclear@0: nuclear@0: const std::string& type = ParseTokenAsString(GetRequiredToken(Type,0)); nuclear@0: const int typedIndex = ParseTokenAsInt(GetRequiredToken(TypedIndex,0)); nuclear@0: nuclear@0: const Scope& top = GetRequiredScope(element); nuclear@0: const ElementCollection candidates = top.GetCollection(type); nuclear@0: nuclear@0: for (ElementMap::const_iterator it = candidates.first; it != candidates.second; ++it) { nuclear@0: const int index = ParseTokenAsInt(GetRequiredToken(*(*it).second,0)); nuclear@0: if(index == typedIndex) { nuclear@0: ReadVertexData(type,typedIndex,GetRequiredScope(*(*it).second)); nuclear@0: return; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ") nuclear@0: << type << ", index: " << typedIndex); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scope& source) nuclear@0: { nuclear@0: const std::string& MappingInformationType = ParseTokenAsString(GetRequiredToken( nuclear@0: GetRequiredElement(source,"MappingInformationType"),0) nuclear@0: ); nuclear@0: nuclear@0: const std::string& ReferenceInformationType = ParseTokenAsString(GetRequiredToken( nuclear@0: GetRequiredElement(source,"ReferenceInformationType"),0) nuclear@0: ); nuclear@0: nuclear@0: if (type == "LayerElementUV") { nuclear@0: if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { nuclear@0: FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ") nuclear@0: << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" ); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: const Element* Name = source["Name"]; nuclear@0: uvNames[index] = ""; nuclear@0: if(Name) { nuclear@0: uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0)); nuclear@0: } nuclear@0: nuclear@0: ReadVertexDataUV(uvs[index],source, nuclear@0: MappingInformationType, nuclear@0: ReferenceInformationType nuclear@0: ); nuclear@0: } nuclear@0: else if (type == "LayerElementMaterial") { nuclear@0: if (materials.size() > 0) { nuclear@0: FBXImporter::LogError("ignoring additional material layer"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: std::vector temp_materials; nuclear@0: nuclear@0: ReadVertexDataMaterials(temp_materials,source, nuclear@0: MappingInformationType, nuclear@0: ReferenceInformationType nuclear@0: ); nuclear@0: nuclear@0: // sometimes, there will be only negative entries. Drop the material nuclear@0: // layer in such a case (I guess it means a default material should nuclear@0: // be used). This is what the converter would do anyway, and it nuclear@0: // avoids loosing the material if there are more material layers nuclear@0: // coming of which at least one contains actual data (did observe nuclear@0: // that with one test file). nuclear@0: const size_t count_neg = std::count_if(temp_materials.begin(),temp_materials.end(),std::bind2nd(std::less(),0)); nuclear@0: if(count_neg == temp_materials.size()) { nuclear@0: FBXImporter::LogWarn("ignoring dummy material layer (all entries -1)"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: std::swap(temp_materials, materials); nuclear@0: } nuclear@0: else if (type == "LayerElementNormal") { nuclear@0: if (normals.size() > 0) { nuclear@0: FBXImporter::LogError("ignoring additional normal layer"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: ReadVertexDataNormals(normals,source, nuclear@0: MappingInformationType, nuclear@0: ReferenceInformationType nuclear@0: ); nuclear@0: } nuclear@0: else if (type == "LayerElementTangent") { nuclear@0: if (tangents.size() > 0) { nuclear@0: FBXImporter::LogError("ignoring additional tangent layer"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: ReadVertexDataTangents(tangents,source, nuclear@0: MappingInformationType, nuclear@0: ReferenceInformationType nuclear@0: ); nuclear@0: } nuclear@0: else if (type == "LayerElementBinormal") { nuclear@0: if (binormals.size() > 0) { nuclear@0: FBXImporter::LogError("ignoring additional binormal layer"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: ReadVertexDataBinormals(binormals,source, nuclear@0: MappingInformationType, nuclear@0: ReferenceInformationType nuclear@0: ); nuclear@0: } nuclear@0: else if (type == "LayerElementColor") { nuclear@0: if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) { nuclear@0: FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ") nuclear@0: << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" ); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: ReadVertexDataColors(colors[index],source, nuclear@0: MappingInformationType, nuclear@0: ReferenceInformationType nuclear@0: ); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: // Lengthy utility function to read and resolve a FBX vertex data array - that is, the nuclear@0: // output is in polygon vertex order. This logic is used for reading normals, UVs, colors, nuclear@0: // tangents .. nuclear@0: template nuclear@0: void ResolveVertexDataArray(std::vector& data_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType, nuclear@0: const char* dataElementName, nuclear@0: const char* indexDataElementName, nuclear@0: size_t vertex_count, nuclear@0: const std::vector& mapping_counts, nuclear@0: const std::vector& mapping_offsets, nuclear@0: const std::vector& mappings) nuclear@0: { nuclear@0: std::vector tempUV; nuclear@0: ParseVectorDataArray(tempUV,GetRequiredElement(source,dataElementName)); nuclear@0: nuclear@0: // handle permutations of Mapping and Reference type - it would be nice to nuclear@0: // deal with this more elegantly and with less redundancy, but right nuclear@0: // now it seems unavoidable. nuclear@0: if (MappingInformationType == "ByVertice" && ReferenceInformationType == "Direct") { nuclear@0: data_out.resize(vertex_count); nuclear@0: for (size_t i = 0, e = tempUV.size(); i < e; ++i) { nuclear@0: nuclear@0: const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; nuclear@0: for (unsigned int j = istart; j < iend; ++j) { nuclear@0: data_out[mappings[j]] = tempUV[i]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else if (MappingInformationType == "ByVertice" && ReferenceInformationType == "IndexToDirect") { nuclear@0: data_out.resize(vertex_count); nuclear@0: nuclear@0: std::vector uvIndices; nuclear@0: ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); nuclear@0: nuclear@0: for (size_t i = 0, e = uvIndices.size(); i < e; ++i) { nuclear@0: nuclear@0: const unsigned int istart = mapping_offsets[i], iend = istart + mapping_counts[i]; nuclear@0: for (unsigned int j = istart; j < iend; ++j) { nuclear@0: if(static_cast(uvIndices[i]) >= tempUV.size()) { nuclear@0: DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); nuclear@0: } nuclear@0: data_out[mappings[j]] = tempUV[uvIndices[i]]; nuclear@0: } nuclear@0: } nuclear@0: } nuclear@0: else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "Direct") { nuclear@0: if (tempUV.size() != vertex_count) { nuclear@0: FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") nuclear@0: << tempUV.size() << ", expected " << vertex_count nuclear@0: ); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: data_out.swap(tempUV); nuclear@0: } nuclear@0: else if (MappingInformationType == "ByPolygonVertex" && ReferenceInformationType == "IndexToDirect") { nuclear@0: data_out.resize(vertex_count); nuclear@0: nuclear@0: std::vector uvIndices; nuclear@0: ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); nuclear@0: nuclear@0: if (uvIndices.size() != vertex_count) { nuclear@0: FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping"); nuclear@0: return; nuclear@0: } nuclear@0: nuclear@0: unsigned int next = 0; nuclear@0: BOOST_FOREACH(int i, uvIndices) { nuclear@0: if(static_cast(i) >= tempUV.size()) { nuclear@0: DOMError("index out of range",&GetRequiredElement(source,indexDataElementName)); nuclear@0: } nuclear@0: nuclear@0: data_out[next++] = tempUV[i]; nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ") nuclear@0: << MappingInformationType << "," << ReferenceInformationType); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexDataNormals(std::vector& normals_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType) nuclear@0: { nuclear@0: ResolveVertexDataArray(normals_out,source,MappingInformationType,ReferenceInformationType, nuclear@0: "Normals", nuclear@0: "NormalsIndex", nuclear@0: vertices.size(), nuclear@0: mapping_counts, nuclear@0: mapping_offsets, nuclear@0: mappings); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexDataUV(std::vector& uv_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType) nuclear@0: { nuclear@0: ResolveVertexDataArray(uv_out,source,MappingInformationType,ReferenceInformationType, nuclear@0: "UV", nuclear@0: "UVIndex", nuclear@0: vertices.size(), nuclear@0: mapping_counts, nuclear@0: mapping_offsets, nuclear@0: mappings); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexDataColors(std::vector& colors_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType) nuclear@0: { nuclear@0: ResolveVertexDataArray(colors_out,source,MappingInformationType,ReferenceInformationType, nuclear@0: "Colors", nuclear@0: "ColorIndex", nuclear@0: vertices.size(), nuclear@0: mapping_counts, nuclear@0: mapping_offsets, nuclear@0: mappings); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexDataTangents(std::vector& tangents_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType) nuclear@0: { nuclear@0: ResolveVertexDataArray(tangents_out,source,MappingInformationType,ReferenceInformationType, nuclear@0: "Tangent", nuclear@0: "TangentIndex", nuclear@0: vertices.size(), nuclear@0: mapping_counts, nuclear@0: mapping_offsets, nuclear@0: mappings); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexDataBinormals(std::vector& binormals_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType) nuclear@0: { nuclear@0: ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, nuclear@0: "Binormal", nuclear@0: "BinormalIndex", nuclear@0: vertices.size(), nuclear@0: mapping_counts, nuclear@0: mapping_offsets, nuclear@0: mappings); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // ------------------------------------------------------------------------------------------------ nuclear@0: void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, const Scope& source, nuclear@0: const std::string& MappingInformationType, nuclear@0: const std::string& ReferenceInformationType) nuclear@0: { nuclear@0: const size_t face_count = faces.size(); nuclear@0: ai_assert(face_count); nuclear@0: nuclear@0: // materials are handled separately. First of all, they are assigned per-face nuclear@0: // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect nuclear@0: // has a slightly different meaning for materials. nuclear@0: ParseVectorDataArray(materials_out,GetRequiredElement(source,"Materials")); nuclear@0: nuclear@0: if (MappingInformationType == "AllSame") { nuclear@0: // easy - same material for all faces nuclear@0: if (materials_out.empty()) { nuclear@0: FBXImporter::LogError(Formatter::format("expected material index, ignoring")); nuclear@0: return; nuclear@0: } nuclear@0: else if (materials_out.size() > 1) { nuclear@0: FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one")); nuclear@0: materials_out.clear(); nuclear@0: } nuclear@0: nuclear@0: materials.assign(vertices.size(),materials_out[0]); nuclear@0: } nuclear@0: else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") { nuclear@0: materials.resize(face_count); nuclear@0: nuclear@0: if(materials_out.size() != face_count) { nuclear@0: FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") nuclear@0: << materials_out.size() << ", expected " << face_count nuclear@0: ); nuclear@0: return; nuclear@0: } nuclear@0: } nuclear@0: else { nuclear@0: FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ") nuclear@0: << MappingInformationType << "," << ReferenceInformationType); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: } // !FBX nuclear@0: } // !Assimp nuclear@0: nuclear@0: #endif nuclear@0: