diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index 47ff361b3..0ec8b872a 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -111,20 +111,9 @@ Discreet3DSImporter::~Discreet3DSImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - std::string extension = GetExtension(pFile); - if (extension == "3ds" || extension == "prj") { - return true; - } - - if (!extension.length() || checkSig) { - uint16_t token[3]; - token[0] = 0x4d4d; - token[1] = 0x3dc2; - //token[2] = 0x3daa; - return CheckMagicToken(pIOHandler, pFile, token, 2, 0, 2); - } - return false; +bool Discreet3DSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint16_t token[] = { 0x4d4d, 0x3dc2 /*, 0x3daa */ }; + return CheckMagicToken(pIOHandler, pFile, token, AI_COUNT_OF(token), 0, sizeof token[0]); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index fc02df7bd..5b0f34c3a 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -90,24 +90,12 @@ D3MFImporter::~D3MFImporter() { // empty } -bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension(GetExtension(filename)); - if (extension == desc.mFileExtensions) { - return true; - } - - if (!extension.length() || checkSig) { - if (nullptr == pIOHandler) { - return false; - } - if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) { - return false; - } - D3MFOpcPackage opcPackage(pIOHandler, filename); - return opcPackage.validate(); +bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bool /*checkSig*/) const { + if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) { + return false; } - - return false; + D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename); + return opcPackage.validate(); } void D3MFImporter::SetupProperties(const Importer*) { diff --git a/code/AssetLib/AC/ACLoader.cpp b/code/AssetLib/AC/ACLoader.cpp index fe9f34f4e..e93624b3e 100644 --- a/code/AssetLib/AC/ACLoader.cpp +++ b/code/AssetLib/AC/ACLoader.cpp @@ -152,18 +152,9 @@ AC3DImporter::~AC3DImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - std::string extension = GetExtension(pFile); - - // fixme: are acc and ac3d *really* used? Some sources say they are - if (extension == "ac" || extension == "ac3d" || extension == "acc") { - return true; - } - if (!extension.length() || checkSig) { - uint32_t token = AI_MAKE_MAGIC("AC3D"); - return CheckMagicToken(pIOHandler, pFile, &token, 1, 0); - } - return false; +bool AC3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { AI_MAKE_MAGIC("AC3D") }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index b44391e1f..4103dcdf4 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -503,19 +503,9 @@ void AMFImporter::ParseNode_Metadata(XmlNode &node) { mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. } -bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "amf") { - return true; - } - - if (extension.empty() || pCheckSig) { - static const char * const tokens[] = { "= iMaterialCount) { - LogWarning("Out of range: material index is too large"); + LogError("Out of range: material index is too large"); iIndex = iMaterialCount - 1; + return; } // get a reference to the material diff --git a/code/AssetLib/B3D/B3DImporter.cpp b/code/AssetLib/B3D/B3DImporter.cpp index 0bc3b7d0b..c4ef75be3 100644 --- a/code/AssetLib/B3D/B3DImporter.cpp +++ b/code/AssetLib/B3D/B3DImporter.cpp @@ -81,7 +81,7 @@ static const aiImporterDesc desc = { //#define DEBUG_B3D -template +template void DeleteAllBarePointers(std::vector &x) { for (auto p : x) { delete p; @@ -89,11 +89,11 @@ void DeleteAllBarePointers(std::vector &x) { } B3DImporter::~B3DImporter() { + // empty } // ------------------------------------------------------------------------------------------------ bool B3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { - size_t pos = pFile.find_last_of('.'); if (pos == string::npos) { return false; diff --git a/code/AssetLib/BVH/BVHLoader.cpp b/code/AssetLib/BVH/BVHLoader.cpp index e09e54184..174836847 100644 --- a/code/AssetLib/BVH/BVHLoader.cpp +++ b/code/AssetLib/BVH/BVHLoader.cpp @@ -92,18 +92,9 @@ BVHLoader::~BVHLoader() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const { - // check file extension - const std::string extension = GetExtension(pFile); - - if (extension == "bvh") - return true; - - if ((!extension.length() || cs) && pIOHandler) { - static const char * const tokens[] = { "HIERARCHY" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - return false; +bool BVHLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "HIERARCHY" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index e99fbec0f..3d9e8017b 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -114,22 +114,14 @@ BlenderImporter::~BlenderImporter() { } static const char * const Tokens[] = { "BLENDER" }; -static const char * const TokensForSearch[] = { "blender" }; // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string &extension = GetExtension(pFile); - if (extension == "blend") { - return true; - } +bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + // note: this won't catch compressed files + static const char *tokens[] = { "(tUnitSizeString.data(), mUnitSize); + } catch (const DeadlyImportError& die) { + std::string warning("Collada: Failed to parse meter parameter to real number. Exception:\n"); + warning.append(die.what()); + ASSIMP_LOG_WARN(warning.data()); + } + } } else if (currentName == "up_axis") { std::string v; if (!XmlParser::getValueAsString(currentNode, v)) { @@ -2242,20 +2251,26 @@ void ColladaParser::ReadNodeGeometry(XmlNode &node, Node *pNode) { if (currentName == "bind_material") { XmlNode techNode = currentNode.child("technique_common"); if (techNode) { - XmlNode instanceMatNode = techNode.child("instance_material"); - // read ID of the geometry subgroup and the target material - std::string group; - XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group); - XmlParser::getStdStrAttribute(instanceMatNode, "target", url); - const char *urlMat = url.c_str(); - Collada::SemanticMappingTable s; - if (urlMat[0] == '#') - urlMat++; + for (XmlNode instanceMatNode = techNode.child("instance_material"); instanceMatNode; instanceMatNode = instanceMatNode.next_sibling()) + { + const std::string &instance_name = instanceMatNode.name(); + if (instance_name == "instance_material") + { + // read ID of the geometry subgroup and the target material + std::string group; + XmlParser::getStdStrAttribute(instanceMatNode, "symbol", group); + XmlParser::getStdStrAttribute(instanceMatNode, "target", url); + const char *urlMat = url.c_str(); + Collada::SemanticMappingTable s; + if (urlMat[0] == '#') + urlMat++; - s.mMatName = urlMat; - // store the association - instance.mMaterials[group] = s; - ReadMaterialVertexInputBinding(instanceMatNode, s); + s.mMatName = urlMat; + // store the association + instance.mMaterials[group] = s; + ReadMaterialVertexInputBinding(instanceMatNode, s); + } + } } } } diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index c18ad8293..6b2dbbe82 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -123,18 +123,9 @@ DXFImporter::~DXFImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool checkSig ) const { - const std::string& extension = GetExtension( filename ); - if ( extension == desc.mFileExtensions ) { - return true; - } - - if ( extension.empty() || checkSig ) { - static const char * const pTokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" }; - return SearchFileHeaderForToken(pIOHandler, filename, pTokens, 4, 32 ); - } - - return false; +bool DXFImporter::CanRead( const std::string& filename, IOSystem* pIOHandler, bool /*checkSig*/ ) const { + static const char *tokens[] = { "SECTION", "HEADER", "ENDSEC", "BLOCKS" }; + return SearchFileHeaderForToken(pIOHandler, filename, tokens, AI_COUNT_OF(tokens), 32); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/FBX/FBXImporter.cpp b/code/AssetLib/FBX/FBXImporter.cpp index 30b8b3215..0f63acc8f 100644 --- a/code/AssetLib/FBX/FBXImporter.cpp +++ b/code/AssetLib/FBX/FBXImporter.cpp @@ -100,18 +100,10 @@ FBXImporter::~FBXImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string &extension = GetExtension(pFile); - if (extension == std::string(desc.mFileExtensions)) { - return true; - } - - else if ((!extension.length() || checkSig) && pIOHandler) { - // at least ASCII-FBX files usually have a 'FBX' somewhere in their head - static const char * const tokens[] = { "fbx" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - return false; +bool FBXImporter::CanRead(const std::string & pFile, IOSystem * pIOHandler, bool /*checkSig*/) const { + // at least ASCII-FBX files usually have a 'FBX' somewhere in their head + static const char *tokens[] = { "fbx" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index cd487914a..93d69dbcd 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -84,20 +84,13 @@ HMPImporter::~HMPImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool HMPImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool cs) const { - const std::string extension = GetExtension(pFile); - if (extension == "hmp") - return true; - - // if check for extension is not enough, check for the magic tokens - if (!extension.length() || cs) { - uint32_t tokens[3]; - tokens[0] = AI_HMP_MAGIC_NUMBER_LE_4; - tokens[1] = AI_HMP_MAGIC_NUMBER_LE_5; - tokens[2] = AI_HMP_MAGIC_NUMBER_LE_7; - return CheckMagicToken(pIOHandler, pFile, tokens, 3, 0); - } - return false; +bool HMPImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { + AI_HMP_MAGIC_NUMBER_LE_4, + AI_HMP_MAGIC_NUMBER_LE_5, + AI_HMP_MAGIC_NUMBER_LE_7 + }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/IFC/IFCBoolean.cpp b/code/AssetLib/IFC/IFCBoolean.cpp index dad45ec13..ee255e612 100644 --- a/code/AssetLib/IFC/IFCBoolean.cpp +++ b/code/AssetLib/IFC/IFCBoolean.cpp @@ -699,7 +699,7 @@ void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedArea continue; } - GenerateOpenings(openings, std::vector(1, IfcVector3(1, 0, 0)), temp, false, true); + GenerateOpenings(openings, temp, false, true); result.Append(temp); vit += pcount; diff --git a/code/AssetLib/IFC/IFCGeometry.cpp b/code/AssetLib/IFC/IFCGeometry.cpp index 64f32ca7b..4c088955f 100644 --- a/code/AssetLib/IFC/IFCGeometry.cpp +++ b/code/AssetLib/IFC/IFCGeometry.cpp @@ -190,7 +190,7 @@ void ProcessPolygonBoundaries(TempMesh& result, const TempMesh& inmesh, size_t m std::copy(outer_vit, outer_vit+outer_polygon_size, std::back_inserter(temp.mVerts)); - GenerateOpenings(fake_openings, normals, temp, false, false); + GenerateOpenings(fake_openings, temp, false, false); result.Append(temp); } @@ -529,6 +529,31 @@ IfcMatrix3 DerivePlaneCoordinateSpace(const TempMesh& curmesh, bool& ok, IfcVect return m; } +const auto closeDistance = 1e-6; + +bool areClose(Schema_2x3::IfcCartesianPoint pt1,Schema_2x3::IfcCartesianPoint pt2) { + if(pt1.Coordinates.size() != pt2.Coordinates.size()) + { + IFCImporter::LogWarn("unable to compare differently-dimensioned points"); + return false; + } + auto coord1 = pt1.Coordinates.begin(); + auto coord2 = pt2.Coordinates.begin(); + // we're just testing each dimension separately rather than doing euclidean distance, as we're + // looking for very close coordinates + for(; coord1 != pt1.Coordinates.end(); coord1++,coord2++) + { + if(std::fabs(*coord1 - *coord2) > closeDistance) + return false; + } + return true; +} + +bool areClose(IfcVector3 pt1,IfcVector3 pt2) { + return (std::fabs(pt1.x - pt2.x) < closeDistance && + std::fabs(pt1.y - pt2.y) < closeDistance && + std::fabs(pt1.z - pt2.z) < closeDistance); +} // Extrudes the given polygon along the direction, converts it into an opening or applies all openings as necessary. void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const TempMesh& curve, const IfcVector3& extrusionDir, TempMesh& result, ConversionData &conv, bool collect_openings) @@ -592,7 +617,21 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te nors.push_back(IfcVector3()); continue; } - nors.push_back(((bounds.mVerts[2] - bounds.mVerts[0]) ^ (bounds.mVerts[1] - bounds.mVerts[0])).Normalize()); + auto nor = ((bounds.mVerts[2] - bounds.mVerts[0]) ^ (bounds.mVerts[1] - bounds.mVerts[0])).Normalize(); + auto vI0 = bounds.mVertcnt[0]; + for(size_t faceI = 0; faceI < bounds.mVertcnt.size(); faceI++) + { + if(bounds.mVertcnt[faceI] >= 3) { + // do a check that this is at least parallel to the base plane + auto nor2 = ((bounds.mVerts[vI0 + 2] - bounds.mVerts[vI0]) ^ (bounds.mVerts[vI0 + 1] - bounds.mVerts[vI0])).Normalize(); + if(!areClose(nor,nor2)) { + std::stringstream msg; + msg << "Face " << faceI << " is not parallel with face 0 - opening on entity " << solid.GetID(); + IFCImporter::LogWarn(msg.str().c_str()); + } + } + } + nors.push_back(nor); } } @@ -613,7 +652,7 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te out.push_back(in[i] + dir); if( openings ) { - if( (in[i] - in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings, nors, temp, true, true, dir) ) { + if( (in[i] - in[next]).Length() > diag * 0.1 && GenerateOpenings(*conv.apply_openings, temp, true, true, dir) ) { ++sides_with_openings; } @@ -622,31 +661,33 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te } } - if( openings ) { + if(openings) { for(TempOpening& opening : *conv.apply_openings) { - if( !opening.wallPoints.empty() ) { - IFCImporter::LogError("failed to generate all window caps"); + if(!opening.wallPoints.empty()) { + std::stringstream msg; + msg << "failed to generate all window caps on ID " << (int)solid.GetID(); + IFCImporter::LogError(msg.str().c_str()); } opening.wallPoints.clear(); } } size_t sides_with_v_openings = 0; - if( has_area ) { + if(has_area) { - for( size_t n = 0; n < 2; ++n ) { - if( n > 0 ) { - for( size_t i = 0; i < in.size(); ++i ) + for(size_t n = 0; n < 2; ++n) { + if(n > 0) { + for(size_t i = 0; i < in.size(); ++i) out.push_back(in[i] + dir); } else { - for( size_t i = in.size(); i--; ) + for(size_t i = in.size(); i--; ) out.push_back(in[i]); } curmesh.mVertcnt.push_back(static_cast(in.size())); - if( openings && in.size() > 2 ) { - if( GenerateOpenings(*conv.apply_openings, nors, temp, true, true, dir) ) { + if(openings && in.size() > 2) { + if(GenerateOpenings(*conv.apply_openings,temp,true,true,dir)) { ++sides_with_v_openings; } @@ -656,8 +697,10 @@ void ProcessExtrudedArea(const Schema_2x3::IfcExtrudedAreaSolid& solid, const Te } } - if( openings && (sides_with_openings == 1 || sides_with_v_openings == 2 ) ) { - IFCImporter::LogWarn("failed to resolve all openings, presumably their topology is not supported by Assimp"); + if (openings && (sides_with_openings == 1 || sides_with_v_openings == 2)) { + std::stringstream msg; + msg << "failed to resolve all openings, presumably their topology is not supported by Assimp - ID " << solid.GetID() << " sides_with_openings " << sides_with_openings << " sides_with_v_openings " << sides_with_v_openings; + IFCImporter::LogWarn(msg.str().c_str()); } IFCImporter::LogVerboseDebug("generate mesh procedurally by extrusion (IfcExtrudedAreaSolid)"); @@ -781,7 +824,9 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned return false; } else { - IFCImporter::LogWarn("skipping unknown IfcGeometricRepresentationItem entity, type is ", geo.GetClassName()); + std::stringstream toLog; + toLog << "skipping unknown IfcGeometricRepresentationItem entity, type is " << geo.GetClassName() << " id is " << geo.GetID(); + IFCImporter::LogWarn(toLog.str().c_str()); return false; } diff --git a/code/AssetLib/IFC/IFCLoader.cpp b/code/AssetLib/IFC/IFCLoader.cpp index 1d2c13ec5..0c20686f4 100644 --- a/code/AssetLib/IFC/IFCLoader.cpp +++ b/code/AssetLib/IFC/IFCLoader.cpp @@ -129,18 +129,12 @@ IFCImporter::~IFCImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool IFCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string &extension = GetExtension(pFile); - if (extension == "ifc" || extension == "ifczip") { - return true; - } else if ((!extension.length() || checkSig) && pIOHandler) { - // note: this is the common identification for STEP-encoded files, so - // it is only unambiguous as long as we don't support any further - // file formats with STEP as their encoding. - static const char * const tokens[] = { "ISO-10303-21" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - return false; +bool IFCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + // note: this is the common identification for STEP-encoded files, so + // it is only unambiguous as long as we don't support any further + // file formats with STEP as their encoding. + static const char *tokens[] = { "ISO-10303-21" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/IFC/IFCOpenings.cpp b/code/AssetLib/IFC/IFCOpenings.cpp index cffb8a014..c261a24b0 100644 --- a/code/AssetLib/IFC/IFCOpenings.cpp +++ b/code/AssetLib/IFC/IFCOpenings.cpp @@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #include +#include +#include namespace Assimp { namespace IFC { @@ -73,7 +75,7 @@ namespace Assimp { // fallback method to generate wall openings - bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std::vector& nors, + bool TryAddOpenings_Poly2Tri(const std::vector& openings, TempMesh& curmesh); @@ -1140,7 +1142,6 @@ IfcMatrix4 ProjectOntoPlane(std::vector& out_contour, const TempMesh // ------------------------------------------------------------------------------------------------ bool GenerateOpenings(std::vector& openings, - const std::vector& nors, TempMesh& curmesh, bool check_intersection, bool generate_connection_geometry, @@ -1340,7 +1341,7 @@ bool GenerateOpenings(std::vector& openings, MergeWindowContours(temp_contour, other, poly); if (poly.size() > 1) { - return TryAddOpenings_Poly2Tri(openings, nors, curmesh); + return TryAddOpenings_Poly2Tri(openings, curmesh); } else if (poly.size() == 0) { IFCImporter::LogWarn("ignoring duplicate opening"); @@ -1427,8 +1428,289 @@ bool GenerateOpenings(std::vector& openings, return true; } +std::vector GetContourInPlane2D(std::shared_ptr mesh,IfcMatrix3 planeSpace, + IfcVector3 planeNor,IfcFloat planeOffset, + IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first,bool& ok) { + std::vector contour; + + const auto outernor = ((mesh->mVerts[2] - mesh->mVerts[0]) ^ (mesh->mVerts[1] - mesh->mVerts[0])).Normalize(); + const IfcFloat dot = planeNor * outernor; + if(std::fabs(dot) < 1.f - 1e-6f) { + std::stringstream msg; + msg << "Skipping: Unaligned opening (" << planeNor.x << ", " << planeNor.y << ", " << planeNor.z << ")"; + msg << " . ( " << outernor.x << ", " << outernor.y << ", " << outernor.z << ") = " << dot; + IFCImporter::LogDebug(msg.str().c_str()); + ok = false; + return contour; + } + + const std::vector& va = mesh->mVerts; + if(va.size() <= 2) { + std::stringstream msg; + msg << "Skipping: Only " << va.size() << " verticies in opening mesh."; + IFCImporter::LogDebug(msg.str().c_str()); + ok = false; + return contour; + } + + for(const IfcVector3& xx : mesh->mVerts) { + IfcVector3 vv = planeSpace * xx,vv_extr = planeSpace * (xx + extrusionDir); + + const bool is_extruded_side = std::fabs(vv.z - planeOffset) > std::fabs(vv_extr.z - planeOffset); + if(first) { + first = false; + if(dot > 0.f) { + wall_extrusion = extrusionDir; + if(is_extruded_side) { + wall_extrusion = -wall_extrusion; + } + } + } + + // XXX should not be necessary - but it is. Why? For precision reasons? + vv = is_extruded_side ? vv_extr : vv; + contour.push_back(IfcVector2(vv.x,vv.y)); + } + ok = true; + + return contour; +} + +const float close { 1e-6f }; + +static bool isClose(IfcVector2 first,IfcVector2 second) { + auto diff = (second - first); + return (std::fabs(diff.x) < close && std::fabs(diff.y) < close); +} + +static void logSegment(std::pair segment) { + std::stringstream msg2; + msg2 << " Segment: \n"; + msg2 << " " << segment.first.x << " " << segment.first.y << " \n"; + msg2 << " " << segment.second.x << " " << segment.second.y << " \n"; + IFCImporter::LogInfo(msg2.str().c_str()); +} + +std::vector> GetContoursInPlane3D(std::shared_ptr mesh,IfcMatrix3 planeSpace, + IfcFloat planeOffset) { + + { + std::stringstream msg; + msg << "GetContoursInPlane3D: planeSpace is \n"; + msg << planeSpace.a1 << " " << planeSpace.a2 << " " << planeSpace.a3 << " " << "\n"; + msg << planeSpace.b1 << " " << planeSpace.b2 << " " << planeSpace.b3 << " " << "\n"; + msg << planeSpace.c1 << " " << planeSpace.c2 << " " << planeSpace.c3 << " " << "\n"; + msg << "\n planeOffset is " << planeOffset; + IFCImporter::LogInfo(msg.str().c_str()); + } + + // we'll put our line segments in here, and then merge them together into contours later + std::deque> lineSegments; + + // find the lines giving the intersection of the faces with the plane - we'll work in planeSpace throughout. + size_t vI0{ 0 }; // vertex index for first vertex in plane + for(auto nVertices : mesh->mVertcnt) { // iterate over faces + { + std::stringstream msg; + msg << "GetContoursInPlane3D: face (transformed) is \n"; + for(auto vI = vI0; vI < vI0 + nVertices; vI++) { + auto v = planeSpace * mesh->mVerts[vI]; + msg << " " << v.x << " " << v.y << " " << v.z << " " << "\n"; + } + IFCImporter::LogInfo(msg.str().c_str()); + } + + if(nVertices <= 2) // not a plane, a point or line + { + std::stringstream msg; + msg << "GetContoursInPlane3D: found point or line when expecting plane (only " << nVertices << " vertices)"; + IFCImporter::LogWarn(msg.str().c_str()); + vI0 += nVertices; + continue; + } + + auto v0 = planeSpace * mesh->mVerts[vI0]; + + // now calculate intersections between face and plane + IfcVector2 firstPoint; + bool gotFirstPoint(false); + + if(std::fabs(v0.z - planeOffset) < close) { + // first point is on the plane + firstPoint.x = v0.x; + firstPoint.y = v0.y; + gotFirstPoint = true; + } + + auto vn = v0; + for(auto vI = vI0 + 1; vI < vI0 + nVertices; vI++) { + auto vp = vn; + vn = planeSpace * mesh->mVerts[vI]; + IfcVector3 intersection; + + if(std::fabs(vn.z - planeOffset) < close) { + // on the plane + intersection = vn; + } + else if((vn.z > planeOffset) != (vp.z > planeOffset)) + { + // passes through the plane + auto vdir = vn - vp; + auto scale = (planeOffset - vp.z) / vdir.z; + intersection = vp + scale * vdir; + } + else { + // nowhere near - move on + continue; + } + + if(!gotFirstPoint) { + if(std::fabs(vp.z - planeOffset) < close) { + // just had a second line along the plane + firstPoint.x = vp.x; + firstPoint.y = vp.y; + IfcVector2 secondPoint(intersection.x,intersection.y); + auto s = std::pair(firstPoint,secondPoint); + logSegment(s); + lineSegments.push_back(s); + // next firstpoint should be this one + } + else { + // store the first intersection point + firstPoint.x = intersection.x; + firstPoint.y = intersection.y; + gotFirstPoint = true; + } + } + else { + // now got the second point, so store the pair + IfcVector2 secondPoint(intersection.x,intersection.y); + auto s = std::pair(firstPoint,secondPoint); + logSegment(s); + lineSegments.push_back(s); + + // - note that we don't move onto the next face as a non-convex face can create two or more intersections with a plane + gotFirstPoint = false; + } + } + if(gotFirstPoint) { + IFCImporter::LogWarn("GetContoursInPlane3D: odd number of intersections with plane"); + } + vI0 += nVertices; + } + + { + std::stringstream msg; + msg << "GetContoursInPlane3D: found " << lineSegments.size() << " line segments:\n"; + IFCImporter::LogInfo(msg.str().c_str()); + + for(auto& s : lineSegments) { + logSegment(s); + } + + } + + // now merge contours until we have the best-looking polygons we can + std::vector contours; + while(!lineSegments.empty()) { + // start with a polygon and make the best closed contour we can + const auto& firstSeg = lineSegments.front(); + std::deque contour{ firstSeg.first, firstSeg.second }; + lineSegments.pop_front(); + bool foundNextPoint{ true }; + bool closedContour{ false }; + while(foundNextPoint) { + foundNextPoint = false; + for(auto nextSeg = lineSegments.begin(); nextSeg != lineSegments.end(); nextSeg++) { + // see if we can match up both ends - in which case we've closed the contour + if((isClose(contour.front(),nextSeg->first) && isClose(contour.back(),nextSeg->second)) || + (isClose(contour.back(),nextSeg->first) && isClose(contour.front(),nextSeg->second)) + ) { + lineSegments.erase(nextSeg); + closedContour = true; + break; + } + + // otherwise, see if we can match up either end + foundNextPoint = true; + if(isClose(contour.front(),nextSeg->first)) { + contour.push_front(nextSeg->second); + } + else if(isClose(contour.front(),nextSeg->second)) { + contour.push_front(nextSeg->first); + } + else if(isClose(contour.back(),nextSeg->first)) { + contour.push_back(nextSeg->second); + } + else if(isClose(contour.back(),nextSeg->second)) { + contour.push_back(nextSeg->first); + } + else { + foundNextPoint = false; + } + if(foundNextPoint) { + lineSegments.erase(nextSeg); + break; + } + } + } + + if(!closedContour) { + IFCImporter::LogWarn("GetContoursInPlane3D: did not close contour"); + } + + // now add the contour if we can + if(contour.size() <= 2) { + IFCImporter::LogWarn("GetContoursInPlane3D: discarding line/point contour"); + continue; + } + Contour c{}; + for(auto p : contour) + { + c.push_back(p); + } + contours.push_back(c); + } + + { + std::stringstream msg; + msg << "GetContoursInPlane3D: found " << contours.size() << " contours:\n"; + + for(auto c : contours) { + msg << " Contour: \n"; + for(auto p : c) { + msg << " " << p.x << " " << p.y << " \n"; + } + } + + IFCImporter::LogInfo(msg.str().c_str()); + } + + + return contours; +} + +std::vector> GetContoursInPlane(std::shared_ptr mesh,IfcMatrix3 planeSpace, + IfcVector3 planeNor,IfcFloat planeOffset, + IfcVector3 extrusionDir,IfcVector3& wall_extrusion,bool& first) { + + if(mesh->mVertcnt.size() == 1) + { + bool ok; + auto contour = GetContourInPlane2D(mesh,planeSpace,planeNor,planeOffset,extrusionDir,wall_extrusion,first,ok); + if(ok) + return std::vector> {contour}; + else + return std::vector> {}; + } + else + { + return GetContoursInPlane3D(mesh,planeSpace,planeOffset); + } +} + // ------------------------------------------------------------------------------------------------ -bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std::vector& nors, +bool TryAddOpenings_Poly2Tri(const std::vector& openings, TempMesh& curmesh) { IFCImporter::LogWarn("forced to use poly2tri fallback method to generate wall openings"); @@ -1498,61 +1780,41 @@ bool TryAddOpenings_Poly2Tri(const std::vector& openings,const std: try { ClipperLib::Clipper clipper_holes; - size_t c = 0; - for(const TempOpening& t :openings) { - const IfcVector3& outernor = nors[c++]; - const IfcFloat dot = nor * outernor; - if (std::fabs(dot)<1.f-1e-6f) { - continue; - } + for(const TempOpening& t : openings) { + auto contours = GetContoursInPlane(t.profileMesh,m,nor,coord,t.extrusionDir,wall_extrusion,first); - const std::vector& va = t.profileMesh->mVerts; - if(va.size() <= 2) { - continue; - } + for(auto& contour : contours) { + // scale to clipping space + ClipperLib::Polygon hole; + for(IfcVector2& pip : contour) { + pip.x = (pip.x - vmin.x) / vmax.x; + pip.y = (pip.y - vmin.y) / vmax.y; - std::vector contour; - - for(const IfcVector3& xx : t.profileMesh->mVerts) { - IfcVector3 vv = m * xx, vv_extr = m * (xx + t.extrusionDir); - - const bool is_extruded_side = std::fabs(vv.z - coord) > std::fabs(vv_extr.z - coord); - if (first) { - first = false; - if (dot > 0.f) { - wall_extrusion = t.extrusionDir; - if (is_extruded_side) { - wall_extrusion = - wall_extrusion; - } - } + hole.push_back(ClipperLib::IntPoint(to_int64(pip.x),to_int64(pip.y))); } - // XXX should not be necessary - but it is. Why? For precision reasons? - vv = is_extruded_side ? vv_extr : vv; - contour.push_back(IfcVector2(vv.x,vv.y)); + if(!ClipperLib::Orientation(hole)) { + std::reverse(hole.begin(),hole.end()); + // assert(ClipperLib::Orientation(hole)); + } + + /*ClipperLib::Polygons pol_temp(1), pol_temp2(1); + pol_temp[0] = hole; + + ClipperLib::OffsetPolygons(pol_temp,pol_temp2,5.0); + hole = pol_temp2[0];*/ + + clipper_holes.AddPolygon(hole,ClipperLib::ptSubject); + { + std::stringstream msg; + msg << "- added polygon "; + for(auto elem : hole) { + msg << " (" << elem.X << ", " << elem.Y << ")"; + } + IFCImporter::LogDebug(msg.str().c_str()); + } } - - ClipperLib::Polygon hole; - for(IfcVector2& pip : contour) { - pip.x = (pip.x - vmin.x) / vmax.x; - pip.y = (pip.y - vmin.y) / vmax.y; - - hole.push_back(ClipperLib::IntPoint( to_int64(pip.x), to_int64(pip.y) )); - } - - if (!ClipperLib::Orientation(hole)) { - std::reverse(hole.begin(), hole.end()); - // assert(ClipperLib::Orientation(hole)); - } - - /*ClipperLib::Polygons pol_temp(1), pol_temp2(1); - pol_temp[0] = hole; - - ClipperLib::OffsetPolygons(pol_temp,pol_temp2,5.0); - hole = pol_temp2[0];*/ - - clipper_holes.AddPolygon(hole,ClipperLib::ptSubject); } clipper_holes.Execute(ClipperLib::ctUnion,holes_union, diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index e80e5623d..b5f72a56d 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -307,7 +307,6 @@ void ProcessBooleanExtrudedAreaSolidDifference(const Schema_2x3::IfcExtrudedArea // IFCOpenings.cpp bool GenerateOpenings(std::vector& openings, - const std::vector& nors, TempMesh& curmesh, bool check_intersection, bool generate_connection_geometry, diff --git a/code/AssetLib/IQM/IQMImporter.cpp b/code/AssetLib/IQM/IQMImporter.cpp new file mode 100644 index 000000000..2bea66c6b --- /dev/null +++ b/code/AssetLib/IQM/IQMImporter.cpp @@ -0,0 +1,322 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "IQMImporter.h" +#include "iqm.h" + +// RESOURCES: +// http://sauerbraten.org/iqm/ +// https://github.com/lsalzman/iqm + + +inline void swap_block( uint32_t *block, size_t size ){ + (void)block; // suppress 'unreferenced formal parameter' MSVC warning + size >>= 2; + for ( size_t i = 0; i < size; ++i ) + AI_SWAP4( block[ i ] ); +} + +static const aiImporterDesc desc = { + "Inter-Quake Model Importer", + "", + "", + "", + aiImporterFlags_SupportBinaryFlavour, + 0, + 0, + 0, + 0, + "iqm" +}; + +namespace Assimp { + +// ------------------------------------------------------------------------------------------------ +// Default constructor +IQMImporter::IQMImporter() : + mScene(nullptr) { + // empty +} + +// ------------------------------------------------------------------------------------------------ +// Returns true, if file is a binary Inter-Quake Model file. +bool IQMImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { + const std::string extension = GetExtension(pFile); + + if (extension == "iqm") + return true; + else if (!extension.length() || checkSig) { + if (!pIOHandler) { + return true; + } + /* + * don't use CheckMagicToken because that checks with swapped bytes too, leading to false + * positives. This magic is not uint32_t, but char[4], so memcmp is the best way + + const char* tokens[] = {"3DMO", "3dmo"}; + return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4); + */ + std::unique_ptr pStream(pIOHandler->Open(pFile, "rb")); + unsigned char data[15]; + if (!pStream || 15 != pStream->Read(data, 1, 15)) { + return false; + } + return !memcmp(data, "INTERQUAKEMODEL", 15); + } + return false; +} + +// ------------------------------------------------------------------------------------------------ +const aiImporterDesc *IQMImporter::GetInfo() const { + return &desc; +} + +// ------------------------------------------------------------------------------------------------ +// Model 3D import implementation +void IQMImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSystem *pIOHandler) { + // Read file into memory + std::unique_ptr pStream(pIOHandler->Open(file, "rb")); + if (!pStream.get()) { + throw DeadlyImportError("Failed to open file ", file, "."); + } + + // Get the file-size and validate it, throwing an exception when fails + const size_t fileSize = pStream->FileSize(); + if (fileSize < sizeof( iqmheader )) { + throw DeadlyImportError("IQM-file ", file, " is too small."); + } + std::vector buffer(fileSize); + unsigned char *data = buffer.data(); + if (fileSize != pStream->Read(data, 1, fileSize)) { + throw DeadlyImportError("Failed to read the file ", file, "."); + } + + // get header + iqmheader &hdr = reinterpret_cast( *data ); + swap_block( &hdr.version, sizeof( iqmheader ) - sizeof( iqmheader::magic ) ); + + // extra check for header + if (memcmp(data, IQM_MAGIC, sizeof( IQM_MAGIC ) ) + || hdr.version != IQM_VERSION + || hdr.filesize != fileSize) { + throw DeadlyImportError("Bad binary header in file ", file, "."); + } + + ASSIMP_LOG_DEBUG("IQM: loading ", file); + + // create the root node + pScene->mRootNode = new aiNode( "" ); + // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system + pScene->mRootNode->mTransformation = aiMatrix4x4( + 1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 0.f, -1.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + pScene->mRootNode->mNumMeshes = hdr.num_meshes; + pScene->mRootNode->mMeshes = new unsigned int[hdr.num_meshes]; + std::iota( pScene->mRootNode->mMeshes, pScene->mRootNode->mMeshes + pScene->mRootNode->mNumMeshes, 0 ); + + mScene = pScene; + + // Allocate output storage + pScene->mNumMeshes = 0; + pScene->mMeshes = new aiMesh *[hdr.num_meshes](); // Set arrays to zero to ensue proper destruction if an exception is raised + + pScene->mNumMaterials = 0; + pScene->mMaterials = new aiMaterial *[hdr.num_meshes](); + + // swap vertex arrays beforehand... + for( auto array = reinterpret_cast( data + hdr.ofs_vertexarrays ), end = array + hdr.num_vertexarrays; array != end; ++array ) + { + swap_block( &array->type, sizeof( iqmvertexarray ) ); + } + + // Read all surfaces from the file + for( auto imesh = reinterpret_cast( data + hdr.ofs_meshes ), end_ = imesh + hdr.num_meshes; imesh != end_; ++imesh ) + { + swap_block( &imesh->name, sizeof( iqmmesh ) ); + // Allocate output mesh & material + auto mesh = pScene->mMeshes[pScene->mNumMeshes++] = new aiMesh(); + mesh->mMaterialIndex = pScene->mNumMaterials; + auto mat = pScene->mMaterials[pScene->mNumMaterials++] = new aiMaterial(); + + { + auto text = reinterpret_cast( data + hdr.ofs_text ); + aiString name( text + imesh->material ); + mat->AddProperty( &name, AI_MATKEY_NAME ); + mat->AddProperty( &name, AI_MATKEY_TEXTURE_DIFFUSE(0) ); + } + + // Fill mesh information + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + mesh->mNumFaces = 0; + mesh->mFaces = new aiFace[imesh->num_triangles]; + + // Fill in all triangles + for( auto tri = reinterpret_cast( data + hdr.ofs_triangles ) + imesh->first_triangle, end = tri + imesh->num_triangles; tri != end; ++tri ) + { + swap_block( tri->vertex, sizeof( tri->vertex ) ); + auto& face = mesh->mFaces[mesh->mNumFaces++]; + face.mNumIndices = 3; + face.mIndices = new unsigned int[3]{ tri->vertex[0] - imesh->first_vertex, + tri->vertex[2] - imesh->first_vertex, + tri->vertex[1] - imesh->first_vertex }; + } + + // Fill in all vertices + for( auto array = reinterpret_cast( data + hdr.ofs_vertexarrays ), end__ = array + hdr.num_vertexarrays; array != end__; ++array ) + { + const unsigned int nVerts = imesh->num_vertexes; + const unsigned int step = array->size; + + switch ( array->type ) + { + case IQM_POSITION: + if( array->format == IQM_FLOAT && step >= 3 ){ + mesh->mNumVertices = nVerts; + auto v = mesh->mVertices = new aiVector3D[nVerts]; + for( auto f = reinterpret_cast( data + array->offset ) + imesh->first_vertex * step, + end = f + nVerts * step; f != end; f += step, ++v ) + { + *v = { AI_BE( f[0] ), + AI_BE( f[1] ), + AI_BE( f[2] ) }; + } + } + break; + case IQM_TEXCOORD: + if( array->format == IQM_FLOAT && step >= 2) + { + auto v = mesh->mTextureCoords[0] = new aiVector3D[nVerts]; + mesh->mNumUVComponents[0] = 2; + for( auto f = reinterpret_cast( data + array->offset ) + imesh->first_vertex * step, + end = f + nVerts * step; f != end; f += step, ++v ) + { + *v = { AI_BE( f[0] ), + 1 - AI_BE( f[1] ), 0 }; + } + } + break; + case IQM_NORMAL: + if (array->format == IQM_FLOAT && step >= 3) + { + auto v = mesh->mNormals = new aiVector3D[nVerts]; + for( auto f = reinterpret_cast( data + array->offset ) + imesh->first_vertex * step, + end = f + nVerts * step; f != end; f += step, ++v ) + { + *v = { AI_BE( f[0] ), + AI_BE( f[1] ), + AI_BE( f[2] ) }; + } + } + break; + case IQM_COLOR: + if (array->format == IQM_UBYTE && step >= 3) + { + auto v = mesh->mColors[0] = new aiColor4D[nVerts]; + for( auto f = ( data + array->offset ) + imesh->first_vertex * step, + end = f + nVerts * step; f != end; f += step, ++v ) + { + *v = { ( f[0] ) / 255.f, + ( f[1] ) / 255.f, + ( f[2] ) / 255.f, + step == 3? 1 : ( f[3] ) / 255.f }; + } + } + else if (array->format == IQM_FLOAT && step >= 3) + { + auto v = mesh->mColors[0] = new aiColor4D[nVerts]; + for( auto f = reinterpret_cast( data + array->offset ) + imesh->first_vertex * step, + end = f + nVerts * step; f != end; f += step, ++v ) + { + *v = { AI_BE( f[0] ), + AI_BE( f[1] ), + AI_BE( f[2] ), + step == 3? 1 : AI_BE( f[3] ) }; + } + } + break; + case IQM_TANGENT: +#if 0 + if (array->format == IQM_FLOAT && step >= 3) + { + auto v = mesh->mTangents = new aiVector3D[nVerts]; + for( auto f = reinterpret_cast( data + array->offset ) + imesh->first_vertex * step, + end = f + nVerts * step; f != end; f += step, ++v ) + { + *v = { AI_BE( f[0] ), + AI_BE( f[1] ), + AI_BE( f[2] ) }; + } + } +#endif + break; + case IQM_BLENDINDEXES: + case IQM_BLENDWEIGHTS: + case IQM_CUSTOM: + break; // these attributes are not relevant. + + default: + break; + } + } + } +} + + +// ------------------------------------------------------------------------------------------------ + +} // Namespace Assimp + +#endif // !! ASSIMP_BUILD_NO_IQM_IMPORTER diff --git a/code/AssetLib/IQM/IQMImporter.h b/code/AssetLib/IQM/IQMImporter.h new file mode 100644 index 000000000..fbeaff8c3 --- /dev/null +++ b/code/AssetLib/IQM/IQMImporter.h @@ -0,0 +1,78 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2021, assimp team + +All rights reserved. + +Redistribution and use of this software in source and binary forms, +with or without modification, are permitted provided that the +following conditions are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the +following disclaimer. + +* Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the +following disclaimer in the documentation and/or other +materials provided with the distribution. + +* Neither the name of the assimp team, nor the names of its +contributors may be used to endorse or promote products +derived from this software without specific prior +written permission of the assimp team. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------------------------------------------------------------------- +*/ + +/** @file IQMImporter.h +* @brief Declares the importer class to read a scene from an Inter-Quake Model file +*/ + +#pragma once + +#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER + +#include +#include + +namespace Assimp { + +class IQMImporter : public BaseImporter { +public: + /// \brief Default constructor + IQMImporter(); + ~IQMImporter() override {} + + /// \brief Returns whether the class can handle the format of the given file. + /// \remark See BaseImporter::CanRead() for details. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + +protected: + //! \brief Appends the supported extension. + const aiImporterDesc *GetInfo() const override; + + //! \brief File import implementation. + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; + +private: + aiScene *mScene = nullptr; // the scene to import to +}; + +} // Namespace Assimp + +#endif // ASSIMP_BUILD_NO_IQM_IMPORTER diff --git a/code/AssetLib/IQM/iqm.h b/code/AssetLib/IQM/iqm.h new file mode 100644 index 000000000..a450504f9 --- /dev/null +++ b/code/AssetLib/IQM/iqm.h @@ -0,0 +1,134 @@ +#ifndef __IQM_H__ +#define __IQM_H__ + +#define IQM_MAGIC "INTERQUAKEMODEL" +#define IQM_VERSION 2 + +struct iqmheader +{ + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; +}; + +struct iqmmesh +{ + unsigned int name; + unsigned int material; + unsigned int first_vertex, num_vertexes; + unsigned int first_triangle, num_triangles; +}; + +enum +{ + IQM_POSITION = 0, + IQM_TEXCOORD = 1, + IQM_NORMAL = 2, + IQM_TANGENT = 3, + IQM_BLENDINDEXES = 4, + IQM_BLENDWEIGHTS = 5, + IQM_COLOR = 6, + IQM_CUSTOM = 0x10 +}; + +enum +{ + IQM_BYTE = 0, + IQM_UBYTE = 1, + IQM_SHORT = 2, + IQM_USHORT = 3, + IQM_INT = 4, + IQM_UINT = 5, + IQM_HALF = 6, + IQM_FLOAT = 7, + IQM_DOUBLE = 8 +}; + +struct iqmtriangle +{ + unsigned int vertex[3]; +}; + +struct iqmadjacency +{ + unsigned int triangle[3]; +}; + +struct iqmjointv1 +{ + unsigned int name; + int parent; + float translate[3], rotate[3], scale[3]; +}; + +struct iqmjoint +{ + unsigned int name; + int parent; + float translate[3], rotate[4], scale[3]; +}; + +struct iqmposev1 +{ + int parent; + unsigned int mask; + float channeloffset[9]; + float channelscale[9]; +}; + +struct iqmpose +{ + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; +}; + +struct iqmanim +{ + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; +}; + +enum +{ + IQM_LOOP = 1<<0 +}; + +struct iqmvertexarray +{ + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; +}; + +struct iqmbounds +{ + float bbmin[3], bbmax[3]; + float xyradius, radius; +}; + +struct iqmextension +{ + unsigned int name; + unsigned int num_data, ofs_data; + unsigned int ofs_extensions; // pointer to next extension +}; + +#endif + diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 1826e8b5e..0061634a6 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -94,23 +94,9 @@ IRRImporter::~IRRImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - if (extension == "irr") { - return true; - } else if (extension == "xml" || checkSig) { - /* If CanRead() is called in order to check whether we - * support a specific file extension in general pIOHandler - * might be nullptr and it's our duty to return true here. - */ - if (nullptr == pIOHandler) { - return true; - } - static const char * const tokens[] = { "irr_scene" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - - return false; +bool IRRImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "irr_scene" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index d02509dfb..90b4d85af 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -85,26 +83,14 @@ IRRMeshImporter::~IRRMeshImporter() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { +bool IRRMeshImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { /* NOTE: A simple check for the file extension is not enough - * here. Irrmesh and irr are easy, but xml is too generic - * and could be collada, too. So we need to open the file and - * search for typical tokens. - */ - const std::string extension = GetExtension(pFile); - - if (extension == "irrmesh") - return true; - else if (extension == "xml" || checkSig) { - /* If CanRead() is called to check whether the loader - * supports a specific file extension in general we - * must return true here. - */ - if (!pIOHandler) return true; - static const char * const tokens[] = { "irrmesh" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - return false; + * here. Irrmesh and irr are easy, but xml is too generic + * and could be collada, too. So we need to open the file and + * search for typical tokens. + */ + static const char *tokens[] = { "irrmesh" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/LWO/LWOLoader.cpp b/code/AssetLib/LWO/LWOLoader.cpp index 696a22a8e..7410fb6cf 100644 --- a/code/AssetLib/LWO/LWOLoader.cpp +++ b/code/AssetLib/LWO/LWOLoader.cpp @@ -105,21 +105,13 @@ LWOImporter::~LWOImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool LWOImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(file); - if (extension == "lwo" || extension == "lxo") { - return true; - } - - // if check for extension is not enough, check for the magic tokens - if (!extension.length() || checkSig) { - uint32_t tokens[3]; - tokens[0] = AI_LWO_FOURCC_LWOB; - tokens[1] = AI_LWO_FOURCC_LWO2; - tokens[2] = AI_LWO_FOURCC_LXOB; - return CheckMagicToken(pIOHandler, file, tokens, 3, 8); - } - return false; +bool LWOImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { + AI_LWO_FOURCC_LWOB, + AI_LWO_FOURCC_LWO2, + AI_LWO_FOURCC_LXOB + }; + return CheckMagicToken(pIOHandler, file, tokens, AI_COUNT_OF(tokens), 8); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index 20932c863..951dbe180 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -147,20 +147,12 @@ LWSImporter::~LWSImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool LWSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - if (extension == "lws" || extension == "mot") { - return true; - } - - // if check for extension is not enough, check for the magic tokens LWSC and LWMO - if (!extension.length() || checkSig) { - uint32_t tokens[2]; - tokens[0] = AI_MAKE_MAGIC("LWSC"); - tokens[1] = AI_MAKE_MAGIC("LWMO"); - return CheckMagicToken(pIOHandler, pFile, tokens, 2); - } - return false; +bool LWSImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { + AI_MAKE_MAGIC("LWSC"), + AI_MAKE_MAGIC("LWMO") + }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 7d2b984c1..895b2bf70 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -111,34 +111,19 @@ M3DImporter::M3DImporter() : // ------------------------------------------------------------------------------------------------ // Returns true, if file is a binary or ASCII Model 3D file. -bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "m3d" - || extension == "a3d" - ) - return true; - else if (!extension.length() || checkSig) { - if (!pIOHandler) { - return true; - } - /* - * don't use CheckMagicToken because that checks with swapped bytes too, leading to false - * positives. This magic is not uint32_t, but char[4], so memcmp is the best way - - const char* tokens[] = {"3DMO", "3dmo"}; - return CheckMagicToken(pIOHandler,pFile,tokens,2,0,4); - */ - std::unique_ptr pStream(pIOHandler->Open(pFile, "rb")); - unsigned char data[4]; - if (!pStream || 4 != pStream->Read(data, 1, 4)) { - return false; - } - return !memcmp(data, "3DMO", 4) /* bin */ - || !memcmp(data, "3dmo", 4) /* ASCII */ - ; +bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + // don't use CheckMagicToken because that checks with swapped bytes too, leading to false + // positives. This magic is not uint32_t, but char[4], so memcmp is the best way + std::unique_ptr pStream(pIOHandler->Open(pFile, "rb")); + unsigned char data[4]; + if (4 != pStream->Read(data, 1, 4)) { + return false; } - return false; + return !memcmp(data, "3DMO", 4) /* bin */ +#ifdef M3D_ASCII + || !memcmp(data, "3dmo", 4) /* ASCII */ +#endif + ; } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MD2/MD2Loader.cpp b/code/AssetLib/MD2/MD2Loader.cpp index 7be31bce3..99ab3f521 100644 --- a/code/AssetLib/MD2/MD2Loader.cpp +++ b/code/AssetLib/MD2/MD2Loader.cpp @@ -107,19 +107,10 @@ MD2Importer::~MD2Importer() // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +bool MD2Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const { - const std::string extension = GetExtension(pFile); - if (extension == "md2") - return true; - - // if check for extension is not enough, check for the magic tokens - if (!extension.length() || checkSig) { - uint32_t tokens[1]; - tokens[0] = AI_MD2_MAGIC_NUMBER_LE; - return CheckMagicToken(pIOHandler,pFile,tokens,1); - } - return false; + static const uint32_t tokens[] = { AI_MD2_MAGIC_NUMBER_LE }; + return CheckMagicToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index 898b82626..6120bd8cb 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -349,18 +349,9 @@ MD3Importer::~MD3Importer() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool MD3Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - if (extension == "md3") - return true; - - // if check for extension is not enough, check for the magic tokens - if (!extension.length() || checkSig) { - uint32_t tokens[1]; - tokens[0] = AI_MD3_MAGIC_NUMBER_LE; - return CheckMagicToken(pIOHandler, pFile, tokens, 1); - } - return false; +bool MD3Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { AI_MD3_MAGIC_NUMBER_LE }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index e289620cd..2d5da4d92 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -100,20 +100,9 @@ MD5Importer::~MD5Importer() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool MD5Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "md5anim" || extension == "md5mesh" || extension == "md5camera") - return true; - else if (!extension.length() || checkSig) { - if (!pIOHandler) { - return true; - } - static const char * const tokens[] = { "MD5Version" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - - return false; +bool MD5Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "MD5Version" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MDC/MDCLoader.cpp b/code/AssetLib/MDC/MDCLoader.cpp index 659e0cfb7..7810800de 100644 --- a/code/AssetLib/MDC/MDCLoader.cpp +++ b/code/AssetLib/MDC/MDCLoader.cpp @@ -111,19 +111,9 @@ MDCImporter::~MDCImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool MDCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - if (extension == "mdc") { - return true; - } - - // if check for extension is not enough, check for the magic tokens - if (!extension.length() || checkSig) { - uint32_t tokens[1]; - tokens[0] = AI_MDC_MAGIC_NUMBER_LE; - return CheckMagicToken(pIOHandler, pFile, tokens, 1); - } - return false; +bool MDCImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { AI_MDC_MAGIC_NUMBER_LE }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index a7eb218d3..1e90c8e71 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -104,23 +104,18 @@ MDLImporter::~MDLImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool MDLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - // if check for extension is not enough, check for the magic tokens - if (extension == "mdl" || !extension.length() || checkSig) { - uint32_t tokens[8]; - tokens[0] = AI_MDL_MAGIC_NUMBER_LE_HL2a; - tokens[1] = AI_MDL_MAGIC_NUMBER_LE_HL2b; - tokens[2] = AI_MDL_MAGIC_NUMBER_LE_GS7; - tokens[3] = AI_MDL_MAGIC_NUMBER_LE_GS5b; - tokens[4] = AI_MDL_MAGIC_NUMBER_LE_GS5a; - tokens[5] = AI_MDL_MAGIC_NUMBER_LE_GS4; - tokens[6] = AI_MDL_MAGIC_NUMBER_LE_GS3; - tokens[7] = AI_MDL_MAGIC_NUMBER_LE; - return CheckMagicToken(pIOHandler, pFile, tokens, 8, 0); - } - return false; +bool MDLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const uint32_t tokens[] = { + AI_MDL_MAGIC_NUMBER_LE_HL2a, + AI_MDL_MAGIC_NUMBER_LE_HL2b, + AI_MDL_MAGIC_NUMBER_LE_GS7, + AI_MDL_MAGIC_NUMBER_LE_GS5b, + AI_MDL_MAGIC_NUMBER_LE_GS5a, + AI_MDL_MAGIC_NUMBER_LE_GS4, + AI_MDL_MAGIC_NUMBER_LE_GS3, + AI_MDL_MAGIC_NUMBER_LE + }; + return CheckMagicToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MMD/MMDImporter.cpp b/code/AssetLib/MMD/MMDImporter.cpp index b8c04491e..e0e68f7c5 100644 --- a/code/AssetLib/MMD/MMDImporter.cpp +++ b/code/AssetLib/MMD/MMDImporter.cpp @@ -89,14 +89,9 @@ MMDImporter::~MMDImporter() { // ------------------------------------------------------------------------------------------------ // Returns true, if file is an pmx file. bool MMDImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, - bool checkSig) const { - if (!checkSig) { - return SimpleExtensionCheck(pFile, "pmx"); - } else { - // Check file Header - static const char * const pTokens[] = { "PMX " }; - return SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 1); - } + bool /*checkSig*/) const { + static const char *tokens[] = { "PMX " }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MS3D/MS3DLoader.cpp b/code/AssetLib/MS3D/MS3DLoader.cpp index 0c35587f9..fc2ed042b 100644 --- a/code/AssetLib/MS3D/MS3DLoader.cpp +++ b/code/AssetLib/MS3D/MS3DLoader.cpp @@ -88,26 +88,12 @@ MS3DImporter::MS3DImporter() // Destructor, private as well MS3DImporter::~MS3DImporter() {} - // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +bool MS3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const { - // first call - simple extension check - const std::string extension = GetExtension(pFile); - if (extension == "ms3d") { - return true; - } - - // second call - check for magic identifiers - else if (!extension.length() || checkSig) { - if (!pIOHandler) { - return true; - } - static const char * const tokens[] = {"MS3D000000"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1); - } - return false; + static const char* tokens[] = { "MS3D000000" }; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/NDO/NDOLoader.cpp b/code/AssetLib/NDO/NDOLoader.cpp index 32e8cb5f8..37c543692 100644 --- a/code/AssetLib/NDO/NDOLoader.cpp +++ b/code/AssetLib/NDO/NDOLoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -82,19 +80,10 @@ NDOImporter::~NDOImporter() // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +bool NDOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const { - // check file extension - const std::string extension = GetExtension(pFile); - - if( extension == "ndo") - return true; - - if ((checkSig || !extension.length()) && pIOHandler) { - static const char * const tokens[] = {"nendo"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,5); - } - return false; + static const char* tokens[] = {"nendo"}; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens),5); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/NFF/NFFLoader.cpp b/code/AssetLib/NFF/NFFLoader.cpp index 94ad65cb6..48271d137 100644 --- a/code/AssetLib/NFF/NFFLoader.cpp +++ b/code/AssetLib/NFF/NFFLoader.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -83,7 +81,7 @@ NFFImporter::~NFFImporter() {} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool NFFImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { +bool NFFImporter::CanRead(const std::string & pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { return SimpleExtensionCheck(pFile, "nff", "enff"); } diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index 41ee74da5..640e79249 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -83,19 +83,10 @@ OFFImporter::~OFFImporter() // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const +bool OFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const { - const std::string extension = GetExtension(pFile); - - if (extension == "off") - return true; - else if (!extension.length() || checkSig) - { - if (!pIOHandler)return true; - static const char * const tokens[] = {"off"}; - return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1,3); - } - return false; + static const char* tokens[] = { "off" }; + return SearchFileHeaderForToken(pIOHandler,pFile,tokens,AI_COUNT_OF(tokens),3); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index 3e56e3137..fd062552f 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -87,16 +87,10 @@ ObjFileImporter::~ObjFileImporter() { } // ------------------------------------------------------------------------------------------------ -// Returns true, if file is an obj file. -bool ObjFileImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - if (!checkSig) { - //Check File Extension - return SimpleExtensionCheck(pFile, "obj"); - } else { - // Check file Header - static const char *pTokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " }; - return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, pTokens, 9, 200, false, true); - } +// Returns true if file is an obj file. +bool ObjFileImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "mtllib", "usemtl", "v ", "vt ", "vn ", "o ", "g ", "s ", "f " }; + return BaseImporter::SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens), 200, false, true); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Ogre/OgreImporter.cpp b/code/AssetLib/Ogre/OgreImporter.cpp index ceb80e91c..860aed727 100644 --- a/code/AssetLib/Ogre/OgreImporter.cpp +++ b/code/AssetLib/Ogre/OgreImporter.cpp @@ -73,14 +73,10 @@ void OgreImporter::SetupProperties(const Importer *pImp) { m_detectTextureTypeFromFilename = pImp->GetPropertyBool(AI_CONFIG_IMPORT_OGRE_TEXTURETYPE_FROM_FILENAME, false); } -bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool checkSig) const { - if (!checkSig) { - return EndsWith(pFile, ".mesh.xml", false) || EndsWith(pFile, ".mesh", false); - } - +bool OgreImporter::CanRead(const std::string &pFile, Assimp::IOSystem *pIOHandler, bool /*checkSig*/) const { if (EndsWith(pFile, ".mesh.xml", false)) { - static const char * const tokens[] = { "" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); + static const char *tokens[] = { "" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } /// @todo Read and validate first header chunk? diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 92cfe65b2..c8e47933d 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -290,16 +290,9 @@ OpenGEXImporter::~OpenGEXImporter() { } //------------------------------------------------------------------------------------------------ -bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool checkSig) const { - bool canRead(false); - if (!checkSig) { - canRead = SimpleExtensionCheck(file, "ogex"); - } else { - static const char * const token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; - canRead = SearchFileHeaderForToken(pIOHandler, file, token, 4); - } - - return canRead; +bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; + return SearchFileHeaderForToken(pIOHandler, file, tokens, AI_COUNT_OF(tokens)); } //------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Ply/PlyLoader.cpp b/code/AssetLib/Ply/PlyLoader.cpp index 4d422cea4..6cf1a1c74 100644 --- a/code/AssetLib/Ply/PlyLoader.cpp +++ b/code/AssetLib/Ply/PlyLoader.cpp @@ -100,24 +100,9 @@ PLYImporter::~PLYImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "ply") { - return true; - } - - if (!extension.length() || checkSig) { - if (!pIOHandler) { - return true; - } - static const char * const tokens[] = { - "ply" - }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - - return false; +bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "ply" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index 111da625b..db9a4a02f 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -156,12 +156,11 @@ Q3BSPFileImporter::~Q3BSPFileImporter() { } // ------------------------------------------------------------------------------------------------ -// Returns true, if the loader can read this. -bool Q3BSPFileImporter::CanRead(const std::string &rFile, IOSystem * /*pIOHandler*/, bool checkSig) const { +// Returns true if the loader can read this. +bool Q3BSPFileImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool checkSig) const { if (!checkSig) { - return SimpleExtensionCheck(rFile, "pk3", "bsp"); + return SimpleExtensionCheck(filename, "pk3", "bsp"); } - return false; } diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index c637ab4cb..c773bbfcd 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -84,18 +84,9 @@ Q3DImporter::~Q3DImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "q3s" || extension == "q3o") - return true; - else if (!extension.length() || checkSig) { - if (!pIOHandler) - return true; - static const char * const tokens[] = { "quick3Do", "quick3Ds" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2); - } - return false; +bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "quick3Do", "quick3Ds" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Raw/RawLoader.cpp b/code/AssetLib/Raw/RawLoader.cpp index d472430f1..bfd2e6d6e 100644 --- a/code/AssetLib/Raw/RawLoader.cpp +++ b/code/AssetLib/Raw/RawLoader.cpp @@ -84,8 +84,8 @@ RAWImporter::~RAWImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool RAWImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { - return SimpleExtensionCheck(pFile, "raw"); +bool RAWImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { + return SimpleExtensionCheck(filename, "raw"); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/SIB/SIBImporter.cpp b/code/AssetLib/SIB/SIBImporter.cpp index 2efb7f399..7b66afa44 100644 --- a/code/AssetLib/SIB/SIBImporter.cpp +++ b/code/AssetLib/SIB/SIBImporter.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2022, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -61,7 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef ASSIMP_USE_HUNTER #include #else -//# include "../contrib/ConvertUTF/ConvertUTF.h" #include "../contrib/utf8cpp/source/utf8.h" #endif #include @@ -217,8 +214,8 @@ SIBImporter::~SIBImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool SIBImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { - return SimpleExtensionCheck(pFile, "sib"); +bool SIBImporter::CanRead(const std::string &filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { + return SimpleExtensionCheck(filename, "sib"); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/SMD/SMDLoader.cpp b/code/AssetLib/SMD/SMDLoader.cpp index f1e4db660..46fd968c8 100644 --- a/code/AssetLib/SMD/SMDLoader.cpp +++ b/code/AssetLib/SMD/SMDLoader.cpp @@ -81,15 +81,15 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer -SMDImporter::SMDImporter() -: configFrameID() -, mBuffer() -, pScene( nullptr ) -, iFileSize( 0 ) -, iSmallestFrame( INT_MAX ) -, dLengthOfAnim( 0.0 ) -, bHasUVs(false ) -, iLineNumber((unsigned int)-1) { +SMDImporter::SMDImporter() : + configFrameID(), + mBuffer(), + pScene( nullptr ), + iFileSize( 0 ), + iSmallestFrame( INT_MAX ), + dLengthOfAnim( 0.0 ), + bHasUVs(false ), + iLineNumber((unsigned int)-1) { // empty } @@ -101,9 +101,8 @@ SMDImporter::~SMDImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool SMDImporter::CanRead( const std::string& pFile, IOSystem* /*pIOHandler*/, bool) const { - // fixme: auto format detection - return SimpleExtensionCheck(pFile,"smd","vta"); +bool SMDImporter::CanRead( const std::string& filename, IOSystem* /*pIOHandler*/, bool) const { + return SimpleExtensionCheck(filename, "smd", "vta"); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 67869c683..8de57f1ee 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -140,22 +140,9 @@ STLImporter::~STLImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - const std::string extension = GetExtension(pFile); - - if (extension == "stl") { - return true; - } - - if (!extension.length() || checkSig) { - if (!pIOHandler) { - return true; - } - static const char * const tokens[] = { "STL", "solid" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2); - } - - return false; +bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "STL", "solid" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Terragen/TerragenLoader.cpp b/code/AssetLib/Terragen/TerragenLoader.cpp index 0556702ff..c9c91db8f 100644 --- a/code/AssetLib/Terragen/TerragenLoader.cpp +++ b/code/AssetLib/Terragen/TerragenLoader.cpp @@ -81,27 +81,9 @@ TerragenImporter::~TerragenImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool TerragenImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - // check file extension - std::string extension = GetExtension(pFile); - - if (extension == "ter") - return true; - - if (!extension.length() || checkSig) { - /* If CanRead() is called in order to check whether we - * support a specific file extension in general pIOHandler - * might be nullptr and it's our duty to return true here. - */ - if (!pIOHandler) { - return true; - } - - static const char * const tokens[] = { "terragen" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); - } - - return false; +bool TerragenImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "terragen" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Unreal/UnrealLoader.cpp b/code/AssetLib/Unreal/UnrealLoader.cpp index b0b326b54..661952b3d 100644 --- a/code/AssetLib/Unreal/UnrealLoader.cpp +++ b/code/AssetLib/Unreal/UnrealLoader.cpp @@ -74,7 +74,7 @@ namespace Unreal { 3 = Masked two-sided 4 = Modulation blended two-sided 8 = Placeholder triangle for weapon positioning (invisible) - */ +*/ enum MeshFlags { MF_NORMAL_OS = 0, MF_NORMAL_TS = 1, @@ -168,16 +168,20 @@ static const aiImporterDesc desc = { // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer UnrealImporter::UnrealImporter() : - mConfigFrameID(0), mConfigHandleFlags(true) {} + mConfigFrameID(0), mConfigHandleFlags(true) { + // empty +} // ------------------------------------------------------------------------------------------------ // Destructor, private as well -UnrealImporter::~UnrealImporter() {} +UnrealImporter::~UnrealImporter() { + // empty +} // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool UnrealImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { - return SimpleExtensionCheck(pFile, "3d", "uc"); +bool UnrealImporter::CanRead(const std::string & filename, IOSystem * /*pIOHandler*/, bool /*checkSig*/) const { + return SimpleExtensionCheck(filename, "3d", "uc"); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index 2c0e05ece..d23eb576c 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -88,17 +88,9 @@ XFileImporter::~XFileImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { - std::string extension = GetExtension(pFile); - if(extension == "x") { - return true; - } - if (!extension.length() || checkSig) { - uint32_t token[1]; - token[0] = AI_MAKE_MAGIC("xof "); - return CheckMagicToken(pIOHandler,pFile,token,1,0); - } - return false; +bool XFileImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool /*checkSig*/) const { + static const uint32_t token[] = { AI_MAKE_MAGIC("xof ") }; + return CheckMagicToken(pIOHandler,pFile,token,AI_COUNT_OF(token)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 4cec756e3..b003f852a 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -104,25 +104,9 @@ XGLImporter::~XGLImporter() { // ------------------------------------------------------------------------------------------------ // Returns whether the class can handle the format of the given file. -bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const { - /* NOTE: A simple check for the file extension is not enough - * here. XGL and ZGL are ok, but xml is too generic - * and might be collada as well. So open the file and - * look for typical signal tokens. - */ - const std::string extension = GetExtension(pFile); - - if (extension == "xgl" || extension == "zgl") { - return true; - } - - if (extension == "xml" || checkSig) { - ai_assert(pIOHandler != nullptr); - static const char * const tokens[] = { "", "", "" }; - return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 3); - } - - return false; +bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /*checkSig*/) const { + static const char *tokens[] = { "", "", "" }; + return SearchFileHeaderForToken(pIOHandler, pFile, tokens, AI_COUNT_OF(tokens)); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/glTF/glTFImporter.cpp b/code/AssetLib/glTF/glTFImporter.cpp index 72103993d..81db12eba 100644 --- a/code/AssetLib/glTF/glTFImporter.cpp +++ b/code/AssetLib/glTF/glTFImporter.cpp @@ -93,24 +93,14 @@ const aiImporterDesc *glTFImporter::GetInfo() const { } bool glTFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool /* checkSig */) const { - const std::string &extension = GetExtension(pFile); - - if (extension != "gltf" && extension != "glb") { + glTF::Asset asset(pIOHandler); + try { + asset.Load(pFile, GetExtension(pFile) == "glb"); + std::string version = asset.asset.version; + return !version.empty() && version[0] == '1'; + } catch (...) { return false; } - - if (pIOHandler) { - glTF::Asset asset(pIOHandler); - try { - asset.Load(pFile, extension == "glb"); - std::string version = asset.asset.version; - return !version.empty() && version[0] == '1'; - } catch (...) { - return false; - } - } - - return false; } inline void SetMaterialColorProperty(std::vector &embeddedTexIdxs, Asset & /*r*/, glTF::TexProperty prop, aiMaterial *mat, diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index bf8690ad2..1f4cafdda 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -98,7 +98,7 @@ static const aiImporterDesc desc = { glTF2Importer::glTF2Importer() : BaseImporter(), meshOffsets(), - embeddedTexIdxs(), + mEmbeddedTexIdxs(), mScene(nullptr) { // empty } @@ -111,21 +111,21 @@ const aiImporterDesc *glTF2Importer::GetInfo() const { return &desc; } -bool glTF2Importer::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig ) const { - const std::string &extension = GetExtension(pFile); +bool glTF2Importer::CanRead(const std::string &filename, IOSystem *pIOHandler, bool checkSig ) const { + const std::string extension = GetExtension(filename); + if (!checkSig && (extension != "gltf") && (extension != "glb")) { + return false; + } - if (!checkSig && (extension != "gltf") && (extension != "glb")) - return false; - - if (pIOHandler) { - glTF2::Asset asset(pIOHandler); - return asset.CanRead(pFile, extension == "glb"); - } + if (pIOHandler) { + glTF2::Asset asset(pIOHandler); + return asset.CanRead(filename, extension == "glb"); + } return false; } -static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) { +static inline aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) { switch (gltfWrapMode) { case SamplerWrap::Mirrored_Repeat: return aiTextureMapMode_Mirror; @@ -140,21 +140,21 @@ static aiTextureMapMode ConvertWrappingMode(SamplerWrap gltfWrapMode) { } } -inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat, +static inline void SetMaterialColorProperty(Asset & /*r*/, vec4 &prop, aiMaterial *mat, const char *pKey, unsigned int type, unsigned int idx) { aiColor4D col; CopyValue(prop, col); mat->AddProperty(&col, 1, pKey, type, idx); } -inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat, +static inline void SetMaterialColorProperty(Asset & /*r*/, vec3 &prop, aiMaterial *mat, const char *pKey, unsigned int type, unsigned int idx) { aiColor4D col; glTFCommon::CopyValue(prop, col); mat->AddProperty(&col, 1, pKey, type, idx); } -inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset & /*r*/, +static void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset & /*r*/, glTF2::TextureInfo prop, aiMaterial *mat, aiTextureType texType, unsigned int texSlot = 0) { if (prop.texture && prop.texture->source) { @@ -371,10 +371,10 @@ void glTF2Importer::ImportMaterials(Asset &r) { mScene->mNumMaterials = numImportedMaterials + 1; mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials]; std::fill(mScene->mMaterials, mScene->mMaterials + mScene->mNumMaterials, nullptr); - mScene->mMaterials[numImportedMaterials] = ImportMaterial(embeddedTexIdxs, r, defaultMaterial); + mScene->mMaterials[numImportedMaterials] = ImportMaterial(mEmbeddedTexIdxs, r, defaultMaterial); for (unsigned int i = 0; i < numImportedMaterials; ++i) { - mScene->mMaterials[i] = ImportMaterial(embeddedTexIdxs, r, r.materials[i]); + mScene->mMaterials[i] = ImportMaterial(mEmbeddedTexIdxs, r, r.materials[i]); } } @@ -802,8 +802,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { if (actualNumFaces < nFaces) { ASSIMP_LOG_WARN("Some faces had out-of-range indices. Those faces were dropped."); } - if (actualNumFaces == 0) - { + if (actualNumFaces == 0) { throw DeadlyImportError("Mesh \"", aim->mName.C_Str(), "\" has no faces"); } aim->mNumFaces = actualNumFaces; @@ -843,7 +842,6 @@ void glTF2Importer::ImportCameras(glTF2::Asset &r) { aicam->mLookAt = aiVector3D(0.f, 0.f, -1.f); if (cam.type == Camera::Perspective) { - aicam->mAspect = cam.cameraProperties.perspective.aspectRatio; aicam->mHorizontalFOV = cam.cameraProperties.perspective.yfov * ((aicam->mAspect == 0.f) ? 1.f : aicam->mAspect); aicam->mClipPlaneFar = cam.cameraProperties.perspective.zfar; @@ -862,8 +860,9 @@ void glTF2Importer::ImportCameras(glTF2::Asset &r) { } void glTF2Importer::ImportLights(glTF2::Asset &r) { - if (!r.lights.Size()) + if (!r.lights.Size()) { return; + } const unsigned int numLights = r.lights.Size(); ASSIMP_LOG_DEBUG("Importing ", numLights, " lights"); @@ -1125,8 +1124,8 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & bone->mNumWeights = static_cast(weights.size()); if (bone->mNumWeights > 0) { - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight)); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + memcpy(bone->mWeights, weights.data(), bone->mNumWeights * sizeof(aiVertexWeight)); } else { // Assimp expects all bones to have at least 1 weight. bone->mWeights = new aiVertexWeight[1]; @@ -1167,8 +1166,7 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & if (!ainode->mMetaData) { ainode->mMetaData = aiMetadata::Alloc(1); ainode->mMetaData->Set(0, "PBR_LightRange", node.light->range.value); - } - else { + } else { ainode->mMetaData->Add("PBR_LightRange", node.light->range.value); } } @@ -1509,16 +1507,20 @@ void glTF2Importer::ImportAnimations(glTF2::Asset &r) { } } -void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { - embeddedTexIdxs.resize(r.images.Size(), -1); - - int numEmbeddedTexs = 0; +static unsigned int countEmbeddedTextures(glTF2::Asset &r) { + unsigned int numEmbeddedTexs = 0; for (size_t i = 0; i < r.images.Size(); ++i) { if (r.images[i].HasData()) { numEmbeddedTexs += 1; } } + return numEmbeddedTexs; +} + +void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { + mEmbeddedTexIdxs.resize(r.images.Size(), -1); + const unsigned int numEmbeddedTexs = countEmbeddedTextures(r); if (numEmbeddedTexs == 0) { return; } @@ -1536,7 +1538,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { } int idx = mScene->mNumTextures++; - embeddedTexIdxs[i] = idx; + mEmbeddedTexIdxs[i] = idx; aiTexture *tex = mScene->mTextures[idx] = new aiTexture(); @@ -1597,7 +1599,7 @@ void glTF2Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IO // clean all member arrays meshOffsets.clear(); - embeddedTexIdxs.clear(); + mEmbeddedTexIdxs.clear(); this->mScene = pScene; diff --git a/code/AssetLib/glTF2/glTF2Importer.h b/code/AssetLib/glTF2/glTF2Importer.h index 9a05c4dba..831bcd7d2 100644 --- a/code/AssetLib/glTF2/glTF2Importer.h +++ b/code/AssetLib/glTF2/glTF2Importer.h @@ -79,7 +79,7 @@ private: private: std::vector meshOffsets; - std::vector embeddedTexIdxs; + std::vector mEmbeddedTexIdxs; aiScene *mScene; /// An instance of rapidjson::IRemoteSchemaDocumentProvider diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 7311129e8..7ce933585 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -376,6 +376,12 @@ ADD_ASSIMP_IMPORTER( IRRMESH AssetLib/Irr/IRRShared.h ) +ADD_ASSIMP_IMPORTER( IQM + AssetLib/IQM/IQMImporter.cpp + AssetLib/IQM/iqm.h + AssetLib/IQM/IQMImporter.h +) + ADD_ASSIMP_IMPORTER( IRR AssetLib/Irr/IRRLoader.cpp AssetLib/Irr/IRRLoader.h diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 5e186bb19..383300ef1 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -156,8 +156,8 @@ void BaseImporter::GetExtensionList(std::set &extensions) { // ------------------------------------------------------------------------------------------------ /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler, const std::string &pFile, - const char * const *tokens, - unsigned int numTokens, + const char **tokens, + std::size_t numTokens, unsigned int searchBytes /* = 200 */, bool tokensSol /* false */, bool noAlphaBeforeTokens /* false */) { @@ -268,10 +268,11 @@ std::string BaseImporter::GetExtension(const std::string &file) { return ret; } + // ------------------------------------------------------------------------------------------------ // Check for magic bytes at the beginning of the file. /* static */ bool BaseImporter::CheckMagicToken(IOSystem *pIOHandler, const std::string &pFile, - const void *_magic, unsigned int num, unsigned int offset, unsigned int size) { + const void *_magic, std::size_t num, unsigned int offset, unsigned int size) { ai_assert(size <= 16); ai_assert(_magic); diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index f88632f0e..5cb32d38f 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -388,15 +388,16 @@ void DefaultLogger::WriteToStreams(const char *message, ErrorSeverity ErrorSev) ai_assert(nullptr != message); // Check whether this is a repeated message - if (!::strncmp(message, lastMsg, lastLen - 1)) { + auto thisLen = ::strlen(message); + if (thisLen == lastLen - 1 && !::strncmp(message, lastMsg, lastLen - 1)) { if (!noRepeatMsg) { noRepeatMsg = true; message = "Skipping one or more lines with the same contents\n"; - } else - return; + } + return; } else { // append a new-line character to the message to be printed - lastLen = ::strlen(message); + lastLen = thisLen; ::memcpy(lastMsg, message, lastLen + 1); ::strcat(lastMsg + lastLen, "\n"); diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index 1d67c9def..52b6097e7 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -617,29 +617,71 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { profiler->BeginRegion("total"); } - // Find an worker class which can handle the file - BaseImporter* imp = nullptr; + // Find an worker class which can handle the file extension. + // Multiple importers may be able to handle the same extension (.xml!); gather them all. SetPropertyInteger("importerIndex", -1); - for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + struct ImporterAndIndex { + BaseImporter * importer; + unsigned int index; + }; + std::vector possibleImporters; + for (unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + + // Every importer has a list of supported extensions. + std::set extensions; + pimpl->mImporter[a]->GetExtensionList(extensions); + + // CAUTION: Do not just search for the extension! + // GetExtension() returns the part after the *last* dot, but some extensions have dots + // inside them, e.g. ogre.mesh.xml. Compare the entire end of the string. + for (std::set::const_iterator it = extensions.cbegin(); it != extensions.cend(); ++it) { + + // Yay for C++<20 not having std::string::ends_with() + std::string extension = "." + *it; + if (extension.length() <= pFile.length()) { + // Possible optimization: Fetch the lowercase filename! + if (0 == ASSIMP_stricmp(pFile.c_str() + pFile.length() - extension.length(), extension.c_str())) { + ImporterAndIndex candidate = { pimpl->mImporter[a], a }; + possibleImporters.push_back(candidate); + break; + } + } - if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, false)) { - imp = pimpl->mImporter[a]; - SetPropertyInteger("importerIndex", a); - break; } + + } + + // If just one importer supports this extension, pick it and close the case. + BaseImporter* imp = nullptr; + if (1 == possibleImporters.size()) { + imp = possibleImporters[0].importer; + SetPropertyInteger("importerIndex", possibleImporters[0].index); + } + // If multiple importers claim this file extension, ask them to look at the actual file data to decide. + // This can happen e.g. with XML (COLLADA vs. Irrlicht). + else { + for (std::vector::const_iterator it = possibleImporters.begin(); it < possibleImporters.end(); ++it) { + BaseImporter & importer = *it->importer; + + ASSIMP_LOG_INFO("Found a possible importer: " + std::string(importer.GetInfo()->mName) + "; trying signature-based detection"); + if (importer.CanRead( pFile, pimpl->mIOHandler, true)) { + imp = &importer; + SetPropertyInteger("importerIndex", it->index); + break; + } + + } + } if (!imp) { // not so bad yet ... try format auto detection. - const std::string::size_type s = pFile.find_last_of('.'); - if (s != std::string::npos) { - ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); - for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { - if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { - imp = pimpl->mImporter[a]; - SetPropertyInteger("importerIndex", a); - break; - } + ASSIMP_LOG_INFO("File extension not known, trying signature-based detection"); + for( unsigned int a = 0; a < pimpl->mImporter.size(); a++) { + if( pimpl->mImporter[a]->CanRead( pFile, pimpl->mIOHandler, true)) { + imp = pimpl->mImporter[a]; + SetPropertyInteger("importerIndex", a); + break; } } // Put a proper error message if no suitable importer was found diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index fd4a7cd6e..78c02d96d 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -202,6 +202,9 @@ corresponding preprocessor flag to selectively disable formats. #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER #include "AssetLib/M3D/M3DImporter.h" #endif +#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER +#include "AssetLib/IQM/IQMImporter.h" +#endif namespace Assimp { @@ -370,6 +373,9 @@ void GetImporterInstanceList(std::vector &out) { #endif #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER out.push_back(new MMDImporter()); +#endif +#ifndef ASSIMP_BUILD_NO_IQM_IMPORTER + out.push_back(new IQMImporter()); #endif //#ifndef ASSIMP_BUILD_NO_STEP_IMPORTER // out.push_back(new StepFile::StepFileImporter()); diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 9351c69da..5a3c764d4 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -95,20 +95,15 @@ public: // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * - * The implementation should be as quick as possible. A check for - * the file extension is enough. If no suitable loader is found with - * this strategy, CanRead() is called again, the 'checkSig' parameter - * set to true this time. Now the implementation is expected to - * perform a full check of the file structure, possibly searching the - * first bytes of the file for magic identifiers or keywords. + * The implementation is expected to perform a full check of the file + * structure, possibly searching the first bytes of the file for magic + * identifiers or keywords. * * @param pFile Path and file name of the file to be examined. * @param pIOHandler The IO handler to use for accessing any file. - * @param checkSig Set to true if this method is called a second time. - * This time, the implementation may take more time to examine the - * contents of the file to be loaded for magic bytes, keywords, etc - * to be able to load files with unknown/not existent file extensions. - * @return true if the class can read this file, false if not. + * @param checkSig Legacy; do not use. + * @return true if the class can read this file, false if not or if + * unsure. */ virtual bool CanRead( const std::string &pFile, @@ -259,8 +254,8 @@ public: // static utilities static bool SearchFileHeaderForToken( IOSystem *pIOSystem, const std::string &file, - const char * const *tokens, - unsigned int numTokens, + const char **tokens, + std::size_t numTokens, unsigned int searchBytes = 200, bool tokensSol = false, bool noAlphaBeforeTokens = false); @@ -305,7 +300,7 @@ public: // static utilities IOSystem *pIOHandler, const std::string &pFile, const void *magic, - unsigned int num, + std::size_t num, unsigned int offset = 0, unsigned int size = 4); diff --git a/include/assimp/defs.h b/include/assimp/defs.h index 232125aa9..9c4422584 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -331,4 +331,6 @@ static const ai_real ai_epsilon = (ai_real)0.00001; #define AI_DEBUG_INVALIDATE_PTR(x) #endif +#define AI_COUNT_OF(X) (sizeof(X) / sizeof((X)[0])) + #endif // !! AI_DEFINES_H_INC diff --git a/test/models/IQM/Body.jpg b/test/models/IQM/Body.jpg new file mode 100644 index 000000000..b394896d4 Binary files /dev/null and b/test/models/IQM/Body.jpg differ diff --git a/test/models/IQM/Head.jpg b/test/models/IQM/Head.jpg new file mode 100644 index 000000000..bfe8b9838 Binary files /dev/null and b/test/models/IQM/Head.jpg differ diff --git a/test/models/IQM/mrfixit.iqm b/test/models/IQM/mrfixit.iqm new file mode 100644 index 000000000..bb94f5afe Binary files /dev/null and b/test/models/IQM/mrfixit.iqm differ